{"thread":{"id":"29315","subject":"[PATCH 0/6] The move to sequencer.c","startedAt":"2012-01-08T12:27:27Z","lastAt":"2012-01-11T18:40:37Z","messageCount":64,"participants":["Ramkumar Ramachandra","Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"182098","messageId":"1326025653-11922-1-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":null,"subject":"[PATCH 0/6] The move to sequencer.c","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:27Z","receivedAt":"2012-01-08T12:27:27Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nI've tried a slightly different approach: the objective of the patches\nseem to be much clearer this time.\n\n[1/6] revert: move replay_action, replay_subcommand to header\n[2/6] revert: decouple sequencer actions from builtin commands\n[3/6] revert: don't let revert continue a cherry-pick\n[4/6] revert: allow mixing \"pick\" and \"revert\" actions\n[5/6] revert: report fine-grained error messages from insn parser\n[6/6] sequencer: factor code out of revert builtin\n\n\n[1/6] first moves out a couple of data structures to the header\n\n[2/6] decouples \"actions\" from a \"commands\" completely.  Although this\nsort of separation might not be necessary at this stage (because we\njust have a couple of actions that directly correspond to builtin\ncommands), I think it makes [4/6] much easier to read.\n\n[3/6] mainly exists so that [4/6] doesn't allow 'git revert\n--continue' to continue a 'git cherry-pick' and viceversa.  Note that\na 'git revert --continue' can execute an instruction sheet with \"pick\"\ninstructions and viceversa after [4/6].\n\n[4/6] should be very clear this time: do_pick_commit() takes an extra\nargument \"action\", and checks that instead of the \"opts->command\"\neverywhere.  The parser is also updated to parse into (commit, action)\npairs.\n\n[5/6] is fairly straightforward.\n\n[6/6] makes the final move.  This is something I've been pushing for\nquite some time: exciting things like 'git continue' will follow this.\n\nCheers!\n\n builtin/revert.c                |  959 +-------------------------------------\n sequencer.c                     |  987 ++++++++++++++++++++++++++++++++++++++-\n sequencer.h                     |   49 ++\n t/t3510-cherry-pick-sequence.sh |   57 ++-\n 4 files changed, 1088 insertions(+), 964 deletions(-)\n\n-- \n1.7.8.2\n"},{"id":"182099","messageId":"1326025653-11922-2-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 1/6] revert: move replay_action, replay_subcommand to header","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:28Z","receivedAt":"2012-01-08T12:27:28Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Our plan to build a sequencer involves leaving the revert builtin with\njust argument parsing work.  Since the enums replay_action and\nreplay_subcommand have nothing to do with this argument parsing, move\nthem to sequencer.h in advance.\n\n\"REVERT\" and \"CHERRY_PICK\" are unsuitable variable names for exposing\npublicly, as their purpose is unclear in the global context: rename\nthem to \"REPLAY_REVERT\" and \"REPLAY_PICK\" respectively.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   42 +++++++++++++++++-------------------------\n sequencer.h      |   12 ++++++++++++\n 2 files changed, 29 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 0d8020c..c804045 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -39,14 +39,6 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-enum replay_action { REVERT, CHERRY_PICK };\n-enum replay_subcommand {\n-\tREPLAY_NONE,\n-\tREPLAY_REMOVE_STATE,\n-\tREPLAY_CONTINUE,\n-\tREPLAY_ROLLBACK\n-};\n-\n struct replay_opts {\n \tenum replay_action action;\n \tenum replay_subcommand subcommand;\n@@ -74,14 +66,14 @@ struct replay_opts {\n \n static const char *action_name(const struct replay_opts *opts)\n {\n-\treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n+\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n static char *get_encoding(const char *message);\n \n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n-\treturn opts->action == REVERT ? revert_usage : cherry_pick_usage;\n+\treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n static int option_parse_x(const struct option *opt,\n@@ -160,7 +152,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tOPT_END(),\n \t};\n \n-\tif (opts->action == CHERRY_PICK) {\n+\tif (opts->action == REPLAY_PICK) {\n \t\tstruct option cp_extra[] = {\n \t\t\tOPT_BOOLEAN('x', NULL, &opts->record_origin, \"append commit name\"),\n \t\t\tOPT_BOOLEAN(0, \"ff\", &opts->allow_ff, \"allow fast-forward\"),\n@@ -374,7 +366,7 @@ static int error_dirty_index(struct replay_opts *opts)\n \t\treturn error_resolve_conflict(action_name(opts));\n \n \t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->action == CHERRY_PICK)\n+\tif (opts->action == REPLAY_PICK)\n \t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n \telse\n \t\terror(_(\"Your local changes would be overwritten by revert.\"));\n@@ -553,7 +545,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->action == REVERT) {\n+\tif (opts->action == REPLAY_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -594,7 +586,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -618,13 +610,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->action == REVERT\n+\t\terror(opts->action == REPLAY_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -644,7 +636,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n static void prepare_revs(struct replay_opts *opts)\n {\n-\tif (opts->action != REVERT)\n+\tif (opts->action != REPLAY_REVERT)\n \t\topts->revs->reverse ^= 1;\n \n \tif (prepare_revision_walk(opts->revs))\n@@ -701,7 +693,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n {\n \tstruct commit_list *cur = NULL;\n \tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->action == REVERT ? \"revert\" : \"pick\";\n+\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n \tconst char *subject;\n \tint subject_len;\n \n@@ -722,10 +714,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \tint saved, status, padding;\n \n \tif (!prefixcmp(bol, \"pick\")) {\n-\t\taction = CHERRY_PICK;\n+\t\taction = REPLAY_PICK;\n \t\tbol += strlen(\"pick\");\n \t} else if (!prefixcmp(bol, \"revert\")) {\n-\t\taction = REVERT;\n+\t\taction = REPLAY_REVERT;\n \t\tbol += strlen(\"revert\");\n \t} else\n \t\treturn NULL;\n@@ -748,7 +740,7 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \t */\n \tif (action != opts->action) {\n \t\tconst char *action_str;\n-\t\taction_str = action == REVERT ? \"revert\" : \"cherry-pick\";\n+\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n \t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n \t\treturn NULL;\n \t}\n@@ -1124,7 +1116,7 @@ static int pick_revisions(struct replay_opts *opts)\n \tif (create_seq_dir() < 0)\n \t\treturn -1;\n \tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REVERT)\n+\t\tif (opts->action == REPLAY_REVERT)\n \t\t\treturn error(_(\"Can't revert as initial commit\"));\n \t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t}\n@@ -1141,7 +1133,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n \t\topts.edit = 1;\n-\topts.action = REVERT;\n+\topts.action = REPLAY_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n@@ -1156,7 +1148,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.action = CHERRY_PICK;\n+\topts.action = REPLAY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\ndiff --git a/sequencer.h b/sequencer.h\nindex 2d4528f..1d9fcec 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -6,6 +6,18 @@\n #define SEQ_TODO_FILE\t\"sequencer/todo\"\n #define SEQ_OPTS_FILE\t\"sequencer/opts\"\n \n+enum replay_action {\n+\tREPLAY_REVERT,\n+\tREPLAY_PICK\n+};\n+\n+enum replay_subcommand {\n+\tREPLAY_NONE,\n+\tREPLAY_REMOVE_STATE,\n+\tREPLAY_CONTINUE,\n+\tREPLAY_ROLLBACK\n+};\n+\n /* Removes SEQ_DIR. */\n extern void remove_sequencer_state(void);\n \n-- \n1.7.8.2\n"},{"id":"182103","messageId":"1326025653-11922-3-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/6] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:29Z","receivedAt":"2012-01-08T12:27:29Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Currently, 'git cherry-pick' fills up the '.git/sequencer/todo'\ninstruction sheet with \"pick\" actions, while 'git revert' fills it up\nwith \"revert\" actions.  Inspired by the way 'rebase -i' works, we\nwould like to permit mixing arbitrary actions in the same instruction\nsheet.  To do this, we first have to decouple the notion of an action\nin the instruction sheet from builtin commands.  So, while a future\ninstruction sheet would look like:\n\n  pick next~4\n  action3 b74fea\n  revert rr/moo^2~34\n\nthe actions \"pick\", \"action3\" and \"revert\" need not necessarily\ncorrespond to the specific builtins.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   62 +++++++++++++++++++++++++++++------------------------\n sequencer.h      |    5 ++++\n 2 files changed, 39 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex c804045..3ac6da0 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -40,7 +40,7 @@ static const char * const cherry_pick_usage[] = {\n };\n \n struct replay_opts {\n-\tenum replay_action action;\n+\tenum replay_command command;\n \tenum replay_subcommand subcommand;\n \n \t/* Boolean options */\n@@ -64,16 +64,21 @@ struct replay_opts {\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n-static const char *action_name(const struct replay_opts *opts)\n+static const char *command_name(struct replay_opts *opts)\n+{\n+\treturn opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\";\n+}\n+\n+static const char *action_name(enum replay_action action)\n {\n-\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n+\treturn action == REPLAY_REVERT ? \"revert\" : \"pick\";\n }\n \n static char *get_encoding(const char *message);\n \n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n-\treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n+\treturn opts->command == REPLAY_CMD_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n static int option_parse_x(const struct option *opt,\n@@ -130,7 +135,7 @@ static void verify_opt_mutually_compatible(const char *me, ...)\n static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n {\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n-\tconst char *me = action_name(opts);\n+\tconst char *me = command_name(opts);\n \tint remove_state = 0;\n \tint contin = 0;\n \tint rollback = 0;\n@@ -152,7 +157,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tOPT_END(),\n \t};\n \n-\tif (opts->action == REPLAY_PICK) {\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK) {\n \t\tstruct option cp_extra[] = {\n \t\t\tOPT_BOOLEAN('x', NULL, &opts->record_origin, \"append commit name\"),\n \t\t\tOPT_BOOLEAN(0, \"ff\", &opts->allow_ff, \"allow fast-forward\"),\n@@ -363,10 +368,10 @@ static struct tree *empty_tree(void)\n static int error_dirty_index(struct replay_opts *opts)\n {\n \tif (read_cache_unmerged())\n-\t\treturn error_resolve_conflict(action_name(opts));\n+\t\treturn error_resolve_conflict(command_name(opts));\n \n \t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->action == REPLAY_PICK)\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK)\n \t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n \telse\n \t\terror(_(\"Your local changes would be overwritten by revert.\"));\n@@ -422,7 +427,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t    (write_cache(index_fd, active_cache, active_nr) ||\n \t     commit_locked_index(&index_lock)))\n \t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n-\t\tdie(_(\"%s: Unable to write new index file\"), action_name(opts));\n+\t\tdie(_(\"%s: Unable to write new index file\"), command_name(opts));\n \trollback_lock_file(&index_lock);\n \n \tif (!clean) {\n@@ -530,7 +535,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t/* TRANSLATORS: The first %s will be \"revert\" or\n \t\t   \"cherry-pick\", the second %s a SHA1 */\n \t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n-\t\t\taction_name(opts), sha1_to_hex(parent->object.sha1));\n+\t\t\tcommand_name(opts), sha1_to_hex(parent->object.sha1));\n \n \tif (get_message(commit, &msg) != 0)\n \t\treturn error(_(\"Cannot get commit message for %s\"),\n@@ -545,7 +550,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->action == REPLAY_REVERT) {\n+\tif (opts->command == REPLAY_CMD_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -586,7 +591,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->command == REPLAY_CMD_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -610,13 +615,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (opts->command == REPLAY_CMD_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->action == REPLAY_REVERT\n+\t\terror(opts->command == REPLAY_CMD_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -636,7 +641,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n static void prepare_revs(struct replay_opts *opts)\n {\n-\tif (opts->action != REPLAY_REVERT)\n+\tif (opts->command != REPLAY_CMD_REVERT)\n \t\topts->revs->reverse ^= 1;\n \n \tif (prepare_revision_walk(opts->revs))\n@@ -651,12 +656,13 @@ static void read_and_refresh_cache(struct replay_opts *opts)\n \tstatic struct lock_file index_lock;\n \tint index_fd = hold_locked_index(&index_lock, 0);\n \tif (read_index_preload(&the_index, NULL) < 0)\n-\t\tdie(_(\"git %s: failed to read the index\"), action_name(opts));\n+\t\tdie(_(\"git %s: failed to read the index\"), command_name(opts));\n \trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n \tif (the_index.cache_changed) {\n \t\tif (write_index(&the_index, index_fd) ||\n \t\t    commit_locked_index(&index_lock))\n-\t\t\tdie(_(\"git %s: failed to refresh the index\"), action_name(opts));\n+\t\t\tdie(_(\"git %s: failed to refresh the index\"),\n+\t\t\t\tcommand_name(opts));\n \t}\n \trollback_lock_file(&index_lock);\n }\n@@ -693,7 +699,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n {\n \tstruct commit_list *cur = NULL;\n \tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n+\tconst char *action_str = opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"pick\";\n \tconst char *subject;\n \tint subject_len;\n \n@@ -738,10 +744,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \t * Verify that the action matches up with the one in\n \t * opts; we don't support arbitrary instructions\n \t */\n-\tif (action != opts->action) {\n-\t\tconst char *action_str;\n-\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n-\t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n+\tif ((action == REPLAY_PICK && opts->command == REPLAY_CMD_REVERT) ||\n+\t\t(action == REPLAY_REVERT && opts->command == REPLAY_CMD_CHERRY_PICK)) {\n+\t\terror(_(\"Cannot %s during a %s\"), action_name(action),\n+\t\t\tcommand_name(opts));\n \t\treturn NULL;\n \t}\n \n@@ -1003,7 +1009,7 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n \tstruct commit_list *cur;\n \tint res;\n \n-\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n \tif (opts->allow_ff)\n \t\tassert(!(opts->signoff || opts->no_commit ||\n \t\t\t\topts->record_origin || opts->edit));\n@@ -1058,7 +1064,7 @@ static int sequencer_continue(struct replay_opts *opts)\n \n static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n-\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n \treturn do_pick_commit(cmit, opts);\n }\n \n@@ -1116,7 +1122,7 @@ static int pick_revisions(struct replay_opts *opts)\n \tif (create_seq_dir() < 0)\n \t\treturn -1;\n \tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REPLAY_REVERT)\n+\t\tif (opts->command == REPLAY_CMD_REVERT)\n \t\t\treturn error(_(\"Can't revert as initial commit\"));\n \t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t}\n@@ -1133,7 +1139,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n \t\topts.edit = 1;\n-\topts.action = REPLAY_REVERT;\n+\topts.command = REPLAY_CMD_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n@@ -1148,7 +1154,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.action = REPLAY_PICK;\n+\topts.command = REPLAY_CMD_CHERRY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\ndiff --git a/sequencer.h b/sequencer.h\nindex 1d9fcec..07e0639 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -11,6 +11,11 @@ enum replay_action {\n \tREPLAY_PICK\n };\n \n+enum replay_command {\n+\tREPLAY_CMD_REVERT,\n+\tREPLAY_CMD_CHERRY_PICK\n+};\n+\n enum replay_subcommand {\n \tREPLAY_NONE,\n \tREPLAY_REMOVE_STATE,\n-- \n1.7.8.2\n"},{"id":"182100","messageId":"1326025653-11922-4-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 3/6] revert: don't let revert continue a cherry-pick","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:30Z","receivedAt":"2012-01-08T12:27:30Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"When we allow mixing \"revert\" and \"pick\" instructions in the same\nsheet in the next patch, the following workflow would be perfectly\nvalid:\n\n  $ git cherry-pick base..latercommit\n  [conflict occurs]\n  $ edit problematicfile\n  $ git add problematicfile\n  $ git revert --continue\n  [finishes successfully]\n\nThis is confusing to the operator, because the sequencer is an\nimplementation detail hidden behind the 'git cherry-pick' and 'git\nrevert' builtins.  So, disallow this workflow by keeping track of the\nbuiltin command executed (either \"revert\" or \"cherry-pick\") in\n'.git/sequencer/cmd'.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c                |   57 +++++++++++++++++++++++++++++++++++++++\n sequencer.h                     |    1 +\n t/t3510-cherry-pick-sequence.sh |   11 +++++++\n 3 files changed, 69 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 3ac6da0..52fa115 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -871,6 +871,50 @@ static int create_seq_dir(void)\n \treturn 0;\n }\n \n+static enum replay_command read_cmd(void)\n+{\n+\tconst char *cmd_file = git_path(SEQ_CMD_FILE);\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tenum replay_command res;\n+\tint fd;\n+\n+\tfd = open(cmd_file, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open %s\"), cmd_file);\n+\tif (strbuf_read(&buf, fd, 0) < 0) {\n+\t\tclose(fd);\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Could not read %s.\"), cmd_file);\n+\t}\n+\tclose(fd);\n+\n+\tif (!strcmp(buf.buf, \"revert\\n\"))\n+\t\tres = REPLAY_CMD_REVERT;\n+\telse if (!strcmp(buf.buf, \"cherry-pick\\n\"))\n+\t\tres = REPLAY_CMD_CHERRY_PICK;\n+\telse {\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Malformed command file: %s\"), cmd_file);\n+\t}\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n+static void save_cmd(struct replay_opts *opts)\n+{\n+\tconst char *cmd_file = git_path(SEQ_CMD_FILE);\n+\tstatic struct lock_file cmd_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&cmd_lock, cmd_file, LOCK_DIE_ON_ERROR);\n+\tstrbuf_addf(&buf, \"%s\\n\", command_name(opts));\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), cmd_file);\n+\tif (commit_lock_file(&cmd_lock) < 0)\n+\t\tdie(_(\"Error wrapping up %s.\"), cmd_file);\n+}\n+\n static void save_head(const char *head)\n {\n \tconst char *head_file = git_path(SEQ_HEAD_FILE);\n@@ -1043,9 +1087,21 @@ static int continue_single_pick(void)\n static int sequencer_continue(struct replay_opts *opts)\n {\n \tstruct commit_list *todo_list = NULL;\n+\tenum replay_command cmd;\n \n \tif (!file_exists(git_path(SEQ_TODO_FILE)))\n \t\treturn continue_single_pick();\n+\n+\t/*\n+\t * Disallow continuing a cherry-pick with 'git revert\n+\t * --continue' and viceversa\n+\t */\n+\tcmd = read_cmd();\n+\tif (cmd != opts->command)\n+\t\treturn error(_(\"cannot %s: a %s is in progress.\"),\n+\t\t\tcommand_name(opts),\n+\t\t\tcmd == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\");\n+\n \tread_populate_opts(&opts);\n \tread_populate_todo(&todo_list, opts);\n \n@@ -1126,6 +1182,7 @@ static int pick_revisions(struct replay_opts *opts)\n \t\t\treturn error(_(\"Can't revert as initial commit\"));\n \t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t}\n+\tsave_cmd(opts);\n \tsave_head(sha1_to_hex(sha1));\n \tsave_opts(opts);\n \treturn pick_commits(todo_list, opts);\ndiff --git a/sequencer.h b/sequencer.h\nindex 07e0639..00ab685 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -2,6 +2,7 @@\n #define SEQUENCER_H\n \n #define SEQ_DIR\t\t\"sequencer\"\n+#define SEQ_CMD_FILE\t\"sequencer/cmd\"\n #define SEQ_HEAD_FILE\t\"sequencer/head\"\n #define SEQ_TODO_FILE\t\"sequencer/todo\"\n #define SEQ_OPTS_FILE\t\"sequencer/opts\"\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 97f3710..73298cf 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -48,6 +48,7 @@ test_expect_success 'cherry-pick persists data on failure' '\n \tpristine_detach initial &&\n \ttest_expect_code 1 git cherry-pick -s base..anotherpick &&\n \ttest_path_is_dir .git/sequencer &&\n+\ttest_path_is_file .git/sequencer/cmd &&\n \ttest_path_is_file .git/sequencer/head &&\n \ttest_path_is_file .git/sequencer/todo &&\n \ttest_path_is_file .git/sequencer/opts\n@@ -69,6 +70,7 @@ test_expect_success 'cherry-pick persists opts correctly' '\n \tpristine_detach initial &&\n \ttest_expect_code 128 git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours initial..anotherpick &&\n \ttest_path_is_dir .git/sequencer &&\n+\ttest_path_is_file .git/sequencer/cmd &&\n \ttest_path_is_file .git/sequencer/head &&\n \ttest_path_is_file .git/sequencer/todo &&\n \ttest_path_is_file .git/sequencer/opts &&\n@@ -517,4 +519,13 @@ test_expect_success 'commit descriptions in insn sheet are optional' '\n \ttest_line_count = 4 commits\n '\n \n+test_expect_success 'revert --continue refuses to follow cherry-pick' '\n+       pristine_detach initial &&\n+       test_expect_code 1 git cherry-pick base..anotherpick &&\n+       echo \"c\" >foo &&\n+       git add foo &&\n+       git commit &&\n+       test_expect_code 128 git revert --continue\n+'\n+\n test_done\n-- \n1.7.8.2\n"},{"id":"182101","messageId":"1326025653-11922-5-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 4/6] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:31Z","receivedAt":"2012-01-08T12:27:31Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Parse the instruction sheet in '.git/sequencer/todo' as a list of\n(action, operand) pairs, instead of assuming that all lines have the\nsame action.  Now, an instruction sheet like the following is\nperfectly valid:\n\n  pick fdc0b12 picked\n  revert 965fed4 anotherpick\n\nThe operator can use this feature by hand-editing the instruction\nsheet and using '--continue' as appropriate:\n\n  $ git cherry-pick foo..bar\n  [conflict occurs]\n  $ edit problematicfile\n  $ git add problematicfile\n  $ edit .git/sequencer/todo\n  $ git cherry-pick --continue\n  [finishes successfully]\n\nHelped-by: Jonathan Nieder <jrnider@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c                |  135 +++++++++++++++++++--------------------\n sequencer.h                     |    6 ++\n t/t3510-cherry-pick-sequence.sh |   46 ++++++++++----\n 3 files changed, 105 insertions(+), 82 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 52fa115..54ea394 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -475,7 +475,8 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n \treturn run_command_v_opt(args, RUN_GIT_CMD);\n }\n \n-static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n+static int do_pick_commit(struct commit *commit, enum replay_action action,\n+\t\t\tstruct replay_opts *opts)\n {\n \tunsigned char head[20];\n \tstruct commit *base, *next, *parent;\n@@ -550,7 +551,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->command == REPLAY_CMD_REVERT) {\n+\tif (action == REPLAY_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -591,7 +592,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->command == REPLAY_CMD_REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -615,13 +616,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->command == REPLAY_CMD_CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->command == REPLAY_CMD_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->command == REPLAY_CMD_REVERT\n+\t\terror(action == REPLAY_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -668,70 +669,70 @@ static void read_and_refresh_cache(struct replay_opts *opts)\n }\n \n /*\n- * Append a commit to the end of the commit_list.\n+ * Append a (commit, action) to the end of the replay_insn_list.\n  *\n  * next starts by pointing to the variable that holds the head of an\n- * empty commit_list, and is updated to point to the \"next\" field of\n- * the last item on the list as new commits are appended.\n+ * empty replay_insn_list, and is updated to point to the \"next\" field of\n+ * the last item on the list as new (commit, action) pairs are appended.\n  *\n  * Usage example:\n  *\n- *     struct commit_list *list;\n- *     struct commit_list **next = &list;\n+ *     struct replay_insn_list *list;\n+ *     struct replay_insn_list **next = &list;\n  *\n- *     next = commit_list_append(c1, next);\n- *     next = commit_list_append(c2, next);\n- *     assert(commit_list_count(list) == 2);\n+ *     next = replay_insn_list_append(c1, a1, next);\n+ *     next = replay_insn_list_append(c2, a2, next);\n+ *     assert(len(list) == 2);\n  *     return list;\n  */\n-static struct commit_list **commit_list_append(struct commit *commit,\n-\t\t\t\t\t       struct commit_list **next)\n+static struct replay_insn_list **replay_insn_list_append(struct commit *operand,\n+\t\t\t\t\t\t\tenum replay_action action,\n+\t\t\t\t\t\t\tstruct replay_insn_list **next)\n {\n-\tstruct commit_list *new = xmalloc(sizeof(struct commit_list));\n-\tnew->item = commit;\n+\tstruct replay_insn_list *new = xmalloc(sizeof(*new));\n+\tnew->action = action;\n+\tnew->operand = operand;\n \t*next = new;\n \tnew->next = NULL;\n \treturn &new->next;\n }\n \n-static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n-\t\tstruct replay_opts *opts)\n+static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n {\n-\tstruct commit_list *cur = NULL;\n-\tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"pick\";\n-\tconst char *subject;\n-\tint subject_len;\n+\tstruct replay_insn_list *cur;\n \n \tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);\n-\t\tsubject_len = find_commit_subject(cur->item->buffer, &subject);\n+\t\tconst char *sha1_abbrev, *action_str, *subject;\n+\t\tint subject_len;\n+\n+\t\taction_str = action_name(cur->action);\n+\t\tsha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);\n+\t\tsubject_len = find_commit_subject(cur->operand->buffer, &subject);\n \t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n \t\t\tsubject_len, subject);\n \t}\n \treturn 0;\n }\n \n-static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)\n+static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n {\n \tunsigned char commit_sha1[20];\n-\tenum replay_action action;\n \tchar *end_of_object_name;\n \tint saved, status, padding;\n \n \tif (!prefixcmp(bol, \"pick\")) {\n-\t\taction = REPLAY_PICK;\n+\t\titem->action = REPLAY_PICK;\n \t\tbol += strlen(\"pick\");\n \t} else if (!prefixcmp(bol, \"revert\")) {\n-\t\taction = REPLAY_REVERT;\n+\t\titem->action = REPLAY_REVERT;\n \t\tbol += strlen(\"revert\");\n \t} else\n-\t\treturn NULL;\n+\t\treturn -1;\n \n \t/* Eat up extra spaces/ tabs before object name */\n \tpadding = strspn(bol, \" \\t\");\n \tif (!padding)\n-\t\treturn NULL;\n+\t\treturn -1;\n \tbol += padding;\n \n \tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n@@ -740,37 +741,29 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \tstatus = get_sha1(bol, commit_sha1);\n \t*end_of_object_name = saved;\n \n-\t/*\n-\t * Verify that the action matches up with the one in\n-\t * opts; we don't support arbitrary instructions\n-\t */\n-\tif ((action == REPLAY_PICK && opts->command == REPLAY_CMD_REVERT) ||\n-\t\t(action == REPLAY_REVERT && opts->command == REPLAY_CMD_CHERRY_PICK)) {\n-\t\terror(_(\"Cannot %s during a %s\"), action_name(action),\n-\t\t\tcommand_name(opts));\n-\t\treturn NULL;\n-\t}\n-\n \tif (status < 0)\n-\t\treturn NULL;\n+\t\treturn -1;\n+\n+\titem->operand = lookup_commit_reference(commit_sha1);\n+\tif (!item->operand)\n+\t\treturn -1;\n \n-\treturn lookup_commit_reference(commit_sha1);\n+\titem->next = NULL;\n+\treturn 0;\n }\n \n-static int parse_insn_buffer(char *buf, struct commit_list **todo_list,\n-\t\t\tstruct replay_opts *opts)\n+static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n {\n-\tstruct commit_list **next = todo_list;\n-\tstruct commit *commit;\n+\tstruct replay_insn_list **next = todo_list;\n+\tstruct replay_insn_list item = { NULL, 0, NULL };\n \tchar *p = buf;\n \tint i;\n \n \tfor (i = 1; *p; i++) {\n \t\tchar *eol = strchrnul(p, '\\n');\n-\t\tcommit = parse_insn_line(p, eol, opts);\n-\t\tif (!commit)\n+\t\tif (parse_insn_line(p, eol, &item) < 0)\n \t\t\treturn error(_(\"Could not parse line %d.\"), i);\n-\t\tnext = commit_list_append(commit, next);\n+\t\tnext = replay_insn_list_append(item.operand, item.action, next);\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n \tif (!*todo_list)\n@@ -778,8 +771,7 @@ static int parse_insn_buffer(char *buf, struct commit_list **todo_list,\n \treturn 0;\n }\n \n-static void read_populate_todo(struct commit_list **todo_list,\n-\t\t\tstruct replay_opts *opts)\n+static void read_populate_todo(struct replay_insn_list **todo_list)\n {\n \tconst char *todo_file = git_path(SEQ_TODO_FILE);\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -795,7 +787,7 @@ static void read_populate_todo(struct commit_list **todo_list,\n \t}\n \tclose(fd);\n \n-\tres = parse_insn_buffer(buf.buf, todo_list, opts);\n+\tres = parse_insn_buffer(buf.buf, todo_list);\n \tstrbuf_release(&buf);\n \tif (res)\n \t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n@@ -844,17 +836,19 @@ static void read_populate_opts(struct replay_opts **opts_ptr)\n \t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n }\n \n-static void walk_revs_populate_todo(struct commit_list **todo_list,\n+static void walk_revs_populate_todo(struct replay_insn_list **todo_list,\n \t\t\t\tstruct replay_opts *opts)\n {\n \tstruct commit *commit;\n-\tstruct commit_list **next;\n+\tstruct replay_insn_list **next;\n+\tenum replay_action action;\n \n \tprepare_revs(opts);\n \n \tnext = todo_list;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n \twhile ((commit = get_revision(opts->revs)))\n-\t\tnext = commit_list_append(commit, next);\n+\t\tnext = replay_insn_list_append(commit, action, next);\n }\n \n static int create_seq_dir(void)\n@@ -996,7 +990,7 @@ fail:\n \treturn -1;\n }\n \n-static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\n+static void save_todo(struct replay_insn_list *todo_list)\n {\n \tconst char *todo_file = git_path(SEQ_TODO_FILE);\n \tstatic struct lock_file todo_lock;\n@@ -1004,7 +998,7 @@ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\n \tint fd;\n \n \tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n-\tif (format_todo(&buf, todo_list, opts) < 0)\n+\tif (format_todo(&buf, todo_list) < 0)\n \t\tdie(_(\"Could not format %s.\"), todo_file);\n \tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n \t\tstrbuf_release(&buf);\n@@ -1048,9 +1042,9 @@ static void save_opts(struct replay_opts *opts)\n \t}\n }\n \n-static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n+static int pick_commits(struct replay_insn_list *todo_list, struct replay_opts *opts)\n {\n-\tstruct commit_list *cur;\n+\tstruct replay_insn_list *cur;\n \tint res;\n \n \tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n@@ -1060,8 +1054,8 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n \tread_and_refresh_cache(opts);\n \n \tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsave_todo(cur, opts);\n-\t\tres = do_pick_commit(cur->item, opts);\n+\t\tsave_todo(cur);\n+\t\tres = do_pick_commit(cur->operand, cur->action, opts);\n \t\tif (res)\n \t\t\treturn res;\n \t}\n@@ -1086,7 +1080,7 @@ static int continue_single_pick(void)\n \n static int sequencer_continue(struct replay_opts *opts)\n {\n-\tstruct commit_list *todo_list = NULL;\n+\tstruct replay_insn_list *todo_list = NULL;\n \tenum replay_command cmd;\n \n \tif (!file_exists(git_path(SEQ_TODO_FILE)))\n@@ -1103,7 +1097,7 @@ static int sequencer_continue(struct replay_opts *opts)\n \t\t\tcmd == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\");\n \n \tread_populate_opts(&opts);\n-\tread_populate_todo(&todo_list, opts);\n+\tread_populate_todo(&todo_list);\n \n \t/* Verify that the conflict has been resolved */\n \tif (file_exists(git_path(\"CHERRY_PICK_HEAD\")) ||\n@@ -1120,13 +1114,16 @@ static int sequencer_continue(struct replay_opts *opts)\n \n static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n+\tenum replay_action action;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n+\n \tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n-\treturn do_pick_commit(cmit, opts);\n+\treturn do_pick_commit(cmit, action, opts);\n }\n \n static int pick_revisions(struct replay_opts *opts)\n {\n-\tstruct commit_list *todo_list = NULL;\n+\tstruct replay_insn_list *todo_list = NULL;\n \tunsigned char sha1[20];\n \n \tif (opts->subcommand == REPLAY_NONE)\ndiff --git a/sequencer.h b/sequencer.h\nindex 00ab685..d1cb5c2 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -24,6 +24,12 @@ enum replay_subcommand {\n \tREPLAY_ROLLBACK\n };\n \n+struct replay_insn_list {\n+\tstruct commit *operand;\n+\tenum replay_action action;\n+\tstruct replay_insn_list *next;\n+};\n+\n /* Removes SEQ_DIR. */\n extern void remove_sequencer_state(void);\n \ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 73298cf..f97ab79 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -456,7 +456,7 @@ test_expect_success 'sign-off needs to be reaffirmed after conflict resolution,\n \t! grep Signed-off-by: msg\n '\n \n-test_expect_success 'malformed instruction sheet 1' '\n+test_expect_success 'malformed instruction sheet, action' '\n \tpristine_detach initial &&\n \ttest_expect_code 1 git cherry-pick base..anotherpick &&\n \techo \"resolved\" >foo &&\n@@ -467,23 +467,12 @@ test_expect_success 'malformed instruction sheet 1' '\n \ttest_expect_code 128 git cherry-pick --continue\n '\n \n-test_expect_success 'malformed instruction sheet 2' '\n-\tpristine_detach initial &&\n-\ttest_expect_code 1 git cherry-pick base..anotherpick &&\n-\techo \"resolved\" >foo &&\n-\tgit add foo &&\n-\tgit commit &&\n-\tsed \"s/pick/revert/\" .git/sequencer/todo >new_sheet &&\n-\tcp new_sheet .git/sequencer/todo &&\n-\ttest_expect_code 128 git cherry-pick --continue\n-'\n-\n test_expect_success 'empty commit set' '\n \tpristine_detach initial &&\n \ttest_expect_code 128 git cherry-pick base..base\n '\n \n-test_expect_success 'malformed instruction sheet 3' '\n+test_expect_success 'malformed instruction sheet, object name' '\n \tpristine_detach initial &&\n \ttest_expect_code 1 git cherry-pick base..anotherpick &&\n \techo \"resolved\" >foo &&\n@@ -528,4 +517,35 @@ test_expect_success 'revert --continue refuses to follow cherry-pick' '\n        test_expect_code 128 git revert --continue\n '\n \n+test_expect_success 'mixed pick and revert instructions' '\n+\tpristine_detach initial &&\n+\ttest_expect_code 1 git cherry-pick base..anotherpick &&\n+\techo \"c\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\n+\toldsha=`git rev-parse --short HEAD~1` &&\n+\techo \"revert $oldsha unrelatedpick\" >>.git/sequencer/todo &&\n+\tgit cherry-pick --continue &&\n+\ttest_path_is_missing .git/sequencer &&\n+\t{\n+\t\tgit rev-list HEAD |\n+\t\tgit diff-tree --root --stdin |\n+\t\tsed \"s/$_x40/OBJID/g\"\n+\t} >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tunrelated\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tfoo\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tfoo\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tunrelated\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\tfoo\n+\t:000000 100644 OBJID OBJID A\tunrelated\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.7.8.2\n"},{"id":"182102","messageId":"1326025653-11922-6-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 5/6] revert: report fine-grained error messages from insn parser","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:32Z","receivedAt":"2012-01-08T12:27:32Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Three kinds of errors can arise from parsing '.git/sequencer/todo':\n1. Unrecognized action\n2. Malformed object name\n3. Object name does not refer to a valid commit\n\nSince we would like to make the instruction sheet user-editable in the\nfuture (much like the 'rebase -i' sheet), report more fine-grained\nparse errors prefixed with the filename and line number.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   45 +++++++++++++++++++++++++++------------------\n 1 files changed, 27 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 54ea394..35553e7 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -714,26 +714,29 @@ static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n \treturn 0;\n }\n \n-static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n+static int parse_insn_line(char *bol, char *eol,\n+\t\t\tstruct replay_insn_list *item, int lineno)\n {\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n \tunsigned char commit_sha1[20];\n \tchar *end_of_object_name;\n-\tint saved, status, padding;\n+\tint saved, status;\n+\tsize_t error_len;\n \n-\tif (!prefixcmp(bol, \"pick\")) {\n+\tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n \t\titem->action = REPLAY_PICK;\n-\t\tbol += strlen(\"pick\");\n-\t} else if (!prefixcmp(bol, \"revert\")) {\n+\t\tbol += strlen(\"pick \");\n+\t} else if (!prefixcmp(bol, \"revert \") || !prefixcmp(bol, \"revert\\t\")) {\n \t\titem->action = REPLAY_REVERT;\n-\t\tbol += strlen(\"revert\");\n-\t} else\n-\t\treturn -1;\n+\t\tbol += strlen(\"revert \");\n+\t} else {\n+\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n+\t\treturn error(_(\"%s:%d: Unrecognized action: %.*s\"),\n+\t\t\ttodo_file, lineno, (int)error_len, bol);\n+\t}\n \n \t/* Eat up extra spaces/ tabs before object name */\n-\tpadding = strspn(bol, \" \\t\");\n-\tif (!padding)\n-\t\treturn -1;\n-\tbol += padding;\n+\tbol += strspn(bol, \" \\t\");\n \n \tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n \tsaved = *end_of_object_name;\n@@ -741,12 +744,18 @@ static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n \tstatus = get_sha1(bol, commit_sha1);\n \t*end_of_object_name = saved;\n \n-\tif (status < 0)\n-\t\treturn -1;\n+\tif (status < 0) {\n+\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n+\t\treturn error(_(\"%s:%d: Malformed object name: %.*s\"),\n+\t\t\ttodo_file, lineno, (int)error_len, bol);\n+\t}\n \n \titem->operand = lookup_commit_reference(commit_sha1);\n-\tif (!item->operand)\n-\t\treturn -1;\n+\tif (!item->operand) {\n+\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n+\t\treturn error(_(\"%s:%d: Not a valid commit: %.*s\"),\n+\t\t\ttodo_file, lineno, (int)error_len, bol);\n+\t}\n \n \titem->next = NULL;\n \treturn 0;\n@@ -761,8 +770,8 @@ static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n \n \tfor (i = 1; *p; i++) {\n \t\tchar *eol = strchrnul(p, '\\n');\n-\t\tif (parse_insn_line(p, eol, &item) < 0)\n-\t\t\treturn error(_(\"Could not parse line %d.\"), i);\n+\t\tif (parse_insn_line(p, eol, &item, i) < 0)\n+\t\t\treturn -1;\n \t\tnext = replay_insn_list_append(item.operand, item.action, next);\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n-- \n1.7.8.2\n"},{"id":"182104","messageId":"1326025653-11922-7-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 6/6] sequencer: factor code out of revert builtin","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T12:27:33Z","receivedAt":"2012-01-08T12:27:33Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Expose the cherry-picking machinery through a public\nsequencer_pick_revisions() (renamed from pick_revisions() in\nbuiltin/revert.c), so that cherry-picking and reverting are special\ncases of a general sequencer operation.  The cherry-pick builtin is\nnow a thin wrapper that does command-line argument parsing before\ncalling into sequencer_pick_revisions().  In the future, we can write\na new \"foo\" builtin that calls into the sequencer like:\n\n  memset(&opts, 0, sizeof(opts));\n  opts.command = REPLAY_CMD_FOO;\n  opts.revisions = xmalloc(sizeof(*opts.revs));\n  parse_args_populate_opts(argc, argv, &opts);\n  init_revisions(opts.revs);\n  sequencer_pick_revisions(&opts);\n\nThis patch does not intend to make any functional changes.  Check\nwith:\n\n  $ git blame -s -C HEAD^..HEAD -- sequencer.c | grep -C3 '^[^^]'\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c | 1006 +-----------------------------------------------------\n sequencer.c      |  987 ++++++++++++++++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |   25 ++\n 3 files changed, 1013 insertions(+), 1005 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 35553e7..47f71f3 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -1,19 +1,9 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n-#include \"object.h\"\n-#include \"commit.h\"\n-#include \"tag.h\"\n-#include \"run-command.h\"\n-#include \"exec_cmd.h\"\n-#include \"utf8.h\"\n #include \"parse-options.h\"\n-#include \"cache-tree.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"rerere.h\"\n-#include \"merge-recursive.h\"\n-#include \"refs.h\"\n-#include \"dir.h\"\n #include \"sequencer.h\"\n \n /*\n@@ -39,43 +29,11 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-struct replay_opts {\n-\tenum replay_command command;\n-\tenum replay_subcommand subcommand;\n-\n-\t/* Boolean options */\n-\tint edit;\n-\tint record_origin;\n-\tint no_commit;\n-\tint signoff;\n-\tint allow_ff;\n-\tint allow_rerere_auto;\n-\n-\tint mainline;\n-\n-\t/* Merge strategy */\n-\tconst char *strategy;\n-\tconst char **xopts;\n-\tsize_t xopts_nr, xopts_alloc;\n-\n-\t/* Only used by REPLAY_NONE */\n-\tstruct rev_info *revs;\n-};\n-\n-#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n-\n static const char *command_name(struct replay_opts *opts)\n {\n \treturn opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n-static const char *action_name(enum replay_action action)\n-{\n-\treturn action == REPLAY_REVERT ? \"revert\" : \"pick\";\n-}\n-\n-static char *get_encoding(const char *message);\n-\n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n \treturn opts->command == REPLAY_CMD_REVERT ? revert_usage : cherry_pick_usage;\n@@ -234,966 +192,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tusage_with_options(usage_str, options);\n }\n \n-struct commit_message {\n-\tchar *parent_label;\n-\tconst char *label;\n-\tconst char *subject;\n-\tchar *reencoded_message;\n-\tconst char *message;\n-};\n-\n-static int get_message(struct commit *commit, struct commit_message *out)\n-{\n-\tconst char *encoding;\n-\tconst char *abbrev, *subject;\n-\tint abbrev_len, subject_len;\n-\tchar *q;\n-\n-\tif (!commit->buffer)\n-\t\treturn -1;\n-\tencoding = get_encoding(commit->buffer);\n-\tif (!encoding)\n-\t\tencoding = \"UTF-8\";\n-\tif (!git_commit_encoding)\n-\t\tgit_commit_encoding = \"UTF-8\";\n-\n-\tout->reencoded_message = NULL;\n-\tout->message = commit->buffer;\n-\tif (strcmp(encoding, git_commit_encoding))\n-\t\tout->reencoded_message = reencode_string(commit->buffer,\n-\t\t\t\t\tgit_commit_encoding, encoding);\n-\tif (out->reencoded_message)\n-\t\tout->message = out->reencoded_message;\n-\n-\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n-\tabbrev_len = strlen(abbrev);\n-\n-\tsubject_len = find_commit_subject(out->message, &subject);\n-\n-\tout->parent_label = xmalloc(strlen(\"parent of \") + abbrev_len +\n-\t\t\t      strlen(\"... \") + subject_len + 1);\n-\tq = out->parent_label;\n-\tq = mempcpy(q, \"parent of \", strlen(\"parent of \"));\n-\tout->label = q;\n-\tq = mempcpy(q, abbrev, abbrev_len);\n-\tq = mempcpy(q, \"... \", strlen(\"... \"));\n-\tout->subject = q;\n-\tq = mempcpy(q, subject, subject_len);\n-\t*q = '\\0';\n-\treturn 0;\n-}\n-\n-static void free_message(struct commit_message *msg)\n-{\n-\tfree(msg->parent_label);\n-\tfree(msg->reencoded_message);\n-}\n-\n-static char *get_encoding(const char *message)\n-{\n-\tconst char *p = message, *eol;\n-\n-\twhile (*p && *p != '\\n') {\n-\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n-\t\t\t; /* do nothing */\n-\t\tif (!prefixcmp(p, \"encoding \")) {\n-\t\t\tchar *result = xmalloc(eol - 8 - p);\n-\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n-\t\t\treturn result;\n-\t\t}\n-\t\tp = eol;\n-\t\tif (*p == '\\n')\n-\t\t\tp++;\n-\t}\n-\treturn NULL;\n-}\n-\n-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n-{\n-\tconst char *filename;\n-\tint fd;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n-\n-\tfilename = git_path(\"%s\", pseudoref);\n-\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n-\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n-\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n-\tstrbuf_release(&buf);\n-}\n-\n-static void print_advice(int show_hint)\n-{\n-\tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n-\n-\tif (msg) {\n-\t\tfprintf(stderr, \"%s\\n\", msg);\n-\t\t/*\n-\t\t * A conflict has occured but the porcelain\n-\t\t * (typically rebase --interactive) wants to take care\n-\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n-\t\t */\n-\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n-\t\treturn;\n-\t}\n-\n-\tif (show_hint) {\n-\t\tadvise(\"after resolving the conflicts, mark the corrected paths\");\n-\t\tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n-\t\tadvise(\"and commit the result with 'git commit'\");\n-\t}\n-}\n-\n-static void write_message(struct strbuf *msgbuf, const char *filename)\n-{\n-\tstatic struct lock_file msg_file;\n-\n-\tint msg_fd = hold_lock_file_for_update(&msg_file, filename,\n-\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n-\tif (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), filename);\n-\tstrbuf_release(msgbuf);\n-\tif (commit_lock_file(&msg_file) < 0)\n-\t\tdie(_(\"Error wrapping up %s\"), filename);\n-}\n-\n-static struct tree *empty_tree(void)\n-{\n-\treturn lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);\n-}\n-\n-static int error_dirty_index(struct replay_opts *opts)\n-{\n-\tif (read_cache_unmerged())\n-\t\treturn error_resolve_conflict(command_name(opts));\n-\n-\t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->command == REPLAY_CMD_CHERRY_PICK)\n-\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n-\telse\n-\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n-\n-\tif (advice_commit_before_merge)\n-\t\tadvise(_(\"Commit your changes or stash them to proceed.\"));\n-\treturn -1;\n-}\n-\n-static int fast_forward_to(const unsigned char *to, const unsigned char *from)\n-{\n-\tstruct ref_lock *ref_lock;\n-\n-\tread_cache();\n-\tif (checkout_fast_forward(from, to))\n-\t\texit(1); /* the callee should have complained already */\n-\tref_lock = lock_any_ref_for_update(\"HEAD\", from, 0);\n-\treturn write_ref_sha1(ref_lock, to, \"cherry-pick\");\n-}\n-\n-static int do_recursive_merge(struct commit *base, struct commit *next,\n-\t\t\t      const char *base_label, const char *next_label,\n-\t\t\t      unsigned char *head, struct strbuf *msgbuf,\n-\t\t\t      struct replay_opts *opts)\n-{\n-\tstruct merge_options o;\n-\tstruct tree *result, *next_tree, *base_tree, *head_tree;\n-\tint clean, index_fd;\n-\tconst char **xopt;\n-\tstatic struct lock_file index_lock;\n-\n-\tindex_fd = hold_locked_index(&index_lock, 1);\n-\n-\tread_cache();\n-\n-\tinit_merge_options(&o);\n-\to.ancestor = base ? base_label : \"(empty tree)\";\n-\to.branch1 = \"HEAD\";\n-\to.branch2 = next ? next_label : \"(empty tree)\";\n-\n-\thead_tree = parse_tree_indirect(head);\n-\tnext_tree = next ? next->tree : empty_tree();\n-\tbase_tree = base ? base->tree : empty_tree();\n-\n-\tfor (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)\n-\t\tparse_merge_opt(&o, *xopt);\n-\n-\tclean = merge_trees(&o,\n-\t\t\t    head_tree,\n-\t\t\t    next_tree, base_tree, &result);\n-\n-\tif (active_cache_changed &&\n-\t    (write_cache(index_fd, active_cache, active_nr) ||\n-\t     commit_locked_index(&index_lock)))\n-\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n-\t\tdie(_(\"%s: Unable to write new index file\"), command_name(opts));\n-\trollback_lock_file(&index_lock);\n-\n-\tif (!clean) {\n-\t\tint i;\n-\t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n-\t\tfor (i = 0; i < active_nr;) {\n-\t\t\tstruct cache_entry *ce = active_cache[i++];\n-\t\t\tif (ce_stage(ce)) {\n-\t\t\t\tstrbuf_addch(msgbuf, '\\t');\n-\t\t\t\tstrbuf_addstr(msgbuf, ce->name);\n-\t\t\t\tstrbuf_addch(msgbuf, '\\n');\n-\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n-\t\t\t\t\t\tactive_cache[i]->name))\n-\t\t\t\t\ti++;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn !clean;\n-}\n-\n-/*\n- * If we are cherry-pick, and if the merge did not result in\n- * hand-editing, we will hit this commit and inherit the original\n- * author date and name.\n- * If we are revert, or if our cherry-pick results in a hand merge,\n- * we had better say that the current user is responsible for that.\n- */\n-static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n-{\n-\t/* 6 is max possible length of our args array including NULL */\n-\tconst char *args[6];\n-\tint i = 0;\n-\n-\targs[i++] = \"commit\";\n-\targs[i++] = \"-n\";\n-\tif (opts->signoff)\n-\t\targs[i++] = \"-s\";\n-\tif (!opts->edit) {\n-\t\targs[i++] = \"-F\";\n-\t\targs[i++] = defmsg;\n-\t}\n-\targs[i] = NULL;\n-\n-\treturn run_command_v_opt(args, RUN_GIT_CMD);\n-}\n-\n-static int do_pick_commit(struct commit *commit, enum replay_action action,\n-\t\t\tstruct replay_opts *opts)\n-{\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, NULL };\n-\tchar *defmsg = NULL;\n-\tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res;\n-\n-\tif (opts->no_commit) {\n-\t\t/*\n-\t\t * We do not intend to commit immediately.  We just want to\n-\t\t * merge the differences in, so let's compute the tree\n-\t\t * that represents the \"current\" state for merge-recursive\n-\t\t * to work on.\n-\t\t */\n-\t\tif (write_cache_as_tree(head, 0, NULL))\n-\t\t\tdie (_(\"Your index file is unmerged.\"));\n-\t} else {\n-\t\tif (get_sha1(\"HEAD\", head))\n-\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n-\t\tif (index_differs_from(\"HEAD\", 0))\n-\t\t\treturn error_dirty_index(opts);\n-\t}\n-\tdiscard_cache();\n-\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-\t\tstruct commit_list *p;\n-\n-\t\tif (!opts->mainline)\n-\t\t\treturn error(_(\"Commit %s is a merge but no -m option was given.\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1));\n-\n-\t\tfor (cnt = 1, p = commit->parents;\n-\t\t     cnt != opts->mainline && p;\n-\t\t     cnt++)\n-\t\t\tp = p->next;\n-\t\tif (cnt != opts->mainline || !p)\n-\t\t\treturn error(_(\"Commit %s does not have parent %d\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1), opts->mainline);\n-\t\tparent = p->item;\n-\t} else if (0 < opts->mainline)\n-\t\treturn error(_(\"Mainline was specified but commit %s is not a merge.\"),\n-\t\t\tsha1_to_hex(commit->object.sha1));\n-\telse\n-\t\tparent = commit->parents->item;\n-\n-\tif (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))\n-\t\treturn fast_forward_to(commit->object.sha1, head);\n-\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-\t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n-\t\t\tcommand_name(opts), sha1_to_hex(parent->object.sha1));\n-\n-\tif (get_message(commit, &msg) != 0)\n-\t\treturn error(_(\"Cannot get commit message for %s\"),\n-\t\t\tsha1_to_hex(commit->object.sha1));\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-\t * on top of the current HEAD if we are cherry-pick.  Or the\n-\t * reverse of it if we are revert.\n-\t */\n-\n-\tdefmsg = git_pathdup(\"MERGE_MSG\");\n-\n-\tif (action == REPLAY_REVERT) {\n-\t\tbase = commit;\n-\t\tbase_label = msg.label;\n-\t\tnext = parent;\n-\t\tnext_label = msg.parent_label;\n-\t\tstrbuf_addstr(&msgbuf, \"Revert \\\"\");\n-\t\tstrbuf_addstr(&msgbuf, msg.subject);\n-\t\tstrbuf_addstr(&msgbuf, \"\\\"\\n\\nThis reverts commit \");\n-\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-\n-\t\tif (commit->parents && commit->parents->next) {\n-\t\t\tstrbuf_addstr(&msgbuf, \", reversing\\nchanges made to \");\n-\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));\n-\t\t}\n-\t\tstrbuf_addstr(&msgbuf, \".\\n\");\n-\t} else {\n-\t\tconst char *p;\n-\n-\t\tbase = parent;\n-\t\tbase_label = msg.parent_label;\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\tp += 2;\n-\t\t\tstrbuf_addstr(&msgbuf, p);\n-\t\t}\n-\n-\t\tif (opts->record_origin) {\n-\t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n-\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-\t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n-\t\t}\n-\t}\n-\n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || action == REPLAY_REVERT) {\n-\t\tres = do_recursive_merge(base, next, base_label, next_label,\n-\t\t\t\t\t head, &msgbuf, opts);\n-\t\twrite_message(&msgbuf, defmsg);\n-\t} else {\n-\t\tstruct commit_list *common = NULL;\n-\t\tstruct commit_list *remotes = NULL;\n-\n-\t\twrite_message(&msgbuf, defmsg);\n-\n-\t\tcommit_list_insert(base, &common);\n-\t\tcommit_list_insert(next, &remotes);\n-\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n-\t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n-\t\tfree_commit_list(common);\n-\t\tfree_commit_list(remotes);\n-\t}\n-\n-\t/*\n-\t * If the merge was clean or if it failed due to conflict, we write\n-\t * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.\n-\t * However, if the merge did not even start, then we don't want to\n-\t * write it at all.\n-\t */\n-\tif (action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n-\n-\tif (res) {\n-\t\terror(action == REPLAY_REVERT\n-\t\t      ? _(\"could not revert %s... %s\")\n-\t\t      : _(\"could not apply %s... %s\"),\n-\t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n-\t\t      msg.subject);\n-\t\tprint_advice(res == 1);\n-\t\trerere(opts->allow_rerere_auto);\n-\t} else {\n-\t\tif (!opts->no_commit)\n-\t\t\tres = run_git_commit(defmsg, opts);\n-\t}\n-\n-\tfree_message(&msg);\n-\tfree(defmsg);\n-\n-\treturn res;\n-}\n-\n-static void prepare_revs(struct replay_opts *opts)\n-{\n-\tif (opts->command != REPLAY_CMD_REVERT)\n-\t\topts->revs->reverse ^= 1;\n-\n-\tif (prepare_revision_walk(opts->revs))\n-\t\tdie(_(\"revision walk setup failed\"));\n-\n-\tif (!opts->revs->commits)\n-\t\tdie(_(\"empty commit set passed\"));\n-}\n-\n-static void read_and_refresh_cache(struct replay_opts *opts)\n-{\n-\tstatic struct lock_file index_lock;\n-\tint index_fd = hold_locked_index(&index_lock, 0);\n-\tif (read_index_preload(&the_index, NULL) < 0)\n-\t\tdie(_(\"git %s: failed to read the index\"), command_name(opts));\n-\trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n-\tif (the_index.cache_changed) {\n-\t\tif (write_index(&the_index, index_fd) ||\n-\t\t    commit_locked_index(&index_lock))\n-\t\t\tdie(_(\"git %s: failed to refresh the index\"),\n-\t\t\t\tcommand_name(opts));\n-\t}\n-\trollback_lock_file(&index_lock);\n-}\n-\n-/*\n- * Append a (commit, action) to the end of the replay_insn_list.\n- *\n- * next starts by pointing to the variable that holds the head of an\n- * empty replay_insn_list, and is updated to point to the \"next\" field of\n- * the last item on the list as new (commit, action) pairs are appended.\n- *\n- * Usage example:\n- *\n- *     struct replay_insn_list *list;\n- *     struct replay_insn_list **next = &list;\n- *\n- *     next = replay_insn_list_append(c1, a1, next);\n- *     next = replay_insn_list_append(c2, a2, next);\n- *     assert(len(list) == 2);\n- *     return list;\n- */\n-static struct replay_insn_list **replay_insn_list_append(struct commit *operand,\n-\t\t\t\t\t\t\tenum replay_action action,\n-\t\t\t\t\t\t\tstruct replay_insn_list **next)\n-{\n-\tstruct replay_insn_list *new = xmalloc(sizeof(*new));\n-\tnew->action = action;\n-\tnew->operand = operand;\n-\t*next = new;\n-\tnew->next = NULL;\n-\treturn &new->next;\n-}\n-\n-static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n-{\n-\tstruct replay_insn_list *cur;\n-\n-\tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tconst char *sha1_abbrev, *action_str, *subject;\n-\t\tint subject_len;\n-\n-\t\taction_str = action_name(cur->action);\n-\t\tsha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);\n-\t\tsubject_len = find_commit_subject(cur->operand->buffer, &subject);\n-\t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n-\t\t\tsubject_len, subject);\n-\t}\n-\treturn 0;\n-}\n-\n-static int parse_insn_line(char *bol, char *eol,\n-\t\t\tstruct replay_insn_list *item, int lineno)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tunsigned char commit_sha1[20];\n-\tchar *end_of_object_name;\n-\tint saved, status;\n-\tsize_t error_len;\n-\n-\tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n-\t\titem->action = REPLAY_PICK;\n-\t\tbol += strlen(\"pick \");\n-\t} else if (!prefixcmp(bol, \"revert \") || !prefixcmp(bol, \"revert\\t\")) {\n-\t\titem->action = REPLAY_REVERT;\n-\t\tbol += strlen(\"revert \");\n-\t} else {\n-\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n-\t\treturn error(_(\"%s:%d: Unrecognized action: %.*s\"),\n-\t\t\ttodo_file, lineno, (int)error_len, bol);\n-\t}\n-\n-\t/* Eat up extra spaces/ tabs before object name */\n-\tbol += strspn(bol, \" \\t\");\n-\n-\tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n-\tsaved = *end_of_object_name;\n-\t*end_of_object_name = '\\0';\n-\tstatus = get_sha1(bol, commit_sha1);\n-\t*end_of_object_name = saved;\n-\n-\tif (status < 0) {\n-\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n-\t\treturn error(_(\"%s:%d: Malformed object name: %.*s\"),\n-\t\t\ttodo_file, lineno, (int)error_len, bol);\n-\t}\n-\n-\titem->operand = lookup_commit_reference(commit_sha1);\n-\tif (!item->operand) {\n-\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n-\t\treturn error(_(\"%s:%d: Not a valid commit: %.*s\"),\n-\t\t\ttodo_file, lineno, (int)error_len, bol);\n-\t}\n-\n-\titem->next = NULL;\n-\treturn 0;\n-}\n-\n-static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n-{\n-\tstruct replay_insn_list **next = todo_list;\n-\tstruct replay_insn_list item = { NULL, 0, NULL };\n-\tchar *p = buf;\n-\tint i;\n-\n-\tfor (i = 1; *p; i++) {\n-\t\tchar *eol = strchrnul(p, '\\n');\n-\t\tif (parse_insn_line(p, eol, &item, i) < 0)\n-\t\t\treturn -1;\n-\t\tnext = replay_insn_list_append(item.operand, item.action, next);\n-\t\tp = *eol ? eol + 1 : eol;\n-\t}\n-\tif (!*todo_list)\n-\t\treturn error(_(\"No commits parsed.\"));\n-\treturn 0;\n-}\n-\n-static void read_populate_todo(struct replay_insn_list **todo_list)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd, res;\n-\n-\tfd = open(todo_file, O_RDONLY);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open %s\"), todo_file);\n-\tif (strbuf_read(&buf, fd, 0) < 0) {\n-\t\tclose(fd);\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Could not read %s.\"), todo_file);\n-\t}\n-\tclose(fd);\n-\n-\tres = parse_insn_buffer(buf.buf, todo_list);\n-\tstrbuf_release(&buf);\n-\tif (res)\n-\t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n-}\n-\n-static int populate_opts_cb(const char *key, const char *value, void *data)\n-{\n-\tstruct replay_opts *opts = data;\n-\tint error_flag = 1;\n-\n-\tif (!value)\n-\t\terror_flag = 0;\n-\telse if (!strcmp(key, \"options.no-commit\"))\n-\t\topts->no_commit = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.edit\"))\n-\t\topts->edit = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.signoff\"))\n-\t\topts->signoff = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.record-origin\"))\n-\t\topts->record_origin = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.allow-ff\"))\n-\t\topts->allow_ff = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.mainline\"))\n-\t\topts->mainline = git_config_int(key, value);\n-\telse if (!strcmp(key, \"options.strategy\"))\n-\t\tgit_config_string(&opts->strategy, key, value);\n-\telse if (!strcmp(key, \"options.strategy-option\")) {\n-\t\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n-\t\topts->xopts[opts->xopts_nr++] = xstrdup(value);\n-\t} else\n-\t\treturn error(_(\"Invalid key: %s\"), key);\n-\n-\tif (!error_flag)\n-\t\treturn error(_(\"Invalid value for %s: %s\"), key, value);\n-\n-\treturn 0;\n-}\n-\n-static void read_populate_opts(struct replay_opts **opts_ptr)\n-{\n-\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n-\n-\tif (!file_exists(opts_file))\n-\t\treturn;\n-\tif (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)\n-\t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n-}\n-\n-static void walk_revs_populate_todo(struct replay_insn_list **todo_list,\n-\t\t\t\tstruct replay_opts *opts)\n-{\n-\tstruct commit *commit;\n-\tstruct replay_insn_list **next;\n-\tenum replay_action action;\n-\n-\tprepare_revs(opts);\n-\n-\tnext = todo_list;\n-\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n-\twhile ((commit = get_revision(opts->revs)))\n-\t\tnext = replay_insn_list_append(commit, action, next);\n-}\n-\n-static int create_seq_dir(void)\n-{\n-\tconst char *seq_dir = git_path(SEQ_DIR);\n-\n-\tif (file_exists(seq_dir)) {\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(seq_dir, 0777) < 0)\n-\t\tdie_errno(_(\"Could not create sequencer directory %s\"), seq_dir);\n-\treturn 0;\n-}\n-\n-static enum replay_command read_cmd(void)\n-{\n-\tconst char *cmd_file = git_path(SEQ_CMD_FILE);\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tenum replay_command res;\n-\tint fd;\n-\n-\tfd = open(cmd_file, O_RDONLY);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open %s\"), cmd_file);\n-\tif (strbuf_read(&buf, fd, 0) < 0) {\n-\t\tclose(fd);\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Could not read %s.\"), cmd_file);\n-\t}\n-\tclose(fd);\n-\n-\tif (!strcmp(buf.buf, \"revert\\n\"))\n-\t\tres = REPLAY_CMD_REVERT;\n-\telse if (!strcmp(buf.buf, \"cherry-pick\\n\"))\n-\t\tres = REPLAY_CMD_CHERRY_PICK;\n-\telse {\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Malformed command file: %s\"), cmd_file);\n-\t}\n-\tstrbuf_release(&buf);\n-\treturn res;\n-}\n-\n-static void save_cmd(struct replay_opts *opts)\n-{\n-\tconst char *cmd_file = git_path(SEQ_CMD_FILE);\n-\tstatic struct lock_file cmd_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&cmd_lock, cmd_file, LOCK_DIE_ON_ERROR);\n-\tstrbuf_addf(&buf, \"%s\\n\", command_name(opts));\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), cmd_file);\n-\tif (commit_lock_file(&cmd_lock) < 0)\n-\t\tdie(_(\"Error wrapping up %s.\"), cmd_file);\n-}\n-\n-static void save_head(const char *head)\n-{\n-\tconst char *head_file = git_path(SEQ_HEAD_FILE);\n-\tstatic struct lock_file head_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);\n-\tstrbuf_addf(&buf, \"%s\\n\", head);\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), head_file);\n-\tif (commit_lock_file(&head_lock) < 0)\n-\t\tdie(_(\"Error wrapping up %s.\"), head_file);\n-}\n-\n-static int reset_for_rollback(const unsigned char *sha1)\n-{\n-\tconst char *argv[4];\t/* reset --merge <arg> + NULL */\n-\targv[0] = \"reset\";\n-\targv[1] = \"--merge\";\n-\targv[2] = sha1_to_hex(sha1);\n-\targv[3] = NULL;\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n-}\n-\n-static int rollback_single_pick(void)\n-{\n-\tunsigned char head_sha1[20];\n-\n-\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n-\t    !file_exists(git_path(\"REVERT_HEAD\")))\n-\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n-\tif (read_ref_full(\"HEAD\", head_sha1, 0, NULL))\n-\t\treturn error(_(\"cannot resolve HEAD\"));\n-\tif (is_null_sha1(head_sha1))\n-\t\treturn error(_(\"cannot abort from a branch yet to be born\"));\n-\treturn reset_for_rollback(head_sha1);\n-}\n-\n-static int sequencer_rollback(struct replay_opts *opts)\n-{\n-\tconst char *filename;\n-\tFILE *f;\n-\tunsigned char sha1[20];\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tfilename = git_path(SEQ_HEAD_FILE);\n-\tf = fopen(filename, \"r\");\n-\tif (!f && errno == ENOENT) {\n-\t\t/*\n-\t\t * There is no multiple-cherry-pick in progress.\n-\t\t * If CHERRY_PICK_HEAD or REVERT_HEAD indicates\n-\t\t * a single-cherry-pick in progress, abort that.\n-\t\t */\n-\t\treturn rollback_single_pick();\n-\t}\n-\tif (!f)\n-\t\treturn error(_(\"cannot open %s: %s\"), filename,\n-\t\t\t\t\t\tstrerror(errno));\n-\tif (strbuf_getline(&buf, f, '\\n')) {\n-\t\terror(_(\"cannot read %s: %s\"), filename, ferror(f) ?\n-\t\t\tstrerror(errno) : _(\"unexpected end of file\"));\n-\t\tfclose(f);\n-\t\tgoto fail;\n-\t}\n-\tfclose(f);\n-\tif (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\\0') {\n-\t\terror(_(\"stored pre-cherry-pick HEAD file '%s' is corrupt\"),\n-\t\t\tfilename);\n-\t\tgoto fail;\n-\t}\n-\tif (reset_for_rollback(sha1))\n-\t\tgoto fail;\n-\tremove_sequencer_state();\n-\tstrbuf_release(&buf);\n-\treturn 0;\n-fail:\n-\tstrbuf_release(&buf);\n-\treturn -1;\n-}\n-\n-static void save_todo(struct replay_insn_list *todo_list)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tstatic struct lock_file todo_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n-\tif (format_todo(&buf, todo_list) < 0)\n-\t\tdie(_(\"Could not format %s.\"), todo_file);\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n-\t\tstrbuf_release(&buf);\n-\t\tdie_errno(_(\"Could not write to %s\"), todo_file);\n-\t}\n-\tif (commit_lock_file(&todo_lock) < 0) {\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Error wrapping up %s.\"), todo_file);\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n-static void save_opts(struct replay_opts *opts)\n-{\n-\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n-\n-\tif (opts->no_commit)\n-\t\tgit_config_set_in_file(opts_file, \"options.no-commit\", \"true\");\n-\tif (opts->edit)\n-\t\tgit_config_set_in_file(opts_file, \"options.edit\", \"true\");\n-\tif (opts->signoff)\n-\t\tgit_config_set_in_file(opts_file, \"options.signoff\", \"true\");\n-\tif (opts->record_origin)\n-\t\tgit_config_set_in_file(opts_file, \"options.record-origin\", \"true\");\n-\tif (opts->allow_ff)\n-\t\tgit_config_set_in_file(opts_file, \"options.allow-ff\", \"true\");\n-\tif (opts->mainline) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n-\t\tgit_config_set_in_file(opts_file, \"options.mainline\", buf.buf);\n-\t\tstrbuf_release(&buf);\n-\t}\n-\tif (opts->strategy)\n-\t\tgit_config_set_in_file(opts_file, \"options.strategy\", opts->strategy);\n-\tif (opts->xopts) {\n-\t\tint i;\n-\t\tfor (i = 0; i < opts->xopts_nr; i++)\n-\t\t\tgit_config_set_multivar_in_file(opts_file,\n-\t\t\t\t\t\t\t\"options.strategy-option\",\n-\t\t\t\t\t\t\topts->xopts[i], \"^$\", 0);\n-\t}\n-}\n-\n-static int pick_commits(struct replay_insn_list *todo_list, struct replay_opts *opts)\n-{\n-\tstruct replay_insn_list *cur;\n-\tint res;\n-\n-\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n-\tif (opts->allow_ff)\n-\t\tassert(!(opts->signoff || opts->no_commit ||\n-\t\t\t\topts->record_origin || opts->edit));\n-\tread_and_refresh_cache(opts);\n-\n-\tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsave_todo(cur);\n-\t\tres = do_pick_commit(cur->operand, cur->action, opts);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\t/*\n-\t * Sequence of picks finished successfully; cleanup by\n-\t * removing the .git/sequencer directory\n-\t */\n-\tremove_sequencer_state();\n-\treturn 0;\n-}\n-\n-static int continue_single_pick(void)\n-{\n-\tconst char *argv[] = { \"commit\", NULL };\n-\n-\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n-\t    !file_exists(git_path(\"REVERT_HEAD\")))\n-\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n-}\n-\n-static int sequencer_continue(struct replay_opts *opts)\n-{\n-\tstruct replay_insn_list *todo_list = NULL;\n-\tenum replay_command cmd;\n-\n-\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n-\t\treturn continue_single_pick();\n-\n-\t/*\n-\t * Disallow continuing a cherry-pick with 'git revert\n-\t * --continue' and viceversa\n-\t */\n-\tcmd = read_cmd();\n-\tif (cmd != opts->command)\n-\t\treturn error(_(\"cannot %s: a %s is in progress.\"),\n-\t\t\tcommand_name(opts),\n-\t\t\tcmd == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\");\n-\n-\tread_populate_opts(&opts);\n-\tread_populate_todo(&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\tint ret = continue_single_pick();\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\t}\n-\tif (index_differs_from(\"HEAD\", 0))\n-\t\treturn error_dirty_index(opts);\n-\ttodo_list = todo_list->next;\n-\treturn pick_commits(todo_list, opts);\n-}\n-\n-static int single_pick(struct commit *cmit, struct replay_opts *opts)\n-{\n-\tenum replay_action action;\n-\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n-\n-\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n-\treturn do_pick_commit(cmit, action, opts);\n-}\n-\n-static int pick_revisions(struct replay_opts *opts)\n-{\n-\tstruct replay_insn_list *todo_list = NULL;\n-\tunsigned char sha1[20];\n-\n-\tif (opts->subcommand == REPLAY_NONE)\n-\t\tassert(opts->revs);\n-\n-\tread_and_refresh_cache(opts);\n-\n-\t/*\n-\t * Decide what to do depending on the arguments; a fresh\n-\t * cherry-pick should be handled differently from an existing\n-\t * one that is being continued\n-\t */\n-\tif (opts->subcommand == REPLAY_REMOVE_STATE) {\n-\t\tremove_sequencer_state();\n-\t\treturn 0;\n-\t}\n-\tif (opts->subcommand == REPLAY_ROLLBACK)\n-\t\treturn sequencer_rollback(opts);\n-\tif (opts->subcommand == REPLAY_CONTINUE)\n-\t\treturn sequencer_continue(opts);\n-\n-\t/*\n-\t * If we were called as \"git cherry-pick <commit>\", just\n-\t * cherry-pick/revert it, set CHERRY_PICK_HEAD /\n-\t * REVERT_HEAD, and don't touch the sequencer state.\n-\t * This means it is possible to cherry-pick in the middle\n-\t * of a cherry-pick sequence.\n-\t */\n-\tif (opts->revs->cmdline.nr == 1 &&\n-\t    opts->revs->cmdline.rev->whence == REV_CMD_REV &&\n-\t    opts->revs->no_walk &&\n-\t    !opts->revs->cmdline.rev->flags) {\n-\t\tstruct commit *cmit;\n-\t\tif (prepare_revision_walk(opts->revs))\n-\t\t\tdie(_(\"revision walk setup failed\"));\n-\t\tcmit = get_revision(opts->revs);\n-\t\tif (!cmit || get_revision(opts->revs))\n-\t\t\tdie(\"BUG: expected exactly one commit from walk\");\n-\t\treturn single_pick(cmit, opts);\n-\t}\n-\n-\t/*\n-\t * Start a new cherry-pick/ revert sequence; but\n-\t * first, make sure that an existing one isn't in\n-\t * progress\n-\t */\n-\n-\twalk_revs_populate_todo(&todo_list, opts);\n-\tif (create_seq_dir() < 0)\n-\t\treturn -1;\n-\tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->command == REPLAY_CMD_REVERT)\n-\t\t\treturn error(_(\"Can't revert as initial commit\"));\n-\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n-\t}\n-\tsave_cmd(opts);\n-\tsave_head(sha1_to_hex(sha1));\n-\tsave_opts(opts);\n-\treturn pick_commits(todo_list, opts);\n-}\n-\n int cmd_revert(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts;\n@@ -1205,7 +203,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \topts.command = REPLAY_CMD_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\tres = pick_revisions(&opts);\n+\tres = sequencer_pick_revisions(&opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n \treturn res;\n@@ -1220,7 +218,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \topts.command = REPLAY_CMD_CHERRY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\tres = pick_revisions(&opts);\n+\tres = sequencer_pick_revisions(&opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\n \treturn res;\ndiff --git a/sequencer.c b/sequencer.c\nindex d1f28a6..a7e494c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,7 +1,20 @@\n #include \"cache.h\"\n #include \"sequencer.h\"\n-#include \"strbuf.h\"\n #include \"dir.h\"\n+#include \"object.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"run-command.h\"\n+#include \"exec_cmd.h\"\n+#include \"utf8.h\"\n+#include \"cache-tree.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"rerere.h\"\n+#include \"merge-recursive.h\"\n+#include \"refs.h\"\n+\n+#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n void remove_sequencer_state(void)\n {\n@@ -11,3 +24,975 @@ void remove_sequencer_state(void)\n \tremove_dir_recursively(&seq_dir, 0);\n \tstrbuf_release(&seq_dir);\n }\n+\n+static const char *command_name(struct replay_opts *opts)\n+{\n+\treturn opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\";\n+}\n+\n+static const char *action_name(enum replay_action action)\n+{\n+\treturn action == REPLAY_REVERT ? \"revert\" : \"pick\";\n+}\n+\n+struct commit_message {\n+\tchar *parent_label;\n+\tconst char *label;\n+\tconst char *subject;\n+\tchar *reencoded_message;\n+\tconst char *message;\n+};\n+\n+static char *get_encoding(const char *message);\n+\n+static int get_message(struct commit *commit, struct commit_message *out)\n+{\n+\tconst char *encoding;\n+\tconst char *abbrev, *subject;\n+\tint abbrev_len, subject_len;\n+\tchar *q;\n+\n+\tif (!commit->buffer)\n+\t\treturn -1;\n+\tencoding = get_encoding(commit->buffer);\n+\tif (!encoding)\n+\t\tencoding = \"UTF-8\";\n+\tif (!git_commit_encoding)\n+\t\tgit_commit_encoding = \"UTF-8\";\n+\n+\tout->reencoded_message = NULL;\n+\tout->message = commit->buffer;\n+\tif (strcmp(encoding, git_commit_encoding))\n+\t\tout->reencoded_message = reencode_string(commit->buffer,\n+\t\t\t\t\tgit_commit_encoding, encoding);\n+\tif (out->reencoded_message)\n+\t\tout->message = out->reencoded_message;\n+\n+\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n+\tabbrev_len = strlen(abbrev);\n+\n+\tsubject_len = find_commit_subject(out->message, &subject);\n+\n+\tout->parent_label = xmalloc(strlen(\"parent of \") + abbrev_len +\n+\t\t\t      strlen(\"... \") + subject_len + 1);\n+\tq = out->parent_label;\n+\tq = mempcpy(q, \"parent of \", strlen(\"parent of \"));\n+\tout->label = q;\n+\tq = mempcpy(q, abbrev, abbrev_len);\n+\tq = mempcpy(q, \"... \", strlen(\"... \"));\n+\tout->subject = q;\n+\tq = mempcpy(q, subject, subject_len);\n+\t*q = '\\0';\n+\treturn 0;\n+}\n+\n+static void free_message(struct commit_message *msg)\n+{\n+\tfree(msg->parent_label);\n+\tfree(msg->reencoded_message);\n+}\n+\n+static char *get_encoding(const char *message)\n+{\n+\tconst char *p = message, *eol;\n+\n+\twhile (*p && *p != '\\n') {\n+\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n+\t\t\t; /* do nothing */\n+\t\tif (!prefixcmp(p, \"encoding \")) {\n+\t\t\tchar *result = xmalloc(eol - 8 - p);\n+\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n+\t\t\treturn result;\n+\t\t}\n+\t\tp = eol;\n+\t\tif (*p == '\\n')\n+\t\t\tp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n+{\n+\tconst char *filename;\n+\tint fd;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n+\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n+\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n+\tstrbuf_release(&buf);\n+}\n+\n+static void print_advice(int show_hint)\n+{\n+\tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n+\n+\tif (msg) {\n+\t\tfprintf(stderr, \"%s\\n\", msg);\n+\t\t/*\n+\t\t * A conflict has occured but the porcelain\n+\t\t * (typically rebase --interactive) wants to take care\n+\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n+\t\t */\n+\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n+\t\treturn;\n+\t}\n+\n+\tif (show_hint) {\n+\t\tadvise(\"after resolving the conflicts, mark the corrected paths\");\n+\t\tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n+\t\tadvise(\"and commit the result with 'git commit'\");\n+\t}\n+}\n+\n+static void write_message(struct strbuf *msgbuf, const char *filename)\n+{\n+\tstatic struct lock_file msg_file;\n+\n+\tint msg_fd = hold_lock_file_for_update(&msg_file, filename,\n+\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n+\tif (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), filename);\n+\tstrbuf_release(msgbuf);\n+\tif (commit_lock_file(&msg_file) < 0)\n+\t\tdie(_(\"Error wrapping up %s\"), filename);\n+}\n+\n+static struct tree *empty_tree(void)\n+{\n+\treturn lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);\n+}\n+\n+static int error_dirty_index(struct replay_opts *opts)\n+{\n+\tif (read_cache_unmerged())\n+\t\treturn error_resolve_conflict(command_name(opts));\n+\n+\t/* Different translation strings for cherry-pick and revert */\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK)\n+\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n+\telse\n+\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n+\n+\tif (advice_commit_before_merge)\n+\t\tadvise(_(\"Commit your changes or stash them to proceed.\"));\n+\treturn -1;\n+}\n+\n+static int fast_forward_to(const unsigned char *to, const unsigned char *from)\n+{\n+\tstruct ref_lock *ref_lock;\n+\n+\tread_cache();\n+\tif (checkout_fast_forward(from, to))\n+\t\texit(1); /* the callee should have complained already */\n+\tref_lock = lock_any_ref_for_update(\"HEAD\", from, 0);\n+\treturn write_ref_sha1(ref_lock, to, \"cherry-pick\");\n+}\n+\n+static int do_recursive_merge(struct commit *base, struct commit *next,\n+\t\t\t      const char *base_label, const char *next_label,\n+\t\t\t      unsigned char *head, struct strbuf *msgbuf,\n+\t\t\t      struct replay_opts *opts)\n+{\n+\tstruct merge_options o;\n+\tstruct tree *result, *next_tree, *base_tree, *head_tree;\n+\tint clean, index_fd;\n+\tconst char **xopt;\n+\tstatic struct lock_file index_lock;\n+\n+\tindex_fd = hold_locked_index(&index_lock, 1);\n+\n+\tread_cache();\n+\n+\tinit_merge_options(&o);\n+\to.ancestor = base ? base_label : \"(empty tree)\";\n+\to.branch1 = \"HEAD\";\n+\to.branch2 = next ? next_label : \"(empty tree)\";\n+\n+\thead_tree = parse_tree_indirect(head);\n+\tnext_tree = next ? next->tree : empty_tree();\n+\tbase_tree = base ? base->tree : empty_tree();\n+\n+\tfor (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)\n+\t\tparse_merge_opt(&o, *xopt);\n+\n+\tclean = merge_trees(&o,\n+\t\t\t    head_tree,\n+\t\t\t    next_tree, base_tree, &result);\n+\n+\tif (active_cache_changed &&\n+\t    (write_cache(index_fd, active_cache, active_nr) ||\n+\t     commit_locked_index(&index_lock)))\n+\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n+\t\tdie(_(\"%s: Unable to write new index file\"), command_name(opts));\n+\trollback_lock_file(&index_lock);\n+\n+\tif (!clean) {\n+\t\tint i;\n+\t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n+\t\tfor (i = 0; i < active_nr;) {\n+\t\t\tstruct cache_entry *ce = active_cache[i++];\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tstrbuf_addch(msgbuf, '\\t');\n+\t\t\t\tstrbuf_addstr(msgbuf, ce->name);\n+\t\t\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n+\t\t\t\t\t\tactive_cache[i]->name))\n+\t\t\t\t\ti++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn !clean;\n+}\n+\n+/*\n+ * If we are cherry-pick, and if the merge did not result in\n+ * hand-editing, we will hit this commit and inherit the original\n+ * author date and name.\n+ * If we are revert, or if our cherry-pick results in a hand merge,\n+ * we had better say that the current user is responsible for that.\n+ */\n+static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n+{\n+\t/* 6 is max possible length of our args array including NULL */\n+\tconst char *args[6];\n+\tint i = 0;\n+\n+\targs[i++] = \"commit\";\n+\targs[i++] = \"-n\";\n+\tif (opts->signoff)\n+\t\targs[i++] = \"-s\";\n+\tif (!opts->edit) {\n+\t\targs[i++] = \"-F\";\n+\t\targs[i++] = defmsg;\n+\t}\n+\targs[i] = NULL;\n+\n+\treturn run_command_v_opt(args, RUN_GIT_CMD);\n+}\n+\n+static int do_pick_commit(struct commit *commit, enum replay_action action,\n+\t\t\tstruct replay_opts *opts)\n+{\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, NULL };\n+\tchar *defmsg = NULL;\n+\tstruct strbuf msgbuf = STRBUF_INIT;\n+\tint res;\n+\n+\tif (opts->no_commit) {\n+\t\t/*\n+\t\t * We do not intend to commit immediately.  We just want to\n+\t\t * merge the differences in, so let's compute the tree\n+\t\t * that represents the \"current\" state for merge-recursive\n+\t\t * to work on.\n+\t\t */\n+\t\tif (write_cache_as_tree(head, 0, NULL))\n+\t\t\tdie (_(\"Your index file is unmerged.\"));\n+\t} else {\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n+\t\tif (index_differs_from(\"HEAD\", 0))\n+\t\t\treturn error_dirty_index(opts);\n+\t}\n+\tdiscard_cache();\n+\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+\t\tstruct commit_list *p;\n+\n+\t\tif (!opts->mainline)\n+\t\t\treturn error(_(\"Commit %s is a merge but no -m option was given.\"),\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\n+\t\tfor (cnt = 1, p = commit->parents;\n+\t\t     cnt != opts->mainline && p;\n+\t\t     cnt++)\n+\t\t\tp = p->next;\n+\t\tif (cnt != opts->mainline || !p)\n+\t\t\treturn error(_(\"Commit %s does not have parent %d\"),\n+\t\t\t\tsha1_to_hex(commit->object.sha1), opts->mainline);\n+\t\tparent = p->item;\n+\t} else if (0 < opts->mainline)\n+\t\treturn error(_(\"Mainline was specified but commit %s is not a merge.\"),\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\telse\n+\t\tparent = commit->parents->item;\n+\n+\tif (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))\n+\t\treturn fast_forward_to(commit->object.sha1, head);\n+\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+\t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n+\t\t\tcommand_name(opts), sha1_to_hex(parent->object.sha1));\n+\n+\tif (get_message(commit, &msg) != 0)\n+\t\treturn error(_(\"Cannot get commit message for %s\"),\n+\t\t\tsha1_to_hex(commit->object.sha1));\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+\t * on top of the current HEAD if we are cherry-pick.  Or the\n+\t * reverse of it if we are revert.\n+\t */\n+\n+\tdefmsg = git_pathdup(\"MERGE_MSG\");\n+\n+\tif (action == REPLAY_REVERT) {\n+\t\tbase = commit;\n+\t\tbase_label = msg.label;\n+\t\tnext = parent;\n+\t\tnext_label = msg.parent_label;\n+\t\tstrbuf_addstr(&msgbuf, \"Revert \\\"\");\n+\t\tstrbuf_addstr(&msgbuf, msg.subject);\n+\t\tstrbuf_addstr(&msgbuf, \"\\\"\\n\\nThis reverts commit \");\n+\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n+\n+\t\tif (commit->parents && commit->parents->next) {\n+\t\t\tstrbuf_addstr(&msgbuf, \", reversing\\nchanges made to \");\n+\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));\n+\t\t}\n+\t\tstrbuf_addstr(&msgbuf, \".\\n\");\n+\t} else {\n+\t\tconst char *p;\n+\n+\t\tbase = parent;\n+\t\tbase_label = msg.parent_label;\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\tp += 2;\n+\t\t\tstrbuf_addstr(&msgbuf, p);\n+\t\t}\n+\n+\t\tif (opts->record_origin) {\n+\t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n+\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n+\t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n+\t\t}\n+\t}\n+\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || action == REPLAY_REVERT) {\n+\t\tres = do_recursive_merge(base, next, base_label, next_label,\n+\t\t\t\t\t head, &msgbuf, opts);\n+\t\twrite_message(&msgbuf, defmsg);\n+\t} else {\n+\t\tstruct commit_list *common = NULL;\n+\t\tstruct commit_list *remotes = NULL;\n+\n+\t\twrite_message(&msgbuf, defmsg);\n+\n+\t\tcommit_list_insert(base, &common);\n+\t\tcommit_list_insert(next, &remotes);\n+\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n+\t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n+\t\tfree_commit_list(common);\n+\t\tfree_commit_list(remotes);\n+\t}\n+\n+\t/*\n+\t * If the merge was clean or if it failed due to conflict, we write\n+\t * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.\n+\t * However, if the merge did not even start, then we don't want to\n+\t * write it at all.\n+\t */\n+\tif (action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n+\tif (action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n+\n+\tif (res) {\n+\t\terror(action == REPLAY_REVERT\n+\t\t      ? _(\"could not revert %s... %s\")\n+\t\t      : _(\"could not apply %s... %s\"),\n+\t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n+\t\t      msg.subject);\n+\t\tprint_advice(res == 1);\n+\t\trerere(opts->allow_rerere_auto);\n+\t} else {\n+\t\tif (!opts->no_commit)\n+\t\t\tres = run_git_commit(defmsg, opts);\n+\t}\n+\n+\tfree_message(&msg);\n+\tfree(defmsg);\n+\n+\treturn res;\n+}\n+\n+static void prepare_revs(struct replay_opts *opts)\n+{\n+\tif (opts->command != REPLAY_CMD_REVERT)\n+\t\topts->revs->reverse ^= 1;\n+\n+\tif (prepare_revision_walk(opts->revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tif (!opts->revs->commits)\n+\t\tdie(_(\"empty commit set passed\"));\n+}\n+\n+static void read_and_refresh_cache(struct replay_opts *opts)\n+{\n+\tstatic struct lock_file index_lock;\n+\tint index_fd = hold_locked_index(&index_lock, 0);\n+\tif (read_index_preload(&the_index, NULL) < 0)\n+\t\tdie(_(\"git %s: failed to read the index\"), command_name(opts));\n+\trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n+\tif (the_index.cache_changed) {\n+\t\tif (write_index(&the_index, index_fd) ||\n+\t\t    commit_locked_index(&index_lock))\n+\t\t\tdie(_(\"git %s: failed to refresh the index\"),\n+\t\t\t\tcommand_name(opts));\n+\t}\n+\trollback_lock_file(&index_lock);\n+}\n+\n+/*\n+ * Append a (commit, action) to the end of the replay_insn_list.\n+ *\n+ * next starts by pointing to the variable that holds the head of an\n+ * empty replay_insn_list, and is updated to point to the \"next\" field of\n+ * the last item on the list as new (commit, action) pairs are appended.\n+ *\n+ * Usage example:\n+ *\n+ *     struct replay_insn_list *list;\n+ *     struct replay_insn_list **next = &list;\n+ *\n+ *     next = replay_insn_list_append(c1, a1, next);\n+ *     next = replay_insn_list_append(c2, a2, next);\n+ *     assert(len(list) == 2);\n+ *     return list;\n+ */\n+static struct replay_insn_list **replay_insn_list_append(struct commit *operand,\n+\t\t\t\t\t\t\tenum replay_action action,\n+\t\t\t\t\t\t\tstruct replay_insn_list **next)\n+{\n+\tstruct replay_insn_list *new = xmalloc(sizeof(*new));\n+\tnew->action = action;\n+\tnew->operand = operand;\n+\t*next = new;\n+\tnew->next = NULL;\n+\treturn &new->next;\n+}\n+\n+static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n+{\n+\tstruct replay_insn_list *cur;\n+\n+\tfor (cur = todo_list; cur; cur = cur->next) {\n+\t\tconst char *sha1_abbrev, *action_str, *subject;\n+\t\tint subject_len;\n+\n+\t\taction_str = action_name(cur->action);\n+\t\tsha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);\n+\t\tsubject_len = find_commit_subject(cur->operand->buffer, &subject);\n+\t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n+\t\t\tsubject_len, subject);\n+\t}\n+\treturn 0;\n+}\n+\n+static int parse_insn_line(char *bol, char *eol,\n+\t\t\tstruct replay_insn_list *item, int lineno)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tunsigned char commit_sha1[20];\n+\tchar *end_of_object_name;\n+\tint saved, status;\n+\tsize_t error_len;\n+\n+\tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n+\t\titem->action = REPLAY_PICK;\n+\t\tbol += strlen(\"pick \");\n+\t} else if (!prefixcmp(bol, \"revert \") || !prefixcmp(bol, \"revert\\t\")) {\n+\t\titem->action = REPLAY_REVERT;\n+\t\tbol += strlen(\"revert \");\n+\t} else {\n+\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n+\t\treturn error(_(\"%s:%d: Unrecognized action: %.*s\"),\n+\t\t\ttodo_file, lineno, (int)error_len, bol);\n+\t}\n+\n+\t/* Eat up extra spaces/ tabs before object name */\n+\tbol += strspn(bol, \" \\t\");\n+\n+\tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n+\tsaved = *end_of_object_name;\n+\t*end_of_object_name = '\\0';\n+\tstatus = get_sha1(bol, commit_sha1);\n+\t*end_of_object_name = saved;\n+\n+\tif (status < 0) {\n+\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n+\t\treturn error(_(\"%s:%d: Malformed object name: %.*s\"),\n+\t\t\ttodo_file, lineno, (int)error_len, bol);\n+\t}\n+\n+\titem->operand = lookup_commit_reference(commit_sha1);\n+\tif (!item->operand) {\n+\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n+\t\treturn error(_(\"%s:%d: Not a valid commit: %.*s\"),\n+\t\t\ttodo_file, lineno, (int)error_len, bol);\n+\t}\n+\n+\titem->next = NULL;\n+\treturn 0;\n+}\n+\n+static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n+{\n+\tstruct replay_insn_list **next = todo_list;\n+\tstruct replay_insn_list item = { NULL, 0, NULL };\n+\tchar *p = buf;\n+\tint i;\n+\n+\tfor (i = 1; *p; i++) {\n+\t\tchar *eol = strchrnul(p, '\\n');\n+\t\tif (parse_insn_line(p, eol, &item, i) < 0)\n+\t\t\treturn -1;\n+\t\tnext = replay_insn_list_append(item.operand, item.action, next);\n+\t\tp = *eol ? eol + 1 : eol;\n+\t}\n+\tif (!*todo_list)\n+\t\treturn error(_(\"No commits parsed.\"));\n+\treturn 0;\n+}\n+\n+static void read_populate_todo(struct replay_insn_list **todo_list)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd, res;\n+\n+\tfd = open(todo_file, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open %s\"), todo_file);\n+\tif (strbuf_read(&buf, fd, 0) < 0) {\n+\t\tclose(fd);\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Could not read %s.\"), todo_file);\n+\t}\n+\tclose(fd);\n+\n+\tres = parse_insn_buffer(buf.buf, todo_list);\n+\tstrbuf_release(&buf);\n+\tif (res)\n+\t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n+}\n+\n+static int populate_opts_cb(const char *key, const char *value, void *data)\n+{\n+\tstruct replay_opts *opts = data;\n+\tint error_flag = 1;\n+\n+\tif (!value)\n+\t\terror_flag = 0;\n+\telse if (!strcmp(key, \"options.no-commit\"))\n+\t\topts->no_commit = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.edit\"))\n+\t\topts->edit = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.signoff\"))\n+\t\topts->signoff = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.record-origin\"))\n+\t\topts->record_origin = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.allow-ff\"))\n+\t\topts->allow_ff = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.mainline\"))\n+\t\topts->mainline = git_config_int(key, value);\n+\telse if (!strcmp(key, \"options.strategy\"))\n+\t\tgit_config_string(&opts->strategy, key, value);\n+\telse if (!strcmp(key, \"options.strategy-option\")) {\n+\t\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n+\t\topts->xopts[opts->xopts_nr++] = xstrdup(value);\n+\t} else\n+\t\treturn error(_(\"Invalid key: %s\"), key);\n+\n+\tif (!error_flag)\n+\t\treturn error(_(\"Invalid value for %s: %s\"), key, value);\n+\n+\treturn 0;\n+}\n+\n+static void read_populate_opts(struct replay_opts **opts_ptr)\n+{\n+\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n+\n+\tif (!file_exists(opts_file))\n+\t\treturn;\n+\tif (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)\n+\t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n+}\n+\n+static void walk_revs_populate_todo(struct replay_insn_list **todo_list,\n+\t\t\t\tstruct replay_opts *opts)\n+{\n+\tstruct commit *commit;\n+\tstruct replay_insn_list **next;\n+\tenum replay_action action;\n+\n+\tprepare_revs(opts);\n+\n+\tnext = todo_list;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n+\twhile ((commit = get_revision(opts->revs)))\n+\t\tnext = replay_insn_list_append(commit, action, next);\n+}\n+\n+static int create_seq_dir(void)\n+{\n+\tconst char *seq_dir = git_path(SEQ_DIR);\n+\n+\tif (file_exists(seq_dir)) {\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(seq_dir, 0777) < 0)\n+\t\tdie_errno(_(\"Could not create sequencer directory %s\"), seq_dir);\n+\treturn 0;\n+}\n+\n+static enum replay_command read_cmd(void)\n+{\n+\tconst char *cmd_file = git_path(SEQ_CMD_FILE);\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tenum replay_command res;\n+\tint fd;\n+\n+\tfd = open(cmd_file, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open %s\"), cmd_file);\n+\tif (strbuf_read(&buf, fd, 0) < 0) {\n+\t\tclose(fd);\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Could not read %s.\"), cmd_file);\n+\t}\n+\tclose(fd);\n+\n+\tif (!strcmp(buf.buf, \"revert\\n\"))\n+\t\tres = REPLAY_CMD_REVERT;\n+\telse if (!strcmp(buf.buf, \"cherry-pick\\n\"))\n+\t\tres = REPLAY_CMD_CHERRY_PICK;\n+\telse {\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Malformed command file: %s\"), cmd_file);\n+\t}\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n+static void save_cmd(struct replay_opts *opts)\n+{\n+\tconst char *cmd_file = git_path(SEQ_CMD_FILE);\n+\tstatic struct lock_file cmd_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&cmd_lock, cmd_file, LOCK_DIE_ON_ERROR);\n+\tstrbuf_addf(&buf, \"%s\\n\", command_name(opts));\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), cmd_file);\n+\tif (commit_lock_file(&cmd_lock) < 0)\n+\t\tdie(_(\"Error wrapping up %s.\"), cmd_file);\n+}\n+\n+static void save_head(const char *head)\n+{\n+\tconst char *head_file = git_path(SEQ_HEAD_FILE);\n+\tstatic struct lock_file head_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);\n+\tstrbuf_addf(&buf, \"%s\\n\", head);\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), head_file);\n+\tif (commit_lock_file(&head_lock) < 0)\n+\t\tdie(_(\"Error wrapping up %s.\"), head_file);\n+}\n+\n+static int reset_for_rollback(const unsigned char *sha1)\n+{\n+\tconst char *argv[4];\t/* reset --merge <arg> + NULL */\n+\targv[0] = \"reset\";\n+\targv[1] = \"--merge\";\n+\targv[2] = sha1_to_hex(sha1);\n+\targv[3] = NULL;\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n+static int rollback_single_pick(void)\n+{\n+\tunsigned char head_sha1[20];\n+\n+\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n+\t    !file_exists(git_path(\"REVERT_HEAD\")))\n+\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n+\tif (read_ref_full(\"HEAD\", head_sha1, 0, NULL))\n+\t\treturn error(_(\"cannot resolve HEAD\"));\n+\tif (is_null_sha1(head_sha1))\n+\t\treturn error(_(\"cannot abort from a branch yet to be born\"));\n+\treturn reset_for_rollback(head_sha1);\n+}\n+\n+static int sequencer_rollback(struct replay_opts *opts)\n+{\n+\tconst char *filename;\n+\tFILE *f;\n+\tunsigned char sha1[20];\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tfilename = git_path(SEQ_HEAD_FILE);\n+\tf = fopen(filename, \"r\");\n+\tif (!f && errno == ENOENT) {\n+\t\t/*\n+\t\t * There is no multiple-cherry-pick in progress.\n+\t\t * If CHERRY_PICK_HEAD or REVERT_HEAD indicates\n+\t\t * a single-cherry-pick in progress, abort that.\n+\t\t */\n+\t\treturn rollback_single_pick();\n+\t}\n+\tif (!f)\n+\t\treturn error(_(\"cannot open %s: %s\"), filename,\n+\t\t\t\t\t\tstrerror(errno));\n+\tif (strbuf_getline(&buf, f, '\\n')) {\n+\t\terror(_(\"cannot read %s: %s\"), filename, ferror(f) ?\n+\t\t\tstrerror(errno) : _(\"unexpected end of file\"));\n+\t\tfclose(f);\n+\t\tgoto fail;\n+\t}\n+\tfclose(f);\n+\tif (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\\0') {\n+\t\terror(_(\"stored pre-cherry-pick HEAD file '%s' is corrupt\"),\n+\t\t\tfilename);\n+\t\tgoto fail;\n+\t}\n+\tif (reset_for_rollback(sha1))\n+\t\tgoto fail;\n+\tremove_sequencer_state();\n+\tstrbuf_release(&buf);\n+\treturn 0;\n+fail:\n+\tstrbuf_release(&buf);\n+\treturn -1;\n+}\n+\n+static void save_todo(struct replay_insn_list *todo_list)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tstatic struct lock_file todo_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n+\tif (format_todo(&buf, todo_list) < 0)\n+\t\tdie(_(\"Could not format %s.\"), todo_file);\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n+\t\tstrbuf_release(&buf);\n+\t\tdie_errno(_(\"Could not write to %s\"), todo_file);\n+\t}\n+\tif (commit_lock_file(&todo_lock) < 0) {\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Error wrapping up %s.\"), todo_file);\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n+static void save_opts(struct replay_opts *opts)\n+{\n+\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n+\n+\tif (opts->no_commit)\n+\t\tgit_config_set_in_file(opts_file, \"options.no-commit\", \"true\");\n+\tif (opts->edit)\n+\t\tgit_config_set_in_file(opts_file, \"options.edit\", \"true\");\n+\tif (opts->signoff)\n+\t\tgit_config_set_in_file(opts_file, \"options.signoff\", \"true\");\n+\tif (opts->record_origin)\n+\t\tgit_config_set_in_file(opts_file, \"options.record-origin\", \"true\");\n+\tif (opts->allow_ff)\n+\t\tgit_config_set_in_file(opts_file, \"options.allow-ff\", \"true\");\n+\tif (opts->mainline) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n+\t\tgit_config_set_in_file(opts_file, \"options.mainline\", buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t}\n+\tif (opts->strategy)\n+\t\tgit_config_set_in_file(opts_file, \"options.strategy\", opts->strategy);\n+\tif (opts->xopts) {\n+\t\tint i;\n+\t\tfor (i = 0; i < opts->xopts_nr; i++)\n+\t\t\tgit_config_set_multivar_in_file(opts_file,\n+\t\t\t\t\t\t\t\"options.strategy-option\",\n+\t\t\t\t\t\t\topts->xopts[i], \"^$\", 0);\n+\t}\n+}\n+\n+static int pick_commits(struct replay_insn_list *todo_list, struct replay_opts *opts)\n+{\n+\tstruct replay_insn_list *cur;\n+\tint res;\n+\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n+\tif (opts->allow_ff)\n+\t\tassert(!(opts->signoff || opts->no_commit ||\n+\t\t\t\topts->record_origin || opts->edit));\n+\tread_and_refresh_cache(opts);\n+\n+\tfor (cur = todo_list; cur; cur = cur->next) {\n+\t\tsave_todo(cur);\n+\t\tres = do_pick_commit(cur->operand, cur->action, opts);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\t/*\n+\t * Sequence of picks finished successfully; cleanup by\n+\t * removing the .git/sequencer directory\n+\t */\n+\tremove_sequencer_state();\n+\treturn 0;\n+}\n+\n+static int continue_single_pick(void)\n+{\n+\tconst char *argv[] = { \"commit\", NULL };\n+\n+\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n+\t    !file_exists(git_path(\"REVERT_HEAD\")))\n+\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n+static int sequencer_continue(struct replay_opts *opts)\n+{\n+\tstruct replay_insn_list *todo_list = NULL;\n+\tenum replay_command cmd;\n+\n+\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n+\t\treturn continue_single_pick();\n+\n+\t/*\n+\t * Disallow continuing a cherry-pick with 'git revert\n+\t * --continue' and viceversa\n+\t */\n+\tcmd = read_cmd();\n+\tif (cmd != opts->command)\n+\t\treturn error(_(\"cannot %s: a %s is in progress.\"),\n+\t\t\tcommand_name(opts),\n+\t\t\tcmd == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\");\n+\n+\tread_populate_opts(&opts);\n+\tread_populate_todo(&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\tint ret = continue_single_pick();\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\tif (index_differs_from(\"HEAD\", 0))\n+\t\treturn error_dirty_index(opts);\n+\ttodo_list = todo_list->next;\n+\treturn pick_commits(todo_list, opts);\n+}\n+\n+static int single_pick(struct commit *cmit, struct replay_opts *opts)\n+{\n+\tenum replay_action action;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n+\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n+\treturn do_pick_commit(cmit, action, opts);\n+}\n+\n+int sequencer_pick_revisions(struct replay_opts *opts)\n+{\n+\tstruct replay_insn_list *todo_list = NULL;\n+\tunsigned char sha1[20];\n+\n+\tif (opts->subcommand == REPLAY_NONE)\n+\t\tassert(opts->revs);\n+\n+\tread_and_refresh_cache(opts);\n+\n+\t/*\n+\t * Decide what to do depending on the arguments; a fresh\n+\t * cherry-pick should be handled differently from an existing\n+\t * one that is being continued\n+\t */\n+\tif (opts->subcommand == REPLAY_REMOVE_STATE) {\n+\t\tremove_sequencer_state();\n+\t\treturn 0;\n+\t}\n+\tif (opts->subcommand == REPLAY_ROLLBACK)\n+\t\treturn sequencer_rollback(opts);\n+\tif (opts->subcommand == REPLAY_CONTINUE)\n+\t\treturn sequencer_continue(opts);\n+\n+\t/*\n+\t * If we were called as \"git cherry-pick <commit>\", just\n+\t * cherry-pick/revert it, set CHERRY_PICK_HEAD /\n+\t * REVERT_HEAD, and don't touch the sequencer state.\n+\t * This means it is possible to cherry-pick in the middle\n+\t * of a cherry-pick sequence.\n+\t */\n+\tif (opts->revs->cmdline.nr == 1 &&\n+\t    opts->revs->cmdline.rev->whence == REV_CMD_REV &&\n+\t    opts->revs->no_walk &&\n+\t    !opts->revs->cmdline.rev->flags) {\n+\t\tstruct commit *cmit;\n+\t\tif (prepare_revision_walk(opts->revs))\n+\t\t\tdie(_(\"revision walk setup failed\"));\n+\t\tcmit = get_revision(opts->revs);\n+\t\tif (!cmit || get_revision(opts->revs))\n+\t\t\tdie(\"BUG: expected exactly one commit from walk\");\n+\t\treturn single_pick(cmit, opts);\n+\t}\n+\n+\t/*\n+\t * Start a new cherry-pick/ revert sequence; but\n+\t * first, make sure that an existing one isn't in\n+\t * progress\n+\t */\n+\n+\twalk_revs_populate_todo(&todo_list, opts);\n+\tif (create_seq_dir() < 0)\n+\t\treturn -1;\n+\tif (get_sha1(\"HEAD\", sha1)) {\n+\t\tif (opts->command == REPLAY_CMD_REVERT)\n+\t\t\treturn error(_(\"Can't revert as initial commit\"));\n+\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n+\t}\n+\tsave_cmd(opts);\n+\tsave_head(sha1_to_hex(sha1));\n+\tsave_opts(opts);\n+\treturn pick_commits(todo_list, opts);\n+}\ndiff --git a/sequencer.h b/sequencer.h\nindex d1cb5c2..58482f1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -24,6 +24,29 @@ enum replay_subcommand {\n \tREPLAY_ROLLBACK\n };\n \n+struct replay_opts {\n+\tenum replay_command command;\n+\tenum replay_subcommand subcommand;\n+\n+\t/* Boolean options */\n+\tint edit;\n+\tint record_origin;\n+\tint no_commit;\n+\tint signoff;\n+\tint allow_ff;\n+\tint allow_rerere_auto;\n+\n+\tint mainline;\n+\n+\t/* Merge strategy */\n+\tconst char *strategy;\n+\tconst char **xopts;\n+\tsize_t xopts_nr, xopts_alloc;\n+\n+\t/* Only used by REPLAY_NONE */\n+\tstruct rev_info *revs;\n+};\n+\n struct replay_insn_list {\n \tstruct commit *operand;\n \tenum replay_action action;\n@@ -33,4 +56,6 @@ struct replay_insn_list {\n /* Removes SEQ_DIR. */\n extern void remove_sequencer_state(void);\n \n+int sequencer_pick_revisions(struct replay_opts *opts);\n+\n #endif\n-- \n1.7.8.2\n"},{"id":"182107","messageId":"20120108192853.GE1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 0/6] The move to sequencer.c","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T19:28:53Z","receivedAt":"2012-01-08T19:28:53Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"(-cc: Junio)\nRamkumar Ramachandra wrote:\n\n> I've tried a slightly different approach: the objective of the patches\n> seem to be much clearer this time.\n\nFor the sake of new and forgetful readers: what is that objective?\n"},{"id":"182108","messageId":"20120108193110.GF1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-2-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 1/6] revert: move replay_action, replay_subcommand to header","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T19:31:10Z","receivedAt":"2012-01-08T19:31:10Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Our plan to build a sequencer involves leaving the revert builtin with\n> just argument parsing work.  Since the enums replay_action and\n> replay_subcommand have nothing to do with this argument parsing, move\n> them to sequencer.h in advance.\n>\n> \"REVERT\" and \"CHERRY_PICK\" are unsuitable variable names for exposing\n> publicly, as their purpose is unclear in the global context: rename\n> them to \"REPLAY_REVERT\" and \"REPLAY_PICK\" respectively.\n\nMy first reaction: this probably would be more self-explanatory if\nsquashed with the patch the moves the rest of the code to sequencer.[ch].\n\nSecond reaction: ah, but the s/REVERT/REPLAY_REVERT/ and\ns/CHERRY_PICK/REPLAY_PICK/ changes would be lost in the noise of the move.\nMaybe it would be possible to make those changes but keep the enum in\nbuiltin/revert.c, explaining that this is a preparatory step and they\nwill be moving in a few moments?\n\nHope that helps,\nJonathan\n"},{"id":"182109","messageId":"20120108193454.GG1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-3-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 2/6] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T19:34:54Z","receivedAt":"2012-01-08T19:34:54Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n>                                                  So, while a future\n> instruction sheet would look like:\n>\n>   pick next~4\n>   action3 b74fea\n>   revert rr/moo^2~34\n>\n> the actions \"pick\", \"action3\" and \"revert\" need not necessarily\n> correspond to the specific builtins.\n\nSo what change does the patch actually make?  Is this a renaming?\n\n[...]\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n[...]\n> @@ -64,16 +64,21 @@ struct replay_opts {\n>  \n>  #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n>  \n> -static const char *action_name(const struct replay_opts *opts)\n> +static const char *command_name(struct replay_opts *opts)\n\nWhy is the const being dropped?  I'm lost, so not reading further.\n"},{"id":"182110","messageId":"20120108193749.GH1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-4-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 3/6] revert: don't let revert continue a cherry-pick","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T19:37:49Z","receivedAt":"2012-01-08T19:37:49Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> When we allow mixing \"revert\" and \"pick\" instructions in the same\n> sheet in the next patch, the following workflow would be perfectly\n> valid:\n>\n>   $ git cherry-pick base..latercommit\n>   [conflict occurs]\n>   $ edit problematicfile\n>   $ git add problematicfile\n>   $ git revert --continue\n>   [finishes successfully]\n\nDoes \"workflow\" mean \"sequence of commands\"?\n\n> This is confusing to the operator, because the sequencer is an\n> implementation detail hidden behind the 'git cherry-pick' and 'git\n> revert' builtins.\n\nI don't know --- it's not confusing to me.  Could you explain further\nwhat harm the current behavior does?  E.g., could it cause me to\nmisunderstand some basic concepts, or could it lead me to run commands\nthat cause me to scratch my head or lose data?\n\n>  builtin/revert.c                |   57 +++++++++++++++++++++++++++++++++++++++\n>  sequencer.h                     |    1 +\n>  t/t3510-cherry-pick-sequence.sh |   11 +++++++\n>  3 files changed, 69 insertions(+), 0 deletions(-)\n\nI haven't read the patch, but based on the above rationale and this\ndiffstat, it doesn't look good.\n"},{"id":"182111","messageId":"20120108194014.GI1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-5-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 4/6] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T19:40:14Z","receivedAt":"2012-01-08T19:40:14Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Parse the instruction sheet in '.git/sequencer/todo' as a list of\n> (action, operand) pairs, instead of assuming that all lines have the\n> same action.\n\nYes, I've always liked this one.\n\nHaven't re-read the patch to avoid wasted effort if there are changes\nwhen the previous patches in the series change.  Maybe it would be\npossible to send as a standalone?\n"},{"id":"182112","messageId":"CALkWK0=xhnXq4_uEAri74Kk9zbAgiS+z-nG7Etm3MCo2cXaNPw@mail.gmail.com","threadId":"29315","inReplyTo":"20120108192853.GE1942@burratino","subject":"Re: [PATCH 0/6] The move to sequencer.c","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T19:51:50Z","receivedAt":"2012-01-08T19:51:50Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> For the sake of new and forgetful readers: what is that objective?\n\nThe objective is to build a generalized sequencer, of which \"git\nrevert\" and \"git cherry-pick\" are two special cases.  We can later\nextend it to encompass more builtins and other commands.  Why?  In\ngeneral, we want uniform semantics to continue, skip, and abort any\noperation that requires user-intervention (example: a conflict that\nneeds to be resolved before proceeding).  A lot of this work is\ninspired by the way \"rebase -i\" works: it presents the user an\ninstruction sheet with a sequence of actions to perform.  Example\nfuture use cases:\n\n  $ git cherry-pick moo..foo\n  [conflict]\n  $ edit problematicfile\n  $ git add problematicfile\n  $ git continue\n  [finishes successfully]\n\n  $ git revert moo..foo\n  [conflict]\n  $ edit problematicfile\n  $ git add problematicfile\n  $ git continue\n  [finishes successfully]\n\nIn these examples, the instruction sheet is uniformly filled with\n\"pick\" or \"revert\" actions, which is not very interesting.  When we\nget an interface to allow easy editing of the instruction sheet and\nencompass more builtins, more interesting sequences of operations will\nbe possible like:\n\n  pick rr/revert-cherry-pick^2~34\n  revert master@{12}\n  merge next\n  am /tmp/jrn.patch\n\nThanks.\n\n-- Ram\n"},{"id":"182113","messageId":"CALkWK0kHn+SDaeRHa8bUHWvOEVkr01sVDzvnw9E+-Nne2cii1Q@mail.gmail.com","threadId":"29315","inReplyTo":"20120108193454.GG1942@burratino","subject":"Re: [PATCH 2/6] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T19:53:43Z","receivedAt":"2012-01-08T19:53:43Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>\n>>                                                  So, while a future\n>> instruction sheet would look like:\n>>\n>>   pick next~4\n>>   action3 b74fea\n>>   revert rr/moo^2~34\n>>\n>> the actions \"pick\", \"action3\" and \"revert\" need not necessarily\n>> correspond to the specific builtins.\n>\n> So what change does the patch actually make?  Is this a renaming?\n\nYes, it renames \"action\" to \"command\" where appropriate.\n\n> [...]\n>> --- a/builtin/revert.c\n>> +++ b/builtin/revert.c\n> [...]\n>> @@ -64,16 +64,21 @@ struct replay_opts {\n>>\n>>  #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n>>\n>> -static const char *action_name(const struct replay_opts *opts)\n>> +static const char *command_name(struct replay_opts *opts)\n>\n> Why is the const being dropped?  I'm lost, so not reading further.\n\nMinor error.  The rest of the patch should be fine.\n\n-- Ram\n"},{"id":"182116","messageId":"CALkWK0=-AWy7HnVASB1rt8njavTYOhV7Zxsdq4TE+VShVZmEzQ@mail.gmail.com","threadId":"29315","inReplyTo":"20120108193749.GH1942@burratino","subject":"Re: [PATCH 3/6] revert: don't let revert continue a cherry-pick","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T20:03:41Z","receivedAt":"2012-01-08T20:03:41Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>\n>> When we allow mixing \"revert\" and \"pick\" instructions in the same\n>> sheet in the next patch, the following workflow would be perfectly\n>> valid:\n>>\n>>   $ git cherry-pick base..latercommit\n>>   [conflict occurs]\n>>   $ edit problematicfile\n>>   $ git add problematicfile\n>>   $ git revert --continue\n>>   [finishes successfully]\n>\n> Does \"workflow\" mean \"sequence of commands\"?\n\nYes.  Clarified wording.\n\n>> This is confusing to the operator, because the sequencer is an\n>> implementation detail hidden behind the 'git cherry-pick' and 'git\n>> revert' builtins.\n>\n> I don't know --- it's not confusing to me.  Could you explain further\n> what harm the current behavior does?  E.g., could it cause me to\n> misunderstand some basic concepts, or could it lead me to run commands\n> that cause me to scratch my head or lose data?\n\nJunio explained this to me in [1].  It's very unnatural for a user to\nwant to execute \"git cherry-pick --continue\" when the previous command\nwas a \"git revert\": it probably means that she forgot about the\nin-progress \"git revert\".  The problem becomes more serious when the\nsequencer grows more capabilities: a \"git merge --continue\" to\ncontinue a \"git am\" sounds much more absurd.  Ofcourse, we will\nprovide a way to continue any sequencer operation in the future: \"git\ncontinue\" seems to be a good candidate.\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/185355\n\nThanks.\n\n-- Ram\n"},{"id":"182114","messageId":"20120108200748.GJ1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-6-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 5/6] revert: report fine-grained error messages from insn parser","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:07:48Z","receivedAt":"2012-01-08T20:07:48Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Three kinds of errors can arise from parsing '.git/sequencer/todo':\n> 1. Unrecognized action\n> 2. Malformed object name\n> 3. Object name does not refer to a valid commit\n>\n> Since we would like to make the instruction sheet user-editable in the\n> future (much like the 'rebase -i' sheet), report more fine-grained\n> parse errors prefixed with the filename and line number.\n\nSeems like a sensible idea.  In other words, the infrastructure that\nparses .git/sequencer/todo is meant to handle arbitrary user input\nsome day, so it can be used as the implementation of \"git rebase\n--interactive\" and \"git sequence --edit\", and currently it is\nsuboptimal for that purpose because the parse error messages just say\n\n\terror: Could not parse line 5.\n\nThis patch shifts responsibility to parse_insn_line(), which can come\nup with a more detailed message like\n\n\terror: .git/sequencer/todo:5: unrecognized action \"frobnicate\"\n\nOnce the operator is allowed to edit the sequence, the message might\nbe adjusted to something meaning\n\n\terror: <sequence you just gave me>:5: unrecognized action \"frobnicate\"\n\ninstead of exposing an implementation detail, or maybe some day \"git\nsequence --edit\" could even re-launch the editor with an error message\nin a comment before the problematic line and the cursor pointing\nthere.  And for now, pointing to the explicit filename is useful since\nthis should only happen if there was filesystem corruption, tampering,\nor a git bug.\n\nBy the way, an example of the output before and after the patch would\nhave been useful in saving some trouble of figuring this out.\n\nOk, onward to the patch.\n\n[...]\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -714,26 +714,29 @@ static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n>  \treturn 0;\n>  }\n>  \n> -static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n> +static int parse_insn_line(char *bol, char *eol,\n> +\t\t\tstruct replay_insn_list *item, int lineno)\n>  {\n> +\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n\nThis idiom is _still_ scary.  I don't want to have to shout about\nthis, but why didn't the commit message at least acknowledge it to put\nthe reader's mind at ease when this has come up again and again?\n\n[...]\n> +\t\tbol += strlen(\"revert \");\n> +\t} else {\n> +\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n> +\t\treturn error(_(\"%s:%d: Unrecognized action: %.*s\"),\n> +\t\t\ttodo_file, lineno, (int)error_len, bol);\n\nAh, so my example above was wrong: the actual message is\n\n\terror: .git/sequencer/todo:5: Unrecognized action: the quick bro\n\twn fox jumps over the lazy dog\n\nI guess that's fine.  Is it intended?\n\n> +\t}\n>  \n>  \t/* Eat up extra spaces/ tabs before object name */\n> -\tpadding = strspn(bol, \" \\t\");\n> -\tif (!padding)\n> -\t\treturn -1;\n> -\tbol += padding;\n> +\tbol += strspn(bol, \" \\t\");\n\nWhat does this have to do with the stated goal of the patch?  It seems\nto me that an unrelated and unadvertised bugfix snuck in.\n\n[...]\n> @@ -741,12 +744,18 @@ static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n>  \tstatus = get_sha1(bol, commit_sha1);\n>  \t*end_of_object_name = saved;\n>  \n> -\tif (status < 0)\n> -\t\treturn -1;\n> +\tif (status < 0) {\n> +\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n> +\t\treturn error(_(\"%s:%d: Malformed object name: %.*s\"),\n> +\t\t\ttodo_file, lineno, (int)error_len, bol);\n> +\t}\n\nThis seems a little repetitive --- maybe it would make sense to\nfactor out a function to emit errors of the form\n\n\terror: file:lineno: message: line\n\nBy the way, this is gross.  Probably get_sha1 should grow a variant\nthat takes a buffer and a length.\n\n[...]\n>  \titem->operand = lookup_commit_reference(commit_sha1);\n> -\tif (!item->operand)\n> -\t\treturn -1;\n> +\tif (!item->operand) {\n> +\t\terror_len = eol - bol > 255 ? 255 : eol - bol;\n> +\t\treturn error(_(\"%s:%d: Not a valid commit: %.*s\"),\n> +\t\t\ttodo_file, lineno, (int)error_len, bol);\n> +\t}\n\nHmm, this one can be emitted even when there was no corruption or\ninternal error, because the user removed a commit she was\ncherry-picking and the gc-ed before a \"git cherry-pick --continue\".\nAlternatively, it can happen because the repository has grown very\ncrowded and what used to be an unambiguous commit name no longer is\none (not enough digits).  Will the error message be intuitive in these\nsituations?\n\n[...]\n> @@ -761,8 +770,8 @@ static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n>  \n>  \tfor (i = 1; *p; i++) {\n>  \t\tchar *eol = strchrnul(p, '\\n');\n> -\t\tif (parse_insn_line(p, eol, &item) < 0)\n> -\t\t\treturn error(_(\"Could not parse line %d.\"), i);\n> +\t\tif (parse_insn_line(p, eol, &item, i) < 0)\n> +\t\t\treturn -1;\n\nNot related to this patch, but why the \"< 0\" test?  It makes this\nreader worry that there is some unusual return value convention that\nhe should be taking into account.\n\nTo sum up: the idea looks promising, but this patch doesn't seem to be\nready yet.\n\nHope that helps,\nJonathan\n"},{"id":"182117","messageId":"CALkWK0=kw=EEXDfnrFkeNV678r4u3v72-=hKh9w8ujRN125NPQ@mail.gmail.com","threadId":"29315","inReplyTo":"20120108200910.GK1942@burratino","subject":"Re: [PATCH 2/6] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T20:07:52Z","receivedAt":"2012-01-08T20:07:52Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>> Jonathan Nieder wrote:\n>\n>>> So what change does the patch actually make?  Is this a renaming?\n>>\n>> Yes, it renames \"action\" to \"command\" where appropriate.\n>\n> Wouldn't a simple renaming have a diffstat with the same number of added\n> and removed lines?\n\nYes, almost.  A few extra lines added because I needed a new data enum\nfor the \"command\"; also added a convenience function: action_name().\n\n-- Ram\n"},{"id":"182115","messageId":"20120108200910.GK1942@burratino","threadId":"29315","inReplyTo":"CALkWK0kHn+SDaeRHa8bUHWvOEVkr01sVDzvnw9E+-Nne2cii1Q@mail.gmail.com","subject":"Re: [PATCH 2/6] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:09:10Z","receivedAt":"2012-01-08T20:09:10Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n\n>> So what change does the patch actually make?  Is this a renaming?\n>\n> Yes, it renames \"action\" to \"command\" where appropriate.\n\nWouldn't a simple renaming have a diffstat with the same number of added\nand removed lines?\n"},{"id":"182118","messageId":"CALkWK0mStgcb4EBB+ni9fisDJY=13cJZWCTEcgfyOUyAXbc=tA@mail.gmail.com","threadId":"29315","inReplyTo":"20120108200748.GJ1942@burratino","subject":"Re: [PATCH 5/6] revert: report fine-grained error messages from insn parser","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T20:16:07Z","receivedAt":"2012-01-08T20:16:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>\n>> Three kinds of errors can arise from parsing '.git/sequencer/todo':\n>> 1. Unrecognized action\n>> 2. Malformed object name\n>> 3. Object name does not refer to a valid commit\n>>\n>> Since we would like to make the instruction sheet user-editable in the\n>> future (much like the 'rebase -i' sheet), report more fine-grained\n>> parse errors prefixed with the filename and line number.\n>\n> Seems like a sensible idea.  In other words,\n> [...]\n\nThanks for the new wording.\n\n> [...]\n>> --- a/builtin/revert.c\n>> +++ b/builtin/revert.c\n>> @@ -714,26 +714,29 @@ static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n>>       return 0;\n>>  }\n>>\n>> -static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n>> +static int parse_insn_line(char *bol, char *eol,\n>> +                     struct replay_insn_list *item, int lineno)\n>>  {\n>> +     const char *todo_file = git_path(SEQ_TODO_FILE);\n>\n> This idiom is _still_ scary.  I don't want to have to shout about\n> this, but why didn't the commit message at least acknowledge it to put\n> the reader's mind at ease when this has come up again and again?\n\nCarried over from the previous re-roll; sorry I didn't pay enough attention.\n\n> [...]\n>> +             bol += strlen(\"revert \");\n>> +     } else {\n>> +             error_len = eol - bol > 255 ? 255 : eol - bol;\n>> +             return error(_(\"%s:%d: Unrecognized action: %.*s\"),\n>> +                     todo_file, lineno, (int)error_len, bol);\n>\n> Ah, so my example above was wrong: the actual message is\n>\n>        error: .git/sequencer/todo:5: Unrecognized action: the quick bro\n>        wn fox jumps over the lazy dog\n>\n> I guess that's fine.  Is it intended?\n>\n>> +     }\n>>\n>>       /* Eat up extra spaces/ tabs before object name */\n>> -     padding = strspn(bol, \" \\t\");\n>> -     if (!padding)\n>> -             return -1;\n>> -     bol += padding;\n>> +     bol += strspn(bol, \" \\t\");\n>\n> What does this have to do with the stated goal of the patch?  It seems\n> to me that an unrelated and unadvertised bugfix snuck in.\n\nNot a bugfix: since I have to report sensible error messages now, I\nchanged the \"pick\" and \"revert\" checks to \"pick \" || \"pick\\t\" and\n\"revert \" || \"revert\\t\" -- then, I can report \"invalid action\" if it\ndoesn't match instead of a useless \"missing space after action\".\n\n> [...]\n> By the way, this is gross.  Probably get_sha1 should grow a variant\n> that takes a buffer and a length.\n\nYes; will do.\n\n> [...]\n>>       item->operand = lookup_commit_reference(commit_sha1);\n>> -     if (!item->operand)\n>> -             return -1;\n>> +     if (!item->operand) {\n>> +             error_len = eol - bol > 255 ? 255 : eol - bol;\n>> +             return error(_(\"%s:%d: Not a valid commit: %.*s\"),\n>> +                     todo_file, lineno, (int)error_len, bol);\n>> +     }\n>\n> Hmm, this one can be emitted even when there was no corruption or\n> internal error, because the user removed a commit she was\n> cherry-picking and the gc-ed before a \"git cherry-pick --continue\".\n> Alternatively, it can happen because the repository has grown very\n> crowded and what used to be an unambiguous commit name no longer is\n> one (not enough digits).  Will the error message be intuitive in these\n> situations?\n\nSomething like \"Unable to look up commit: %s\" perhaps?\n\n> [...]\n>> @@ -761,8 +770,8 @@ static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n>>\n>>       for (i = 1; *p; i++) {\n>>               char *eol = strchrnul(p, '\\n');\n>> -             if (parse_insn_line(p, eol, &item) < 0)\n>> -                     return error(_(\"Could not parse line %d.\"), i);\n>> +             if (parse_insn_line(p, eol, &item, i) < 0)\n>> +                     return -1;\n>\n> Not related to this patch, but why the \"< 0\" test?  It makes this\n> reader worry that there is some unusual return value convention that\n> he should be taking into account.\n\nRight.  Will fix.\n\nThanks.\n\n-- Ram\n"},{"id":"182120","messageId":"CALkWK0mYbBsZN1UX9YM0VWQezZsBpJCcEgKirCggtNXs0HZ-8g@mail.gmail.com","threadId":"29315","inReplyTo":"20120108194014.GI1942@burratino","subject":"Re: [PATCH 4/6] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T20:17:33Z","receivedAt":"2012-01-08T20:17:33Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>\n>> Parse the instruction sheet in '.git/sequencer/todo' as a list of\n>> (action, operand) pairs, instead of assuming that all lines have the\n>> same action.\n>\n> Yes, I've always liked this one.\n>\n> Haven't re-read the patch to avoid wasted effort if there are changes\n> when the previous patches in the series change.  Maybe it would be\n> possible to send as a standalone?\n\nIf I don't get manage to get the series right in a couple of re-rolls,\nI'll do that.\n\n-- Ram\n"},{"id":"182119","messageId":"20120108202216.GL1942@burratino","threadId":"29315","inReplyTo":"CALkWK0=-AWy7HnVASB1rt8njavTYOhV7Zxsdq4TE+VShVZmEzQ@mail.gmail.com","subject":"Re: [PATCH 3/6] revert: don't let revert continue a cherry-pick","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:22:16Z","receivedAt":"2012-01-08T20:22:16Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n\n>> I don't know --- it's not confusing to me.  Could you explain further\n>> what harm the current behavior does?  E.g., could it cause me to\n>> misunderstand some basic concepts, or could it lead me to run commands\n>> that cause me to scratch my head or lose data?\n>\n> Junio explained this to me in [1].  It's very unnatural for a user to\n> want to execute \"git cherry-pick --continue\" when the previous command\n> was a \"git revert\": it probably means that she forgot about the\n> in-progress \"git revert\".\n[...]\n> [1]: http://thread.gmane.org/gmane.comp.version-control.git/185355\n\nI don't think that's what Junio said.\n\nDid this actually happen, or is it a theoretical worry?  I think I would\nbe more likely to run \"git cherry-pick <foo>..<bar>\" than \"git\ncherry-pick --continue\" if I had forgotten about an in-progress\nrevert.  The former already errors out with a sensible message.\n\nOr is the problem that I might run:\n\n\tgit revert foo..bar\n\tgit reset --merge; # conflict --- let's clean this up\n\n\t# ah, I remember reverting the patch that conflicted before;\n\t# let's reuse the resolution.\n\tgit cherry-pick baz\n\tedit file.c; # another conflict, sigh\n\tgit add file.c\n\tgit cherry-pick --continue; # oops!\n\n?  That seems like a real worry, but the same problem could happen\nwith cherry-pick used both for the multipick and single-pick, so I\ndon't think your patch fundamentally addresses it.\n\nIn other words, this is a problem caused by the overloading of the\nsame cherry-pick command for single-pick and multi-pick.  I think it\nshould be preventable by remembering which action failed when stopping\na sequence and doing only a single-pick resume if\nCHERRY_PICK_HEAD/REVERT_HEAD/whatever doesn't match that.\n\nThe \"oops\" is bad since the operator might have been intending to run\nsome more tests and amend as necessary before continuing the\nmulti-pick.  It is not _that_ bad, since more typically one would have\nalready run some tests before running cherry-pick --continue to commit\nthe resolution.  Still probably worth fixing.\n\n> The problem becomes more serious when the\n> sequencer grows more capabilities: a \"git merge --continue\" to\n> continue a \"git am\" sounds much more absurd.  Ofcourse, we will\n> provide a way to continue any sequencer operation in the future: \"git\n> continue\" seems to be a good candidate.\n\nI don't understand why \"cherry-pick --continue\" resuming a revert\nsequence implies that \"merge --continue\" would have to as well.\n\nAll that said, forbidding cherry-pick --continue from resuming a\nrevert sequence would be fine with me, _as long as the semantics are\nclearly spelled out in the commit message and documentation_.  What\nhappens when there is a mixture of picks and reverts?\n\nThanks.\nJonathan\n"},{"id":"182121","messageId":"CALkWK0kwbzXRyFf=JjfAW9yD7M_FTB80+q1UPOCv-Em4qO2RKQ@mail.gmail.com","threadId":"29315","inReplyTo":"20120108202216.GL1942@burratino","subject":"Re: [PATCH 3/6] revert: don't let revert continue a cherry-pick","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-08T20:28:07Z","receivedAt":"2012-01-08T20:28:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi again,\n\nJonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>> Jonathan Nieder wrote:\n> [...]\n>> Junio explained this to me in [1].  It's very unnatural for a user to\n>> want to execute \"git cherry-pick --continue\" when the previous command\n>> was a \"git revert\": it probably means that she forgot about the\n>> in-progress \"git revert\".\n> [...]\n>> [1]: http://thread.gmane.org/gmane.comp.version-control.git/185355\n>\n> I don't think that's what Junio said.\n>\n> Did this actually happen, or is it a theoretical worry?  I think I would\n> be more likely to run \"git cherry-pick <foo>..<bar>\" than \"git\n> cherry-pick --continue\" if I had forgotten about an in-progress\n> revert.  The former already errors out with a sensible message.\n>\n> Or is the problem that I might run:\n>\n>        git revert foo..bar\n>        git reset --merge; # conflict --- let's clean this up\n>\n>        # ah, I remember reverting the patch that conflicted before;\n>        # let's reuse the resolution.\n>        git cherry-pick baz\n>        edit file.c; # another conflict, sigh\n>        git add file.c\n>        git cherry-pick --continue; # oops!\n>\n> ?  That seems like a real worry, but the same problem could happen\n> with cherry-pick used both for the multipick and single-pick, so I\n> don't think your patch fundamentally addresses it.\n\nGood catch.  I didn't replay this scenario in my head earlier.\n\n> In other words, this is a problem caused by the overloading of the\n> same cherry-pick command for single-pick and multi-pick.  I think it\n> should be preventable by remembering which action failed when stopping\n> a sequence and doing only a single-pick resume if\n> CHERRY_PICK_HEAD/REVERT_HEAD/whatever doesn't match that.\n\nI was attempting to fix this to simplify the life of the user, not\ncomplicate it further- the user might have no idea what the next\ncommand in the sequence is, and I don't see the point in\ninconveniencing her.  In retrospect, I think we should simply drop\nthis patch.\n\n> What\n> happens when there is a mixture of picks and reverts?\n\nPermitted.\n\n-- Ram\n"},{"id":"182122","messageId":"20120108203800.GM1942@burratino","threadId":"29315","inReplyTo":"1326025653-11922-7-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 6/6] sequencer: factor code out of revert builtin","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:38:00Z","receivedAt":"2012-01-08T20:38:00Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Expose the cherry-picking machinery through a public\n> sequencer_pick_revisions() (renamed from pick_revisions() in\n> builtin/revert.c), so that cherry-picking and reverting are special\n> cases of a general sequencer operation.  The cherry-pick builtin is\n> now a thin wrapper that does command-line argument parsing before\n> calling into sequencer_pick_revisions().  In the future, we can write\n> a new \"foo\" builtin that calls into the sequencer like:\n\nNice.\n\nI don't think the plan is actually a \"foo\" builtin.\n\nI guess I would say \"So now your 'foo' builtin can use the sequencer\nmachinery by implementing a 'parse_args_populate_opts' function and\nthen running the following:\" so as to leave the reader more excited\nand not to imply a promise we are not going to keep. :)\n\n>   memset(&opts, 0, sizeof(opts));\n>   opts.command = REPLAY_CMD_FOO;\n>   opts.revisions = xmalloc(sizeof(*opts.revs));\n>   parse_args_populate_opts(argc, argv, &opts);\n>   init_revisions(opts.revs);\n>   sequencer_pick_revisions(&opts);\n\nHm, I wonder if opts.command should be a string so each new caller\ndoes not have to add to the enum and switch statements...\n\n> This patch does not intend to make any functional changes.  Check\n> with:\n>\n>   $ git blame -s -C HEAD^..HEAD -- sequencer.c\n\nNeat.\n\n[...]\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -24,6 +24,29 @@ enum replay_subcommand {\n>  \tREPLAY_ROLLBACK\n>  };\n>  \n> +struct replay_opts {\n[...]\n> +};\n[...]\n> @@ -33,4 +56,6 @@ struct replay_insn_list {\n[...]\n> +int sequencer_pick_revisions(struct replay_opts *opts);\n\nLooks sensible.  Maybe it would be useful to include a\nDocumentation/technical/api-sequencer.txt explaining how this is meant\nto be used.  (Not much detail needed, just an overview.  See the\nsurrounding files for some examples.)\n\nHaven't checked the code movement yet, since it is sitting on top of a\nslushy base.  Next time, hopefully.\n\nThanks,\nJonathan\n"},{"id":"182123","messageId":"20120108204338.GN1942@burratino","threadId":"29315","inReplyTo":"CALkWK0=xhnXq4_uEAri74Kk9zbAgiS+z-nG7Etm3MCo2cXaNPw@mail.gmail.com","subject":"Re: [PATCH 0/6] The move to sequencer.c","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:43:38Z","receivedAt":"2012-01-08T20:43:38Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n\n>> For the sake of new and forgetful readers: what is that objective?\n>\n> The objective is to build a generalized sequencer\n[...]\n> In these examples, the instruction sheet is uniformly filled with\n> \"pick\" or \"revert\" actions, which is not very interesting.  When we\n> get an interface to allow easy editing of the instruction sheet and\n> encompass more builtins, more interesting sequences of operations will\n> be possible like:\n>\n>   pick rr/revert-cherry-pick^2~34\n>   revert master@{12}\n>   merge next\n>   am /tmp/jrn.patch\n\nAh, okay.  So the tasks at hand are (1) allowing heterogeneous\ninstruction sheets (pick + revert for now) and (2) exposing the\nsequencer interface in sequencer.h.  (2) does not strictly\ndepend on (1) but (1) has more short-term benefit that is easy\nto test so it comes first.  No UI aside from editing the instruction\nsheet manually for now, but that can come later.  Thanks for\nexplaining.\n\nJonathan\n"},{"id":"182124","messageId":"20120108204539.GO1942@burratino","threadId":"29315","inReplyTo":"CALkWK0kwbzXRyFf=JjfAW9yD7M_FTB80+q1UPOCv-Em4qO2RKQ@mail.gmail.com","subject":"Re: [PATCH 3/6] revert: don't let revert continue a cherry-pick","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:45:39Z","receivedAt":"2012-01-08T20:45:39Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> In retrospect, I think we should simply drop\n> this patch.\n\nGreat --- I don't think it had much to do with this series.\n\nI'll keep this mail in my inbox.  Who knows --- maybe I'll get a\nmoment to teach conflicted single-picks to write a\n.git/sequencer/when-continuing-just-continue-the-single-pick file.\n"},{"id":"182125","messageId":"20120108204815.GP1942@burratino","threadId":"29315","inReplyTo":"CALkWK0=kw=EEXDfnrFkeNV678r4u3v72-=hKh9w8ujRN125NPQ@mail.gmail.com","subject":"Re: [PATCH 2/6] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T20:48:15Z","receivedAt":"2012-01-08T20:48:15Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n>> Ramkumar Ramachandra wrote:\n>>> Jonathan Nieder wrote:\n\n>>>> So what change does the patch actually make?  Is this a renaming?\n>>>\n>>> Yes, it renames \"action\" to \"command\" where appropriate.\n>>\n>> Wouldn't a simple renaming have a diffstat with the same number of added\n>> and removed lines?\n>\n> Yes, almost.  A few extra lines added because I needed a new data enum\n> for the \"command\"; also added a convenience function: action_name().\n\nIt's not a simple renaming, then.\n\nWhat user-visible effect will this have, if any?  What programmer-visible\neffect will it have, if any?  I _really_ should not have to read the patch\nto learn the impact of a patch.\n"},{"id":"182133","messageId":"20120108213318.GQ1942@burratino","threadId":"29315","inReplyTo":"CALkWK0mStgcb4EBB+ni9fisDJY=13cJZWCTEcgfyOUyAXbc=tA@mail.gmail.com","subject":"Re: [PATCH 5/6] revert: report fine-grained error messages from insn parser","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T21:33:18Z","receivedAt":"2012-01-08T21:33:18Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n>> Ramkumar Ramachandra wrote:\n\n>>>       /* Eat up extra spaces/ tabs before object name */\n>>> -     padding = strspn(bol, \" \\t\");\n>>> -     if (!padding)\n>>> -             return -1;\n>>> -     bol += padding;\n>>> +     bol += strspn(bol, \" \\t\");\n[...]\n> Not a bugfix: since I have to report sensible error messages now, I\n> changed the \"pick\" and \"revert\" checks to \"pick \" || \"pick\\t\" and\n> \"revert \" || \"revert\\t\" -- then, I can report \"invalid action\" if it\n> doesn't match instead of a useless \"missing space after action\".\n\nAh, I forgot that the \"if (!padding)\" check noticed errors like\n\n\tpicking foo\n\nbefore.  So this is just a code cleanup, with no functional effect.\n\nHowever, you can still report \"invalid action\" with the old code\nstructure --- it would just mean duplicating an error message in the\ncode, since you reach the same conclusion by two code paths.  So it's\na relevant cleanup, but I'd still suggest lifting it into a patch that\ncomes before so future readers can assure themselves that it introduces\nno functional change instead of being confused.\n\n[...]\n>>> +             return error(_(\"%s:%d: Not a valid commit: %.*s\"),\n>>> +                     todo_file, lineno, (int)error_len, bol);\n>>> +     }\n>>\n>> Hmm, this one can be emitted even when there was no corruption or\n>> internal error, because the user removed a commit she was\n>> cherry-picking and the gc-ed before a \"git cherry-pick --continue\".\n>> Alternatively, it can happen because the repository has grown very\n>> crowded and what used to be an unambiguous commit name no longer is\n>> one (not enough digits).  Will the error message be intuitive in these\n>> situations?\n>\n> Something like \"Unable to look up commit: %s\" perhaps?\n\nMy \"alternatively\" was bogus --- lookup_commit_reference takes a (raw)\nfull commit name as its argument.\n\nI dunno.  The question was not actually rhetorical --- I just meant\nthat it's worth thinking about these cases.  There are a few cases:\n\n - missing object\n - object is present but corrupt\n - object is a blob, not a commit\n\nIn the second case, there's an error message printed describing the\nproblem, but in the other two there isn't.  The other callers tend to\nsay \"not a valid <foo>\" or \"could not lookup commit <foo>, so I guess\n\n\terror: .git/sequencer/todo:5: not a valid commit: 78a89f493\n\nwould be fine.\n\nExcept that this focusses on the .git/sequencer/todo filename which\nwould leave the person debugging astray.  It is not that\n.git/sequencer/todo contains a typo (that would have been caught by\nget_sha1), but that it referred to a bad object or non-commit.  Maybe\nsomething in the direction of\n\n\terror: cannot pick 78a89f493 because it is not a valid commit\n\nwould be more helpful.\n\nIs this the right moment to report that error?  Will the operator be\nhappy that we errored out right away before cherry-picking anything\nand wasting the human's time in assisting with that process, or will\nshe be unhappy that inability to do something later that she might\nhave been planning on skipping anyway prevented making progress right\naway?  (I'm not sure what the best thing to do is --- I guess some\nadvice like\n\n\thint: to abort, use cherry-pick --abort\n\thint: to skip or replace that commit, use cherry-pick --edit\n\nwould help.)\n\nThanks for some food for thought.\nJonathan\n"},{"id":"182134","messageId":"20120108214042.GR1942@burratino","threadId":"29315","inReplyTo":"CALkWK0mYbBsZN1UX9YM0VWQezZsBpJCcEgKirCggtNXs0HZ-8g@mail.gmail.com","subject":"Re: [PATCH 4/6] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T21:40:42Z","receivedAt":"2012-01-08T21:40:42Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n\n>> Yes, I've always liked this one.\n>>\n>> Haven't re-read the patch to avoid wasted effort if there are changes\n>> when the previous patches in the series change.  Maybe it would be\n>> possible to send as a standalone?\n>\n> If I don't get manage to get the series right in a couple of re-rolls,\n> I'll do that.\n\nSplitting out unrelated changes (or at least putting the uncontroversial\nones near the front) is part of getting a series right.  I'll pick up\nthis patch, play around with it and send separately.\n"},{"id":"182136","messageId":"20120108215510.GS1942@burratino","threadId":"29315","inReplyTo":"20120108214042.GR1942@burratino","subject":"Re: [PATCH 4/6] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-08T21:55:10Z","receivedAt":"2012-01-08T21:55:10Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jonathan Nieder wrote:\n\n> I'll pick up\n> this patch, play around with it and send separately.\n\nOn second thought, do you have a link to the last submitted version,\nand do you remember if there were any important changes since then?\nThe base for that one should be closer to \"master\", I think.\n"},{"id":"182202","messageId":"CALkWK0mahdUKSWdeE18z4_9ubE4MY+qatzr2pCJ=0kFEWRhaHw@mail.gmail.com","threadId":"29315","inReplyTo":"20120108215510.GS1942@burratino","subject":"Re: [PATCH 4/6] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T03:40:45Z","receivedAt":"2012-01-10T03:40:45Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder wrote:\n> On second thought, do you have a link to the last submitted version,\n> and do you remember if there were any important changes since then?\n> The base for that one should be closer to \"master\", I think.\n\nThere you go: http://thread.gmane.org/gmane.comp.version-control.git/186638/focus=186644\n\n-- Ram\n"},{"id":"182241","messageId":"CALkWK0mYPS+FRihHOGfoK52nvKfNh9rGFdy=_0hri+uqocRzBA@mail.gmail.com","threadId":"29315","inReplyTo":"20120108203800.GM1942@burratino","subject":"Re: [PATCH 6/6] sequencer: factor code out of revert builtin","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T15:21:26Z","receivedAt":"2012-01-10T15:21:26Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>>   memset(&opts, 0, sizeof(opts));\n>>   opts.command = REPLAY_CMD_FOO;\n>>   opts.revisions = xmalloc(sizeof(*opts.revs));\n>>   parse_args_populate_opts(argc, argv, &opts);\n>>   init_revisions(opts.revs);\n>>   sequencer_pick_revisions(&opts);\n>\n> Hm, I wonder if opts.command should be a string so each new caller\n> does not have to add to the enum and switch statements...\n\nThe new caller would have to add enum and switch statements to define\na new action anyway, so I think this should be an enum too.\n\n-- Ram\n"},{"id":"182242","messageId":"CALkWK0kepNnnBUvCyXMY5Da3nJ5P6F66g7yy=VmDxVZ060=e4Q@mail.gmail.com","threadId":"29315","inReplyTo":"20120108213318.GQ1942@burratino","subject":"Re: [PATCH 5/6] revert: report fine-grained error messages from insn parser","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T15:24:35Z","receivedAt":"2012-01-10T15:24:35Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> My \"alternatively\" was bogus --- lookup_commit_reference takes a (raw)\n> full commit name as its argument.\n>\n> I dunno.  The question was not actually rhetorical --- I just meant\n> that it's worth thinking about these cases.  There are a few cases:\n>\n>  - missing object\n>  - object is present but corrupt\n>  - object is a blob, not a commit\n>\n> In the second case, there's an error message printed describing the\n> problem, but in the other two there isn't.  The other callers tend to\n> say \"not a valid <foo>\" or \"could not lookup commit <foo>, so I guess\n>\n>        error: .git/sequencer/todo:5: not a valid commit: 78a89f493\n>\n> would be fine.\n>\n> Except that this focusses on the .git/sequencer/todo filename which\n> would leave the person debugging astray.  It is not that\n> .git/sequencer/todo contains a typo (that would have been caught by\n> get_sha1), but that it referred to a bad object or non-commit.  Maybe\n> something in the direction of\n>\n>        error: cannot pick 78a89f493 because it is not a valid commit\n>\n> would be more helpful.\n>\n> Is this the right moment to report that error?  Will the operator be\n> happy that we errored out right away before cherry-picking anything\n> and wasting the human's time in assisting with that process, or will\n> she be unhappy that inability to do something later that she might\n> have been planning on skipping anyway prevented making progress right\n> away?  (I'm not sure what the best thing to do is --- I guess some\n> advice like\n>\n>        hint: to abort, use cherry-pick --abort\n>        hint: to skip or replace that commit, use cherry-pick --edit\n>\n> would help.)\n\nIgnoring this in the re-roll: I'd be inclined to put these changes in\na separate series that begins with bolting on some advice\nconfiguration to the sequencer.\n\nThanks for thinking about these things.\n\n-- Ram\n"},{"id":"182244","messageId":"1326212039-13806-1-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326025653-11922-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v2 0/8] The move to sequencer.c","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:51Z","receivedAt":"2012-01-10T16:13:51Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nThe big changes in this round are:\n\n1. Dropped \"revert: don't let revert continue a cherry-pick\" from the\n   last round after a quick discussion with Jonathan.\n\n2. Separated out \"revert: separate out parse errors logically\" from\n   \"revert: report fine-grained errors from insn parser\".  Definitely\n   looks clearer.\n\n3. Improved \"revert: report fine-grained errors from insn parser\" by\n   eliminating repetition.  20 is a bit arbitrary, but it looks pretty\n   enough on my terminal.\n\n4. Added \"sha1_name: introduce getn_sha1() to take length\" and\n   \"revert: use getn_sha1() to simplify insn parsing\".  I'm happy with\n   them.  Name is inspired from the strncmp() variant of strcmp().\n\n5. Included minimal API documentation with \"sequencer: factor code out\n   of revert builtin\".\n\nThanks for reading.  I think I'll work on fixing the memory leaks now.\n\nRamkumar Ramachandra (8):\n  revert: prepare to move replay_action to header\n  revert: decouple sequencer actions from builtin commands\n  revert: allow mixing \"pick\" and \"revert\" actions\n  revert: separate out parse errors logically\n  revert: report fine-grained errors from insn parser\n  sha1_name: introduce getn_sha1() to take length\n  revert: use getn_sha1() to simplify insn parsing\n  sequencer: factor code out of revert builtin\n\n Documentation/technical/api-sequencer.txt |   22 +\n builtin/revert.c                          |  958 +----------------------------\n cache.h                                   |    1 +\n sequencer.c                               |  925 ++++++++++++++++++++++++++++-\n sequencer.h                               |   48 ++\n sha1_name.c                               |   23 +-\n t/t3510-cherry-pick-sequence.sh           |   46 +-\n 7 files changed, 1056 insertions(+), 967 deletions(-)\n create mode 100644 Documentation/technical/api-sequencer.txt\n\n-- \n1.7.8.2\n"},{"id":"182245","messageId":"1326212039-13806-2-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 1/8] revert: prepare to move replay_action to header","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:52Z","receivedAt":"2012-01-10T16:13:52Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Later in the series, we will build a generalized sequencer by\nfactoring code out of the revert builtin, and leaving it with just\nargument parsing work.  This involves moving \"replay_action\" to\nsequencer.h, so that both sequencer.c and builtin/revert.c can use it.\nUnfortunately, \"REVERT\" and \"CHERRY_PICK\" are unsuitable variable\nnames, as their purpose is unclear in the global context; in\nanticipation of the move, rename them to \"REPLAY_REVERT\" and\n\"REPLAY_PICK\" respectively.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   40 ++++++++++++++++++++++------------------\n 1 files changed, 22 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 0d8020c..2739405 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -39,7 +39,11 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-enum replay_action { REVERT, CHERRY_PICK };\n+enum replay_action {\n+\tREPLAY_REVERT,\n+\tREPLAY_PICK\n+};\n+\n enum replay_subcommand {\n \tREPLAY_NONE,\n \tREPLAY_REMOVE_STATE,\n@@ -74,14 +78,14 @@ struct replay_opts {\n \n static const char *action_name(const struct replay_opts *opts)\n {\n-\treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n+\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n static char *get_encoding(const char *message);\n \n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n-\treturn opts->action == REVERT ? revert_usage : cherry_pick_usage;\n+\treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n static int option_parse_x(const struct option *opt,\n@@ -160,7 +164,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tOPT_END(),\n \t};\n \n-\tif (opts->action == CHERRY_PICK) {\n+\tif (opts->action == REPLAY_PICK) {\n \t\tstruct option cp_extra[] = {\n \t\t\tOPT_BOOLEAN('x', NULL, &opts->record_origin, \"append commit name\"),\n \t\t\tOPT_BOOLEAN(0, \"ff\", &opts->allow_ff, \"allow fast-forward\"),\n@@ -374,7 +378,7 @@ static int error_dirty_index(struct replay_opts *opts)\n \t\treturn error_resolve_conflict(action_name(opts));\n \n \t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->action == CHERRY_PICK)\n+\tif (opts->action == REPLAY_PICK)\n \t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n \telse\n \t\terror(_(\"Your local changes would be overwritten by revert.\"));\n@@ -553,7 +557,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->action == REVERT) {\n+\tif (opts->action == REPLAY_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -594,7 +598,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -618,13 +622,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->action == REVERT\n+\t\terror(opts->action == REPLAY_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -644,7 +648,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n static void prepare_revs(struct replay_opts *opts)\n {\n-\tif (opts->action != REVERT)\n+\tif (opts->action != REPLAY_REVERT)\n \t\topts->revs->reverse ^= 1;\n \n \tif (prepare_revision_walk(opts->revs))\n@@ -701,7 +705,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n {\n \tstruct commit_list *cur = NULL;\n \tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->action == REVERT ? \"revert\" : \"pick\";\n+\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n \tconst char *subject;\n \tint subject_len;\n \n@@ -722,10 +726,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \tint saved, status, padding;\n \n \tif (!prefixcmp(bol, \"pick\")) {\n-\t\taction = CHERRY_PICK;\n+\t\taction = REPLAY_PICK;\n \t\tbol += strlen(\"pick\");\n \t} else if (!prefixcmp(bol, \"revert\")) {\n-\t\taction = REVERT;\n+\t\taction = REPLAY_REVERT;\n \t\tbol += strlen(\"revert\");\n \t} else\n \t\treturn NULL;\n@@ -748,7 +752,7 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \t */\n \tif (action != opts->action) {\n \t\tconst char *action_str;\n-\t\taction_str = action == REVERT ? \"revert\" : \"cherry-pick\";\n+\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n \t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n \t\treturn NULL;\n \t}\n@@ -1124,7 +1128,7 @@ static int pick_revisions(struct replay_opts *opts)\n \tif (create_seq_dir() < 0)\n \t\treturn -1;\n \tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REVERT)\n+\t\tif (opts->action == REPLAY_REVERT)\n \t\t\treturn error(_(\"Can't revert as initial commit\"));\n \t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t}\n@@ -1141,7 +1145,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n \t\topts.edit = 1;\n-\topts.action = REVERT;\n+\topts.action = REPLAY_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n@@ -1156,7 +1160,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.action = CHERRY_PICK;\n+\topts.action = REPLAY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n-- \n1.7.8.2\n"},{"id":"182246","messageId":"1326212039-13806-3-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:53Z","receivedAt":"2012-01-10T16:13:53Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Currently, 'git cherry-pick' fills up the '.git/sequencer/todo'\ninstruction sheet with \"pick\" actions, while 'git revert' fills it up\nwith \"revert\" actions.  Inspired by the way 'rebase -i' works, we\nwould like to permit mixing arbitrary actions in the same instruction\nsheet.  To do this, we first have to decouple the notion of an action\nin the instruction sheet from builtin commands.  While a future\ninstruction sheet would look like:\n\n  pickle next~4\n  action3 b74fea\n  revert rr/moo^2~34\n\nthe actions \"pickle\", \"action3\" and \"revert\" need not necessarily\ncorrespond to the specific builtins.  Introduce a \"replay_command\",\nand replace instances of \"replay_action\" with \"replay_command\"\neverywhere except in parse_insn_line(), the function that parses the\ninstruction sheet.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   63 ++++++++++++++++++++++++++++++------------------------\n 1 files changed, 35 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 2739405..9bca9c7 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -44,6 +44,11 @@ enum replay_action {\n \tREPLAY_PICK\n };\n \n+enum replay_command {\n+\tREPLAY_CMD_REVERT,\n+\tREPLAY_CMD_CHERRY_PICK\n+};\n+\n enum replay_subcommand {\n \tREPLAY_NONE,\n \tREPLAY_REMOVE_STATE,\n@@ -52,7 +57,7 @@ enum replay_subcommand {\n };\n \n struct replay_opts {\n-\tenum replay_action action;\n+\tenum replay_command command;\n \tenum replay_subcommand subcommand;\n \n \t/* Boolean options */\n@@ -76,16 +81,16 @@ struct replay_opts {\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n-static const char *action_name(const struct replay_opts *opts)\n+static const char *command_name(struct replay_opts *opts)\n {\n-\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n+\treturn opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n static char *get_encoding(const char *message);\n \n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n-\treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n+\treturn opts->command == REPLAY_CMD_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n static int option_parse_x(const struct option *opt,\n@@ -142,7 +147,7 @@ static void verify_opt_mutually_compatible(const char *me, ...)\n static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n {\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n-\tconst char *me = action_name(opts);\n+\tconst char *me = command_name(opts);\n \tint remove_state = 0;\n \tint contin = 0;\n \tint rollback = 0;\n@@ -164,7 +169,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tOPT_END(),\n \t};\n \n-\tif (opts->action == REPLAY_PICK) {\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK) {\n \t\tstruct option cp_extra[] = {\n \t\t\tOPT_BOOLEAN('x', NULL, &opts->record_origin, \"append commit name\"),\n \t\t\tOPT_BOOLEAN(0, \"ff\", &opts->allow_ff, \"allow fast-forward\"),\n@@ -375,10 +380,10 @@ static struct tree *empty_tree(void)\n static int error_dirty_index(struct replay_opts *opts)\n {\n \tif (read_cache_unmerged())\n-\t\treturn error_resolve_conflict(action_name(opts));\n+\t\treturn error_resolve_conflict(command_name(opts));\n \n \t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->action == REPLAY_PICK)\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK)\n \t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n \telse\n \t\terror(_(\"Your local changes would be overwritten by revert.\"));\n@@ -434,7 +439,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \t    (write_cache(index_fd, active_cache, active_nr) ||\n \t     commit_locked_index(&index_lock)))\n \t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n-\t\tdie(_(\"%s: Unable to write new index file\"), action_name(opts));\n+\t\tdie(_(\"%s: Unable to write new index file\"), command_name(opts));\n \trollback_lock_file(&index_lock);\n \n \tif (!clean) {\n@@ -542,7 +547,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t/* TRANSLATORS: The first %s will be \"revert\" or\n \t\t   \"cherry-pick\", the second %s a SHA1 */\n \t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n-\t\t\taction_name(opts), sha1_to_hex(parent->object.sha1));\n+\t\t\tcommand_name(opts), sha1_to_hex(parent->object.sha1));\n \n \tif (get_message(commit, &msg) != 0)\n \t\treturn error(_(\"Cannot get commit message for %s\"),\n@@ -557,7 +562,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->action == REPLAY_REVERT) {\n+\tif (opts->command == REPLAY_CMD_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -598,7 +603,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->command == REPLAY_CMD_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -622,13 +627,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (opts->command == REPLAY_CMD_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->action == REPLAY_REVERT\n+\t\terror(opts->command == REPLAY_CMD_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -648,7 +653,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n static void prepare_revs(struct replay_opts *opts)\n {\n-\tif (opts->action != REPLAY_REVERT)\n+\tif (opts->command != REPLAY_CMD_REVERT)\n \t\topts->revs->reverse ^= 1;\n \n \tif (prepare_revision_walk(opts->revs))\n@@ -663,12 +668,13 @@ static void read_and_refresh_cache(struct replay_opts *opts)\n \tstatic struct lock_file index_lock;\n \tint index_fd = hold_locked_index(&index_lock, 0);\n \tif (read_index_preload(&the_index, NULL) < 0)\n-\t\tdie(_(\"git %s: failed to read the index\"), action_name(opts));\n+\t\tdie(_(\"git %s: failed to read the index\"), command_name(opts));\n \trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n \tif (the_index.cache_changed) {\n \t\tif (write_index(&the_index, index_fd) ||\n \t\t    commit_locked_index(&index_lock))\n-\t\t\tdie(_(\"git %s: failed to refresh the index\"), action_name(opts));\n+\t\t\tdie(_(\"git %s: failed to refresh the index\"),\n+\t\t\t\tcommand_name(opts));\n \t}\n \trollback_lock_file(&index_lock);\n }\n@@ -705,7 +711,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n {\n \tstruct commit_list *cur = NULL;\n \tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n+\tconst char *action_str = opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"pick\";\n \tconst char *subject;\n \tint subject_len;\n \n@@ -750,10 +756,11 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \t * Verify that the action matches up with the one in\n \t * opts; we don't support arbitrary instructions\n \t */\n-\tif (action != opts->action) {\n-\t\tconst char *action_str;\n-\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n-\t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n+\tif ((action == REPLAY_PICK && opts->command == REPLAY_CMD_REVERT) ||\n+\t\t(action == REPLAY_REVERT && opts->command == REPLAY_CMD_CHERRY_PICK)) {\n+\t\terror(_(\"Cannot %s during a %s\"),\n+\t\t\taction == REPLAY_REVERT ? \"revert\" : \"pick\",\n+\t\t\tcommand_name(opts));\n \t\treturn NULL;\n \t}\n \n@@ -1015,7 +1022,7 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n \tstruct commit_list *cur;\n \tint res;\n \n-\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n \tif (opts->allow_ff)\n \t\tassert(!(opts->signoff || opts->no_commit ||\n \t\t\t\topts->record_origin || opts->edit));\n@@ -1070,7 +1077,7 @@ static int sequencer_continue(struct replay_opts *opts)\n \n static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n-\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n \treturn do_pick_commit(cmit, opts);\n }\n \n@@ -1128,7 +1135,7 @@ static int pick_revisions(struct replay_opts *opts)\n \tif (create_seq_dir() < 0)\n \t\treturn -1;\n \tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REPLAY_REVERT)\n+\t\tif (opts->command == REPLAY_CMD_REVERT)\n \t\t\treturn error(_(\"Can't revert as initial commit\"));\n \t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t}\n@@ -1145,7 +1152,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n \t\topts.edit = 1;\n-\topts.action = REPLAY_REVERT;\n+\topts.command = REPLAY_CMD_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n@@ -1160,7 +1167,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.action = REPLAY_PICK;\n+\topts.command = REPLAY_CMD_CHERRY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n-- \n1.7.8.2\n"},{"id":"182247","messageId":"1326212039-13806-4-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 3/8] revert: allow mixing \"pick\" and \"revert\" actions","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:54Z","receivedAt":"2012-01-10T16:13:54Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Parse the instruction sheet in '.git/sequencer/todo' as a list of\n(action, operand) pairs, instead of assuming that all lines have the\nsame action.  Now, an instruction sheet like the following is\nperfectly valid:\n\n  pick fdc0b12 picked\n  revert 965fed4 anotherpick\n\nThe operator can use this feature by hand-editing the instruction\nsheet and using '--continue' as appropriate:\n\n  $ git cherry-pick foo..bar\n  [conflict occurs]\n  $ edit problematicfile\n  $ git add problematicfile\n  $ edit .git/sequencer/todo\n  $ git revert --continue\n  [finishes successfully]\n\nConsequently, this means that a 'git cherry-pick --continue' can\ncontinue an ongoing 'git revert' operation, and viceversa.\n\nHelped-by: Jonathan Nieder <jrnider@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c                |  142 ++++++++++++++++++++-------------------\n t/t3510-cherry-pick-sequence.sh |   46 +++++++++----\n 2 files changed, 105 insertions(+), 83 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 9bca9c7..1841ffa 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -56,6 +56,12 @@ enum replay_subcommand {\n \tREPLAY_ROLLBACK\n };\n \n+struct replay_insn_list {\n+\tstruct commit *operand;\n+\tenum replay_action action;\n+\tstruct replay_insn_list *next;\n+};\n+\n struct replay_opts {\n \tenum replay_command command;\n \tenum replay_subcommand subcommand;\n@@ -487,7 +493,8 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n \treturn run_command_v_opt(args, RUN_GIT_CMD);\n }\n \n-static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n+static int do_pick_commit(struct commit *commit, enum replay_action action,\n+\t\t\tstruct replay_opts *opts)\n {\n \tunsigned char head[20];\n \tstruct commit *base, *next, *parent;\n@@ -562,7 +569,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->command == REPLAY_CMD_REVERT) {\n+\tif (action == REPLAY_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -603,7 +610,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->command == REPLAY_CMD_REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -627,13 +634,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->command == REPLAY_CMD_CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->command == REPLAY_CMD_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->command == REPLAY_CMD_REVERT\n+\t\terror(action == REPLAY_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -680,70 +687,70 @@ static void read_and_refresh_cache(struct replay_opts *opts)\n }\n \n /*\n- * Append a commit to the end of the commit_list.\n+ * Append a (commit, action) to the end of the replay_insn_list.\n  *\n  * next starts by pointing to the variable that holds the head of an\n- * empty commit_list, and is updated to point to the \"next\" field of\n- * the last item on the list as new commits are appended.\n+ * empty replay_insn_list, and is updated to point to the \"next\" field of\n+ * the last item on the list as new (commit, action) pairs are appended.\n  *\n  * Usage example:\n  *\n- *     struct commit_list *list;\n- *     struct commit_list **next = &list;\n+ *     struct replay_insn_list *list;\n+ *     struct replay_insn_list **next = &list;\n  *\n- *     next = commit_list_append(c1, next);\n- *     next = commit_list_append(c2, next);\n- *     assert(commit_list_count(list) == 2);\n+ *     next = replay_insn_list_append(c1, a1, next);\n+ *     next = replay_insn_list_append(c2, a2, next);\n+ *     assert(len(list) == 2);\n  *     return list;\n  */\n-static struct commit_list **commit_list_append(struct commit *commit,\n-\t\t\t\t\t       struct commit_list **next)\n+static struct replay_insn_list **replay_insn_list_append(struct commit *operand,\n+\t\t\t\t\t\t\tenum replay_action action,\n+\t\t\t\t\t\t\tstruct replay_insn_list **next)\n {\n-\tstruct commit_list *new = xmalloc(sizeof(struct commit_list));\n-\tnew->item = commit;\n+\tstruct replay_insn_list *new = xmalloc(sizeof(*new));\n+\tnew->action = action;\n+\tnew->operand = operand;\n \t*next = new;\n \tnew->next = NULL;\n \treturn &new->next;\n }\n \n-static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n-\t\tstruct replay_opts *opts)\n+static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n {\n-\tstruct commit_list *cur = NULL;\n-\tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"pick\";\n-\tconst char *subject;\n-\tint subject_len;\n+\tstruct replay_insn_list *cur;\n \n \tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);\n-\t\tsubject_len = find_commit_subject(cur->item->buffer, &subject);\n+\t\tconst char *sha1_abbrev, *action_str, *subject;\n+\t\tint subject_len;\n+\n+\t\taction_str = cur->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n+\t\tsha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);\n+\t\tsubject_len = find_commit_subject(cur->operand->buffer, &subject);\n \t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n \t\t\tsubject_len, subject);\n \t}\n \treturn 0;\n }\n \n-static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)\n+static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n {\n \tunsigned char commit_sha1[20];\n-\tenum replay_action action;\n \tchar *end_of_object_name;\n \tint saved, status, padding;\n \n \tif (!prefixcmp(bol, \"pick\")) {\n-\t\taction = REPLAY_PICK;\n+\t\titem->action = REPLAY_PICK;\n \t\tbol += strlen(\"pick\");\n \t} else if (!prefixcmp(bol, \"revert\")) {\n-\t\taction = REPLAY_REVERT;\n+\t\titem->action = REPLAY_REVERT;\n \t\tbol += strlen(\"revert\");\n \t} else\n-\t\treturn NULL;\n+\t\treturn -1;\n \n \t/* Eat up extra spaces/ tabs before object name */\n \tpadding = strspn(bol, \" \\t\");\n \tif (!padding)\n-\t\treturn NULL;\n+\t\treturn -1;\n \tbol += padding;\n \n \tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n@@ -752,38 +759,29 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \tstatus = get_sha1(bol, commit_sha1);\n \t*end_of_object_name = saved;\n \n-\t/*\n-\t * Verify that the action matches up with the one in\n-\t * opts; we don't support arbitrary instructions\n-\t */\n-\tif ((action == REPLAY_PICK && opts->command == REPLAY_CMD_REVERT) ||\n-\t\t(action == REPLAY_REVERT && opts->command == REPLAY_CMD_CHERRY_PICK)) {\n-\t\terror(_(\"Cannot %s during a %s\"),\n-\t\t\taction == REPLAY_REVERT ? \"revert\" : \"pick\",\n-\t\t\tcommand_name(opts));\n-\t\treturn NULL;\n-\t}\n-\n \tif (status < 0)\n-\t\treturn NULL;\n+\t\treturn -1;\n+\n+\titem->operand = lookup_commit_reference(commit_sha1);\n+\tif (!item->operand)\n+\t\treturn -1;\n \n-\treturn lookup_commit_reference(commit_sha1);\n+\titem->next = NULL;\n+\treturn 0;\n }\n \n-static int parse_insn_buffer(char *buf, struct commit_list **todo_list,\n-\t\t\tstruct replay_opts *opts)\n+static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n {\n-\tstruct commit_list **next = todo_list;\n-\tstruct commit *commit;\n+\tstruct replay_insn_list **next = todo_list;\n+\tstruct replay_insn_list item = { NULL, 0, NULL };\n \tchar *p = buf;\n \tint i;\n \n \tfor (i = 1; *p; i++) {\n \t\tchar *eol = strchrnul(p, '\\n');\n-\t\tcommit = parse_insn_line(p, eol, opts);\n-\t\tif (!commit)\n+\t\tif (parse_insn_line(p, eol, &item))\n \t\t\treturn error(_(\"Could not parse line %d.\"), i);\n-\t\tnext = commit_list_append(commit, next);\n+\t\tnext = replay_insn_list_append(item.operand, item.action, next);\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n \tif (!*todo_list)\n@@ -791,8 +789,7 @@ static int parse_insn_buffer(char *buf, struct commit_list **todo_list,\n \treturn 0;\n }\n \n-static void read_populate_todo(struct commit_list **todo_list,\n-\t\t\tstruct replay_opts *opts)\n+static void read_populate_todo(struct replay_insn_list **todo_list)\n {\n \tconst char *todo_file = git_path(SEQ_TODO_FILE);\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -808,7 +805,7 @@ static void read_populate_todo(struct commit_list **todo_list,\n \t}\n \tclose(fd);\n \n-\tres = parse_insn_buffer(buf.buf, todo_list, opts);\n+\tres = parse_insn_buffer(buf.buf, todo_list);\n \tstrbuf_release(&buf);\n \tif (res)\n \t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n@@ -857,17 +854,19 @@ static void read_populate_opts(struct replay_opts **opts_ptr)\n \t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n }\n \n-static void walk_revs_populate_todo(struct commit_list **todo_list,\n+static void walk_revs_populate_todo(struct replay_insn_list **todo_list,\n \t\t\t\tstruct replay_opts *opts)\n {\n \tstruct commit *commit;\n-\tstruct commit_list **next;\n+\tstruct replay_insn_list **next;\n+\tenum replay_action action;\n \n \tprepare_revs(opts);\n \n \tnext = todo_list;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n \twhile ((commit = get_revision(opts->revs)))\n-\t\tnext = commit_list_append(commit, next);\n+\t\tnext = replay_insn_list_append(commit, action, next);\n }\n \n static int create_seq_dir(void)\n@@ -965,7 +964,7 @@ fail:\n \treturn -1;\n }\n \n-static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\n+static void save_todo(struct replay_insn_list *todo_list)\n {\n \tconst char *todo_file = git_path(SEQ_TODO_FILE);\n \tstatic struct lock_file todo_lock;\n@@ -973,7 +972,7 @@ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\n \tint fd;\n \n \tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n-\tif (format_todo(&buf, todo_list, opts) < 0)\n+\tif (format_todo(&buf, todo_list) < 0)\n \t\tdie(_(\"Could not format %s.\"), todo_file);\n \tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n \t\tstrbuf_release(&buf);\n@@ -1017,9 +1016,9 @@ static void save_opts(struct replay_opts *opts)\n \t}\n }\n \n-static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n+static int pick_commits(struct replay_insn_list *todo_list, struct replay_opts *opts)\n {\n-\tstruct commit_list *cur;\n+\tstruct replay_insn_list *cur;\n \tint res;\n \n \tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n@@ -1029,8 +1028,8 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n \tread_and_refresh_cache(opts);\n \n \tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsave_todo(cur, opts);\n-\t\tres = do_pick_commit(cur->item, opts);\n+\t\tsave_todo(cur);\n+\t\tres = do_pick_commit(cur->operand, cur->action, opts);\n \t\tif (res)\n \t\t\treturn res;\n \t}\n@@ -1055,12 +1054,12 @@ static int continue_single_pick(void)\n \n static int sequencer_continue(struct replay_opts *opts)\n {\n-\tstruct commit_list *todo_list = NULL;\n+\tstruct replay_insn_list *todo_list = NULL;\n \n \tif (!file_exists(git_path(SEQ_TODO_FILE)))\n \t\treturn continue_single_pick();\n \tread_populate_opts(&opts);\n-\tread_populate_todo(&todo_list, opts);\n+\tread_populate_todo(&todo_list);\n \n \t/* Verify that the conflict has been resolved */\n \tif (file_exists(git_path(\"CHERRY_PICK_HEAD\")) ||\n@@ -1077,13 +1076,16 @@ static int sequencer_continue(struct replay_opts *opts)\n \n static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n+\tenum replay_action action;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n+\n \tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n-\treturn do_pick_commit(cmit, opts);\n+\treturn do_pick_commit(cmit, action, opts);\n }\n \n static int pick_revisions(struct replay_opts *opts)\n {\n-\tstruct commit_list *todo_list = NULL;\n+\tstruct replay_insn_list *todo_list = NULL;\n \tunsigned char sha1[20];\n \n \tif (opts->subcommand == REPLAY_NONE)\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 97f3710..af747c8 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -454,7 +454,7 @@ test_expect_success 'sign-off needs to be reaffirmed after conflict resolution,\n \t! grep Signed-off-by: msg\n '\n \n-test_expect_success 'malformed instruction sheet 1' '\n+test_expect_success 'malformed instruction sheet, action' '\n \tpristine_detach initial &&\n \ttest_expect_code 1 git cherry-pick base..anotherpick &&\n \techo \"resolved\" >foo &&\n@@ -465,23 +465,12 @@ test_expect_success 'malformed instruction sheet 1' '\n \ttest_expect_code 128 git cherry-pick --continue\n '\n \n-test_expect_success 'malformed instruction sheet 2' '\n-\tpristine_detach initial &&\n-\ttest_expect_code 1 git cherry-pick base..anotherpick &&\n-\techo \"resolved\" >foo &&\n-\tgit add foo &&\n-\tgit commit &&\n-\tsed \"s/pick/revert/\" .git/sequencer/todo >new_sheet &&\n-\tcp new_sheet .git/sequencer/todo &&\n-\ttest_expect_code 128 git cherry-pick --continue\n-'\n-\n test_expect_success 'empty commit set' '\n \tpristine_detach initial &&\n \ttest_expect_code 128 git cherry-pick base..base\n '\n \n-test_expect_success 'malformed instruction sheet 3' '\n+test_expect_success 'malformed instruction sheet, object name' '\n \tpristine_detach initial &&\n \ttest_expect_code 1 git cherry-pick base..anotherpick &&\n \techo \"resolved\" >foo &&\n@@ -517,4 +506,35 @@ test_expect_success 'commit descriptions in insn sheet are optional' '\n \ttest_line_count = 4 commits\n '\n \n+test_expect_success 'mixed pick and revert instructions' '\n+\tpristine_detach initial &&\n+\ttest_expect_code 1 git cherry-pick base..anotherpick &&\n+\techo \"c\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\n+\toldsha=`git rev-parse --short HEAD~1` &&\n+\techo \"revert $oldsha unrelatedpick\" >>.git/sequencer/todo &&\n+\tgit revert --continue &&\n+\ttest_path_is_missing .git/sequencer &&\n+\t{\n+\t\tgit rev-list HEAD |\n+\t\tgit diff-tree --root --stdin |\n+\t\tsed \"s/$_x40/OBJID/g\"\n+\t} >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tunrelated\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tfoo\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tfoo\n+\tOBJID\n+\t:100644 100644 OBJID OBJID M\tunrelated\n+\tOBJID\n+\t:000000 100644 OBJID OBJID A\tfoo\n+\t:000000 100644 OBJID OBJID A\tunrelated\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.7.8.2\n"},{"id":"182250","messageId":"1326212039-13806-5-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 4/8] revert: separate out parse errors logically","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:55Z","receivedAt":"2012-01-10T16:13:55Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Three kinds of errors can arise from parsing the instruction sheet:\n1. Unrecognized action\n2. Malformed object name\n3. Object name does not refer to a valid commit\n\nThe next patch makes an attempt to make the parser report meaningful\nerrors by replacing the \"return -1\" with \"return error(...)\"\nappropriately.  For the first kind of error, it is insufficient to\ncheck if the buffer beings with a \"pick\" or \"revert\", otherwise the\nfollowing insn sheet would be interpreted as having a malformed object\nname:\n\npickle a1fe57~2\n\nIn reality, the issue is that \"pickle\" is an unrecognized instruction.\nSo, check that the buffer starts with (\"pick \" or \"pick\\t\") and\n(\"revert \" or \"revert\\t\").\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   15 ++++++---------\n 1 files changed, 6 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 1841ffa..9a09471 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -736,22 +736,19 @@ static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n {\n \tunsigned char commit_sha1[20];\n \tchar *end_of_object_name;\n-\tint saved, status, padding;\n+\tint saved, status;\n \n-\tif (!prefixcmp(bol, \"pick\")) {\n+\tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n \t\titem->action = REPLAY_PICK;\n-\t\tbol += strlen(\"pick\");\n-\t} else if (!prefixcmp(bol, \"revert\")) {\n+\t\tbol += strlen(\"pick \");\n+\t} else if (!prefixcmp(bol, \"revert \") || !prefixcmp(bol, \"revert\\t\")) {\n \t\titem->action = REPLAY_REVERT;\n-\t\tbol += strlen(\"revert\");\n+\t\tbol += strlen(\"revert \");\n \t} else\n \t\treturn -1;\n \n \t/* Eat up extra spaces/ tabs before object name */\n-\tpadding = strspn(bol, \" \\t\");\n-\tif (!padding)\n-\t\treturn -1;\n-\tbol += padding;\n+\tbol += strspn(bol, \" \\t\");\n \n \tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n \tsaved = *end_of_object_name;\n-- \n1.7.8.2\n"},{"id":"182249","messageId":"1326212039-13806-6-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 5/8] revert: report fine-grained errors from insn parser","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:56Z","receivedAt":"2012-01-10T16:13:56Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The infrastructure that parses '.git/sequencer/todo' is meant to\nhandle arbitrary user input some day, so it can be used as the\nimplementation of 'git rebase --interactive' and 'git sequence\n--edit'.  It is currently sub-optimal for that purpose because the\nparse error messages just say:\n\n  error: Could not parse line 5.\n\nThis patch shifts responsibility to parse_insn_line(), which can come\nup with a more detailed message like:\n\n  error: .git/sequencer/todo:5: unrecognized action: frobnicate\n\nOnce the operator is allowed to edit the sequence, the message might\nbe adjusted to something like:\n\n  error: <sequence you just gave me>:5: unrecognized action: frobnicate\n\ninstead of exposing an implementation detail. Some day \"git sequence\n--edit\" could even re-launch the editor with an error message in a\ncomment before the problematic line and the cursor pointing there.\nFor now, pointing to the explicit filename is useful since this should\nonly happen if there was filesystem corruption, tampering, or a git\nbug.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   30 ++++++++++++++++++++++++------\n 1 files changed, 24 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 9a09471..0954d22 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -732,7 +732,22 @@ static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n \treturn 0;\n }\n \n-static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n+static int parse_error(const char *message, const char *file,\n+\t\tint lineno, char *error_line)\n+{\n+\tconst char *suffix = \"\";\n+\tint error_len = strcspn(error_line, \" \\t\\n\");\n+\n+\tif (error_len > 20) {\n+\t\terror_len = 20;\n+\t\tsuffix = \"...\";\n+\t}\n+\treturn error(_(\"%s:%d: %s: %.*s%s\"), file, lineno, message,\n+\t\terror_len, error_line, suffix);\n+}\n+\n+static int parse_insn_line(char *bol, char *eol,\n+\t\t\tstruct replay_insn_list *item, int lineno)\n {\n \tunsigned char commit_sha1[20];\n \tchar *end_of_object_name;\n@@ -745,7 +760,8 @@ static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n \t\titem->action = REPLAY_REVERT;\n \t\tbol += strlen(\"revert \");\n \t} else\n-\t\treturn -1;\n+\t\treturn parse_error(_(\"unrecognized action\"),\n+\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n \n \t/* Eat up extra spaces/ tabs before object name */\n \tbol += strspn(bol, \" \\t\");\n@@ -757,11 +773,13 @@ static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n \t*end_of_object_name = saved;\n \n \tif (status < 0)\n-\t\treturn -1;\n+\t\treturn parse_error(_(\"malformed object name\"),\n+\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n \n \titem->operand = lookup_commit_reference(commit_sha1);\n \tif (!item->operand)\n-\t\treturn -1;\n+\t\treturn parse_error(_(\"not a valid commit\"),\n+\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n \n \titem->next = NULL;\n \treturn 0;\n@@ -776,8 +794,8 @@ static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n \n \tfor (i = 1; *p; i++) {\n \t\tchar *eol = strchrnul(p, '\\n');\n-\t\tif (parse_insn_line(p, eol, &item))\n-\t\t\treturn error(_(\"Could not parse line %d.\"), i);\n+\t\tif (parse_insn_line(p, eol, &item, i))\n+\t\t\treturn -1;\n \t\tnext = replay_insn_list_append(item.operand, item.action, next);\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n-- \n1.7.8.2\n"},{"id":"182251","messageId":"1326212039-13806-7-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 6/8] sha1_name: introduce getn_sha1() to take length","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:57Z","receivedAt":"2012-01-10T16:13:57Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Introduce a variant of get_sha1() that additionally takes the length\nof the buffer, so it can parse object names from buffers that don't\nnecessarily terminate with NUL.\n\nSuggested-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n cache.h     |    1 +\n sha1_name.c |   23 +++++++++++++++++++----\n 2 files changed, 20 insertions(+), 4 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 10afd71..8bb6759 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -812,6 +812,7 @@ struct object_context {\n };\n \n extern int get_sha1(const char *str, unsigned char *sha1);\n+extern int getn_sha1(const char *name, int namelen, unsigned char *sha1);\n extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);\n static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)\n {\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 03ffc2c..31d412e 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1019,12 +1019,11 @@ static char *resolve_relative_path(const char *rel)\n \t\t\t   rel);\n }\n \n-int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n-\t\t\t    struct object_context *oc,\n-\t\t\t    int only_to_die, const char *prefix)\n+static int getn_sha1_with_context_1(const char *name, int namelen,\n+\t\t\t\tunsigned char *sha1, struct object_context *oc,\n+\t\t\t\tint only_to_die, const char *prefix)\n {\n \tint ret, bracket_depth;\n-\tint namelen = strlen(name);\n \tconst char *cp;\n \n \tmemset(oc, 0, sizeof(*oc));\n@@ -1134,3 +1133,19 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n \t}\n \treturn ret;\n }\n+\n+int get_sha1_with_context_1(const char *name, unsigned char *sha1,\n+\t\t\tstruct object_context *oc,\n+\t\t\tint only_to_die, const char *prefix)\n+{\n+\tint namelen = strlen(name);\n+\treturn getn_sha1_with_context_1(name, namelen, sha1,\n+\t\t\t\t\toc, only_to_die, prefix);\n+}\n+\n+/* A variant of get_sha1 that takes a length. */\n+int getn_sha1(const char *name, int namelen, unsigned char *sha1)\n+{\n+\tstruct object_context unused;\n+\treturn getn_sha1_with_context_1(name, namelen, sha1, &unused, 0, NULL);\n+}\n-- \n1.7.8.2\n"},{"id":"182248","messageId":"1326212039-13806-8-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 7/8] revert: use getn_sha1() to simplify insn parsing","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:58Z","receivedAt":"2012-01-10T16:13:58Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"To read the object name in the instruction sheet, we currently\nmanipulate the buffer to artificially introduce a NUL after the\nsupposed object name, and then use get_sha1() to read the object name\nbefore restoring the buffer.  Get rid of this ugliness by using\ngetn_sha1(), a function introduced in the previous patch.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   12 +++---------\n 1 files changed, 3 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 0954d22..187c317 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -750,8 +750,7 @@ static int parse_insn_line(char *bol, char *eol,\n \t\t\tstruct replay_insn_list *item, int lineno)\n {\n \tunsigned char commit_sha1[20];\n-\tchar *end_of_object_name;\n-\tint saved, status;\n+\tint namelen;\n \n \tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n \t\titem->action = REPLAY_PICK;\n@@ -766,13 +765,8 @@ static int parse_insn_line(char *bol, char *eol,\n \t/* Eat up extra spaces/ tabs before object name */\n \tbol += strspn(bol, \" \\t\");\n \n-\tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n-\tsaved = *end_of_object_name;\n-\t*end_of_object_name = '\\0';\n-\tstatus = get_sha1(bol, commit_sha1);\n-\t*end_of_object_name = saved;\n-\n-\tif (status < 0)\n+\tnamelen = strcspn(bol, \" \\t\\n\");\n+\tif (getn_sha1(bol, namelen, commit_sha1))\n \t\treturn parse_error(_(\"malformed object name\"),\n \t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n \n-- \n1.7.8.2\n"},{"id":"182252","messageId":"1326212039-13806-9-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326212039-13806-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 8/8] sequencer: factor code out of revert builtin","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-10T16:13:59Z","receivedAt":"2012-01-10T16:13:59Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Expose the cherry-picking machinery through a public\nsequencer_pick_revisions() (renamed from pick_revisions() in\nbuiltin/revert.c), so that cherry-picking and reverting are special\ncases of a general sequencer operation.  The cherry-pick builtin is\nnow a thin wrapper that does command-line argument parsing before\ncalling into sequencer_pick_revisions().  So now, your \"foo\" builtin\ncan use the sequencer machinery by implementing a\nparse_args_populate_opts() function and then running the following:\n\n  memset(&opts, 0, sizeof(opts));\n  opts.command = REPLAY_CMD_FOO;\n  opts.revisions = xmalloc(sizeof(*opts.revs));\n  parse_args_populate_opts(argc, argv, &opts);\n  init_revisions(opts.revs);\n  sequencer_pick_revisions(&opts);\n\nThis patch does not intend to make any functional changes.  Check\nwith:\n\n  $ git blame -s -C HEAD^..HEAD -- sequencer.c | grep -C3 '^[^^]'\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Documentation/technical/api-sequencer.txt |   22 +\n builtin/revert.c                          |  966 +----------------------------\n sequencer.c                               |  925 +++++++++++++++++++++++++++-\n sequencer.h                               |   48 ++\n 4 files changed, 996 insertions(+), 965 deletions(-)\n create mode 100644 Documentation/technical/api-sequencer.txt\n\ndiff --git a/Documentation/technical/api-sequencer.txt b/Documentation/technical/api-sequencer.txt\nnew file mode 100644\nindex 0000000..22e9a95\n--- /dev/null\n+++ b/Documentation/technical/api-sequencer.txt\n@@ -0,0 +1,22 @@\n+sequencer API\n+=============\n+\n+The sequencer API makes it easy for builtins to execute a sequence of\n+commands operating on a range of commits.  It provides uniform\n+semantics to '--continue', '--quit', and '--abort' the sequence when a\n+conflict is encountered.\n+\n+Currently, the 'git cherry-pick' and 'git revert' builtins utilize the\n+API.  A new builtin \"foo\" can use the sequencer machinery by\n+implementing a parse_args_populate_opts() function and then running\n+the following:\n+\n+  memset(&opts, 0, sizeof(opts));\n+  opts.command = REPLAY_CMD_FOO;\n+  opts.revisions = xmalloc(sizeof(*opts.revs));\n+  parse_args_populate_opts(argc, argv, &opts);\n+  init_revisions(opts.revs);\n+  sequencer_pick_revisions(&opts);\n+\n+\n+(Ram)\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 187c317..54a1c50 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -1,18 +1,9 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n-#include \"object.h\"\n-#include \"commit.h\"\n-#include \"tag.h\"\n-#include \"run-command.h\"\n-#include \"exec_cmd.h\"\n-#include \"utf8.h\"\n #include \"parse-options.h\"\n-#include \"cache-tree.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"rerere.h\"\n-#include \"merge-recursive.h\"\n-#include \"refs.h\"\n #include \"dir.h\"\n #include \"sequencer.h\"\n \n@@ -39,61 +30,11 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-enum replay_action {\n-\tREPLAY_REVERT,\n-\tREPLAY_PICK\n-};\n-\n-enum replay_command {\n-\tREPLAY_CMD_REVERT,\n-\tREPLAY_CMD_CHERRY_PICK\n-};\n-\n-enum replay_subcommand {\n-\tREPLAY_NONE,\n-\tREPLAY_REMOVE_STATE,\n-\tREPLAY_CONTINUE,\n-\tREPLAY_ROLLBACK\n-};\n-\n-struct replay_insn_list {\n-\tstruct commit *operand;\n-\tenum replay_action action;\n-\tstruct replay_insn_list *next;\n-};\n-\n-struct replay_opts {\n-\tenum replay_command command;\n-\tenum replay_subcommand subcommand;\n-\n-\t/* Boolean options */\n-\tint edit;\n-\tint record_origin;\n-\tint no_commit;\n-\tint signoff;\n-\tint allow_ff;\n-\tint allow_rerere_auto;\n-\n-\tint mainline;\n-\n-\t/* Merge strategy */\n-\tconst char *strategy;\n-\tconst char **xopts;\n-\tsize_t xopts_nr, xopts_alloc;\n-\n-\t/* Only used by REPLAY_NONE */\n-\tstruct rev_info *revs;\n-};\n-\n-#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n-\n static const char *command_name(struct replay_opts *opts)\n {\n \treturn opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n-static char *get_encoding(const char *message);\n-\n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n \treturn opts->command == REPLAY_CMD_REVERT ? revert_usage : cherry_pick_usage;\n@@ -252,909 +193,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tusage_with_options(usage_str, options);\n }\n \n-struct commit_message {\n-\tchar *parent_label;\n-\tconst char *label;\n-\tconst char *subject;\n-\tchar *reencoded_message;\n-\tconst char *message;\n-};\n-\n-static int get_message(struct commit *commit, struct commit_message *out)\n-{\n-\tconst char *encoding;\n-\tconst char *abbrev, *subject;\n-\tint abbrev_len, subject_len;\n-\tchar *q;\n-\n-\tif (!commit->buffer)\n-\t\treturn -1;\n-\tencoding = get_encoding(commit->buffer);\n-\tif (!encoding)\n-\t\tencoding = \"UTF-8\";\n-\tif (!git_commit_encoding)\n-\t\tgit_commit_encoding = \"UTF-8\";\n-\n-\tout->reencoded_message = NULL;\n-\tout->message = commit->buffer;\n-\tif (strcmp(encoding, git_commit_encoding))\n-\t\tout->reencoded_message = reencode_string(commit->buffer,\n-\t\t\t\t\tgit_commit_encoding, encoding);\n-\tif (out->reencoded_message)\n-\t\tout->message = out->reencoded_message;\n-\n-\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n-\tabbrev_len = strlen(abbrev);\n-\n-\tsubject_len = find_commit_subject(out->message, &subject);\n-\n-\tout->parent_label = xmalloc(strlen(\"parent of \") + abbrev_len +\n-\t\t\t      strlen(\"... \") + subject_len + 1);\n-\tq = out->parent_label;\n-\tq = mempcpy(q, \"parent of \", strlen(\"parent of \"));\n-\tout->label = q;\n-\tq = mempcpy(q, abbrev, abbrev_len);\n-\tq = mempcpy(q, \"... \", strlen(\"... \"));\n-\tout->subject = q;\n-\tq = mempcpy(q, subject, subject_len);\n-\t*q = '\\0';\n-\treturn 0;\n-}\n-\n-static void free_message(struct commit_message *msg)\n-{\n-\tfree(msg->parent_label);\n-\tfree(msg->reencoded_message);\n-}\n-\n-static char *get_encoding(const char *message)\n-{\n-\tconst char *p = message, *eol;\n-\n-\twhile (*p && *p != '\\n') {\n-\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n-\t\t\t; /* do nothing */\n-\t\tif (!prefixcmp(p, \"encoding \")) {\n-\t\t\tchar *result = xmalloc(eol - 8 - p);\n-\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n-\t\t\treturn result;\n-\t\t}\n-\t\tp = eol;\n-\t\tif (*p == '\\n')\n-\t\t\tp++;\n-\t}\n-\treturn NULL;\n-}\n-\n-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n-{\n-\tconst char *filename;\n-\tint fd;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n-\n-\tfilename = git_path(\"%s\", pseudoref);\n-\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n-\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n-\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n-\tstrbuf_release(&buf);\n-}\n-\n-static void print_advice(int show_hint)\n-{\n-\tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n-\n-\tif (msg) {\n-\t\tfprintf(stderr, \"%s\\n\", msg);\n-\t\t/*\n-\t\t * A conflict has occured but the porcelain\n-\t\t * (typically rebase --interactive) wants to take care\n-\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n-\t\t */\n-\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n-\t\treturn;\n-\t}\n-\n-\tif (show_hint) {\n-\t\tadvise(\"after resolving the conflicts, mark the corrected paths\");\n-\t\tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n-\t\tadvise(\"and commit the result with 'git commit'\");\n-\t}\n-}\n-\n-static void write_message(struct strbuf *msgbuf, const char *filename)\n-{\n-\tstatic struct lock_file msg_file;\n-\n-\tint msg_fd = hold_lock_file_for_update(&msg_file, filename,\n-\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n-\tif (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), filename);\n-\tstrbuf_release(msgbuf);\n-\tif (commit_lock_file(&msg_file) < 0)\n-\t\tdie(_(\"Error wrapping up %s\"), filename);\n-}\n-\n-static struct tree *empty_tree(void)\n-{\n-\treturn lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);\n-}\n-\n-static int error_dirty_index(struct replay_opts *opts)\n-{\n-\tif (read_cache_unmerged())\n-\t\treturn error_resolve_conflict(command_name(opts));\n-\n-\t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->command == REPLAY_CMD_CHERRY_PICK)\n-\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n-\telse\n-\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n-\n-\tif (advice_commit_before_merge)\n-\t\tadvise(_(\"Commit your changes or stash them to proceed.\"));\n-\treturn -1;\n-}\n-\n-static int fast_forward_to(const unsigned char *to, const unsigned char *from)\n-{\n-\tstruct ref_lock *ref_lock;\n-\n-\tread_cache();\n-\tif (checkout_fast_forward(from, to))\n-\t\texit(1); /* the callee should have complained already */\n-\tref_lock = lock_any_ref_for_update(\"HEAD\", from, 0);\n-\treturn write_ref_sha1(ref_lock, to, \"cherry-pick\");\n-}\n-\n-static int do_recursive_merge(struct commit *base, struct commit *next,\n-\t\t\t      const char *base_label, const char *next_label,\n-\t\t\t      unsigned char *head, struct strbuf *msgbuf,\n-\t\t\t      struct replay_opts *opts)\n-{\n-\tstruct merge_options o;\n-\tstruct tree *result, *next_tree, *base_tree, *head_tree;\n-\tint clean, index_fd;\n-\tconst char **xopt;\n-\tstatic struct lock_file index_lock;\n-\n-\tindex_fd = hold_locked_index(&index_lock, 1);\n-\n-\tread_cache();\n-\n-\tinit_merge_options(&o);\n-\to.ancestor = base ? base_label : \"(empty tree)\";\n-\to.branch1 = \"HEAD\";\n-\to.branch2 = next ? next_label : \"(empty tree)\";\n-\n-\thead_tree = parse_tree_indirect(head);\n-\tnext_tree = next ? next->tree : empty_tree();\n-\tbase_tree = base ? base->tree : empty_tree();\n-\n-\tfor (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)\n-\t\tparse_merge_opt(&o, *xopt);\n-\n-\tclean = merge_trees(&o,\n-\t\t\t    head_tree,\n-\t\t\t    next_tree, base_tree, &result);\n-\n-\tif (active_cache_changed &&\n-\t    (write_cache(index_fd, active_cache, active_nr) ||\n-\t     commit_locked_index(&index_lock)))\n-\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n-\t\tdie(_(\"%s: Unable to write new index file\"), command_name(opts));\n-\trollback_lock_file(&index_lock);\n-\n-\tif (!clean) {\n-\t\tint i;\n-\t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n-\t\tfor (i = 0; i < active_nr;) {\n-\t\t\tstruct cache_entry *ce = active_cache[i++];\n-\t\t\tif (ce_stage(ce)) {\n-\t\t\t\tstrbuf_addch(msgbuf, '\\t');\n-\t\t\t\tstrbuf_addstr(msgbuf, ce->name);\n-\t\t\t\tstrbuf_addch(msgbuf, '\\n');\n-\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n-\t\t\t\t\t\tactive_cache[i]->name))\n-\t\t\t\t\ti++;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn !clean;\n-}\n-\n-/*\n- * If we are cherry-pick, and if the merge did not result in\n- * hand-editing, we will hit this commit and inherit the original\n- * author date and name.\n- * If we are revert, or if our cherry-pick results in a hand merge,\n- * we had better say that the current user is responsible for that.\n- */\n-static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n-{\n-\t/* 6 is max possible length of our args array including NULL */\n-\tconst char *args[6];\n-\tint i = 0;\n-\n-\targs[i++] = \"commit\";\n-\targs[i++] = \"-n\";\n-\tif (opts->signoff)\n-\t\targs[i++] = \"-s\";\n-\tif (!opts->edit) {\n-\t\targs[i++] = \"-F\";\n-\t\targs[i++] = defmsg;\n-\t}\n-\targs[i] = NULL;\n-\n-\treturn run_command_v_opt(args, RUN_GIT_CMD);\n-}\n-\n-static int do_pick_commit(struct commit *commit, enum replay_action action,\n-\t\t\tstruct replay_opts *opts)\n-{\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, NULL };\n-\tchar *defmsg = NULL;\n-\tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res;\n-\n-\tif (opts->no_commit) {\n-\t\t/*\n-\t\t * We do not intend to commit immediately.  We just want to\n-\t\t * merge the differences in, so let's compute the tree\n-\t\t * that represents the \"current\" state for merge-recursive\n-\t\t * to work on.\n-\t\t */\n-\t\tif (write_cache_as_tree(head, 0, NULL))\n-\t\t\tdie (_(\"Your index file is unmerged.\"));\n-\t} else {\n-\t\tif (get_sha1(\"HEAD\", head))\n-\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n-\t\tif (index_differs_from(\"HEAD\", 0))\n-\t\t\treturn error_dirty_index(opts);\n-\t}\n-\tdiscard_cache();\n-\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-\t\tstruct commit_list *p;\n-\n-\t\tif (!opts->mainline)\n-\t\t\treturn error(_(\"Commit %s is a merge but no -m option was given.\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1));\n-\n-\t\tfor (cnt = 1, p = commit->parents;\n-\t\t     cnt != opts->mainline && p;\n-\t\t     cnt++)\n-\t\t\tp = p->next;\n-\t\tif (cnt != opts->mainline || !p)\n-\t\t\treturn error(_(\"Commit %s does not have parent %d\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1), opts->mainline);\n-\t\tparent = p->item;\n-\t} else if (0 < opts->mainline)\n-\t\treturn error(_(\"Mainline was specified but commit %s is not a merge.\"),\n-\t\t\tsha1_to_hex(commit->object.sha1));\n-\telse\n-\t\tparent = commit->parents->item;\n-\n-\tif (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))\n-\t\treturn fast_forward_to(commit->object.sha1, head);\n-\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-\t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n-\t\t\tcommand_name(opts), sha1_to_hex(parent->object.sha1));\n-\n-\tif (get_message(commit, &msg) != 0)\n-\t\treturn error(_(\"Cannot get commit message for %s\"),\n-\t\t\tsha1_to_hex(commit->object.sha1));\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-\t * on top of the current HEAD if we are cherry-pick.  Or the\n-\t * reverse of it if we are revert.\n-\t */\n-\n-\tdefmsg = git_pathdup(\"MERGE_MSG\");\n-\n-\tif (action == REPLAY_REVERT) {\n-\t\tbase = commit;\n-\t\tbase_label = msg.label;\n-\t\tnext = parent;\n-\t\tnext_label = msg.parent_label;\n-\t\tstrbuf_addstr(&msgbuf, \"Revert \\\"\");\n-\t\tstrbuf_addstr(&msgbuf, msg.subject);\n-\t\tstrbuf_addstr(&msgbuf, \"\\\"\\n\\nThis reverts commit \");\n-\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-\n-\t\tif (commit->parents && commit->parents->next) {\n-\t\t\tstrbuf_addstr(&msgbuf, \", reversing\\nchanges made to \");\n-\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));\n-\t\t}\n-\t\tstrbuf_addstr(&msgbuf, \".\\n\");\n-\t} else {\n-\t\tconst char *p;\n-\n-\t\tbase = parent;\n-\t\tbase_label = msg.parent_label;\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\tp += 2;\n-\t\t\tstrbuf_addstr(&msgbuf, p);\n-\t\t}\n-\n-\t\tif (opts->record_origin) {\n-\t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n-\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-\t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n-\t\t}\n-\t}\n-\n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || action == REPLAY_REVERT) {\n-\t\tres = do_recursive_merge(base, next, base_label, next_label,\n-\t\t\t\t\t head, &msgbuf, opts);\n-\t\twrite_message(&msgbuf, defmsg);\n-\t} else {\n-\t\tstruct commit_list *common = NULL;\n-\t\tstruct commit_list *remotes = NULL;\n-\n-\t\twrite_message(&msgbuf, defmsg);\n-\n-\t\tcommit_list_insert(base, &common);\n-\t\tcommit_list_insert(next, &remotes);\n-\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n-\t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n-\t\tfree_commit_list(common);\n-\t\tfree_commit_list(remotes);\n-\t}\n-\n-\t/*\n-\t * If the merge was clean or if it failed due to conflict, we write\n-\t * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.\n-\t * However, if the merge did not even start, then we don't want to\n-\t * write it at all.\n-\t */\n-\tif (action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n-\n-\tif (res) {\n-\t\terror(action == REPLAY_REVERT\n-\t\t      ? _(\"could not revert %s... %s\")\n-\t\t      : _(\"could not apply %s... %s\"),\n-\t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n-\t\t      msg.subject);\n-\t\tprint_advice(res == 1);\n-\t\trerere(opts->allow_rerere_auto);\n-\t} else {\n-\t\tif (!opts->no_commit)\n-\t\t\tres = run_git_commit(defmsg, opts);\n-\t}\n-\n-\tfree_message(&msg);\n-\tfree(defmsg);\n-\n-\treturn res;\n-}\n-\n-static void prepare_revs(struct replay_opts *opts)\n-{\n-\tif (opts->command != REPLAY_CMD_REVERT)\n-\t\topts->revs->reverse ^= 1;\n-\n-\tif (prepare_revision_walk(opts->revs))\n-\t\tdie(_(\"revision walk setup failed\"));\n-\n-\tif (!opts->revs->commits)\n-\t\tdie(_(\"empty commit set passed\"));\n-}\n-\n-static void read_and_refresh_cache(struct replay_opts *opts)\n-{\n-\tstatic struct lock_file index_lock;\n-\tint index_fd = hold_locked_index(&index_lock, 0);\n-\tif (read_index_preload(&the_index, NULL) < 0)\n-\t\tdie(_(\"git %s: failed to read the index\"), command_name(opts));\n-\trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n-\tif (the_index.cache_changed) {\n-\t\tif (write_index(&the_index, index_fd) ||\n-\t\t    commit_locked_index(&index_lock))\n-\t\t\tdie(_(\"git %s: failed to refresh the index\"),\n-\t\t\t\tcommand_name(opts));\n-\t}\n-\trollback_lock_file(&index_lock);\n-}\n-\n-/*\n- * Append a (commit, action) to the end of the replay_insn_list.\n- *\n- * next starts by pointing to the variable that holds the head of an\n- * empty replay_insn_list, and is updated to point to the \"next\" field of\n- * the last item on the list as new (commit, action) pairs are appended.\n- *\n- * Usage example:\n- *\n- *     struct replay_insn_list *list;\n- *     struct replay_insn_list **next = &list;\n- *\n- *     next = replay_insn_list_append(c1, a1, next);\n- *     next = replay_insn_list_append(c2, a2, next);\n- *     assert(len(list) == 2);\n- *     return list;\n- */\n-static struct replay_insn_list **replay_insn_list_append(struct commit *operand,\n-\t\t\t\t\t\t\tenum replay_action action,\n-\t\t\t\t\t\t\tstruct replay_insn_list **next)\n-{\n-\tstruct replay_insn_list *new = xmalloc(sizeof(*new));\n-\tnew->action = action;\n-\tnew->operand = operand;\n-\t*next = new;\n-\tnew->next = NULL;\n-\treturn &new->next;\n-}\n-\n-static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n-{\n-\tstruct replay_insn_list *cur;\n-\n-\tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tconst char *sha1_abbrev, *action_str, *subject;\n-\t\tint subject_len;\n-\n-\t\taction_str = cur->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n-\t\tsha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);\n-\t\tsubject_len = find_commit_subject(cur->operand->buffer, &subject);\n-\t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n-\t\t\tsubject_len, subject);\n-\t}\n-\treturn 0;\n-}\n-\n-static int parse_error(const char *message, const char *file,\n-\t\tint lineno, char *error_line)\n-{\n-\tconst char *suffix = \"\";\n-\tint error_len = strcspn(error_line, \" \\t\\n\");\n-\n-\tif (error_len > 20) {\n-\t\terror_len = 20;\n-\t\tsuffix = \"...\";\n-\t}\n-\treturn error(_(\"%s:%d: %s: %.*s%s\"), file, lineno, message,\n-\t\terror_len, error_line, suffix);\n-}\n-\n-static int parse_insn_line(char *bol, char *eol,\n-\t\t\tstruct replay_insn_list *item, int lineno)\n-{\n-\tunsigned char commit_sha1[20];\n-\tint namelen;\n-\n-\tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n-\t\titem->action = REPLAY_PICK;\n-\t\tbol += strlen(\"pick \");\n-\t} else if (!prefixcmp(bol, \"revert \") || !prefixcmp(bol, \"revert\\t\")) {\n-\t\titem->action = REPLAY_REVERT;\n-\t\tbol += strlen(\"revert \");\n-\t} else\n-\t\treturn parse_error(_(\"unrecognized action\"),\n-\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n-\n-\t/* Eat up extra spaces/ tabs before object name */\n-\tbol += strspn(bol, \" \\t\");\n-\n-\tnamelen = strcspn(bol, \" \\t\\n\");\n-\tif (getn_sha1(bol, namelen, commit_sha1))\n-\t\treturn parse_error(_(\"malformed object name\"),\n-\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n-\n-\titem->operand = lookup_commit_reference(commit_sha1);\n-\tif (!item->operand)\n-\t\treturn parse_error(_(\"not a valid commit\"),\n-\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n-\n-\titem->next = NULL;\n-\treturn 0;\n-}\n-\n-static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n-{\n-\tstruct replay_insn_list **next = todo_list;\n-\tstruct replay_insn_list item = { NULL, 0, NULL };\n-\tchar *p = buf;\n-\tint i;\n-\n-\tfor (i = 1; *p; i++) {\n-\t\tchar *eol = strchrnul(p, '\\n');\n-\t\tif (parse_insn_line(p, eol, &item, i))\n-\t\t\treturn -1;\n-\t\tnext = replay_insn_list_append(item.operand, item.action, next);\n-\t\tp = *eol ? eol + 1 : eol;\n-\t}\n-\tif (!*todo_list)\n-\t\treturn error(_(\"No commits parsed.\"));\n-\treturn 0;\n-}\n-\n-static void read_populate_todo(struct replay_insn_list **todo_list)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd, res;\n-\n-\tfd = open(todo_file, O_RDONLY);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open %s\"), todo_file);\n-\tif (strbuf_read(&buf, fd, 0) < 0) {\n-\t\tclose(fd);\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Could not read %s.\"), todo_file);\n-\t}\n-\tclose(fd);\n-\n-\tres = parse_insn_buffer(buf.buf, todo_list);\n-\tstrbuf_release(&buf);\n-\tif (res)\n-\t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n-}\n-\n-static int populate_opts_cb(const char *key, const char *value, void *data)\n-{\n-\tstruct replay_opts *opts = data;\n-\tint error_flag = 1;\n-\n-\tif (!value)\n-\t\terror_flag = 0;\n-\telse if (!strcmp(key, \"options.no-commit\"))\n-\t\topts->no_commit = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.edit\"))\n-\t\topts->edit = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.signoff\"))\n-\t\topts->signoff = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.record-origin\"))\n-\t\topts->record_origin = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.allow-ff\"))\n-\t\topts->allow_ff = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.mainline\"))\n-\t\topts->mainline = git_config_int(key, value);\n-\telse if (!strcmp(key, \"options.strategy\"))\n-\t\tgit_config_string(&opts->strategy, key, value);\n-\telse if (!strcmp(key, \"options.strategy-option\")) {\n-\t\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n-\t\topts->xopts[opts->xopts_nr++] = xstrdup(value);\n-\t} else\n-\t\treturn error(_(\"Invalid key: %s\"), key);\n-\n-\tif (!error_flag)\n-\t\treturn error(_(\"Invalid value for %s: %s\"), key, value);\n-\n-\treturn 0;\n-}\n-\n-static void read_populate_opts(struct replay_opts **opts_ptr)\n-{\n-\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n-\n-\tif (!file_exists(opts_file))\n-\t\treturn;\n-\tif (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)\n-\t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n-}\n-\n-static void walk_revs_populate_todo(struct replay_insn_list **todo_list,\n-\t\t\t\tstruct replay_opts *opts)\n-{\n-\tstruct commit *commit;\n-\tstruct replay_insn_list **next;\n-\tenum replay_action action;\n-\n-\tprepare_revs(opts);\n-\n-\tnext = todo_list;\n-\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n-\twhile ((commit = get_revision(opts->revs)))\n-\t\tnext = replay_insn_list_append(commit, action, next);\n-}\n-\n-static int create_seq_dir(void)\n-{\n-\tconst char *seq_dir = git_path(SEQ_DIR);\n-\n-\tif (file_exists(seq_dir)) {\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(seq_dir, 0777) < 0)\n-\t\tdie_errno(_(\"Could not create sequencer directory %s\"), seq_dir);\n-\treturn 0;\n-}\n-\n-static void save_head(const char *head)\n-{\n-\tconst char *head_file = git_path(SEQ_HEAD_FILE);\n-\tstatic struct lock_file head_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);\n-\tstrbuf_addf(&buf, \"%s\\n\", head);\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), head_file);\n-\tif (commit_lock_file(&head_lock) < 0)\n-\t\tdie(_(\"Error wrapping up %s.\"), head_file);\n-}\n-\n-static int reset_for_rollback(const unsigned char *sha1)\n-{\n-\tconst char *argv[4];\t/* reset --merge <arg> + NULL */\n-\targv[0] = \"reset\";\n-\targv[1] = \"--merge\";\n-\targv[2] = sha1_to_hex(sha1);\n-\targv[3] = NULL;\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n-}\n-\n-static int rollback_single_pick(void)\n-{\n-\tunsigned char head_sha1[20];\n-\n-\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n-\t    !file_exists(git_path(\"REVERT_HEAD\")))\n-\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n-\tif (read_ref_full(\"HEAD\", head_sha1, 0, NULL))\n-\t\treturn error(_(\"cannot resolve HEAD\"));\n-\tif (is_null_sha1(head_sha1))\n-\t\treturn error(_(\"cannot abort from a branch yet to be born\"));\n-\treturn reset_for_rollback(head_sha1);\n-}\n-\n-static int sequencer_rollback(struct replay_opts *opts)\n-{\n-\tconst char *filename;\n-\tFILE *f;\n-\tunsigned char sha1[20];\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tfilename = git_path(SEQ_HEAD_FILE);\n-\tf = fopen(filename, \"r\");\n-\tif (!f && errno == ENOENT) {\n-\t\t/*\n-\t\t * There is no multiple-cherry-pick in progress.\n-\t\t * If CHERRY_PICK_HEAD or REVERT_HEAD indicates\n-\t\t * a single-cherry-pick in progress, abort that.\n-\t\t */\n-\t\treturn rollback_single_pick();\n-\t}\n-\tif (!f)\n-\t\treturn error(_(\"cannot open %s: %s\"), filename,\n-\t\t\t\t\t\tstrerror(errno));\n-\tif (strbuf_getline(&buf, f, '\\n')) {\n-\t\terror(_(\"cannot read %s: %s\"), filename, ferror(f) ?\n-\t\t\tstrerror(errno) : _(\"unexpected end of file\"));\n-\t\tfclose(f);\n-\t\tgoto fail;\n-\t}\n-\tfclose(f);\n-\tif (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\\0') {\n-\t\terror(_(\"stored pre-cherry-pick HEAD file '%s' is corrupt\"),\n-\t\t\tfilename);\n-\t\tgoto fail;\n-\t}\n-\tif (reset_for_rollback(sha1))\n-\t\tgoto fail;\n-\tremove_sequencer_state();\n-\tstrbuf_release(&buf);\n-\treturn 0;\n-fail:\n-\tstrbuf_release(&buf);\n-\treturn -1;\n-}\n-\n-static void save_todo(struct replay_insn_list *todo_list)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tstatic struct lock_file todo_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n-\tif (format_todo(&buf, todo_list) < 0)\n-\t\tdie(_(\"Could not format %s.\"), todo_file);\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n-\t\tstrbuf_release(&buf);\n-\t\tdie_errno(_(\"Could not write to %s\"), todo_file);\n-\t}\n-\tif (commit_lock_file(&todo_lock) < 0) {\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Error wrapping up %s.\"), todo_file);\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n-static void save_opts(struct replay_opts *opts)\n-{\n-\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n-\n-\tif (opts->no_commit)\n-\t\tgit_config_set_in_file(opts_file, \"options.no-commit\", \"true\");\n-\tif (opts->edit)\n-\t\tgit_config_set_in_file(opts_file, \"options.edit\", \"true\");\n-\tif (opts->signoff)\n-\t\tgit_config_set_in_file(opts_file, \"options.signoff\", \"true\");\n-\tif (opts->record_origin)\n-\t\tgit_config_set_in_file(opts_file, \"options.record-origin\", \"true\");\n-\tif (opts->allow_ff)\n-\t\tgit_config_set_in_file(opts_file, \"options.allow-ff\", \"true\");\n-\tif (opts->mainline) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n-\t\tgit_config_set_in_file(opts_file, \"options.mainline\", buf.buf);\n-\t\tstrbuf_release(&buf);\n-\t}\n-\tif (opts->strategy)\n-\t\tgit_config_set_in_file(opts_file, \"options.strategy\", opts->strategy);\n-\tif (opts->xopts) {\n-\t\tint i;\n-\t\tfor (i = 0; i < opts->xopts_nr; i++)\n-\t\t\tgit_config_set_multivar_in_file(opts_file,\n-\t\t\t\t\t\t\t\"options.strategy-option\",\n-\t\t\t\t\t\t\topts->xopts[i], \"^$\", 0);\n-\t}\n-}\n-\n-static int pick_commits(struct replay_insn_list *todo_list, struct replay_opts *opts)\n-{\n-\tstruct replay_insn_list *cur;\n-\tint res;\n-\n-\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n-\tif (opts->allow_ff)\n-\t\tassert(!(opts->signoff || opts->no_commit ||\n-\t\t\t\topts->record_origin || opts->edit));\n-\tread_and_refresh_cache(opts);\n-\n-\tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsave_todo(cur);\n-\t\tres = do_pick_commit(cur->operand, cur->action, opts);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\t/*\n-\t * Sequence of picks finished successfully; cleanup by\n-\t * removing the .git/sequencer directory\n-\t */\n-\tremove_sequencer_state();\n-\treturn 0;\n-}\n-\n-static int continue_single_pick(void)\n-{\n-\tconst char *argv[] = { \"commit\", NULL };\n-\n-\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n-\t    !file_exists(git_path(\"REVERT_HEAD\")))\n-\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n-}\n-\n-static int sequencer_continue(struct replay_opts *opts)\n-{\n-\tstruct replay_insn_list *todo_list = NULL;\n-\n-\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n-\t\treturn continue_single_pick();\n-\tread_populate_opts(&opts);\n-\tread_populate_todo(&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\tint ret = continue_single_pick();\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\t}\n-\tif (index_differs_from(\"HEAD\", 0))\n-\t\treturn error_dirty_index(opts);\n-\ttodo_list = todo_list->next;\n-\treturn pick_commits(todo_list, opts);\n-}\n-\n-static int single_pick(struct commit *cmit, struct replay_opts *opts)\n-{\n-\tenum replay_action action;\n-\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n-\n-\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n-\treturn do_pick_commit(cmit, action, opts);\n-}\n-\n-static int pick_revisions(struct replay_opts *opts)\n-{\n-\tstruct replay_insn_list *todo_list = NULL;\n-\tunsigned char sha1[20];\n-\n-\tif (opts->subcommand == REPLAY_NONE)\n-\t\tassert(opts->revs);\n-\n-\tread_and_refresh_cache(opts);\n-\n-\t/*\n-\t * Decide what to do depending on the arguments; a fresh\n-\t * cherry-pick should be handled differently from an existing\n-\t * one that is being continued\n-\t */\n-\tif (opts->subcommand == REPLAY_REMOVE_STATE) {\n-\t\tremove_sequencer_state();\n-\t\treturn 0;\n-\t}\n-\tif (opts->subcommand == REPLAY_ROLLBACK)\n-\t\treturn sequencer_rollback(opts);\n-\tif (opts->subcommand == REPLAY_CONTINUE)\n-\t\treturn sequencer_continue(opts);\n-\n-\t/*\n-\t * If we were called as \"git cherry-pick <commit>\", just\n-\t * cherry-pick/revert it, set CHERRY_PICK_HEAD /\n-\t * REVERT_HEAD, and don't touch the sequencer state.\n-\t * This means it is possible to cherry-pick in the middle\n-\t * of a cherry-pick sequence.\n-\t */\n-\tif (opts->revs->cmdline.nr == 1 &&\n-\t    opts->revs->cmdline.rev->whence == REV_CMD_REV &&\n-\t    opts->revs->no_walk &&\n-\t    !opts->revs->cmdline.rev->flags) {\n-\t\tstruct commit *cmit;\n-\t\tif (prepare_revision_walk(opts->revs))\n-\t\t\tdie(_(\"revision walk setup failed\"));\n-\t\tcmit = get_revision(opts->revs);\n-\t\tif (!cmit || get_revision(opts->revs))\n-\t\t\tdie(\"BUG: expected exactly one commit from walk\");\n-\t\treturn single_pick(cmit, opts);\n-\t}\n-\n-\t/*\n-\t * Start a new cherry-pick/ revert sequence; but\n-\t * first, make sure that an existing one isn't in\n-\t * progress\n-\t */\n-\n-\twalk_revs_populate_todo(&todo_list, opts);\n-\tif (create_seq_dir() < 0)\n-\t\treturn -1;\n-\tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->command == REPLAY_CMD_REVERT)\n-\t\t\treturn error(_(\"Can't revert as initial commit\"));\n-\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n-\t}\n-\tsave_head(sha1_to_hex(sha1));\n-\tsave_opts(opts);\n-\treturn pick_commits(todo_list, opts);\n-}\n-\n int cmd_revert(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts;\n@@ -1166,7 +204,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \topts.command = REPLAY_CMD_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\tres = pick_revisions(&opts);\n+\tres = sequencer_pick_revisions(&opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n \treturn res;\n@@ -1181,7 +219,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \topts.command = REPLAY_CMD_CHERRY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\tres = pick_revisions(&opts);\n+\tres = sequencer_pick_revisions(&opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\n \treturn res;\ndiff --git a/sequencer.c b/sequencer.c\nindex d1f28a6..a9c3bfa 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,7 +1,20 @@\n #include \"cache.h\"\n #include \"sequencer.h\"\n-#include \"strbuf.h\"\n #include \"dir.h\"\n+#include \"object.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"run-command.h\"\n+#include \"exec_cmd.h\"\n+#include \"utf8.h\"\n+#include \"cache-tree.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"rerere.h\"\n+#include \"merge-recursive.h\"\n+#include \"refs.h\"\n+\n+#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n void remove_sequencer_state(void)\n {\n@@ -11,3 +24,913 @@ void remove_sequencer_state(void)\n \tremove_dir_recursively(&seq_dir, 0);\n \tstrbuf_release(&seq_dir);\n }\n+\n+static const char *command_name(struct replay_opts *opts)\n+{\n+\treturn opts->command == REPLAY_CMD_REVERT ? \"revert\" : \"cherry-pick\";\n+}\n+\n+static char *get_encoding(const char *message);\n+\n+struct commit_message {\n+\tchar *parent_label;\n+\tconst char *label;\n+\tconst char *subject;\n+\tchar *reencoded_message;\n+\tconst char *message;\n+};\n+\n+static int get_message(struct commit *commit, struct commit_message *out)\n+{\n+\tconst char *encoding;\n+\tconst char *abbrev, *subject;\n+\tint abbrev_len, subject_len;\n+\tchar *q;\n+\n+\tif (!commit->buffer)\n+\t\treturn -1;\n+\tencoding = get_encoding(commit->buffer);\n+\tif (!encoding)\n+\t\tencoding = \"UTF-8\";\n+\tif (!git_commit_encoding)\n+\t\tgit_commit_encoding = \"UTF-8\";\n+\n+\tout->reencoded_message = NULL;\n+\tout->message = commit->buffer;\n+\tif (strcmp(encoding, git_commit_encoding))\n+\t\tout->reencoded_message = reencode_string(commit->buffer,\n+\t\t\t\t\tgit_commit_encoding, encoding);\n+\tif (out->reencoded_message)\n+\t\tout->message = out->reencoded_message;\n+\n+\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n+\tabbrev_len = strlen(abbrev);\n+\n+\tsubject_len = find_commit_subject(out->message, &subject);\n+\n+\tout->parent_label = xmalloc(strlen(\"parent of \") + abbrev_len +\n+\t\t\t      strlen(\"... \") + subject_len + 1);\n+\tq = out->parent_label;\n+\tq = mempcpy(q, \"parent of \", strlen(\"parent of \"));\n+\tout->label = q;\n+\tq = mempcpy(q, abbrev, abbrev_len);\n+\tq = mempcpy(q, \"... \", strlen(\"... \"));\n+\tout->subject = q;\n+\tq = mempcpy(q, subject, subject_len);\n+\t*q = '\\0';\n+\treturn 0;\n+}\n+\n+static void free_message(struct commit_message *msg)\n+{\n+\tfree(msg->parent_label);\n+\tfree(msg->reencoded_message);\n+}\n+\n+static char *get_encoding(const char *message)\n+{\n+\tconst char *p = message, *eol;\n+\n+\twhile (*p && *p != '\\n') {\n+\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n+\t\t\t; /* do nothing */\n+\t\tif (!prefixcmp(p, \"encoding \")) {\n+\t\t\tchar *result = xmalloc(eol - 8 - p);\n+\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n+\t\t\treturn result;\n+\t\t}\n+\t\tp = eol;\n+\t\tif (*p == '\\n')\n+\t\t\tp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n+{\n+\tconst char *filename;\n+\tint fd;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n+\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n+\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n+\tstrbuf_release(&buf);\n+}\n+\n+static void print_advice(int show_hint)\n+{\n+\tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n+\n+\tif (msg) {\n+\t\tfprintf(stderr, \"%s\\n\", msg);\n+\t\t/*\n+\t\t * A conflict has occured but the porcelain\n+\t\t * (typically rebase --interactive) wants to take care\n+\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n+\t\t */\n+\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n+\t\treturn;\n+\t}\n+\n+\tif (show_hint) {\n+\t\tadvise(\"after resolving the conflicts, mark the corrected paths\");\n+\t\tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n+\t\tadvise(\"and commit the result with 'git commit'\");\n+\t}\n+}\n+\n+static void write_message(struct strbuf *msgbuf, const char *filename)\n+{\n+\tstatic struct lock_file msg_file;\n+\n+\tint msg_fd = hold_lock_file_for_update(&msg_file, filename,\n+\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n+\tif (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), filename);\n+\tstrbuf_release(msgbuf);\n+\tif (commit_lock_file(&msg_file) < 0)\n+\t\tdie(_(\"Error wrapping up %s\"), filename);\n+}\n+\n+static struct tree *empty_tree(void)\n+{\n+\treturn lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);\n+}\n+\n+static int error_dirty_index(struct replay_opts *opts)\n+{\n+\tif (read_cache_unmerged())\n+\t\treturn error_resolve_conflict(command_name(opts));\n+\n+\t/* Different translation strings for cherry-pick and revert */\n+\tif (opts->command == REPLAY_CMD_CHERRY_PICK)\n+\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n+\telse\n+\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n+\n+\tif (advice_commit_before_merge)\n+\t\tadvise(_(\"Commit your changes or stash them to proceed.\"));\n+\treturn -1;\n+}\n+\n+static int fast_forward_to(const unsigned char *to, const unsigned char *from)\n+{\n+\tstruct ref_lock *ref_lock;\n+\n+\tread_cache();\n+\tif (checkout_fast_forward(from, to))\n+\t\texit(1); /* the callee should have complained already */\n+\tref_lock = lock_any_ref_for_update(\"HEAD\", from, 0);\n+\treturn write_ref_sha1(ref_lock, to, \"cherry-pick\");\n+}\n+\n+static int do_recursive_merge(struct commit *base, struct commit *next,\n+\t\t\t      const char *base_label, const char *next_label,\n+\t\t\t      unsigned char *head, struct strbuf *msgbuf,\n+\t\t\t      struct replay_opts *opts)\n+{\n+\tstruct merge_options o;\n+\tstruct tree *result, *next_tree, *base_tree, *head_tree;\n+\tint clean, index_fd;\n+\tconst char **xopt;\n+\tstatic struct lock_file index_lock;\n+\n+\tindex_fd = hold_locked_index(&index_lock, 1);\n+\n+\tread_cache();\n+\n+\tinit_merge_options(&o);\n+\to.ancestor = base ? base_label : \"(empty tree)\";\n+\to.branch1 = \"HEAD\";\n+\to.branch2 = next ? next_label : \"(empty tree)\";\n+\n+\thead_tree = parse_tree_indirect(head);\n+\tnext_tree = next ? next->tree : empty_tree();\n+\tbase_tree = base ? base->tree : empty_tree();\n+\n+\tfor (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)\n+\t\tparse_merge_opt(&o, *xopt);\n+\n+\tclean = merge_trees(&o,\n+\t\t\t    head_tree,\n+\t\t\t    next_tree, base_tree, &result);\n+\n+\tif (active_cache_changed &&\n+\t    (write_cache(index_fd, active_cache, active_nr) ||\n+\t     commit_locked_index(&index_lock)))\n+\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n+\t\tdie(_(\"%s: Unable to write new index file\"), command_name(opts));\n+\trollback_lock_file(&index_lock);\n+\n+\tif (!clean) {\n+\t\tint i;\n+\t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n+\t\tfor (i = 0; i < active_nr;) {\n+\t\t\tstruct cache_entry *ce = active_cache[i++];\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tstrbuf_addch(msgbuf, '\\t');\n+\t\t\t\tstrbuf_addstr(msgbuf, ce->name);\n+\t\t\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n+\t\t\t\t\t\tactive_cache[i]->name))\n+\t\t\t\t\ti++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn !clean;\n+}\n+\n+/*\n+ * If we are cherry-pick, and if the merge did not result in\n+ * hand-editing, we will hit this commit and inherit the original\n+ * author date and name.\n+ * If we are revert, or if our cherry-pick results in a hand merge,\n+ * we had better say that the current user is responsible for that.\n+ */\n+static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n+{\n+\t/* 6 is max possible length of our args array including NULL */\n+\tconst char *args[6];\n+\tint i = 0;\n+\n+\targs[i++] = \"commit\";\n+\targs[i++] = \"-n\";\n+\tif (opts->signoff)\n+\t\targs[i++] = \"-s\";\n+\tif (!opts->edit) {\n+\t\targs[i++] = \"-F\";\n+\t\targs[i++] = defmsg;\n+\t}\n+\targs[i] = NULL;\n+\n+\treturn run_command_v_opt(args, RUN_GIT_CMD);\n+}\n+\n+static int do_pick_commit(struct commit *commit, enum replay_action action,\n+\t\t\tstruct replay_opts *opts)\n+{\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, NULL };\n+\tchar *defmsg = NULL;\n+\tstruct strbuf msgbuf = STRBUF_INIT;\n+\tint res;\n+\n+\tif (opts->no_commit) {\n+\t\t/*\n+\t\t * We do not intend to commit immediately.  We just want to\n+\t\t * merge the differences in, so let's compute the tree\n+\t\t * that represents the \"current\" state for merge-recursive\n+\t\t * to work on.\n+\t\t */\n+\t\tif (write_cache_as_tree(head, 0, NULL))\n+\t\t\tdie (_(\"Your index file is unmerged.\"));\n+\t} else {\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n+\t\tif (index_differs_from(\"HEAD\", 0))\n+\t\t\treturn error_dirty_index(opts);\n+\t}\n+\tdiscard_cache();\n+\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+\t\tstruct commit_list *p;\n+\n+\t\tif (!opts->mainline)\n+\t\t\treturn error(_(\"Commit %s is a merge but no -m option was given.\"),\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\n+\t\tfor (cnt = 1, p = commit->parents;\n+\t\t     cnt != opts->mainline && p;\n+\t\t     cnt++)\n+\t\t\tp = p->next;\n+\t\tif (cnt != opts->mainline || !p)\n+\t\t\treturn error(_(\"Commit %s does not have parent %d\"),\n+\t\t\t\tsha1_to_hex(commit->object.sha1), opts->mainline);\n+\t\tparent = p->item;\n+\t} else if (0 < opts->mainline)\n+\t\treturn error(_(\"Mainline was specified but commit %s is not a merge.\"),\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\telse\n+\t\tparent = commit->parents->item;\n+\n+\tif (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))\n+\t\treturn fast_forward_to(commit->object.sha1, head);\n+\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+\t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n+\t\t\tcommand_name(opts), sha1_to_hex(parent->object.sha1));\n+\n+\tif (get_message(commit, &msg) != 0)\n+\t\treturn error(_(\"Cannot get commit message for %s\"),\n+\t\t\tsha1_to_hex(commit->object.sha1));\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+\t * on top of the current HEAD if we are cherry-pick.  Or the\n+\t * reverse of it if we are revert.\n+\t */\n+\n+\tdefmsg = git_pathdup(\"MERGE_MSG\");\n+\n+\tif (action == REPLAY_REVERT) {\n+\t\tbase = commit;\n+\t\tbase_label = msg.label;\n+\t\tnext = parent;\n+\t\tnext_label = msg.parent_label;\n+\t\tstrbuf_addstr(&msgbuf, \"Revert \\\"\");\n+\t\tstrbuf_addstr(&msgbuf, msg.subject);\n+\t\tstrbuf_addstr(&msgbuf, \"\\\"\\n\\nThis reverts commit \");\n+\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n+\n+\t\tif (commit->parents && commit->parents->next) {\n+\t\t\tstrbuf_addstr(&msgbuf, \", reversing\\nchanges made to \");\n+\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));\n+\t\t}\n+\t\tstrbuf_addstr(&msgbuf, \".\\n\");\n+\t} else {\n+\t\tconst char *p;\n+\n+\t\tbase = parent;\n+\t\tbase_label = msg.parent_label;\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\tp += 2;\n+\t\t\tstrbuf_addstr(&msgbuf, p);\n+\t\t}\n+\n+\t\tif (opts->record_origin) {\n+\t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n+\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n+\t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n+\t\t}\n+\t}\n+\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || action == REPLAY_REVERT) {\n+\t\tres = do_recursive_merge(base, next, base_label, next_label,\n+\t\t\t\t\t head, &msgbuf, opts);\n+\t\twrite_message(&msgbuf, defmsg);\n+\t} else {\n+\t\tstruct commit_list *common = NULL;\n+\t\tstruct commit_list *remotes = NULL;\n+\n+\t\twrite_message(&msgbuf, defmsg);\n+\n+\t\tcommit_list_insert(base, &common);\n+\t\tcommit_list_insert(next, &remotes);\n+\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n+\t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n+\t\tfree_commit_list(common);\n+\t\tfree_commit_list(remotes);\n+\t}\n+\n+\t/*\n+\t * If the merge was clean or if it failed due to conflict, we write\n+\t * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.\n+\t * However, if the merge did not even start, then we don't want to\n+\t * write it at all.\n+\t */\n+\tif (action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n+\tif (action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n+\n+\tif (res) {\n+\t\terror(action == REPLAY_REVERT\n+\t\t      ? _(\"could not revert %s... %s\")\n+\t\t      : _(\"could not apply %s... %s\"),\n+\t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n+\t\t      msg.subject);\n+\t\tprint_advice(res == 1);\n+\t\trerere(opts->allow_rerere_auto);\n+\t} else {\n+\t\tif (!opts->no_commit)\n+\t\t\tres = run_git_commit(defmsg, opts);\n+\t}\n+\n+\tfree_message(&msg);\n+\tfree(defmsg);\n+\n+\treturn res;\n+}\n+\n+static void prepare_revs(struct replay_opts *opts)\n+{\n+\tif (opts->command != REPLAY_CMD_REVERT)\n+\t\topts->revs->reverse ^= 1;\n+\n+\tif (prepare_revision_walk(opts->revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tif (!opts->revs->commits)\n+\t\tdie(_(\"empty commit set passed\"));\n+}\n+\n+static void read_and_refresh_cache(struct replay_opts *opts)\n+{\n+\tstatic struct lock_file index_lock;\n+\tint index_fd = hold_locked_index(&index_lock, 0);\n+\tif (read_index_preload(&the_index, NULL) < 0)\n+\t\tdie(_(\"git %s: failed to read the index\"), command_name(opts));\n+\trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n+\tif (the_index.cache_changed) {\n+\t\tif (write_index(&the_index, index_fd) ||\n+\t\t    commit_locked_index(&index_lock))\n+\t\t\tdie(_(\"git %s: failed to refresh the index\"),\n+\t\t\t\tcommand_name(opts));\n+\t}\n+\trollback_lock_file(&index_lock);\n+}\n+\n+/*\n+ * Append a (commit, action) to the end of the replay_insn_list.\n+ *\n+ * next starts by pointing to the variable that holds the head of an\n+ * empty replay_insn_list, and is updated to point to the \"next\" field of\n+ * the last item on the list as new (commit, action) pairs are appended.\n+ *\n+ * Usage example:\n+ *\n+ *     struct replay_insn_list *list;\n+ *     struct replay_insn_list **next = &list;\n+ *\n+ *     next = replay_insn_list_append(c1, a1, next);\n+ *     next = replay_insn_list_append(c2, a2, next);\n+ *     assert(len(list) == 2);\n+ *     return list;\n+ */\n+static struct replay_insn_list **replay_insn_list_append(struct commit *operand,\n+\t\t\t\t\t\t\tenum replay_action action,\n+\t\t\t\t\t\t\tstruct replay_insn_list **next)\n+{\n+\tstruct replay_insn_list *new = xmalloc(sizeof(*new));\n+\tnew->action = action;\n+\tnew->operand = operand;\n+\t*next = new;\n+\tnew->next = NULL;\n+\treturn &new->next;\n+}\n+\n+static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n+{\n+\tstruct replay_insn_list *cur;\n+\n+\tfor (cur = todo_list; cur; cur = cur->next) {\n+\t\tconst char *sha1_abbrev, *action_str, *subject;\n+\t\tint subject_len;\n+\n+\t\taction_str = cur->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n+\t\tsha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);\n+\t\tsubject_len = find_commit_subject(cur->operand->buffer, &subject);\n+\t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n+\t\t\tsubject_len, subject);\n+\t}\n+\treturn 0;\n+}\n+\n+static int parse_error(const char *message, const char *file,\n+\t\tint lineno, char *error_line)\n+{\n+\tconst char *suffix = \"\";\n+\tint error_len = strcspn(error_line, \" \\t\\n\");\n+\n+\tif (error_len > 20) {\n+\t\terror_len = 20;\n+\t\tsuffix = \"...\";\n+\t}\n+\treturn error(_(\"%s:%d: %s: %.*s%s\"), file, lineno, message,\n+\t\terror_len, error_line, suffix);\n+}\n+\n+static int parse_insn_line(char *bol, char *eol,\n+\t\t\tstruct replay_insn_list *item, int lineno)\n+{\n+\tunsigned char commit_sha1[20];\n+\tint namelen;\n+\n+\tif (!prefixcmp(bol, \"pick \") || !prefixcmp(bol, \"pick\\t\")) {\n+\t\titem->action = REPLAY_PICK;\n+\t\tbol += strlen(\"pick \");\n+\t} else if (!prefixcmp(bol, \"revert \") || !prefixcmp(bol, \"revert\\t\")) {\n+\t\titem->action = REPLAY_REVERT;\n+\t\tbol += strlen(\"revert \");\n+\t} else\n+\t\treturn parse_error(_(\"unrecognized action\"),\n+\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n+\n+\t/* Eat up extra spaces/ tabs before object name */\n+\tbol += strspn(bol, \" \\t\");\n+\n+\tnamelen = strcspn(bol, \" \\t\\n\");\n+\tif (getn_sha1(bol, namelen, commit_sha1))\n+\t\treturn parse_error(_(\"malformed object name\"),\n+\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n+\n+\titem->operand = lookup_commit_reference(commit_sha1);\n+\tif (!item->operand)\n+\t\treturn parse_error(_(\"not a valid commit\"),\n+\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n+\n+\titem->next = NULL;\n+\treturn 0;\n+}\n+\n+static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)\n+{\n+\tstruct replay_insn_list **next = todo_list;\n+\tstruct replay_insn_list item = { NULL, 0, NULL };\n+\tchar *p = buf;\n+\tint i;\n+\n+\tfor (i = 1; *p; i++) {\n+\t\tchar *eol = strchrnul(p, '\\n');\n+\t\tif (parse_insn_line(p, eol, &item, i))\n+\t\t\treturn -1;\n+\t\tnext = replay_insn_list_append(item.operand, item.action, next);\n+\t\tp = *eol ? eol + 1 : eol;\n+\t}\n+\tif (!*todo_list)\n+\t\treturn error(_(\"No commits parsed.\"));\n+\treturn 0;\n+}\n+\n+static void read_populate_todo(struct replay_insn_list **todo_list)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd, res;\n+\n+\tfd = open(todo_file, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open %s\"), todo_file);\n+\tif (strbuf_read(&buf, fd, 0) < 0) {\n+\t\tclose(fd);\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Could not read %s.\"), todo_file);\n+\t}\n+\tclose(fd);\n+\n+\tres = parse_insn_buffer(buf.buf, todo_list);\n+\tstrbuf_release(&buf);\n+\tif (res)\n+\t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n+}\n+\n+static int populate_opts_cb(const char *key, const char *value, void *data)\n+{\n+\tstruct replay_opts *opts = data;\n+\tint error_flag = 1;\n+\n+\tif (!value)\n+\t\terror_flag = 0;\n+\telse if (!strcmp(key, \"options.no-commit\"))\n+\t\topts->no_commit = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.edit\"))\n+\t\topts->edit = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.signoff\"))\n+\t\topts->signoff = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.record-origin\"))\n+\t\topts->record_origin = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.allow-ff\"))\n+\t\topts->allow_ff = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.mainline\"))\n+\t\topts->mainline = git_config_int(key, value);\n+\telse if (!strcmp(key, \"options.strategy\"))\n+\t\tgit_config_string(&opts->strategy, key, value);\n+\telse if (!strcmp(key, \"options.strategy-option\")) {\n+\t\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n+\t\topts->xopts[opts->xopts_nr++] = xstrdup(value);\n+\t} else\n+\t\treturn error(_(\"Invalid key: %s\"), key);\n+\n+\tif (!error_flag)\n+\t\treturn error(_(\"Invalid value for %s: %s\"), key, value);\n+\n+\treturn 0;\n+}\n+\n+static void read_populate_opts(struct replay_opts **opts_ptr)\n+{\n+\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n+\n+\tif (!file_exists(opts_file))\n+\t\treturn;\n+\tif (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)\n+\t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n+}\n+\n+static void walk_revs_populate_todo(struct replay_insn_list **todo_list,\n+\t\t\t\tstruct replay_opts *opts)\n+{\n+\tstruct commit *commit;\n+\tstruct replay_insn_list **next;\n+\tenum replay_action action;\n+\n+\tprepare_revs(opts);\n+\n+\tnext = todo_list;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n+\twhile ((commit = get_revision(opts->revs)))\n+\t\tnext = replay_insn_list_append(commit, action, next);\n+}\n+\n+static int create_seq_dir(void)\n+{\n+\tconst char *seq_dir = git_path(SEQ_DIR);\n+\n+\tif (file_exists(seq_dir)) {\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(seq_dir, 0777) < 0)\n+\t\tdie_errno(_(\"Could not create sequencer directory %s\"), seq_dir);\n+\treturn 0;\n+}\n+\n+static void save_head(const char *head)\n+{\n+\tconst char *head_file = git_path(SEQ_HEAD_FILE);\n+\tstatic struct lock_file head_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);\n+\tstrbuf_addf(&buf, \"%s\\n\", head);\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), head_file);\n+\tif (commit_lock_file(&head_lock) < 0)\n+\t\tdie(_(\"Error wrapping up %s.\"), head_file);\n+}\n+\n+static int reset_for_rollback(const unsigned char *sha1)\n+{\n+\tconst char *argv[4];\t/* reset --merge <arg> + NULL */\n+\targv[0] = \"reset\";\n+\targv[1] = \"--merge\";\n+\targv[2] = sha1_to_hex(sha1);\n+\targv[3] = NULL;\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n+static int rollback_single_pick(void)\n+{\n+\tunsigned char head_sha1[20];\n+\n+\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n+\t    !file_exists(git_path(\"REVERT_HEAD\")))\n+\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n+\tif (read_ref_full(\"HEAD\", head_sha1, 0, NULL))\n+\t\treturn error(_(\"cannot resolve HEAD\"));\n+\tif (is_null_sha1(head_sha1))\n+\t\treturn error(_(\"cannot abort from a branch yet to be born\"));\n+\treturn reset_for_rollback(head_sha1);\n+}\n+\n+static int sequencer_rollback(struct replay_opts *opts)\n+{\n+\tconst char *filename;\n+\tFILE *f;\n+\tunsigned char sha1[20];\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tfilename = git_path(SEQ_HEAD_FILE);\n+\tf = fopen(filename, \"r\");\n+\tif (!f && errno == ENOENT) {\n+\t\t/*\n+\t\t * There is no multiple-cherry-pick in progress.\n+\t\t * If CHERRY_PICK_HEAD or REVERT_HEAD indicates\n+\t\t * a single-cherry-pick in progress, abort that.\n+\t\t */\n+\t\treturn rollback_single_pick();\n+\t}\n+\tif (!f)\n+\t\treturn error(_(\"cannot open %s: %s\"), filename,\n+\t\t\t\t\t\tstrerror(errno));\n+\tif (strbuf_getline(&buf, f, '\\n')) {\n+\t\terror(_(\"cannot read %s: %s\"), filename, ferror(f) ?\n+\t\t\tstrerror(errno) : _(\"unexpected end of file\"));\n+\t\tfclose(f);\n+\t\tgoto fail;\n+\t}\n+\tfclose(f);\n+\tif (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\\0') {\n+\t\terror(_(\"stored pre-cherry-pick HEAD file '%s' is corrupt\"),\n+\t\t\tfilename);\n+\t\tgoto fail;\n+\t}\n+\tif (reset_for_rollback(sha1))\n+\t\tgoto fail;\n+\tremove_sequencer_state();\n+\tstrbuf_release(&buf);\n+\treturn 0;\n+fail:\n+\tstrbuf_release(&buf);\n+\treturn -1;\n+}\n+\n+static void save_todo(struct replay_insn_list *todo_list)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tstatic struct lock_file todo_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n+\tif (format_todo(&buf, todo_list) < 0)\n+\t\tdie(_(\"Could not format %s.\"), todo_file);\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n+\t\tstrbuf_release(&buf);\n+\t\tdie_errno(_(\"Could not write to %s\"), todo_file);\n+\t}\n+\tif (commit_lock_file(&todo_lock) < 0) {\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Error wrapping up %s.\"), todo_file);\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n+static void save_opts(struct replay_opts *opts)\n+{\n+\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n+\n+\tif (opts->no_commit)\n+\t\tgit_config_set_in_file(opts_file, \"options.no-commit\", \"true\");\n+\tif (opts->edit)\n+\t\tgit_config_set_in_file(opts_file, \"options.edit\", \"true\");\n+\tif (opts->signoff)\n+\t\tgit_config_set_in_file(opts_file, \"options.signoff\", \"true\");\n+\tif (opts->record_origin)\n+\t\tgit_config_set_in_file(opts_file, \"options.record-origin\", \"true\");\n+\tif (opts->allow_ff)\n+\t\tgit_config_set_in_file(opts_file, \"options.allow-ff\", \"true\");\n+\tif (opts->mainline) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n+\t\tgit_config_set_in_file(opts_file, \"options.mainline\", buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t}\n+\tif (opts->strategy)\n+\t\tgit_config_set_in_file(opts_file, \"options.strategy\", opts->strategy);\n+\tif (opts->xopts) {\n+\t\tint i;\n+\t\tfor (i = 0; i < opts->xopts_nr; i++)\n+\t\t\tgit_config_set_multivar_in_file(opts_file,\n+\t\t\t\t\t\t\t\"options.strategy-option\",\n+\t\t\t\t\t\t\topts->xopts[i], \"^$\", 0);\n+\t}\n+}\n+\n+static int pick_commits(struct replay_insn_list *todo_list, struct replay_opts *opts)\n+{\n+\tstruct replay_insn_list *cur;\n+\tint res;\n+\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n+\tif (opts->allow_ff)\n+\t\tassert(!(opts->signoff || opts->no_commit ||\n+\t\t\t\topts->record_origin || opts->edit));\n+\tread_and_refresh_cache(opts);\n+\n+\tfor (cur = todo_list; cur; cur = cur->next) {\n+\t\tsave_todo(cur);\n+\t\tres = do_pick_commit(cur->operand, cur->action, opts);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\t/*\n+\t * Sequence of picks finished successfully; cleanup by\n+\t * removing the .git/sequencer directory\n+\t */\n+\tremove_sequencer_state();\n+\treturn 0;\n+}\n+\n+static int continue_single_pick(void)\n+{\n+\tconst char *argv[] = { \"commit\", NULL };\n+\n+\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n+\t    !file_exists(git_path(\"REVERT_HEAD\")))\n+\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n+static int sequencer_continue(struct replay_opts *opts)\n+{\n+\tstruct replay_insn_list *todo_list = NULL;\n+\n+\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n+\t\treturn continue_single_pick();\n+\tread_populate_opts(&opts);\n+\tread_populate_todo(&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\tint ret = continue_single_pick();\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\tif (index_differs_from(\"HEAD\", 0))\n+\t\treturn error_dirty_index(opts);\n+\ttodo_list = todo_list->next;\n+\treturn pick_commits(todo_list, opts);\n+}\n+\n+static int single_pick(struct commit *cmit, struct replay_opts *opts)\n+{\n+\tenum replay_action action;\n+\taction = opts->command == REPLAY_CMD_REVERT ? REPLAY_REVERT : REPLAY_PICK;\n+\n+\tsetenv(GIT_REFLOG_ACTION, command_name(opts), 0);\n+\treturn do_pick_commit(cmit, action, opts);\n+}\n+\n+int sequencer_pick_revisions(struct replay_opts *opts)\n+{\n+\tstruct replay_insn_list *todo_list = NULL;\n+\tunsigned char sha1[20];\n+\n+\tif (opts->subcommand == REPLAY_NONE)\n+\t\tassert(opts->revs);\n+\n+\tread_and_refresh_cache(opts);\n+\n+\t/*\n+\t * Decide what to do depending on the arguments; a fresh\n+\t * cherry-pick should be handled differently from an existing\n+\t * one that is being continued\n+\t */\n+\tif (opts->subcommand == REPLAY_REMOVE_STATE) {\n+\t\tremove_sequencer_state();\n+\t\treturn 0;\n+\t}\n+\tif (opts->subcommand == REPLAY_ROLLBACK)\n+\t\treturn sequencer_rollback(opts);\n+\tif (opts->subcommand == REPLAY_CONTINUE)\n+\t\treturn sequencer_continue(opts);\n+\n+\t/*\n+\t * If we were called as \"git cherry-pick <commit>\", just\n+\t * cherry-pick/revert it, set CHERRY_PICK_HEAD /\n+\t * REVERT_HEAD, and don't touch the sequencer state.\n+\t * This means it is possible to cherry-pick in the middle\n+\t * of a cherry-pick sequence.\n+\t */\n+\tif (opts->revs->cmdline.nr == 1 &&\n+\t    opts->revs->cmdline.rev->whence == REV_CMD_REV &&\n+\t    opts->revs->no_walk &&\n+\t    !opts->revs->cmdline.rev->flags) {\n+\t\tstruct commit *cmit;\n+\t\tif (prepare_revision_walk(opts->revs))\n+\t\t\tdie(_(\"revision walk setup failed\"));\n+\t\tcmit = get_revision(opts->revs);\n+\t\tif (!cmit || get_revision(opts->revs))\n+\t\t\tdie(\"BUG: expected exactly one commit from walk\");\n+\t\treturn single_pick(cmit, opts);\n+\t}\n+\n+\t/*\n+\t * Start a new cherry-pick/ revert sequence; but\n+\t * first, make sure that an existing one isn't in\n+\t * progress\n+\t */\n+\n+\twalk_revs_populate_todo(&todo_list, opts);\n+\tif (create_seq_dir() < 0)\n+\t\treturn -1;\n+\tif (get_sha1(\"HEAD\", sha1)) {\n+\t\tif (opts->command == REPLAY_CMD_REVERT)\n+\t\t\treturn error(_(\"Can't revert as initial commit\"));\n+\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n+\t}\n+\tsave_head(sha1_to_hex(sha1));\n+\tsave_opts(opts);\n+\treturn pick_commits(todo_list, opts);\n+}\ndiff --git a/sequencer.h b/sequencer.h\nindex 2d4528f..7035908 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -6,7 +6,55 @@\n #define SEQ_TODO_FILE\t\"sequencer/todo\"\n #define SEQ_OPTS_FILE\t\"sequencer/opts\"\n \n+enum replay_action {\n+\tREPLAY_REVERT,\n+\tREPLAY_PICK\n+};\n+\n+enum replay_command {\n+\tREPLAY_CMD_REVERT,\n+\tREPLAY_CMD_CHERRY_PICK\n+};\n+\n+enum replay_subcommand {\n+\tREPLAY_NONE,\n+\tREPLAY_REMOVE_STATE,\n+\tREPLAY_CONTINUE,\n+\tREPLAY_ROLLBACK\n+};\n+\n+struct replay_insn_list {\n+\tstruct commit *operand;\n+\tenum replay_action action;\n+\tstruct replay_insn_list *next;\n+};\n+\n+struct replay_opts {\n+\tenum replay_command command;\n+\tenum replay_subcommand subcommand;\n+\n+\t/* Boolean options */\n+\tint edit;\n+\tint record_origin;\n+\tint no_commit;\n+\tint signoff;\n+\tint allow_ff;\n+\tint allow_rerere_auto;\n+\n+\tint mainline;\n+\n+\t/* Merge strategy */\n+\tconst char *strategy;\n+\tconst char **xopts;\n+\tsize_t xopts_nr, xopts_alloc;\n+\n+\t/* Only used by REPLAY_NONE */\n+\tstruct rev_info *revs;\n+};\n+\n /* Removes SEQ_DIR. */\n extern void remove_sequencer_state(void);\n \n+extern int sequencer_pick_revisions(struct replay_opts *opts);\n+\n #endif\n-- \n1.7.8.2\n"},{"id":"182261","messageId":"20120110182659.GB22184@burratino","threadId":"29315","inReplyTo":"1326212039-13806-2-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 1/8] revert: prepare to move replay_action to header","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-10T18:27:00Z","receivedAt":"2012-01-10T18:27:00Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Later in the series, we will build a generalized sequencer by\n> factoring code out of the revert builtin, and leaving it with just\n> argument parsing work.  This involves moving \"replay_action\" to\n> sequencer.h, so that both sequencer.c and builtin/revert.c can use it.\n> Unfortunately, \"REVERT\" and \"CHERRY_PICK\" are unsuitable variable\n> names, as their purpose is unclear in the global context; in\n> anticipation of the move, rename them to \"REPLAY_REVERT\" and\n> \"REPLAY_PICK\" respectively.\n\nThis could be pruned to just the final bit.  Something like:\n\n\tREVERT and CHERRY_PICK and are unsuitable names for an enumerator in\n\ta public interface, because they are generic enough to be likely to\n\tclash with identifiers with other meanings.  Rename to REPLAY_REVERT\n\tand REPLAY_PICK as preparation for exposing them.\n\nThe patch proper looks safe and the effect positive to me, so I would\nbe all for applying it as long as the commit message is cleaned up.\n"},{"id":"182264","messageId":"20120110183857.GC22184@burratino","threadId":"29315","inReplyTo":"1326212039-13806-3-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-10T18:38:57Z","receivedAt":"2012-01-10T18:38:57Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Currently, 'git cherry-pick' fills up the '.git/sequencer/todo'\n> instruction sheet with \"pick\" actions, while 'git revert' fills it up\n> with \"revert\" actions.  Inspired by the way 'rebase -i' works, we\n> would like to permit mixing arbitrary actions in the same instruction\n> sheet.\n\nOk.\n\n>        To do this, we first have to decouple the notion of an action\n> in the instruction sheet from builtin commands.\n\nWha?\n\nI guess at this point I get lost because you are using jargon.  What\nis the difference between an action in the instruction sheet and a\nbuiltin command?  How does being a busybox-style hard link to the main\n\"git\" binary as opposed to a separate binary (which is what \"builtin\"\nmeans) have anything to do with this at all?\n\nAnd I don't have any sense of the impact.  Will this change the\nbehavior of \"git cherry-pick\"?  Will it avoid some confusion on the\npart of future people modifying the code?  Is it a necessary step\nbefore some future change we want?  Does it just make the code\nprettier, and help the sanity of future readers in the process (which\nis definitely valuable, too)?  The above doesn't make it clear to me.\n\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n[...]\n> @@ -52,7 +57,7 @@ enum replay_subcommand {\n>  };\n>  \n>  struct replay_opts {\n> -\tenum replay_action action;\n> +\tenum replay_command command;\n>  \tenum replay_subcommand subcommand;\n\nThis part seems to be renaming \"action\" to \"command\".  A cosmetic\nchange, which I don't have an immediate opinion about either way.\n\n[...]\n> -static const char *action_name(const struct replay_opts *opts)\n> +static const char *command_name(struct replay_opts *opts)\n\nThis part does a similar renaming, and drops a const while at it for\nno intelligible reason.\n\n[...]\n> @@ -142,7 +147,7 @@ static void verify_opt_mutually_compatible(const char *me, ...)\n>  static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n>  {\n>  \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n> -\tconst char *me = action_name(opts);\n> +\tconst char *me = command_name(opts);\n\nThe rest is stuff like this, which follows from the first part.\n\nStepping back, I think the idea is that \"enum replay_action\" is not a\ngood way to identify the command name in error messages like\n\n\tfatal: cherry-pick: --abort cannot be used with --continue\n\nSo you introduce a _new_ enum to represent the command name.  Why not\njust use a string, so commands using the nice and generic sequencer\nlibrary do not have to register themselves in a global callers list to\nuse it?\n\nPlus the renaming of the ->action field and action_name() function\nfeel gratuitous, and drowned out the actual changes in the noise.\n\nDoes that clarify?\n\nI guess I agree with the problem you are solving, but I don't agree\nwith the solution at all.\n\nThanks,\nJonathan\n"},{"id":"182267","messageId":"20120110190311.GD22184@burratino","threadId":"29315","inReplyTo":"1326212039-13806-5-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 4/8] revert: separate out parse errors logically","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-10T19:03:11Z","receivedAt":"2012-01-10T19:03:11Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n>                 For the first kind of error, it is insufficient to\n> check if the buffer beings with a \"pick\" or \"revert\", otherwise the\n> following insn sheet would be interpreted as having a malformed object\n> name:\n\n> pickle a1fe57~2\n>\n> In reality, the issue is that \"pickle\" is an unrecognized instruction.\n> So, check that the buffer starts with (\"pick \" or \"pick\\t\") and\n> (\"revert \" or \"revert\\t\").\n\nSorry, the above description just leaves me more confused than before.\n\nWhat _actual impact_ does this patch have?  And why do we want it?\nAnd what could be the bad side effects?  Everything else is just\nirrelevant.\n\nBefore reading the above description, I thought this was just a code\ncleanup.  So either the description or my reading is completely\nconfused.\n"},{"id":"182308","messageId":"CALkWK0k=44znLr2oYSx61Mk=qdAurona0f0H4i4=YXNSAeQhHQ@mail.gmail.com","threadId":"29315","inReplyTo":"20120110183857.GC22184@burratino","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T04:02:18Z","receivedAt":"2012-01-11T04:02:18Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n> [...]\n>> -static const char *action_name(const struct replay_opts *opts)\n>> +static const char *command_name(struct replay_opts *opts)\n>\n> This part does a similar renaming, and drops a const while at it for\n> no intelligible reason.\n\nCarried over from the previous iteration- sorry I forgot to fix this.\n\n> [...]\n>> @@ -142,7 +147,7 @@ static void verify_opt_mutually_compatible(const char *me, ...)\n>>  static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n>>  {\n>>       const char * const * usage_str = revert_or_cherry_pick_usage(opts);\n>> -     const char *me = action_name(opts);\n>> +     const char *me = command_name(opts);\n>\n> The rest is stuff like this, which follows from the first part.\n>\n> Stepping back, I think the idea is that \"enum replay_action\" is not a\n> good way to identify the command name in error messages like\n>\n>        fatal: cherry-pick: --abort cannot be used with --continue\n>\n> So you introduce a _new_ enum to represent the command name.  Why not\n> just use a string, so commands using the nice and generic sequencer\n> library do not have to register themselves in a global callers list to\n> use it?\n\nFine;  I'm sold on the string idea.  Also, I figured it would be\neasier to explain the changes if I change this enum to a string -- I\nshould probably use \"ease of explaining changes\" as a stronger\ncriterion in the future when I have two competing implementations in\nmind.\n\nThanks.\n\n-- Ram\n"},{"id":"182309","messageId":"CALkWK0=bEPPv4rtPrMrQnk3MK=JY4-wwAByWPmzg86NBm_56iQ@mail.gmail.com","threadId":"29315","inReplyTo":"CALkWK0k=44znLr2oYSx61Mk=qdAurona0f0H4i4=YXNSAeQhHQ@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T04:17:51Z","receivedAt":"2012-01-11T04:17:51Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Fine;  I'm sold on the string idea.  Also, I figured it would be\n> easier to explain the changes if I change this enum to a string -- I\n> should probably use \"ease of explaining changes\" as a stronger\n> criterion in the future when I have two competing implementations in\n> mind.\n\nI wrote that too quickly.  I can't stand seeing so many strcmp() calls\nall over my codebase -- look at the number of instances of matching\nopts->command to REPLAY_CMD_*.  Why should I have to use strcmp() when\nthe data is semantic?  It makes no sense: by spelling out the string\nin so many places, I'm just making the code more error-prone, because\nthe compiler can't warn me if I make a spelling mistake in one place.\nWhy do you like the string so much?  A new caller will have to\nregister new actions in the replay_actions enum and modify the\ncodebase to define a codepath for its specific case anyway: so I don't\nmind it registering a new command in replay_command.\n\n-- Ram\n"},{"id":"182316","messageId":"20120111050404.GA13507@burratino","threadId":"29315","inReplyTo":"CALkWK0=bEPPv4rtPrMrQnk3MK=JY4-wwAByWPmzg86NBm_56iQ@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T05:04:04Z","receivedAt":"2012-01-11T05:04:04Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> I wrote that too quickly.  I can't stand seeing so many strcmp() calls\n> all over my codebase -- look at the number of instances of matching\n> opts->command to REPLAY_CMD_*.\n\nIf the number of such in sequencer.c is greater than 0, it's probably\na bug.  Why would the sequencer change behavior based on its caller's\nname?\n\nIt's very easy for me to be wrong, though --- this is just a first\nreaction, and I haven't looked at this version of sequencer.c yet,\nsince it doesn't exist. :)\n\nIs this what you're talking about?\n\n\tif (opts->action == CHERRY_PICK)\n\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n\telse\n\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n\nNow imagine \"git rebase\" wants to hook into the same stuff. What\nshould the message be?\n\nMaybe:\n\n\terror(_(\"your local changes would be overwritten - aborting\"));\n\nOr:\n\n\terror(_(opts->error_msgs[REPLAY_WOULD_CLOBBER_LOCAL_CHANGES]));\n"},{"id":"182317","messageId":"CALkWK0kJpEXvBMV=D7h91sz7U2sLvXdW1UzomW0kG2bbM+byYA@mail.gmail.com","threadId":"29315","inReplyTo":"20120111050404.GA13507@burratino","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T05:14:28Z","receivedAt":"2012-01-11T05:14:28Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Ramkumar Ramachandra wrote:\n>\n>> I wrote that too quickly.  I can't stand seeing so many strcmp() calls\n>> all over my codebase -- look at the number of instances of matching\n>> opts->command to REPLAY_CMD_*.\n>\n> If the number of such in sequencer.c is greater than 0, it's probably\n> a bug.  Why would the sequencer change behavior based on its caller's\n> name?\n\nIn the super-final version, yes-  I agree.  However, we're not there\nyet -- this patch is more of a transition, to make the life of\n\"revert: allow mixing \"pick\" and \"revert\" actions\" easier.  Once the\npainful move to sequencer.c is completed, we can think about all these\nthings.  Right now, I'm only focusing on the move, and everything that\nshould logically precede it (in my opinion).\n\n> Is this what you're talking about?\n>\n>        if (opts->action == CHERRY_PICK)\n>                error(_(\"Your local changes would be overwritten by cherry-pick.\"));\n>        else\n>                error(_(\"Your local changes would be overwritten by revert.\"));\n\n1. Yes, error messages like this.\n2. Since I haven't modified function prototypes to include a\n\"replay_action\" at the \"revert: decouple sequencer actions from\nbuiltin commands\" stage, functions like do_pick_commit() have to rely\non opts->command!  That's the entire point: I want to show how this\n\"opts->command\" is changing to \"action\" in most places in \"revert:\nallow mixing \"pick\" and \"revert\" actions\".\n\nI'd rationalize that taking care of (1) is not urgent- we can do it\nafter we move to sequencer.c.  As for (2), we have little choice at\nthis point- either make it a string like it's supposed to be in the\nsuper-final version, and struggle with the ugliness of strcmp() now,\nor make it an enum now; I choose the latter.\n\nThanks for making me explain this.\n\n-- Ram\n"},{"id":"182318","messageId":"CALkWK0muXXKu37_qQ8E+LEZiCxebWvWghkc8QjyfdBazjLOstw@mail.gmail.com","threadId":"29315","inReplyTo":"CALkWK0kJpEXvBMV=D7h91sz7U2sLvXdW1UzomW0kG2bbM+byYA@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T05:26:03Z","receivedAt":"2012-01-11T05:26:03Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nI wrote a new commit message for this patch.  Perhaps it can help clarify?\n\n  revert: decouple sequencer actions from commands\n\n  Currently, we have two actions \"pick\" and \"revert\" that directly\n  correspond to the action of the commands 'git cherry-pick' and 'git\n  revert' respectively.  Since we would like to permit mixing different\n  actions in the same instruction sheet inspired by the way 'git rebase\n  --interactive' works, a future instruction sheet would look like:\n\n    pickle next~4\n    revert rr/moo^2~34\n    action3 b74fea\n\n  where the actions \"pickle\", \"revert\" and \"action3\" need not directly\n  correspond to the specific commands.  So, instead of using the same\n  representation for both the command executed and the sequencer action\n  (the \"replay_action\" enum), store the command separately as a\n  \"replay_command\" enum.\n\n  Since we don't allow mixing actions yet, the entire operation of 'git\n  cherry-pick'/ 'git revert' relies on the command, with the\n  exception of the instruction sheet parser which converts a\n  \"replay_action\" to a \"replay_command\".  As we support mixing actions\n  in future patches, more portions of the code will rely on the\n  action, demoting the command to a string used in error reporting.\n\n-- Ram\n"},{"id":"182319","messageId":"20120111054954.GB13507@burratino","threadId":"29315","inReplyTo":"CALkWK0muXXKu37_qQ8E+LEZiCxebWvWghkc8QjyfdBazjLOstw@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T05:49:54Z","receivedAt":"2012-01-11T05:49:54Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Hi Jonathan,\n>\n> I wrote a new commit message for this patch.  Perhaps it can help clarify?\n\nThanks.\n\n>   revert: decouple sequencer actions from commands\n>\n>   Currently, we have two actions \"pick\" and \"revert\" that directly\n>   correspond to the action of the commands 'git cherry-pick' and 'git\n>   revert' respectively.\n\nWell, let's start here.  The two insns \"pick\" and \"revert\" and the\nability to mix them doesn't have much to do with the picture, does it?\n\nI think the actual problem being solved is that insn types, as described\nby the replay_action enum, are being abused to refer to top-level git\ncommands \"revert\" and \"cherry-pick\".  The sequencer isn't supposed to\ncare which top-level git command called it, except in some messages, so\nwe'd certainly like to stop pretending that has something to do with\ninsn types.\n\nBased on what you've said, correcting this cleanly is complicated in\nsome places by the inconvenient fact that the sequencer _does_ care\nwhich top-level git command called it.  (I haven't checked this; I'm\njust taking it on faith from you.)  If we want to let other git\ncommands (like \"git rebase\" or \"git sequence\") call into the\nsequencer, that sounds like a way bigger problem than any conflict of\nterminology.\n"},{"id":"182333","messageId":"CALkWK0=o+KkV08G9JuTaG8Vtb-AzHQVMQPzMy8td_iBVnGX4Dg@mail.gmail.com","threadId":"29315","inReplyTo":"20120111054954.GB13507@burratino","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T09:19:56Z","receivedAt":"2012-01-11T09:19:56Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Well, let's start here.  The two insns \"pick\" and \"revert\" and the\n> ability to mix them doesn't have much to do with the picture, does it?\n>\n> I think the actual problem being solved is that insn types, as described\n> by the replay_action enum, are being abused to refer to top-level git\n> commands \"revert\" and \"cherry-pick\".  The sequencer isn't supposed to\n> care which top-level git command called it, except in some messages, so\n> we'd certainly like to stop pretending that has something to do with\n> insn types.\n\nExactly.  I'll update the commit message.\n\n> Based on what you've said, correcting this cleanly is complicated in\n> some places by the inconvenient fact that the sequencer _does_ care\n> which top-level git command called it.  (I haven't checked this; I'm\n> just taking it on faith from you.)  If we want to let other git\n> commands (like \"git rebase\" or \"git sequence\") call into the\n> sequencer, that sounds like a way bigger problem than any conflict of\n> terminology.\n\nThere's no need to trust me: just think about the problem.  We fill\nout a replay_opts structure to call into the sequencer with- how does\nthe sequencer know what to do with this hypothetical command string\n(say \"cherry-pick\") on a fresh invocation?  It needs to translate this\ninto a replay_action at some point, right?  There are atleast three\nplaces where this happens: prepare_revs(), walk_revs_populate_todo(),\nand single_pick().\n\n-- Ram\n"},{"id":"182335","messageId":"20120111095236.GB31670@burratino","threadId":"29315","inReplyTo":"CALkWK0=o+KkV08G9JuTaG8Vtb-AzHQVMQPzMy8td_iBVnGX4Dg@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T09:52:36Z","receivedAt":"2012-01-11T09:52:36Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n>                                                              how does\n> the sequencer know what to do with this hypothetical command string\n> (say \"cherry-pick\") on a fresh invocation?  It needs to translate this\n> into a replay_action at some point, right?  There are atleast three\n> places where this happens: prepare_revs(), walk_revs_populate_todo(),\n> and single_pick().\n\nI see.\n\nPerhaps cherry-pick and revert should be different values for\nreplay_subcommand, to avoid conflating the mechanics and the command\nname?  Resulting in something like this:\n\n\tenum replay_subcommand {\n\t\tREPLAY_PICK_REVISIONS,\n\t\tREPLAY_REVERT_REVISIONS,\n\t\tREPLAY_EDIT_SEQUENCE,\n\t\tREPLAY_REMOVE_STATE,\n\t\tREPLAY_CONTINUE,\n\t\tREPLAY_SKIP,\n\t\tREPLAY_ROLLBACK\n\t};\n\nThough this dispatcher on an enum to perform many different actions\nalready felt a bit awkward, so an alternative could be\n\n\textern int pick_revisions(struct replay_opts *opts);\n\textern int revert_revisions(struct replay_opts *opts);\n\n\textern int launch_sequence_editor(struct replay_opts *opts);\n\textern void remove_sequencer_state(void);\n\textern int sequencer_continue(struct replay_opts *opts);\n\textern int sequencer_skip(struct replay_opts *opts);\n\textern int sequencer_rollback(struct replay_opts *opts);\n\nwhich would make it easier to add arguments specific to any one of the\nroutines as appropriate.\n"},{"id":"182339","messageId":"CALkWK0mv2jzmDA==pJg5R4jH0yxo=OopYM_WzAWusiffnb+4HQ@mail.gmail.com","threadId":"29315","inReplyTo":"20120111095236.GB31670@burratino","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T10:11:31Z","receivedAt":"2012-01-11T10:11:31Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Perhaps cherry-pick and revert should be different values for\n> replay_subcommand, to avoid conflating the mechanics and the command\n> name?  Resulting in something like this:\n>\n>        enum replay_subcommand {\n>                REPLAY_PICK_REVISIONS,\n>                REPLAY_REVERT_REVISIONS,\n>                REPLAY_EDIT_SEQUENCE,\n>                REPLAY_REMOVE_STATE,\n>                REPLAY_CONTINUE,\n>                REPLAY_SKIP,\n>                REPLAY_ROLLBACK\n>        };\n\nWe'd be prematurely locking ourselves into a design where we can't\ntell which top-level command issued the continue/ abort -- this means\nthat there's no way to deny a 'git rebase --continue' from running\nafter a 'git cherry-pick' conflicts (assuming that rebase is\nimplemented in terms of the sequencer ofcourse).  Even if that\nspecific objection isn't to your taste, I'm not comfortable about\npainting ourselves into such a tight corner so early on.  My sincere\nsuggestion is to procrastinate the problem until we have a tighter\nusecase (a new top-level command or action added, for instance).  I\ndon't think we have to worry about preserving backward compatibility\nin the sequencer API?\n\n-- Ram\n"},{"id":"182345","messageId":"CALkWK0mubn6+7E09DMgVuFfhHeZfXamLkkOwAPAPgy3s2q9=PA@mail.gmail.com","threadId":"29315","inReplyTo":"20120110190311.GD22184@burratino","subject":"Re: [PATCH 4/8] revert: separate out parse errors logically","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T12:38:14Z","receivedAt":"2012-01-11T12:38:14Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"New commit message.\n\n    revert: simplify insn parsing logic\n\n    Our instruction sheet parser first looks for a valid action by\n    checking that the buffer starts with either \"pick\" or \"revert\".  Then,\n    it looks for either spaces or tabs before looking for the object name,\n    erroring out if it doesn't find any.  Simplify this logic without\n    making any functional changes by looking for (\"pick \" or \"pick\\t\") or\n    (\"revert \" or \"revert\\t\") in the first place.\n\n-- Ram\n"},{"id":"182346","messageId":"20120111124432.GF32173@burratino","threadId":"29315","inReplyTo":"1326212039-13806-6-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 5/8] revert: report fine-grained errors from insn parser","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T12:44:32Z","receivedAt":"2012-01-11T12:44:32Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -732,7 +732,22 @@ static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)\n>  \treturn 0;\n>  }\n>  \n> -static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n> +static int parse_error(const char *message, const char *file,\n> +\t\tint lineno, char *error_line)\n> +{\n> +\tconst char *suffix = \"\";\n> +\tint error_len = strcspn(error_line, \" \\t\\n\");\n> +\n> +\tif (error_len > 20) {\n> +\t\terror_len = 20;\n> +\t\tsuffix = \"...\";\n> +\t}\n> +\treturn error(_(\"%s:%d: %s: %.*s%s\"), file, lineno, message,\n> +\t\terror_len, error_line, suffix);\n\nSince the snippet used in an error message is a single word,\nwhy is it called error_line?  (And why is the signature written\nin a way that implies we might modify it, by the way?)\n\nMissing /* TRANSLATORS: ... */ comment.\n\n[...]\n> @@ -757,11 +773,13 @@ static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)\n>  \t*end_of_object_name = saved;\n>  \n>  \tif (status < 0)\n> -\t\treturn -1;\n> +\t\treturn parse_error(_(\"malformed object name\"),\n> +\t\t\t\tgit_path(SEQ_TODO_FILE), lineno, bol);\n\nThis is the message I'll get if I misspell \"master\" as \"mister\" or try\nto cherry-pick HEAD~100000 when the history is not that deep.  When I\nread \"malformed object name\", I'll look for syntax errors and be\nconfused.  (They are valid syntax denoting commits that just happen\nnot to exist.)\n"},{"id":"182350","messageId":"20120111131854.GG32173@burratino","threadId":"29315","inReplyTo":"CALkWK0kJpEXvBMV=D7h91sz7U2sLvXdW1UzomW0kG2bbM+byYA@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T13:18:54Z","receivedAt":"2012-01-11T13:18:54Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n>                                                             Once the\n> painful move to sequencer.c is completed, we can think about all these\n> things.\n\nHonestly, moving code verbatim between files is very easy.  Repeatedly\nrebasing a patch that carries out such a move would presumably be\nhard, though.  But this pain is unnecessary!\n\nJust like I haven't been reviewing the code movement, I'd be perfectly\nhappy to read a \"patch\" that says\n\n\t\"And then we move the functions from the following list to\n\tsequencer.c.  I'll send a patch doing so once work has settled\n\tdown in patches earlier in this series.\"\n\nNow you are telling me that in the super-final future my worries are\nvalid, but I should forget about them today, because later in this\nseries there is some code movement.  That we need to get this painful\npart over with.  I would be much more comforted if you said that in\nthe future my worries were _not_ valid, that the current design is a\ngood one, and that these patches are not making the program worse;\notherwise, wouldn't it be better to skip whichever are the\nquestionable patches and just carry out the code movement, which\ndoesn't depend on them?\n"},{"id":"182352","messageId":"20120111134011.GH32173@burratino","threadId":"29315","inReplyTo":"CALkWK0mv2jzmDA==pJg5R4jH0yxo=OopYM_WzAWusiffnb+4HQ@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T13:40:12Z","receivedAt":"2012-01-11T13:40:12Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> We'd be prematurely locking ourselves into a design where we can't\n> tell which top-level command issued the continue/ abort\n\nThe .git/sequencer/todo file already doesn't record which top-level\ncommand initiated the sequence and doesn't seem to operate under a\nmodel in which that is a useful question.  Honestly, that's my only\nobjection to the \"git revert --continue during git cherry-pick\" check.\n\nI think it is not premature to think about whether that matters.  I've\nalready said a little about related cases where it seemed to matter\nbut there was instead something else at play.  Can you offer some\nexamples of how people might use the \"git cherry-pick\" / \"git revert\"\ncommands and get stuck or run into trouble, and how git can help them?\n\n[...]\n>                                                           My sincere\n> suggestion is to procrastinate the problem until we have a tighter\n> usecase (a new top-level command or action added, for instance).\n\nThinking carefully about sequencer use cases also seems like a good\nidea.  Is it intended that \"git am\" and \"git rebase\" should be\nreimplemented on top of the sequencer?  Do you have goals in mind for\ncommands like \"git sequence --step\" that could be used to examine,\ninfluence, and carry out git's idea of what should happen next?\n\nIf we have no use case, then there's no reason to change the code.  It\nalready works[*] for cherry-pick and revert.  I think we shouldn't be\nmoving this code to sequencer.c or cleaning up the API to suit other\ncommands (e.g., introducing two ways to sane \"am I picking or\nreverting\") without having one in mind.\n\n[*] Modulo bugs and some missing features such as --skip.\n"},{"id":"182357","messageId":"CALkWK0=gvsvqk7Th7YY_eRzb+Ri52AZbOVokC98i9BXVAJOZEw@mail.gmail.com","threadId":"29315","inReplyTo":"20120111131854.GG32173@burratino","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T16:39:04Z","receivedAt":"2012-01-11T16:39:04Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> Honestly, moving code verbatim between files is very easy.  Repeatedly\n> rebasing a patch that carries out such a move would presumably be\n> hard, though.  But this pain is unnecessary!\n>\n> Just like I haven't been reviewing the code movement, I'd be perfectly\n> happy to read a \"patch\" that says\n>\n>        \"And then we move the functions from the following list to\n>        sequencer.c.  I'll send a patch doing so once work has settled\n>        down in patches earlier in this series.\"\n\nMore than the pain of rebasing the patch everytime, I guess what I'm\nasking is: is it worth stretching my foresight like this?  Once the\ncode is in sequencer.c, it just becomes so much easier for me to write\nscratch code to help me wrap my head around the generalization.  If\nthe answer to the question is yes, I suppose it makes sense to submit\nthe good parts now and work on the other parts over an extended period\nof time.\n\n-- Ram\n"},{"id":"182358","messageId":"20120111164758.GD1891@burratino","threadId":"29315","inReplyTo":"CALkWK0=gvsvqk7Th7YY_eRzb+Ri52AZbOVokC98i9BXVAJOZEw@mail.gmail.com","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T16:47:58Z","receivedAt":"2012-01-11T16:47:58Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> More than the pain of rebasing the patch everytime, I guess what I'm\n> asking is: is it worth stretching my foresight like this?  Once the\n> code is in sequencer.c, it just becomes so much easier for me to write\n> scratch code to help me wrap my head around the generalization.\n\nIn that case, why not just a patch to move the code to sequencer.c,\nwith whatever is the minimum of related fixes (just namespace stuff,\nI'd imagine) before it?\n"},{"id":"182360","messageId":"CALkWK0=obkB=uxtgKJpBWQ-YUsiOjHeegx3d5NHMTub-8nU_Dw@mail.gmail.com","threadId":"29315","inReplyTo":"20120111164758.GD1891@burratino","subject":"Re: [PATCH 2/8] revert: decouple sequencer actions from builtin commands","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T16:52:25Z","receivedAt":"2012-01-11T16:52:25Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jonathan Nieder wrote:\n> In that case, why not just a patch to move the code to sequencer.c,\n> with whatever is the minimum of related fixes (just namespace stuff,\n> I'd imagine) before it?\n\nCool, I'll do that then.  I thought what I'd originally posted was an\nacceptable minimum to show the motivation for the move.\n\n-- Ram\n"},{"id":"182365","messageId":"1326305757-27525-1-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"20120111164758.GD1891@burratino","subject":"[PATCH v3 0/2] The move to sequencer.c","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T18:15:55Z","receivedAt":"2012-01-11T18:15:55Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nI figured I'd just post a minimal series performing the move:\n\n1. I can write up scratch code and toy with various ideas I have on\n   the generalization.  It'll help with my thought process.\n\n2. I can stop thinking about how to begin every commit message with a\n   note about anticipating this move.  This'll be a big relief!\n\nThanks.\n\nRamkumar Ramachandra (2):\n  revert: prepare to move replay_action to header\n  sequencer: factor code out of revert builtin\n\n builtin/revert.c |  952 +-----------------------------------------------------\n sequencer.c      |  918 ++++++++++++++++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |   37 +++\n 3 files changed, 961 insertions(+), 946 deletions(-)\n\n-- \n1.7.8.2\n"},{"id":"182366","messageId":"1326305757-27525-2-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326305757-27525-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 1/2] revert: prepare to move replay_action to header","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T18:15:56Z","receivedAt":"2012-01-11T18:15:56Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"REVERT and CHERRY_PICK and are unsuitable names for an enumerator in a\npublic interface, because they are generic enough to be likely to\nclash with identifiers with other meanings.  Rename to REPLAY_REVERT\nand REPLAY_PICK as preparation for exposing them.\n\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   40 ++++++++++++++++++++++------------------\n 1 files changed, 22 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 0d8020c..2739405 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -39,7 +39,11 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-enum replay_action { REVERT, CHERRY_PICK };\n+enum replay_action {\n+\tREPLAY_REVERT,\n+\tREPLAY_PICK\n+};\n+\n enum replay_subcommand {\n \tREPLAY_NONE,\n \tREPLAY_REMOVE_STATE,\n@@ -74,14 +78,14 @@ struct replay_opts {\n \n static const char *action_name(const struct replay_opts *opts)\n {\n-\treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n+\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n static char *get_encoding(const char *message);\n \n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n-\treturn opts->action == REVERT ? revert_usage : cherry_pick_usage;\n+\treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n static int option_parse_x(const struct option *opt,\n@@ -160,7 +164,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tOPT_END(),\n \t};\n \n-\tif (opts->action == CHERRY_PICK) {\n+\tif (opts->action == REPLAY_PICK) {\n \t\tstruct option cp_extra[] = {\n \t\t\tOPT_BOOLEAN('x', NULL, &opts->record_origin, \"append commit name\"),\n \t\t\tOPT_BOOLEAN(0, \"ff\", &opts->allow_ff, \"allow fast-forward\"),\n@@ -374,7 +378,7 @@ static int error_dirty_index(struct replay_opts *opts)\n \t\treturn error_resolve_conflict(action_name(opts));\n \n \t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->action == CHERRY_PICK)\n+\tif (opts->action == REPLAY_PICK)\n \t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n \telse\n \t\terror(_(\"Your local changes would be overwritten by revert.\"));\n@@ -553,7 +557,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (opts->action == REVERT) {\n+\tif (opts->action == REPLAY_REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -594,7 +598,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\twrite_message(&msgbuf, defmsg);\n@@ -618,13 +622,13 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t * However, if the merge did not even start, then we don't want to\n \t * write it at all.\n \t */\n-\tif (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n \t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n \t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n \n \tif (res) {\n-\t\terror(opts->action == REVERT\n+\t\terror(opts->action == REPLAY_REVERT\n \t\t      ? _(\"could not revert %s... %s\")\n \t\t      : _(\"could not apply %s... %s\"),\n \t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n@@ -644,7 +648,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n static void prepare_revs(struct replay_opts *opts)\n {\n-\tif (opts->action != REVERT)\n+\tif (opts->action != REPLAY_REVERT)\n \t\topts->revs->reverse ^= 1;\n \n \tif (prepare_revision_walk(opts->revs))\n@@ -701,7 +705,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n {\n \tstruct commit_list *cur = NULL;\n \tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->action == REVERT ? \"revert\" : \"pick\";\n+\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n \tconst char *subject;\n \tint subject_len;\n \n@@ -722,10 +726,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \tint saved, status, padding;\n \n \tif (!prefixcmp(bol, \"pick\")) {\n-\t\taction = CHERRY_PICK;\n+\t\taction = REPLAY_PICK;\n \t\tbol += strlen(\"pick\");\n \t} else if (!prefixcmp(bol, \"revert\")) {\n-\t\taction = REVERT;\n+\t\taction = REPLAY_REVERT;\n \t\tbol += strlen(\"revert\");\n \t} else\n \t\treturn NULL;\n@@ -748,7 +752,7 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *\n \t */\n \tif (action != opts->action) {\n \t\tconst char *action_str;\n-\t\taction_str = action == REVERT ? \"revert\" : \"cherry-pick\";\n+\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n \t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n \t\treturn NULL;\n \t}\n@@ -1124,7 +1128,7 @@ static int pick_revisions(struct replay_opts *opts)\n \tif (create_seq_dir() < 0)\n \t\treturn -1;\n \tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REVERT)\n+\t\tif (opts->action == REPLAY_REVERT)\n \t\t\treturn error(_(\"Can't revert as initial commit\"));\n \t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t}\n@@ -1141,7 +1145,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n \t\topts.edit = 1;\n-\topts.action = REVERT;\n+\topts.action = REPLAY_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n@@ -1156,7 +1160,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n-\topts.action = CHERRY_PICK;\n+\topts.action = REPLAY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n \tres = pick_revisions(&opts);\n-- \n1.7.8.2\n"},{"id":"182367","messageId":"1326305757-27525-3-git-send-email-artagnon@gmail.com","threadId":"29315","inReplyTo":"1326305757-27525-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/2] sequencer: factor code out of revert builtin","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2012-01-11T18:15:57Z","receivedAt":"2012-01-11T18:15:57Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Expose the cherry-picking machinery through a public\nsequencer_pick_revisions() (renamed from pick_revisions() in\nbuiltin/revert.c), so that cherry-picking and reverting are special\ncases of a general sequencer operation.  The cherry-pick builtin is\nnow a thin wrapper that does command-line argument parsing before\ncalling into sequencer_pick_revisions().  In the future, we can write\na new \"foo\" builtin that calls into the sequencer like:\n\n  memset(&opts, 0, sizeof(opts));\n  opts.action = REPLAY_FOO;\n  opts.revisions = xmalloc(sizeof(*opts.revs));\n  parse_args_populate_opts(argc, argv, &opts);\n  init_revisions(opts.revs);\n  sequencer_pick_revisions(&opts);\n\nThis patch does not intend to make any functional changes.  Check\nwith:\n\n  $ git blame -s -C HEAD^..HEAD -- sequencer.c | grep -C3 '^[^^]'\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |  946 +-----------------------------------------------------\n sequencer.c      |  918 ++++++++++++++++++++++++++++++++++++++++++++++++++++-\n sequencer.h      |   37 +++\n 3 files changed, 956 insertions(+), 945 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 2739405..4116f2d 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -1,18 +1,9 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n-#include \"object.h\"\n-#include \"commit.h\"\n-#include \"tag.h\"\n-#include \"run-command.h\"\n-#include \"exec_cmd.h\"\n-#include \"utf8.h\"\n #include \"parse-options.h\"\n-#include \"cache-tree.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"rerere.h\"\n-#include \"merge-recursive.h\"\n-#include \"refs.h\"\n #include \"dir.h\"\n #include \"sequencer.h\"\n \n@@ -39,41 +30,6 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-enum replay_action {\n-\tREPLAY_REVERT,\n-\tREPLAY_PICK\n-};\n-\n-enum replay_subcommand {\n-\tREPLAY_NONE,\n-\tREPLAY_REMOVE_STATE,\n-\tREPLAY_CONTINUE,\n-\tREPLAY_ROLLBACK\n-};\n-\n-struct replay_opts {\n-\tenum replay_action action;\n-\tenum replay_subcommand subcommand;\n-\n-\t/* Boolean options */\n-\tint edit;\n-\tint record_origin;\n-\tint no_commit;\n-\tint signoff;\n-\tint allow_ff;\n-\tint allow_rerere_auto;\n-\n-\tint mainline;\n-\n-\t/* Merge strategy */\n-\tconst char *strategy;\n-\tconst char **xopts;\n-\tsize_t xopts_nr, xopts_alloc;\n-\n-\t/* Only used by REPLAY_NONE */\n-\tstruct rev_info *revs;\n-};\n-\n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n static const char *action_name(const struct replay_opts *opts)\n@@ -81,8 +37,6 @@ static const char *action_name(const struct replay_opts *opts)\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n }\n \n-static char *get_encoding(const char *message);\n-\n static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n@@ -241,902 +195,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\tusage_with_options(usage_str, options);\n }\n \n-struct commit_message {\n-\tchar *parent_label;\n-\tconst char *label;\n-\tconst char *subject;\n-\tchar *reencoded_message;\n-\tconst char *message;\n-};\n-\n-static int get_message(struct commit *commit, struct commit_message *out)\n-{\n-\tconst char *encoding;\n-\tconst char *abbrev, *subject;\n-\tint abbrev_len, subject_len;\n-\tchar *q;\n-\n-\tif (!commit->buffer)\n-\t\treturn -1;\n-\tencoding = get_encoding(commit->buffer);\n-\tif (!encoding)\n-\t\tencoding = \"UTF-8\";\n-\tif (!git_commit_encoding)\n-\t\tgit_commit_encoding = \"UTF-8\";\n-\n-\tout->reencoded_message = NULL;\n-\tout->message = commit->buffer;\n-\tif (strcmp(encoding, git_commit_encoding))\n-\t\tout->reencoded_message = reencode_string(commit->buffer,\n-\t\t\t\t\tgit_commit_encoding, encoding);\n-\tif (out->reencoded_message)\n-\t\tout->message = out->reencoded_message;\n-\n-\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n-\tabbrev_len = strlen(abbrev);\n-\n-\tsubject_len = find_commit_subject(out->message, &subject);\n-\n-\tout->parent_label = xmalloc(strlen(\"parent of \") + abbrev_len +\n-\t\t\t      strlen(\"... \") + subject_len + 1);\n-\tq = out->parent_label;\n-\tq = mempcpy(q, \"parent of \", strlen(\"parent of \"));\n-\tout->label = q;\n-\tq = mempcpy(q, abbrev, abbrev_len);\n-\tq = mempcpy(q, \"... \", strlen(\"... \"));\n-\tout->subject = q;\n-\tq = mempcpy(q, subject, subject_len);\n-\t*q = '\\0';\n-\treturn 0;\n-}\n-\n-static void free_message(struct commit_message *msg)\n-{\n-\tfree(msg->parent_label);\n-\tfree(msg->reencoded_message);\n-}\n-\n-static char *get_encoding(const char *message)\n-{\n-\tconst char *p = message, *eol;\n-\n-\twhile (*p && *p != '\\n') {\n-\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n-\t\t\t; /* do nothing */\n-\t\tif (!prefixcmp(p, \"encoding \")) {\n-\t\t\tchar *result = xmalloc(eol - 8 - p);\n-\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n-\t\t\treturn result;\n-\t\t}\n-\t\tp = eol;\n-\t\tif (*p == '\\n')\n-\t\t\tp++;\n-\t}\n-\treturn NULL;\n-}\n-\n-static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n-{\n-\tconst char *filename;\n-\tint fd;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n-\n-\tfilename = git_path(\"%s\", pseudoref);\n-\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n-\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n-\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n-\tstrbuf_release(&buf);\n-}\n-\n-static void print_advice(int show_hint)\n-{\n-\tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n-\n-\tif (msg) {\n-\t\tfprintf(stderr, \"%s\\n\", msg);\n-\t\t/*\n-\t\t * A conflict has occured but the porcelain\n-\t\t * (typically rebase --interactive) wants to take care\n-\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n-\t\t */\n-\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n-\t\treturn;\n-\t}\n-\n-\tif (show_hint) {\n-\t\tadvise(\"after resolving the conflicts, mark the corrected paths\");\n-\t\tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n-\t\tadvise(\"and commit the result with 'git commit'\");\n-\t}\n-}\n-\n-static void write_message(struct strbuf *msgbuf, const char *filename)\n-{\n-\tstatic struct lock_file msg_file;\n-\n-\tint msg_fd = hold_lock_file_for_update(&msg_file, filename,\n-\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n-\tif (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), filename);\n-\tstrbuf_release(msgbuf);\n-\tif (commit_lock_file(&msg_file) < 0)\n-\t\tdie(_(\"Error wrapping up %s\"), filename);\n-}\n-\n-static struct tree *empty_tree(void)\n-{\n-\treturn lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);\n-}\n-\n-static int error_dirty_index(struct replay_opts *opts)\n-{\n-\tif (read_cache_unmerged())\n-\t\treturn error_resolve_conflict(action_name(opts));\n-\n-\t/* Different translation strings for cherry-pick and revert */\n-\tif (opts->action == REPLAY_PICK)\n-\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n-\telse\n-\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n-\n-\tif (advice_commit_before_merge)\n-\t\tadvise(_(\"Commit your changes or stash them to proceed.\"));\n-\treturn -1;\n-}\n-\n-static int fast_forward_to(const unsigned char *to, const unsigned char *from)\n-{\n-\tstruct ref_lock *ref_lock;\n-\n-\tread_cache();\n-\tif (checkout_fast_forward(from, to))\n-\t\texit(1); /* the callee should have complained already */\n-\tref_lock = lock_any_ref_for_update(\"HEAD\", from, 0);\n-\treturn write_ref_sha1(ref_lock, to, \"cherry-pick\");\n-}\n-\n-static int do_recursive_merge(struct commit *base, struct commit *next,\n-\t\t\t      const char *base_label, const char *next_label,\n-\t\t\t      unsigned char *head, struct strbuf *msgbuf,\n-\t\t\t      struct replay_opts *opts)\n-{\n-\tstruct merge_options o;\n-\tstruct tree *result, *next_tree, *base_tree, *head_tree;\n-\tint clean, index_fd;\n-\tconst char **xopt;\n-\tstatic struct lock_file index_lock;\n-\n-\tindex_fd = hold_locked_index(&index_lock, 1);\n-\n-\tread_cache();\n-\n-\tinit_merge_options(&o);\n-\to.ancestor = base ? base_label : \"(empty tree)\";\n-\to.branch1 = \"HEAD\";\n-\to.branch2 = next ? next_label : \"(empty tree)\";\n-\n-\thead_tree = parse_tree_indirect(head);\n-\tnext_tree = next ? next->tree : empty_tree();\n-\tbase_tree = base ? base->tree : empty_tree();\n-\n-\tfor (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)\n-\t\tparse_merge_opt(&o, *xopt);\n-\n-\tclean = merge_trees(&o,\n-\t\t\t    head_tree,\n-\t\t\t    next_tree, base_tree, &result);\n-\n-\tif (active_cache_changed &&\n-\t    (write_cache(index_fd, active_cache, active_nr) ||\n-\t     commit_locked_index(&index_lock)))\n-\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n-\t\tdie(_(\"%s: Unable to write new index file\"), action_name(opts));\n-\trollback_lock_file(&index_lock);\n-\n-\tif (!clean) {\n-\t\tint i;\n-\t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n-\t\tfor (i = 0; i < active_nr;) {\n-\t\t\tstruct cache_entry *ce = active_cache[i++];\n-\t\t\tif (ce_stage(ce)) {\n-\t\t\t\tstrbuf_addch(msgbuf, '\\t');\n-\t\t\t\tstrbuf_addstr(msgbuf, ce->name);\n-\t\t\t\tstrbuf_addch(msgbuf, '\\n');\n-\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n-\t\t\t\t\t\tactive_cache[i]->name))\n-\t\t\t\t\ti++;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\treturn !clean;\n-}\n-\n-/*\n- * If we are cherry-pick, and if the merge did not result in\n- * hand-editing, we will hit this commit and inherit the original\n- * author date and name.\n- * If we are revert, or if our cherry-pick results in a hand merge,\n- * we had better say that the current user is responsible for that.\n- */\n-static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n-{\n-\t/* 6 is max possible length of our args array including NULL */\n-\tconst char *args[6];\n-\tint i = 0;\n-\n-\targs[i++] = \"commit\";\n-\targs[i++] = \"-n\";\n-\tif (opts->signoff)\n-\t\targs[i++] = \"-s\";\n-\tif (!opts->edit) {\n-\t\targs[i++] = \"-F\";\n-\t\targs[i++] = defmsg;\n-\t}\n-\targs[i] = NULL;\n-\n-\treturn run_command_v_opt(args, RUN_GIT_CMD);\n-}\n-\n-static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n-{\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, NULL };\n-\tchar *defmsg = NULL;\n-\tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res;\n-\n-\tif (opts->no_commit) {\n-\t\t/*\n-\t\t * We do not intend to commit immediately.  We just want to\n-\t\t * merge the differences in, so let's compute the tree\n-\t\t * that represents the \"current\" state for merge-recursive\n-\t\t * to work on.\n-\t\t */\n-\t\tif (write_cache_as_tree(head, 0, NULL))\n-\t\t\tdie (_(\"Your index file is unmerged.\"));\n-\t} else {\n-\t\tif (get_sha1(\"HEAD\", head))\n-\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n-\t\tif (index_differs_from(\"HEAD\", 0))\n-\t\t\treturn error_dirty_index(opts);\n-\t}\n-\tdiscard_cache();\n-\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-\t\tstruct commit_list *p;\n-\n-\t\tif (!opts->mainline)\n-\t\t\treturn error(_(\"Commit %s is a merge but no -m option was given.\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1));\n-\n-\t\tfor (cnt = 1, p = commit->parents;\n-\t\t     cnt != opts->mainline && p;\n-\t\t     cnt++)\n-\t\t\tp = p->next;\n-\t\tif (cnt != opts->mainline || !p)\n-\t\t\treturn error(_(\"Commit %s does not have parent %d\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1), opts->mainline);\n-\t\tparent = p->item;\n-\t} else if (0 < opts->mainline)\n-\t\treturn error(_(\"Mainline was specified but commit %s is not a merge.\"),\n-\t\t\tsha1_to_hex(commit->object.sha1));\n-\telse\n-\t\tparent = commit->parents->item;\n-\n-\tif (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))\n-\t\treturn fast_forward_to(commit->object.sha1, head);\n-\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-\t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n-\t\t\taction_name(opts), sha1_to_hex(parent->object.sha1));\n-\n-\tif (get_message(commit, &msg) != 0)\n-\t\treturn error(_(\"Cannot get commit message for %s\"),\n-\t\t\tsha1_to_hex(commit->object.sha1));\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-\t * on top of the current HEAD if we are cherry-pick.  Or the\n-\t * reverse of it if we are revert.\n-\t */\n-\n-\tdefmsg = git_pathdup(\"MERGE_MSG\");\n-\n-\tif (opts->action == REPLAY_REVERT) {\n-\t\tbase = commit;\n-\t\tbase_label = msg.label;\n-\t\tnext = parent;\n-\t\tnext_label = msg.parent_label;\n-\t\tstrbuf_addstr(&msgbuf, \"Revert \\\"\");\n-\t\tstrbuf_addstr(&msgbuf, msg.subject);\n-\t\tstrbuf_addstr(&msgbuf, \"\\\"\\n\\nThis reverts commit \");\n-\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-\n-\t\tif (commit->parents && commit->parents->next) {\n-\t\t\tstrbuf_addstr(&msgbuf, \", reversing\\nchanges made to \");\n-\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));\n-\t\t}\n-\t\tstrbuf_addstr(&msgbuf, \".\\n\");\n-\t} else {\n-\t\tconst char *p;\n-\n-\t\tbase = parent;\n-\t\tbase_label = msg.parent_label;\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\tp += 2;\n-\t\t\tstrbuf_addstr(&msgbuf, p);\n-\t\t}\n-\n-\t\tif (opts->record_origin) {\n-\t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n-\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-\t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n-\t\t}\n-\t}\n-\n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n-\t\tres = do_recursive_merge(base, next, base_label, next_label,\n-\t\t\t\t\t head, &msgbuf, opts);\n-\t\twrite_message(&msgbuf, defmsg);\n-\t} else {\n-\t\tstruct commit_list *common = NULL;\n-\t\tstruct commit_list *remotes = NULL;\n-\n-\t\twrite_message(&msgbuf, defmsg);\n-\n-\t\tcommit_list_insert(base, &common);\n-\t\tcommit_list_insert(next, &remotes);\n-\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n-\t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n-\t\tfree_commit_list(common);\n-\t\tfree_commit_list(remotes);\n-\t}\n-\n-\t/*\n-\t * If the merge was clean or if it failed due to conflict, we write\n-\t * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.\n-\t * However, if the merge did not even start, then we don't want to\n-\t * write it at all.\n-\t */\n-\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n-\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n-\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n-\n-\tif (res) {\n-\t\terror(opts->action == REPLAY_REVERT\n-\t\t      ? _(\"could not revert %s... %s\")\n-\t\t      : _(\"could not apply %s... %s\"),\n-\t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n-\t\t      msg.subject);\n-\t\tprint_advice(res == 1);\n-\t\trerere(opts->allow_rerere_auto);\n-\t} else {\n-\t\tif (!opts->no_commit)\n-\t\t\tres = run_git_commit(defmsg, opts);\n-\t}\n-\n-\tfree_message(&msg);\n-\tfree(defmsg);\n-\n-\treturn res;\n-}\n-\n-static void prepare_revs(struct replay_opts *opts)\n-{\n-\tif (opts->action != REPLAY_REVERT)\n-\t\topts->revs->reverse ^= 1;\n-\n-\tif (prepare_revision_walk(opts->revs))\n-\t\tdie(_(\"revision walk setup failed\"));\n-\n-\tif (!opts->revs->commits)\n-\t\tdie(_(\"empty commit set passed\"));\n-}\n-\n-static void read_and_refresh_cache(struct replay_opts *opts)\n-{\n-\tstatic struct lock_file index_lock;\n-\tint index_fd = hold_locked_index(&index_lock, 0);\n-\tif (read_index_preload(&the_index, NULL) < 0)\n-\t\tdie(_(\"git %s: failed to read the index\"), action_name(opts));\n-\trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n-\tif (the_index.cache_changed) {\n-\t\tif (write_index(&the_index, index_fd) ||\n-\t\t    commit_locked_index(&index_lock))\n-\t\t\tdie(_(\"git %s: failed to refresh the index\"), action_name(opts));\n-\t}\n-\trollback_lock_file(&index_lock);\n-}\n-\n-/*\n- * Append a commit to the end of the commit_list.\n- *\n- * next starts by pointing to the variable that holds the head of an\n- * empty commit_list, and is updated to point to the \"next\" field of\n- * the last item on the list as new commits are appended.\n- *\n- * Usage example:\n- *\n- *     struct commit_list *list;\n- *     struct commit_list **next = &list;\n- *\n- *     next = commit_list_append(c1, next);\n- *     next = commit_list_append(c2, next);\n- *     assert(commit_list_count(list) == 2);\n- *     return list;\n- */\n-static struct commit_list **commit_list_append(struct commit *commit,\n-\t\t\t\t\t       struct commit_list **next)\n-{\n-\tstruct commit_list *new = xmalloc(sizeof(struct commit_list));\n-\tnew->item = commit;\n-\t*next = new;\n-\tnew->next = NULL;\n-\treturn &new->next;\n-}\n-\n-static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n-\t\tstruct replay_opts *opts)\n-{\n-\tstruct commit_list *cur = NULL;\n-\tconst char *sha1_abbrev = NULL;\n-\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n-\tconst char *subject;\n-\tint subject_len;\n-\n-\tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);\n-\t\tsubject_len = find_commit_subject(cur->item->buffer, &subject);\n-\t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n-\t\t\tsubject_len, subject);\n-\t}\n-\treturn 0;\n-}\n-\n-static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)\n-{\n-\tunsigned char commit_sha1[20];\n-\tenum replay_action action;\n-\tchar *end_of_object_name;\n-\tint saved, status, padding;\n-\n-\tif (!prefixcmp(bol, \"pick\")) {\n-\t\taction = REPLAY_PICK;\n-\t\tbol += strlen(\"pick\");\n-\t} else if (!prefixcmp(bol, \"revert\")) {\n-\t\taction = REPLAY_REVERT;\n-\t\tbol += strlen(\"revert\");\n-\t} else\n-\t\treturn NULL;\n-\n-\t/* Eat up extra spaces/ tabs before object name */\n-\tpadding = strspn(bol, \" \\t\");\n-\tif (!padding)\n-\t\treturn NULL;\n-\tbol += padding;\n-\n-\tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n-\tsaved = *end_of_object_name;\n-\t*end_of_object_name = '\\0';\n-\tstatus = get_sha1(bol, commit_sha1);\n-\t*end_of_object_name = saved;\n-\n-\t/*\n-\t * Verify that the action matches up with the one in\n-\t * opts; we don't support arbitrary instructions\n-\t */\n-\tif (action != opts->action) {\n-\t\tconst char *action_str;\n-\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n-\t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n-\t\treturn NULL;\n-\t}\n-\n-\tif (status < 0)\n-\t\treturn NULL;\n-\n-\treturn lookup_commit_reference(commit_sha1);\n-}\n-\n-static int parse_insn_buffer(char *buf, struct commit_list **todo_list,\n-\t\t\tstruct replay_opts *opts)\n-{\n-\tstruct commit_list **next = todo_list;\n-\tstruct commit *commit;\n-\tchar *p = buf;\n-\tint i;\n-\n-\tfor (i = 1; *p; i++) {\n-\t\tchar *eol = strchrnul(p, '\\n');\n-\t\tcommit = parse_insn_line(p, eol, opts);\n-\t\tif (!commit)\n-\t\t\treturn error(_(\"Could not parse line %d.\"), i);\n-\t\tnext = commit_list_append(commit, next);\n-\t\tp = *eol ? eol + 1 : eol;\n-\t}\n-\tif (!*todo_list)\n-\t\treturn error(_(\"No commits parsed.\"));\n-\treturn 0;\n-}\n-\n-static void read_populate_todo(struct commit_list **todo_list,\n-\t\t\tstruct replay_opts *opts)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd, res;\n-\n-\tfd = open(todo_file, O_RDONLY);\n-\tif (fd < 0)\n-\t\tdie_errno(_(\"Could not open %s\"), todo_file);\n-\tif (strbuf_read(&buf, fd, 0) < 0) {\n-\t\tclose(fd);\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Could not read %s.\"), todo_file);\n-\t}\n-\tclose(fd);\n-\n-\tres = parse_insn_buffer(buf.buf, todo_list, opts);\n-\tstrbuf_release(&buf);\n-\tif (res)\n-\t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n-}\n-\n-static int populate_opts_cb(const char *key, const char *value, void *data)\n-{\n-\tstruct replay_opts *opts = data;\n-\tint error_flag = 1;\n-\n-\tif (!value)\n-\t\terror_flag = 0;\n-\telse if (!strcmp(key, \"options.no-commit\"))\n-\t\topts->no_commit = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.edit\"))\n-\t\topts->edit = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.signoff\"))\n-\t\topts->signoff = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.record-origin\"))\n-\t\topts->record_origin = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.allow-ff\"))\n-\t\topts->allow_ff = git_config_bool_or_int(key, value, &error_flag);\n-\telse if (!strcmp(key, \"options.mainline\"))\n-\t\topts->mainline = git_config_int(key, value);\n-\telse if (!strcmp(key, \"options.strategy\"))\n-\t\tgit_config_string(&opts->strategy, key, value);\n-\telse if (!strcmp(key, \"options.strategy-option\")) {\n-\t\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n-\t\topts->xopts[opts->xopts_nr++] = xstrdup(value);\n-\t} else\n-\t\treturn error(_(\"Invalid key: %s\"), key);\n-\n-\tif (!error_flag)\n-\t\treturn error(_(\"Invalid value for %s: %s\"), key, value);\n-\n-\treturn 0;\n-}\n-\n-static void read_populate_opts(struct replay_opts **opts_ptr)\n-{\n-\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n-\n-\tif (!file_exists(opts_file))\n-\t\treturn;\n-\tif (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)\n-\t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n-}\n-\n-static void walk_revs_populate_todo(struct commit_list **todo_list,\n-\t\t\t\tstruct replay_opts *opts)\n-{\n-\tstruct commit *commit;\n-\tstruct commit_list **next;\n-\n-\tprepare_revs(opts);\n-\n-\tnext = todo_list;\n-\twhile ((commit = get_revision(opts->revs)))\n-\t\tnext = commit_list_append(commit, next);\n-}\n-\n-static int create_seq_dir(void)\n-{\n-\tconst char *seq_dir = git_path(SEQ_DIR);\n-\n-\tif (file_exists(seq_dir)) {\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(seq_dir, 0777) < 0)\n-\t\tdie_errno(_(\"Could not create sequencer directory %s\"), seq_dir);\n-\treturn 0;\n-}\n-\n-static void save_head(const char *head)\n-{\n-\tconst char *head_file = git_path(SEQ_HEAD_FILE);\n-\tstatic struct lock_file head_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);\n-\tstrbuf_addf(&buf, \"%s\\n\", head);\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n-\t\tdie_errno(_(\"Could not write to %s\"), head_file);\n-\tif (commit_lock_file(&head_lock) < 0)\n-\t\tdie(_(\"Error wrapping up %s.\"), head_file);\n-}\n-\n-static int reset_for_rollback(const unsigned char *sha1)\n-{\n-\tconst char *argv[4];\t/* reset --merge <arg> + NULL */\n-\targv[0] = \"reset\";\n-\targv[1] = \"--merge\";\n-\targv[2] = sha1_to_hex(sha1);\n-\targv[3] = NULL;\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n-}\n-\n-static int rollback_single_pick(void)\n-{\n-\tunsigned char head_sha1[20];\n-\n-\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n-\t    !file_exists(git_path(\"REVERT_HEAD\")))\n-\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n-\tif (read_ref_full(\"HEAD\", head_sha1, 0, NULL))\n-\t\treturn error(_(\"cannot resolve HEAD\"));\n-\tif (is_null_sha1(head_sha1))\n-\t\treturn error(_(\"cannot abort from a branch yet to be born\"));\n-\treturn reset_for_rollback(head_sha1);\n-}\n-\n-static int sequencer_rollback(struct replay_opts *opts)\n-{\n-\tconst char *filename;\n-\tFILE *f;\n-\tunsigned char sha1[20];\n-\tstruct strbuf buf = STRBUF_INIT;\n-\n-\tfilename = git_path(SEQ_HEAD_FILE);\n-\tf = fopen(filename, \"r\");\n-\tif (!f && errno == ENOENT) {\n-\t\t/*\n-\t\t * There is no multiple-cherry-pick in progress.\n-\t\t * If CHERRY_PICK_HEAD or REVERT_HEAD indicates\n-\t\t * a single-cherry-pick in progress, abort that.\n-\t\t */\n-\t\treturn rollback_single_pick();\n-\t}\n-\tif (!f)\n-\t\treturn error(_(\"cannot open %s: %s\"), filename,\n-\t\t\t\t\t\tstrerror(errno));\n-\tif (strbuf_getline(&buf, f, '\\n')) {\n-\t\terror(_(\"cannot read %s: %s\"), filename, ferror(f) ?\n-\t\t\tstrerror(errno) : _(\"unexpected end of file\"));\n-\t\tfclose(f);\n-\t\tgoto fail;\n-\t}\n-\tfclose(f);\n-\tif (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\\0') {\n-\t\terror(_(\"stored pre-cherry-pick HEAD file '%s' is corrupt\"),\n-\t\t\tfilename);\n-\t\tgoto fail;\n-\t}\n-\tif (reset_for_rollback(sha1))\n-\t\tgoto fail;\n-\tremove_sequencer_state();\n-\tstrbuf_release(&buf);\n-\treturn 0;\n-fail:\n-\tstrbuf_release(&buf);\n-\treturn -1;\n-}\n-\n-static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\n-{\n-\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n-\tstatic struct lock_file todo_lock;\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tint fd;\n-\n-\tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n-\tif (format_todo(&buf, todo_list, opts) < 0)\n-\t\tdie(_(\"Could not format %s.\"), todo_file);\n-\tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n-\t\tstrbuf_release(&buf);\n-\t\tdie_errno(_(\"Could not write to %s\"), todo_file);\n-\t}\n-\tif (commit_lock_file(&todo_lock) < 0) {\n-\t\tstrbuf_release(&buf);\n-\t\tdie(_(\"Error wrapping up %s.\"), todo_file);\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n-static void save_opts(struct replay_opts *opts)\n-{\n-\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n-\n-\tif (opts->no_commit)\n-\t\tgit_config_set_in_file(opts_file, \"options.no-commit\", \"true\");\n-\tif (opts->edit)\n-\t\tgit_config_set_in_file(opts_file, \"options.edit\", \"true\");\n-\tif (opts->signoff)\n-\t\tgit_config_set_in_file(opts_file, \"options.signoff\", \"true\");\n-\tif (opts->record_origin)\n-\t\tgit_config_set_in_file(opts_file, \"options.record-origin\", \"true\");\n-\tif (opts->allow_ff)\n-\t\tgit_config_set_in_file(opts_file, \"options.allow-ff\", \"true\");\n-\tif (opts->mainline) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n-\t\tgit_config_set_in_file(opts_file, \"options.mainline\", buf.buf);\n-\t\tstrbuf_release(&buf);\n-\t}\n-\tif (opts->strategy)\n-\t\tgit_config_set_in_file(opts_file, \"options.strategy\", opts->strategy);\n-\tif (opts->xopts) {\n-\t\tint i;\n-\t\tfor (i = 0; i < opts->xopts_nr; i++)\n-\t\t\tgit_config_set_multivar_in_file(opts_file,\n-\t\t\t\t\t\t\t\"options.strategy-option\",\n-\t\t\t\t\t\t\topts->xopts[i], \"^$\", 0);\n-\t}\n-}\n-\n-static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n-{\n-\tstruct commit_list *cur;\n-\tint res;\n-\n-\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n-\tif (opts->allow_ff)\n-\t\tassert(!(opts->signoff || opts->no_commit ||\n-\t\t\t\topts->record_origin || opts->edit));\n-\tread_and_refresh_cache(opts);\n-\n-\tfor (cur = todo_list; cur; cur = cur->next) {\n-\t\tsave_todo(cur, opts);\n-\t\tres = do_pick_commit(cur->item, opts);\n-\t\tif (res)\n-\t\t\treturn res;\n-\t}\n-\n-\t/*\n-\t * Sequence of picks finished successfully; cleanup by\n-\t * removing the .git/sequencer directory\n-\t */\n-\tremove_sequencer_state();\n-\treturn 0;\n-}\n-\n-static int continue_single_pick(void)\n-{\n-\tconst char *argv[] = { \"commit\", NULL };\n-\n-\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n-\t    !file_exists(git_path(\"REVERT_HEAD\")))\n-\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n-\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n-}\n-\n-static int sequencer_continue(struct replay_opts *opts)\n-{\n-\tstruct commit_list *todo_list = NULL;\n-\n-\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n-\t\treturn continue_single_pick();\n-\tread_populate_opts(&opts);\n-\tread_populate_todo(&todo_list, opts);\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-\t}\n-\tif (index_differs_from(\"HEAD\", 0))\n-\t\treturn error_dirty_index(opts);\n-\ttodo_list = todo_list->next;\n-\treturn pick_commits(todo_list, opts);\n-}\n-\n-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(cmit, opts);\n-}\n-\n-static int pick_revisions(struct replay_opts *opts)\n-{\n-\tstruct commit_list *todo_list = NULL;\n-\tunsigned char sha1[20];\n-\n-\tif (opts->subcommand == REPLAY_NONE)\n-\t\tassert(opts->revs);\n-\n-\tread_and_refresh_cache(opts);\n-\n-\t/*\n-\t * Decide what to do depending on the arguments; a fresh\n-\t * cherry-pick should be handled differently from an existing\n-\t * one that is being continued\n-\t */\n-\tif (opts->subcommand == REPLAY_REMOVE_STATE) {\n-\t\tremove_sequencer_state();\n-\t\treturn 0;\n-\t}\n-\tif (opts->subcommand == REPLAY_ROLLBACK)\n-\t\treturn sequencer_rollback(opts);\n-\tif (opts->subcommand == REPLAY_CONTINUE)\n-\t\treturn sequencer_continue(opts);\n-\n-\t/*\n-\t * If we were called as \"git cherry-pick <commit>\", just\n-\t * cherry-pick/revert it, set CHERRY_PICK_HEAD /\n-\t * REVERT_HEAD, and don't touch the sequencer state.\n-\t * This means it is possible to cherry-pick in the middle\n-\t * of a cherry-pick sequence.\n-\t */\n-\tif (opts->revs->cmdline.nr == 1 &&\n-\t    opts->revs->cmdline.rev->whence == REV_CMD_REV &&\n-\t    opts->revs->no_walk &&\n-\t    !opts->revs->cmdline.rev->flags) {\n-\t\tstruct commit *cmit;\n-\t\tif (prepare_revision_walk(opts->revs))\n-\t\t\tdie(_(\"revision walk setup failed\"));\n-\t\tcmit = get_revision(opts->revs);\n-\t\tif (!cmit || get_revision(opts->revs))\n-\t\t\tdie(\"BUG: expected exactly one commit from walk\");\n-\t\treturn single_pick(cmit, opts);\n-\t}\n-\n-\t/*\n-\t * Start a new cherry-pick/ revert sequence; but\n-\t * first, make sure that an existing one isn't in\n-\t * progress\n-\t */\n-\n-\twalk_revs_populate_todo(&todo_list, opts);\n-\tif (create_seq_dir() < 0)\n-\t\treturn -1;\n-\tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REPLAY_REVERT)\n-\t\t\treturn error(_(\"Can't revert as initial commit\"));\n-\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n-\t}\n-\tsave_head(sha1_to_hex(sha1));\n-\tsave_opts(opts);\n-\treturn pick_commits(todo_list, opts);\n-}\n-\n int cmd_revert(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts;\n@@ -1148,7 +206,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \topts.action = REPLAY_REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\tres = pick_revisions(&opts);\n+\tres = sequencer_pick_revisions(&opts);\n \tif (res < 0)\n \t\tdie(_(\"revert failed\"));\n \treturn res;\n@@ -1163,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \topts.action = REPLAY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\tres = pick_revisions(&opts);\n+\tres = sequencer_pick_revisions(&opts);\n \tif (res < 0)\n \t\tdie(_(\"cherry-pick failed\"));\n \treturn res;\ndiff --git a/sequencer.c b/sequencer.c\nindex d1f28a6..5477119 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1,7 +1,20 @@\n #include \"cache.h\"\n #include \"sequencer.h\"\n-#include \"strbuf.h\"\n #include \"dir.h\"\n+#include \"object.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"run-command.h\"\n+#include \"exec_cmd.h\"\n+#include \"utf8.h\"\n+#include \"cache-tree.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"rerere.h\"\n+#include \"merge-recursive.h\"\n+#include \"refs.h\"\n+\n+#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n void remove_sequencer_state(void)\n {\n@@ -11,3 +24,906 @@ void remove_sequencer_state(void)\n \tremove_dir_recursively(&seq_dir, 0);\n \tstrbuf_release(&seq_dir);\n }\n+\n+static const char *action_name(const struct replay_opts *opts)\n+{\n+\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n+}\n+\n+static char *get_encoding(const char *message);\n+\n+struct commit_message {\n+\tchar *parent_label;\n+\tconst char *label;\n+\tconst char *subject;\n+\tchar *reencoded_message;\n+\tconst char *message;\n+};\n+\n+static int get_message(struct commit *commit, struct commit_message *out)\n+{\n+\tconst char *encoding;\n+\tconst char *abbrev, *subject;\n+\tint abbrev_len, subject_len;\n+\tchar *q;\n+\n+\tif (!commit->buffer)\n+\t\treturn -1;\n+\tencoding = get_encoding(commit->buffer);\n+\tif (!encoding)\n+\t\tencoding = \"UTF-8\";\n+\tif (!git_commit_encoding)\n+\t\tgit_commit_encoding = \"UTF-8\";\n+\n+\tout->reencoded_message = NULL;\n+\tout->message = commit->buffer;\n+\tif (strcmp(encoding, git_commit_encoding))\n+\t\tout->reencoded_message = reencode_string(commit->buffer,\n+\t\t\t\t\tgit_commit_encoding, encoding);\n+\tif (out->reencoded_message)\n+\t\tout->message = out->reencoded_message;\n+\n+\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n+\tabbrev_len = strlen(abbrev);\n+\n+\tsubject_len = find_commit_subject(out->message, &subject);\n+\n+\tout->parent_label = xmalloc(strlen(\"parent of \") + abbrev_len +\n+\t\t\t      strlen(\"... \") + subject_len + 1);\n+\tq = out->parent_label;\n+\tq = mempcpy(q, \"parent of \", strlen(\"parent of \"));\n+\tout->label = q;\n+\tq = mempcpy(q, abbrev, abbrev_len);\n+\tq = mempcpy(q, \"... \", strlen(\"... \"));\n+\tout->subject = q;\n+\tq = mempcpy(q, subject, subject_len);\n+\t*q = '\\0';\n+\treturn 0;\n+}\n+\n+static void free_message(struct commit_message *msg)\n+{\n+\tfree(msg->parent_label);\n+\tfree(msg->reencoded_message);\n+}\n+\n+static char *get_encoding(const char *message)\n+{\n+\tconst char *p = message, *eol;\n+\n+\twhile (*p && *p != '\\n') {\n+\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n+\t\t\t; /* do nothing */\n+\t\tif (!prefixcmp(p, \"encoding \")) {\n+\t\t\tchar *result = xmalloc(eol - 8 - p);\n+\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n+\t\t\treturn result;\n+\t\t}\n+\t\tp = eol;\n+\t\tif (*p == '\\n')\n+\t\t\tp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)\n+{\n+\tconst char *filename;\n+\tint fd;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n+\n+\tfilename = git_path(\"%s\", pseudoref);\n+\tfd = open(filename, O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open '%s' for writing\"), filename);\n+\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n+\t\tdie_errno(_(\"Could not write to '%s'\"), filename);\n+\tstrbuf_release(&buf);\n+}\n+\n+static void print_advice(int show_hint)\n+{\n+\tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n+\n+\tif (msg) {\n+\t\tfprintf(stderr, \"%s\\n\", msg);\n+\t\t/*\n+\t\t * A conflict has occured but the porcelain\n+\t\t * (typically rebase --interactive) wants to take care\n+\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n+\t\t */\n+\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n+\t\treturn;\n+\t}\n+\n+\tif (show_hint) {\n+\t\tadvise(\"after resolving the conflicts, mark the corrected paths\");\n+\t\tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n+\t\tadvise(\"and commit the result with 'git commit'\");\n+\t}\n+}\n+\n+static void write_message(struct strbuf *msgbuf, const char *filename)\n+{\n+\tstatic struct lock_file msg_file;\n+\n+\tint msg_fd = hold_lock_file_for_update(&msg_file, filename,\n+\t\t\t\t\t       LOCK_DIE_ON_ERROR);\n+\tif (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), filename);\n+\tstrbuf_release(msgbuf);\n+\tif (commit_lock_file(&msg_file) < 0)\n+\t\tdie(_(\"Error wrapping up %s\"), filename);\n+}\n+\n+static struct tree *empty_tree(void)\n+{\n+\treturn lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);\n+}\n+\n+static int error_dirty_index(struct replay_opts *opts)\n+{\n+\tif (read_cache_unmerged())\n+\t\treturn error_resolve_conflict(action_name(opts));\n+\n+\t/* Different translation strings for cherry-pick and revert */\n+\tif (opts->action == REPLAY_PICK)\n+\t\terror(_(\"Your local changes would be overwritten by cherry-pick.\"));\n+\telse\n+\t\terror(_(\"Your local changes would be overwritten by revert.\"));\n+\n+\tif (advice_commit_before_merge)\n+\t\tadvise(_(\"Commit your changes or stash them to proceed.\"));\n+\treturn -1;\n+}\n+\n+static int fast_forward_to(const unsigned char *to, const unsigned char *from)\n+{\n+\tstruct ref_lock *ref_lock;\n+\n+\tread_cache();\n+\tif (checkout_fast_forward(from, to))\n+\t\texit(1); /* the callee should have complained already */\n+\tref_lock = lock_any_ref_for_update(\"HEAD\", from, 0);\n+\treturn write_ref_sha1(ref_lock, to, \"cherry-pick\");\n+}\n+\n+static int do_recursive_merge(struct commit *base, struct commit *next,\n+\t\t\t      const char *base_label, const char *next_label,\n+\t\t\t      unsigned char *head, struct strbuf *msgbuf,\n+\t\t\t      struct replay_opts *opts)\n+{\n+\tstruct merge_options o;\n+\tstruct tree *result, *next_tree, *base_tree, *head_tree;\n+\tint clean, index_fd;\n+\tconst char **xopt;\n+\tstatic struct lock_file index_lock;\n+\n+\tindex_fd = hold_locked_index(&index_lock, 1);\n+\n+\tread_cache();\n+\n+\tinit_merge_options(&o);\n+\to.ancestor = base ? base_label : \"(empty tree)\";\n+\to.branch1 = \"HEAD\";\n+\to.branch2 = next ? next_label : \"(empty tree)\";\n+\n+\thead_tree = parse_tree_indirect(head);\n+\tnext_tree = next ? next->tree : empty_tree();\n+\tbase_tree = base ? base->tree : empty_tree();\n+\n+\tfor (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)\n+\t\tparse_merge_opt(&o, *xopt);\n+\n+\tclean = merge_trees(&o,\n+\t\t\t    head_tree,\n+\t\t\t    next_tree, base_tree, &result);\n+\n+\tif (active_cache_changed &&\n+\t    (write_cache(index_fd, active_cache, active_nr) ||\n+\t     commit_locked_index(&index_lock)))\n+\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n+\t\tdie(_(\"%s: Unable to write new index file\"), action_name(opts));\n+\trollback_lock_file(&index_lock);\n+\n+\tif (!clean) {\n+\t\tint i;\n+\t\tstrbuf_addstr(msgbuf, \"\\nConflicts:\\n\\n\");\n+\t\tfor (i = 0; i < active_nr;) {\n+\t\t\tstruct cache_entry *ce = active_cache[i++];\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tstrbuf_addch(msgbuf, '\\t');\n+\t\t\t\tstrbuf_addstr(msgbuf, ce->name);\n+\t\t\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n+\t\t\t\t\t\tactive_cache[i]->name))\n+\t\t\t\t\ti++;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn !clean;\n+}\n+\n+/*\n+ * If we are cherry-pick, and if the merge did not result in\n+ * hand-editing, we will hit this commit and inherit the original\n+ * author date and name.\n+ * If we are revert, or if our cherry-pick results in a hand merge,\n+ * we had better say that the current user is responsible for that.\n+ */\n+static int run_git_commit(const char *defmsg, struct replay_opts *opts)\n+{\n+\t/* 6 is max possible length of our args array including NULL */\n+\tconst char *args[6];\n+\tint i = 0;\n+\n+\targs[i++] = \"commit\";\n+\targs[i++] = \"-n\";\n+\tif (opts->signoff)\n+\t\targs[i++] = \"-s\";\n+\tif (!opts->edit) {\n+\t\targs[i++] = \"-F\";\n+\t\targs[i++] = defmsg;\n+\t}\n+\targs[i] = NULL;\n+\n+\treturn run_command_v_opt(args, RUN_GIT_CMD);\n+}\n+\n+static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n+{\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, NULL };\n+\tchar *defmsg = NULL;\n+\tstruct strbuf msgbuf = STRBUF_INIT;\n+\tint res;\n+\n+\tif (opts->no_commit) {\n+\t\t/*\n+\t\t * We do not intend to commit immediately.  We just want to\n+\t\t * merge the differences in, so let's compute the tree\n+\t\t * that represents the \"current\" state for merge-recursive\n+\t\t * to work on.\n+\t\t */\n+\t\tif (write_cache_as_tree(head, 0, NULL))\n+\t\t\tdie (_(\"Your index file is unmerged.\"));\n+\t} else {\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n+\t\tif (index_differs_from(\"HEAD\", 0))\n+\t\t\treturn error_dirty_index(opts);\n+\t}\n+\tdiscard_cache();\n+\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+\t\tstruct commit_list *p;\n+\n+\t\tif (!opts->mainline)\n+\t\t\treturn error(_(\"Commit %s is a merge but no -m option was given.\"),\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\n+\t\tfor (cnt = 1, p = commit->parents;\n+\t\t     cnt != opts->mainline && p;\n+\t\t     cnt++)\n+\t\t\tp = p->next;\n+\t\tif (cnt != opts->mainline || !p)\n+\t\t\treturn error(_(\"Commit %s does not have parent %d\"),\n+\t\t\t\tsha1_to_hex(commit->object.sha1), opts->mainline);\n+\t\tparent = p->item;\n+\t} else if (0 < opts->mainline)\n+\t\treturn error(_(\"Mainline was specified but commit %s is not a merge.\"),\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\telse\n+\t\tparent = commit->parents->item;\n+\n+\tif (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))\n+\t\treturn fast_forward_to(commit->object.sha1, head);\n+\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+\t\treturn error(_(\"%s: cannot parse parent commit %s\"),\n+\t\t\taction_name(opts), sha1_to_hex(parent->object.sha1));\n+\n+\tif (get_message(commit, &msg) != 0)\n+\t\treturn error(_(\"Cannot get commit message for %s\"),\n+\t\t\tsha1_to_hex(commit->object.sha1));\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+\t * on top of the current HEAD if we are cherry-pick.  Or the\n+\t * reverse of it if we are revert.\n+\t */\n+\n+\tdefmsg = git_pathdup(\"MERGE_MSG\");\n+\n+\tif (opts->action == REPLAY_REVERT) {\n+\t\tbase = commit;\n+\t\tbase_label = msg.label;\n+\t\tnext = parent;\n+\t\tnext_label = msg.parent_label;\n+\t\tstrbuf_addstr(&msgbuf, \"Revert \\\"\");\n+\t\tstrbuf_addstr(&msgbuf, msg.subject);\n+\t\tstrbuf_addstr(&msgbuf, \"\\\"\\n\\nThis reverts commit \");\n+\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n+\n+\t\tif (commit->parents && commit->parents->next) {\n+\t\t\tstrbuf_addstr(&msgbuf, \", reversing\\nchanges made to \");\n+\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));\n+\t\t}\n+\t\tstrbuf_addstr(&msgbuf, \".\\n\");\n+\t} else {\n+\t\tconst char *p;\n+\n+\t\tbase = parent;\n+\t\tbase_label = msg.parent_label;\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\tp += 2;\n+\t\t\tstrbuf_addstr(&msgbuf, p);\n+\t\t}\n+\n+\t\tif (opts->record_origin) {\n+\t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n+\t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n+\t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n+\t\t}\n+\t}\n+\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n+\t\tres = do_recursive_merge(base, next, base_label, next_label,\n+\t\t\t\t\t head, &msgbuf, opts);\n+\t\twrite_message(&msgbuf, defmsg);\n+\t} else {\n+\t\tstruct commit_list *common = NULL;\n+\t\tstruct commit_list *remotes = NULL;\n+\n+\t\twrite_message(&msgbuf, defmsg);\n+\n+\t\tcommit_list_insert(base, &common);\n+\t\tcommit_list_insert(next, &remotes);\n+\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n+\t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n+\t\tfree_commit_list(common);\n+\t\tfree_commit_list(remotes);\n+\t}\n+\n+\t/*\n+\t * If the merge was clean or if it failed due to conflict, we write\n+\t * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.\n+\t * However, if the merge did not even start, then we don't want to\n+\t * write it at all.\n+\t */\n+\tif (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))\n+\t\twrite_cherry_pick_head(commit, \"CHERRY_PICK_HEAD\");\n+\tif (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))\n+\t\twrite_cherry_pick_head(commit, \"REVERT_HEAD\");\n+\n+\tif (res) {\n+\t\terror(opts->action == REPLAY_REVERT\n+\t\t      ? _(\"could not revert %s... %s\")\n+\t\t      : _(\"could not apply %s... %s\"),\n+\t\t      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),\n+\t\t      msg.subject);\n+\t\tprint_advice(res == 1);\n+\t\trerere(opts->allow_rerere_auto);\n+\t} else {\n+\t\tif (!opts->no_commit)\n+\t\t\tres = run_git_commit(defmsg, opts);\n+\t}\n+\n+\tfree_message(&msg);\n+\tfree(defmsg);\n+\n+\treturn res;\n+}\n+\n+static void prepare_revs(struct replay_opts *opts)\n+{\n+\tif (opts->action != REPLAY_REVERT)\n+\t\topts->revs->reverse ^= 1;\n+\n+\tif (prepare_revision_walk(opts->revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\n+\tif (!opts->revs->commits)\n+\t\tdie(_(\"empty commit set passed\"));\n+}\n+\n+static void read_and_refresh_cache(struct replay_opts *opts)\n+{\n+\tstatic struct lock_file index_lock;\n+\tint index_fd = hold_locked_index(&index_lock, 0);\n+\tif (read_index_preload(&the_index, NULL) < 0)\n+\t\tdie(_(\"git %s: failed to read the index\"), action_name(opts));\n+\trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);\n+\tif (the_index.cache_changed) {\n+\t\tif (write_index(&the_index, index_fd) ||\n+\t\t    commit_locked_index(&index_lock))\n+\t\t\tdie(_(\"git %s: failed to refresh the index\"), action_name(opts));\n+\t}\n+\trollback_lock_file(&index_lock);\n+}\n+\n+/*\n+ * Append a commit to the end of the commit_list.\n+ *\n+ * next starts by pointing to the variable that holds the head of an\n+ * empty commit_list, and is updated to point to the \"next\" field of\n+ * the last item on the list as new commits are appended.\n+ *\n+ * Usage example:\n+ *\n+ *     struct commit_list *list;\n+ *     struct commit_list **next = &list;\n+ *\n+ *     next = commit_list_append(c1, next);\n+ *     next = commit_list_append(c2, next);\n+ *     assert(commit_list_count(list) == 2);\n+ *     return list;\n+ */\n+static struct commit_list **commit_list_append(struct commit *commit,\n+\t\t\t\t\t       struct commit_list **next)\n+{\n+\tstruct commit_list *new = xmalloc(sizeof(struct commit_list));\n+\tnew->item = commit;\n+\t*next = new;\n+\tnew->next = NULL;\n+\treturn &new->next;\n+}\n+\n+static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n+\t\tstruct replay_opts *opts)\n+{\n+\tstruct commit_list *cur = NULL;\n+\tconst char *sha1_abbrev = NULL;\n+\tconst char *action_str = opts->action == REPLAY_REVERT ? \"revert\" : \"pick\";\n+\tconst char *subject;\n+\tint subject_len;\n+\n+\tfor (cur = todo_list; cur; cur = cur->next) {\n+\t\tsha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);\n+\t\tsubject_len = find_commit_subject(cur->item->buffer, &subject);\n+\t\tstrbuf_addf(buf, \"%s %s %.*s\\n\", action_str, sha1_abbrev,\n+\t\t\tsubject_len, subject);\n+\t}\n+\treturn 0;\n+}\n+\n+static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)\n+{\n+\tunsigned char commit_sha1[20];\n+\tenum replay_action action;\n+\tchar *end_of_object_name;\n+\tint saved, status, padding;\n+\n+\tif (!prefixcmp(bol, \"pick\")) {\n+\t\taction = REPLAY_PICK;\n+\t\tbol += strlen(\"pick\");\n+\t} else if (!prefixcmp(bol, \"revert\")) {\n+\t\taction = REPLAY_REVERT;\n+\t\tbol += strlen(\"revert\");\n+\t} else\n+\t\treturn NULL;\n+\n+\t/* Eat up extra spaces/ tabs before object name */\n+\tpadding = strspn(bol, \" \\t\");\n+\tif (!padding)\n+\t\treturn NULL;\n+\tbol += padding;\n+\n+\tend_of_object_name = bol + strcspn(bol, \" \\t\\n\");\n+\tsaved = *end_of_object_name;\n+\t*end_of_object_name = '\\0';\n+\tstatus = get_sha1(bol, commit_sha1);\n+\t*end_of_object_name = saved;\n+\n+\t/*\n+\t * Verify that the action matches up with the one in\n+\t * opts; we don't support arbitrary instructions\n+\t */\n+\tif (action != opts->action) {\n+\t\tconst char *action_str;\n+\t\taction_str = action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n+\t\terror(_(\"Cannot %s during a %s\"), action_str, action_name(opts));\n+\t\treturn NULL;\n+\t}\n+\n+\tif (status < 0)\n+\t\treturn NULL;\n+\n+\treturn lookup_commit_reference(commit_sha1);\n+}\n+\n+static int parse_insn_buffer(char *buf, struct commit_list **todo_list,\n+\t\t\tstruct replay_opts *opts)\n+{\n+\tstruct commit_list **next = todo_list;\n+\tstruct commit *commit;\n+\tchar *p = buf;\n+\tint i;\n+\n+\tfor (i = 1; *p; i++) {\n+\t\tchar *eol = strchrnul(p, '\\n');\n+\t\tcommit = parse_insn_line(p, eol, opts);\n+\t\tif (!commit)\n+\t\t\treturn error(_(\"Could not parse line %d.\"), i);\n+\t\tnext = commit_list_append(commit, next);\n+\t\tp = *eol ? eol + 1 : eol;\n+\t}\n+\tif (!*todo_list)\n+\t\treturn error(_(\"No commits parsed.\"));\n+\treturn 0;\n+}\n+\n+static void read_populate_todo(struct commit_list **todo_list,\n+\t\t\tstruct replay_opts *opts)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd, res;\n+\n+\tfd = open(todo_file, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie_errno(_(\"Could not open %s\"), todo_file);\n+\tif (strbuf_read(&buf, fd, 0) < 0) {\n+\t\tclose(fd);\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Could not read %s.\"), todo_file);\n+\t}\n+\tclose(fd);\n+\n+\tres = parse_insn_buffer(buf.buf, todo_list, opts);\n+\tstrbuf_release(&buf);\n+\tif (res)\n+\t\tdie(_(\"Unusable instruction sheet: %s\"), todo_file);\n+}\n+\n+static int populate_opts_cb(const char *key, const char *value, void *data)\n+{\n+\tstruct replay_opts *opts = data;\n+\tint error_flag = 1;\n+\n+\tif (!value)\n+\t\terror_flag = 0;\n+\telse if (!strcmp(key, \"options.no-commit\"))\n+\t\topts->no_commit = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.edit\"))\n+\t\topts->edit = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.signoff\"))\n+\t\topts->signoff = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.record-origin\"))\n+\t\topts->record_origin = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.allow-ff\"))\n+\t\topts->allow_ff = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.mainline\"))\n+\t\topts->mainline = git_config_int(key, value);\n+\telse if (!strcmp(key, \"options.strategy\"))\n+\t\tgit_config_string(&opts->strategy, key, value);\n+\telse if (!strcmp(key, \"options.strategy-option\")) {\n+\t\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n+\t\topts->xopts[opts->xopts_nr++] = xstrdup(value);\n+\t} else\n+\t\treturn error(_(\"Invalid key: %s\"), key);\n+\n+\tif (!error_flag)\n+\t\treturn error(_(\"Invalid value for %s: %s\"), key, value);\n+\n+\treturn 0;\n+}\n+\n+static void read_populate_opts(struct replay_opts **opts_ptr)\n+{\n+\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n+\n+\tif (!file_exists(opts_file))\n+\t\treturn;\n+\tif (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)\n+\t\tdie(_(\"Malformed options sheet: %s\"), opts_file);\n+}\n+\n+static void walk_revs_populate_todo(struct commit_list **todo_list,\n+\t\t\t\tstruct replay_opts *opts)\n+{\n+\tstruct commit *commit;\n+\tstruct commit_list **next;\n+\n+\tprepare_revs(opts);\n+\n+\tnext = todo_list;\n+\twhile ((commit = get_revision(opts->revs)))\n+\t\tnext = commit_list_append(commit, next);\n+}\n+\n+static int create_seq_dir(void)\n+{\n+\tconst char *seq_dir = git_path(SEQ_DIR);\n+\n+\tif (file_exists(seq_dir)) {\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(seq_dir, 0777) < 0)\n+\t\tdie_errno(_(\"Could not create sequencer directory %s\"), seq_dir);\n+\treturn 0;\n+}\n+\n+static void save_head(const char *head)\n+{\n+\tconst char *head_file = git_path(SEQ_HEAD_FILE);\n+\tstatic struct lock_file head_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);\n+\tstrbuf_addf(&buf, \"%s\\n\", head);\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0)\n+\t\tdie_errno(_(\"Could not write to %s\"), head_file);\n+\tif (commit_lock_file(&head_lock) < 0)\n+\t\tdie(_(\"Error wrapping up %s.\"), head_file);\n+}\n+\n+static int reset_for_rollback(const unsigned char *sha1)\n+{\n+\tconst char *argv[4];\t/* reset --merge <arg> + NULL */\n+\targv[0] = \"reset\";\n+\targv[1] = \"--merge\";\n+\targv[2] = sha1_to_hex(sha1);\n+\targv[3] = NULL;\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n+static int rollback_single_pick(void)\n+{\n+\tunsigned char head_sha1[20];\n+\n+\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n+\t    !file_exists(git_path(\"REVERT_HEAD\")))\n+\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n+\tif (read_ref_full(\"HEAD\", head_sha1, 0, NULL))\n+\t\treturn error(_(\"cannot resolve HEAD\"));\n+\tif (is_null_sha1(head_sha1))\n+\t\treturn error(_(\"cannot abort from a branch yet to be born\"));\n+\treturn reset_for_rollback(head_sha1);\n+}\n+\n+static int sequencer_rollback(struct replay_opts *opts)\n+{\n+\tconst char *filename;\n+\tFILE *f;\n+\tunsigned char sha1[20];\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tfilename = git_path(SEQ_HEAD_FILE);\n+\tf = fopen(filename, \"r\");\n+\tif (!f && errno == ENOENT) {\n+\t\t/*\n+\t\t * There is no multiple-cherry-pick in progress.\n+\t\t * If CHERRY_PICK_HEAD or REVERT_HEAD indicates\n+\t\t * a single-cherry-pick in progress, abort that.\n+\t\t */\n+\t\treturn rollback_single_pick();\n+\t}\n+\tif (!f)\n+\t\treturn error(_(\"cannot open %s: %s\"), filename,\n+\t\t\t\t\t\tstrerror(errno));\n+\tif (strbuf_getline(&buf, f, '\\n')) {\n+\t\terror(_(\"cannot read %s: %s\"), filename, ferror(f) ?\n+\t\t\tstrerror(errno) : _(\"unexpected end of file\"));\n+\t\tfclose(f);\n+\t\tgoto fail;\n+\t}\n+\tfclose(f);\n+\tif (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\\0') {\n+\t\terror(_(\"stored pre-cherry-pick HEAD file '%s' is corrupt\"),\n+\t\t\tfilename);\n+\t\tgoto fail;\n+\t}\n+\tif (reset_for_rollback(sha1))\n+\t\tgoto fail;\n+\tremove_sequencer_state();\n+\tstrbuf_release(&buf);\n+\treturn 0;\n+fail:\n+\tstrbuf_release(&buf);\n+\treturn -1;\n+}\n+\n+static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\n+{\n+\tconst char *todo_file = git_path(SEQ_TODO_FILE);\n+\tstatic struct lock_file todo_lock;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint fd;\n+\n+\tfd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);\n+\tif (format_todo(&buf, todo_list, opts) < 0)\n+\t\tdie(_(\"Could not format %s.\"), todo_file);\n+\tif (write_in_full(fd, buf.buf, buf.len) < 0) {\n+\t\tstrbuf_release(&buf);\n+\t\tdie_errno(_(\"Could not write to %s\"), todo_file);\n+\t}\n+\tif (commit_lock_file(&todo_lock) < 0) {\n+\t\tstrbuf_release(&buf);\n+\t\tdie(_(\"Error wrapping up %s.\"), todo_file);\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n+static void save_opts(struct replay_opts *opts)\n+{\n+\tconst char *opts_file = git_path(SEQ_OPTS_FILE);\n+\n+\tif (opts->no_commit)\n+\t\tgit_config_set_in_file(opts_file, \"options.no-commit\", \"true\");\n+\tif (opts->edit)\n+\t\tgit_config_set_in_file(opts_file, \"options.edit\", \"true\");\n+\tif (opts->signoff)\n+\t\tgit_config_set_in_file(opts_file, \"options.signoff\", \"true\");\n+\tif (opts->record_origin)\n+\t\tgit_config_set_in_file(opts_file, \"options.record-origin\", \"true\");\n+\tif (opts->allow_ff)\n+\t\tgit_config_set_in_file(opts_file, \"options.allow-ff\", \"true\");\n+\tif (opts->mainline) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n+\t\tgit_config_set_in_file(opts_file, \"options.mainline\", buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t}\n+\tif (opts->strategy)\n+\t\tgit_config_set_in_file(opts_file, \"options.strategy\", opts->strategy);\n+\tif (opts->xopts) {\n+\t\tint i;\n+\t\tfor (i = 0; i < opts->xopts_nr; i++)\n+\t\t\tgit_config_set_multivar_in_file(opts_file,\n+\t\t\t\t\t\t\t\"options.strategy-option\",\n+\t\t\t\t\t\t\topts->xopts[i], \"^$\", 0);\n+\t}\n+}\n+\n+static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n+{\n+\tstruct commit_list *cur;\n+\tint res;\n+\n+\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n+\tif (opts->allow_ff)\n+\t\tassert(!(opts->signoff || opts->no_commit ||\n+\t\t\t\topts->record_origin || opts->edit));\n+\tread_and_refresh_cache(opts);\n+\n+\tfor (cur = todo_list; cur; cur = cur->next) {\n+\t\tsave_todo(cur, opts);\n+\t\tres = do_pick_commit(cur->item, opts);\n+\t\tif (res)\n+\t\t\treturn res;\n+\t}\n+\n+\t/*\n+\t * Sequence of picks finished successfully; cleanup by\n+\t * removing the .git/sequencer directory\n+\t */\n+\tremove_sequencer_state();\n+\treturn 0;\n+}\n+\n+static int continue_single_pick(void)\n+{\n+\tconst char *argv[] = { \"commit\", NULL };\n+\n+\tif (!file_exists(git_path(\"CHERRY_PICK_HEAD\")) &&\n+\t    !file_exists(git_path(\"REVERT_HEAD\")))\n+\t\treturn error(_(\"no cherry-pick or revert in progress\"));\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n+static int sequencer_continue(struct replay_opts *opts)\n+{\n+\tstruct commit_list *todo_list = NULL;\n+\n+\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n+\t\treturn continue_single_pick();\n+\tread_populate_opts(&opts);\n+\tread_populate_todo(&todo_list, opts);\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+\t}\n+\tif (index_differs_from(\"HEAD\", 0))\n+\t\treturn error_dirty_index(opts);\n+\ttodo_list = todo_list->next;\n+\treturn pick_commits(todo_list, opts);\n+}\n+\n+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(cmit, opts);\n+}\n+\n+int sequencer_pick_revisions(struct replay_opts *opts)\n+{\n+\tstruct commit_list *todo_list = NULL;\n+\tunsigned char sha1[20];\n+\n+\tif (opts->subcommand == REPLAY_NONE)\n+\t\tassert(opts->revs);\n+\n+\tread_and_refresh_cache(opts);\n+\n+\t/*\n+\t * Decide what to do depending on the arguments; a fresh\n+\t * cherry-pick should be handled differently from an existing\n+\t * one that is being continued\n+\t */\n+\tif (opts->subcommand == REPLAY_REMOVE_STATE) {\n+\t\tremove_sequencer_state();\n+\t\treturn 0;\n+\t}\n+\tif (opts->subcommand == REPLAY_ROLLBACK)\n+\t\treturn sequencer_rollback(opts);\n+\tif (opts->subcommand == REPLAY_CONTINUE)\n+\t\treturn sequencer_continue(opts);\n+\n+\t/*\n+\t * If we were called as \"git cherry-pick <commit>\", just\n+\t * cherry-pick/revert it, set CHERRY_PICK_HEAD /\n+\t * REVERT_HEAD, and don't touch the sequencer state.\n+\t * This means it is possible to cherry-pick in the middle\n+\t * of a cherry-pick sequence.\n+\t */\n+\tif (opts->revs->cmdline.nr == 1 &&\n+\t    opts->revs->cmdline.rev->whence == REV_CMD_REV &&\n+\t    opts->revs->no_walk &&\n+\t    !opts->revs->cmdline.rev->flags) {\n+\t\tstruct commit *cmit;\n+\t\tif (prepare_revision_walk(opts->revs))\n+\t\t\tdie(_(\"revision walk setup failed\"));\n+\t\tcmit = get_revision(opts->revs);\n+\t\tif (!cmit || get_revision(opts->revs))\n+\t\t\tdie(\"BUG: expected exactly one commit from walk\");\n+\t\treturn single_pick(cmit, opts);\n+\t}\n+\n+\t/*\n+\t * Start a new cherry-pick/ revert sequence; but\n+\t * first, make sure that an existing one isn't in\n+\t * progress\n+\t */\n+\n+\twalk_revs_populate_todo(&todo_list, opts);\n+\tif (create_seq_dir() < 0)\n+\t\treturn -1;\n+\tif (get_sha1(\"HEAD\", sha1)) {\n+\t\tif (opts->action == REPLAY_REVERT)\n+\t\t\treturn error(_(\"Can't revert as initial commit\"));\n+\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n+\t}\n+\tsave_head(sha1_to_hex(sha1));\n+\tsave_opts(opts);\n+\treturn pick_commits(todo_list, opts);\n+}\ndiff --git a/sequencer.h b/sequencer.h\nindex 2d4528f..bb4b138 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -6,7 +6,44 @@\n #define SEQ_TODO_FILE\t\"sequencer/todo\"\n #define SEQ_OPTS_FILE\t\"sequencer/opts\"\n \n+enum replay_action {\n+\tREPLAY_REVERT,\n+\tREPLAY_PICK\n+};\n+\n+enum replay_subcommand {\n+\tREPLAY_NONE,\n+\tREPLAY_REMOVE_STATE,\n+\tREPLAY_CONTINUE,\n+\tREPLAY_ROLLBACK\n+};\n+\n+struct replay_opts {\n+\tenum replay_action action;\n+\tenum replay_subcommand subcommand;\n+\n+\t/* Boolean options */\n+\tint edit;\n+\tint record_origin;\n+\tint no_commit;\n+\tint signoff;\n+\tint allow_ff;\n+\tint allow_rerere_auto;\n+\n+\tint mainline;\n+\n+\t/* Merge strategy */\n+\tconst char *strategy;\n+\tconst char **xopts;\n+\tsize_t xopts_nr, xopts_alloc;\n+\n+\t/* Only used by REPLAY_NONE */\n+\tstruct rev_info *revs;\n+};\n+\n /* Removes SEQ_DIR. */\n extern void remove_sequencer_state(void);\n \n+int sequencer_pick_revisions(struct replay_opts *opts);\n+\n #endif\n-- \n1.7.8.2\n"},{"id":"182368","messageId":"20120111184036.GC3005@burratino","threadId":"29315","inReplyTo":"1326305757-27525-1-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH v3 0/2] The move to sequencer.c","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-01-11T18:40:37Z","receivedAt":"2012-01-11T18:40:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Ramkumar Ramachandra (2):\n>   revert: prepare to move replay_action to header\n>   sequencer: factor code out of revert builtin\n\nAh.  \"git diff HEAD^:builtin/revert.c HEAD:sequencer.c\" gives a sane\ndiff, and the remaining stuff in builtin/revert.c feels pleasant.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n\nHere's a patchlet for squashing into patch 2/2.\n---\n builtin/revert.c |    2 --\n 1 files changed, 0 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 4116f2d3..e6840f23 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -30,8 +30,6 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n-\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n-- \n"}]}