{"thread":{"id":"28021","subject":"[PATCH 00/18] Sequencer for inclusion v6","startedAt":"2011-08-04T10:38:58Z","lastAt":"2011-08-09T05:26:07Z","messageCount":25,"participants":["Ramkumar Ramachandra","Christian Couder","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":18},"messages":[{"id":"172901","messageId":"1312454356-3070-1-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":null,"subject":"[PATCH 00/18] Sequencer for inclusion v6","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:38:58Z","receivedAt":"2011-08-04T10:38:58Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nApart from the minor style changes, there are two big hilights in this\niteration of the series:\n1. The \"--continue complains when there are unresolved conflicts\" test\nis fixed.  I'd written \"base..picked\" instead of \"base..anotherpick\"\nearlier.\n2. Special handling for the last line in the instruction sheet (when\nit is not terminated by a newline).  See [1] for an especially\nconvincing explanation from Junio.\n\nAll the changes made in this iteration are a direct result of\nChristian's reviews -- a big thanks.\n\nThanks for reading.\n\n-- Ram\n\n[1]: http://mid.gmane.org/7v4o1ycsi4.fsf@alter.siamese.dyndns.org\n\nRamkumar Ramachandra (18):\n  advice: Introduce error_resolve_conflict\n  config: Introduce functions to write non-standard file\n  revert: Simplify and inline add_message_to_msg\n  revert: Don't check lone argument in get_encoding\n  revert: Rename no_replay to record_origin\n  revert: Eliminate global \"commit\" variable\n  revert: Introduce struct to keep command-line options\n  revert: Separate cmdline parsing from functional code\n  revert: Don't create invalid replay_opts in parse_args\n  revert: Save data for continuing after conflict resolution\n  revert: Save command-line options for continuing operation\n  revert: Make pick_commits functionally act on a commit list\n  revert: Introduce --reset to remove sequencer state\n  reset: Make reset remove the sequencer state\n  revert: Remove sequencer state when no commits are pending\n  revert: Don't implicitly stomp pending sequencer operation\n  revert: Introduce --continue to continue the operation\n  revert: Propagate errors upwards from do_pick_commit\n\n Documentation/git-cherry-pick.txt |    6 +\n Documentation/git-revert.txt      |    6 +\n Documentation/sequencer.txt       |    9 +\n Makefile                          |    2 +\n advice.c                          |   31 ++-\n advice.h                          |    3 +-\n branch.c                          |    2 +\n builtin/revert.c                  |  737 +++++++++++++++++++++++++++++--------\n cache.h                           |    2 +\n config.c                          |   36 ++-\n sequencer.c                       |   19 +\n sequencer.h                       |   20 +\n t/7106-reset-sequence.sh          |   44 +++\n t/t3510-cherry-pick-sequence.sh   |  214 +++++++++++\n 14 files changed, 960 insertions(+), 171 deletions(-)\n create mode 100644 Documentation/sequencer.txt\n create mode 100644 sequencer.c\n create mode 100644 sequencer.h\n create mode 100755 t/7106-reset-sequence.sh\n create mode 100755 t/t3510-cherry-pick-sequence.sh\n\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172903","messageId":"1312454356-3070-2-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 01/18] advice: Introduce error_resolve_conflict","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:38:59Z","receivedAt":"2011-08-04T10:38:59Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Enable future callers to report a conflict and not die immediately by\nintroducing a new function called error_resolve_conflict.\nRe-implement die_resolve_conflict as a call to error_resolve_conflict\nfollowed by a call to die.  Consequently, the message printed by\ndie_resolve_conflict changes from\n\n  fatal: 'commit' is not possible because you have unmerged files.\n         Please, fix them up in the work tree ...\n         ...\n\nto\n\n  error: 'commit' is not possible because you have unmerged files.\n  hint: Fix them up in the work tree ...\n  hint: ...\n  fatal: Exiting because of an unresolved conflict.\n\nHints are printed using the same advise function introduced in\nv1.7.3-rc0~26^2~3 (Introduce advise() to print hints, 2010-08-11).\n\nInspired-by: Christian Couder <chistian.couder@gmail.com>\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n advice.c         |   31 ++++++++++++++++++++++++-------\n advice.h         |    3 ++-\n builtin/revert.c |    9 ---------\n 3 files changed, 26 insertions(+), 17 deletions(-)\n\ndiff --git a/advice.c b/advice.c\nindex 0be4b5f..e02e632 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -19,6 +19,15 @@ static struct {\n \t{ \"detachedhead\", &advice_detached_head },\n };\n \n+void advise(const char *advice, ...)\n+{\n+\tva_list params;\n+\n+\tva_start(params, advice);\n+\tvreportf(\"hint: \", advice, params);\n+\tva_end(params);\n+}\n+\n int git_default_advice_config(const char *var, const char *value)\n {\n \tconst char *k = skip_prefix(var, \"advice.\");\n@@ -34,16 +43,24 @@ int git_default_advice_config(const char *var, const char *value)\n \treturn 0;\n }\n \n-void NORETURN die_resolve_conflict(const char *me)\n+int error_resolve_conflict(const char *me)\n {\n-\tif (advice_resolve_conflict)\n+\terror(\"'%s' is not possible because you have unmerged files.\", me);\n+\tif (advice_resolve_conflict) {\n \t\t/*\n \t\t * Message used both when 'git commit' fails and when\n \t\t * other commands doing a merge do.\n \t\t */\n-\t\tdie(\"'%s' is not possible because you have unmerged files.\\n\"\n-\t\t    \"Please, fix them up in the work tree, and then use 'git add/rm <file>' as\\n\"\n-\t\t    \"appropriate to mark resolution and make a commit, or use 'git commit -a'.\", me);\n-\telse\n-\t\tdie(\"'%s' is not possible because you have unmerged files.\", me);\n+\t\tadvise(\"Fix them up in the work tree,\");\n+\t\tadvise(\"and then use 'git add/rm <file>' as\");\n+\t\tadvise(\"appropriate to mark resolution and make a commit,\");\n+\t\tadvise(\"or use 'git commit -a'.\");\n+\t}\n+\treturn -1;\n+}\n+\n+void NORETURN die_resolve_conflict(const char *me)\n+{\n+\terror_resolve_conflict(me);\n+\tdie(\"Exiting because of an unresolved conflict.\");\n }\ndiff --git a/advice.h b/advice.h\nindex 3244ebb..e5d0af7 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -11,7 +11,8 @@ extern int advice_implicit_identity;\n extern int advice_detached_head;\n \n int git_default_advice_config(const char *var, const char *value);\n-\n+void advise(const char *advice, ...);\n+int error_resolve_conflict(const char *me);\n extern void NORETURN die_resolve_conflict(const char *me);\n \n #endif /* ADVICE_H */\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 1f27c63..2df3f3b 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -214,15 +214,6 @@ static void write_cherry_pick_head(void)\n \tstrbuf_release(&buf);\n }\n \n-static void advise(const char *advice, ...)\n-{\n-\tva_list params;\n-\n-\tva_start(params, advice);\n-\tvreportf(\"hint: \", advice, params);\n-\tva_end(params);\n-}\n-\n static void print_advice(void)\n {\n \tchar *msg = getenv(\"GIT_CHERRY_PICK_HELP\");\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172902","messageId":"1312454356-3070-3-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 02/18] config: Introduce functions to write non-standard file","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:00Z","receivedAt":"2011-08-04T10:39:00Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Introduce two new functions corresponding to \"git_config_set\" and\n\"git_config_set_multivar\" to write a non-standard configuration file.\nExpose these new functions in cache.h for other git programs to use.\n\nHelped-by: Jeff King <peff@peff.net>\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n cache.h  |    2 ++\n config.c |   36 +++++++++++++++++++++++++++---------\n 2 files changed, 29 insertions(+), 9 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 9e12d55..33d3428 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1069,9 +1069,11 @@ extern int git_config_bool(const char *, const char *);\n extern int git_config_maybe_bool(const char *, const char *);\n extern int git_config_string(const char **, const char *, const char *);\n extern int git_config_pathname(const char **, const char *, const char *);\n+extern int git_config_set_in_file(const char *, const char *, const char *);\n extern int git_config_set(const char *, const char *);\n extern int git_config_parse_key(const char *, char **, int *);\n extern int git_config_set_multivar(const char *, const char *, const char *, int);\n+extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n extern int git_config_rename_section(const char *, const char *);\n extern const char *git_etc_gitconfig(void);\n extern int check_repository_format_version(const char *var, const char *value, void *cb);\ndiff --git a/config.c b/config.c\nindex e42c59b..9e8580b 100644\n--- a/config.c\n+++ b/config.c\n@@ -1092,6 +1092,12 @@ contline:\n \treturn offset;\n }\n \n+int git_config_set_in_file(const char *config_filename,\n+\t\t\tconst char *key, const char *value)\n+{\n+\treturn git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);\n+}\n+\n int git_config_set(const char *key, const char *value)\n {\n \treturn git_config_set_multivar(key, value, NULL, 0);\n@@ -1189,19 +1195,14 @@ out_free_ret_1:\n  * - the config file is removed and the lock file rename()d to it.\n  *\n  */\n-int git_config_set_multivar(const char *key, const char *value,\n-\tconst char *value_regex, int multi_replace)\n+int git_config_set_multivar_in_file(const char *config_filename,\n+\t\t\t\tconst char *key, const char *value,\n+\t\t\t\tconst char *value_regex, int multi_replace)\n {\n \tint fd = -1, in_fd;\n \tint ret;\n-\tchar *config_filename;\n \tstruct lock_file *lock = NULL;\n \n-\tif (config_exclusive_filename)\n-\t\tconfig_filename = xstrdup(config_exclusive_filename);\n-\telse\n-\t\tconfig_filename = git_pathdup(\"config\");\n-\n \t/* parse-key returns negative; flip the sign to feed exit(3) */\n \tret = 0 - git_config_parse_key(key, &store.key, &store.baselen);\n \tif (ret)\n@@ -1378,7 +1379,6 @@ int git_config_set_multivar(const char *key, const char *value,\n out_free:\n \tif (lock)\n \t\trollback_lock_file(lock);\n-\tfree(config_filename);\n \treturn ret;\n \n write_err_out:\n@@ -1387,6 +1387,24 @@ write_err_out:\n \n }\n \n+int git_config_set_multivar(const char *key, const char *value,\n+\t\t\tconst char *value_regex, int multi_replace)\n+{\n+\tconst char *config_filename;\n+\tchar *buf = NULL;\n+\tint ret;\n+\n+\tif (config_exclusive_filename)\n+\t\tconfig_filename = config_exclusive_filename;\n+\telse\n+\t\tconfig_filename = buf = git_pathdup(\"config\");\n+\n+\tret = git_config_set_multivar_in_file(config_filename, key, value,\n+\t\t\t\t\tvalue_regex, multi_replace);\n+\tfree(buf);\n+\treturn ret;\n+}\n+\n static int section_name_match (const char *buf, const char *name)\n {\n \tint i = 0, j = 0, dot = 0;\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172904","messageId":"1312454356-3070-4-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 03/18] revert: Simplify and inline add_message_to_msg","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:01Z","receivedAt":"2011-08-04T10:39:01Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The add_message_to_msg function has some dead code, an unclear API,\nonly one callsite.  While it originally intended fill up an empty\ncommit message with the commit object name while picking, it really\ndoesn't do this -- a bug introduced in v1.5.1-rc1~65^2~2 (Make\ngit-revert & git-cherry-pick a builtin, 2007-03-01).  Today, tests in\nt3505-cherry-pick-empty.sh indicate that not filling up an empty\ncommit message is the desired behavior.  Re-implement and inline the\nfunction accordingly, with a beneficial side-effect: don't dereference\na NULL pointer when the commit doesn't have a delimeter after the\nheader.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |   28 ++++++++++++++--------------\n 1 files changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 2df3f3b..7dfe295 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -185,19 +185,6 @@ static char *get_encoding(const char *message)\n \treturn NULL;\n }\n \n-static void add_message_to_msg(struct strbuf *msgbuf, const char *message)\n-{\n-\tconst char *p = message;\n-\twhile (*p && (*p != '\\n' || p[1] != '\\n'))\n-\t\tp++;\n-\n-\tif (!*p)\n-\t\tstrbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));\n-\n-\tp += 2;\n-\tstrbuf_addstr(msgbuf, p);\n-}\n-\n static void write_cherry_pick_head(void)\n {\n \tint fd;\n@@ -462,11 +449,24 @@ static int do_pick_commit(void)\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-\t\tadd_message_to_msg(&msgbuf, msg.message);\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 (no_replay) {\n \t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\n \t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172905","messageId":"1312454356-3070-5-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 04/18] revert: Don't check lone argument in get_encoding","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:02Z","receivedAt":"2011-08-04T10:39:02Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The only place get_encoding uses the global \"commit\" variable is when\nwriting an error message explaining that its lone argument was NULL.\nSince the function's only caller ensures that a NULL argument isn't\npassed, we can remove this check with two beneficial consequences:\n\n1. Since the function doesn't use the global \"commit\" variable any\n   more, it won't need to change when we eliminate the global variable\n   later in the series.\n2. Translators no longer need to localize an error message that will\n   never be shown.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nMentored-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |    3 ---\n 1 files changed, 0 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 7dfe295..30b39c0 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -167,9 +167,6 @@ static char *get_encoding(const char *message)\n {\n \tconst char *p = message, *eol;\n \n-\tif (!p)\n-\t\tdie (_(\"Could not read commit message of %s\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1));\n \twhile (*p && *p != '\\n') {\n \t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n \t\t\t; /* do nothing */\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172906","messageId":"1312454356-3070-6-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 05/18] revert: Rename no_replay to record_origin","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:03Z","receivedAt":"2011-08-04T10:39:03Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The \"-x\" command-line option is used to record the name of the\noriginal commits being picked in the commit message.  The variable\ncorresponding to this option is named \"no_replay\" for historical\nreasons; the name is especially confusing because the term \"replay\" is\nused to describe what cherry-pick does (for example, in the\ndocumentation of the \"--mainline\" option).  So, give the variable\ncorresponding to the \"-x\" command-line option a better name:\n\"record_origin\".\n\nMentored-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/revert.c |    8 ++++----\n 1 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 30b39c0..794c050 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -35,7 +35,7 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-static int edit, no_replay, no_commit, mainline, signoff, allow_ff;\n+static int edit, record_origin, no_commit, mainline, signoff, allow_ff;\n static enum { REVERT, CHERRY_PICK } action;\n static struct commit *commit;\n static int commit_argc;\n@@ -91,7 +91,7 @@ static void parse_args(int argc, const char **argv)\n \n \tif (action == CHERRY_PICK) {\n \t\tstruct option cp_extra[] = {\n-\t\t\tOPT_BOOLEAN('x', NULL, &no_replay, \"append commit name\"),\n+\t\t\tOPT_BOOLEAN('x', NULL, &record_origin, \"append commit name\"),\n \t\t\tOPT_BOOLEAN(0, \"ff\", &allow_ff, \"allow fast-forward\"),\n \t\t\tOPT_END(),\n \t\t};\n@@ -464,7 +464,7 @@ static int do_pick_commit(void)\n \t\t\tstrbuf_addstr(&msgbuf, p);\n \t\t}\n \n-\t\tif (no_replay) {\n+\t\tif (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@@ -559,7 +559,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --signoff\"));\n \t\tif (no_commit)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --no-commit\"));\n-\t\tif (no_replay)\n+\t\tif (record_origin)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with -x\"));\n \t\tif (edit)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --edit\"));\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172907","messageId":"1312454356-3070-7-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 06/18] revert: Eliminate global \"commit\" variable","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:04Z","receivedAt":"2011-08-04T10:39:04Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Functions which act on commits currently rely on a file-scope static\nvariable to be set before they're called.  Consequently, the API and\ncorresponding callsites are ugly and unclear.  Remove this variable\nand change their API to accept the commit to act on as additional\nargument so that the callsites change from looking like\n\n  commit = prepare_a_commit();\n  act_on_commit();\n\nto looking like\n\n  commit = prepare_a_commit();\n  act_on_commit(commit);\n\nThis change is also in line with our long-term goal of exposing some\nof these functions through a public API.\n\nInspired-by: Christian Couder <chriscool@tuxfamily.org>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c |   22 +++++++++++-----------\n 1 files changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 794c050..d6c9f1d 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -37,7 +37,6 @@ static const char * const cherry_pick_usage[] = {\n \n static int edit, record_origin, no_commit, mainline, signoff, allow_ff;\n static enum { REVERT, CHERRY_PICK } action;\n-static struct commit *commit;\n static int commit_argc;\n static const char **commit_argv;\n static int allow_rerere_auto;\n@@ -116,25 +115,25 @@ struct commit_message {\n \tconst char *message;\n };\n \n-static int get_message(const char *raw_message, struct commit_message *out)\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 (!raw_message)\n+\tif (!commit->buffer)\n \t\treturn -1;\n-\tencoding = get_encoding(raw_message);\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 = raw_message;\n+\tout->message = commit->buffer;\n \tif (strcmp(encoding, git_commit_encoding))\n-\t\tout->reencoded_message = reencode_string(raw_message,\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@@ -182,7 +181,7 @@ static char *get_encoding(const char *message)\n \treturn NULL;\n }\n \n-static void write_cherry_pick_head(void)\n+static void write_cherry_pick_head(struct commit *commit)\n {\n \tint fd;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -355,7 +354,7 @@ static int run_git_commit(const char *defmsg)\n \treturn run_command_v_opt(args, RUN_GIT_CMD);\n }\n \n-static int do_pick_commit(void)\n+static int do_pick_commit(struct commit *commit)\n {\n \tunsigned char head[20];\n \tstruct commit *base, *next, *parent;\n@@ -417,7 +416,7 @@ static int do_pick_commit(void)\n \t\tdie(_(\"%s: cannot parse parent commit %s\"),\n \t\t    me, sha1_to_hex(parent->object.sha1));\n \n-\tif (get_message(commit->buffer, &msg) != 0)\n+\tif (get_message(commit, &msg) != 0)\n \t\tdie(_(\"Cannot get commit message for %s\"),\n \t\t\t\tsha1_to_hex(commit->object.sha1));\n \n@@ -470,7 +469,7 @@ static int do_pick_commit(void)\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n \t\tif (!no_commit)\n-\t\t\twrite_cherry_pick_head();\n+\t\t\twrite_cherry_pick_head(commit);\n \t}\n \n \tif (!strategy || !strcmp(strategy, \"recursive\") || action == REVERT) {\n@@ -548,6 +547,7 @@ static void read_and_refresh_cache(const char *me)\n static int revert_or_cherry_pick(int argc, const char **argv)\n {\n \tstruct rev_info revs;\n+\tstruct commit *commit;\n \n \tgit_config(git_default_config, NULL);\n \tme = action == REVERT ? \"revert\" : \"cherry-pick\";\n@@ -570,7 +570,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)\n \tprepare_revs(&revs);\n \n \twhile ((commit = get_revision(&revs))) {\n-\t\tint res = do_pick_commit();\n+\t\tint res = do_pick_commit(commit);\n \t\tif (res)\n \t\t\treturn res;\n \t}\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172908","messageId":"1312454356-3070-8-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 07/18] revert: Introduce struct to keep command-line options","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:05Z","receivedAt":"2011-08-04T10:39:05Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The current code uses a set of file-scope static variables to tell the\ncherry-pick/ revert machinery how to replay the changes, and\ninitializes them by parsing the command-line arguments.  In later\nsteps in this series, we would like to introduce an API function that\ncalls into this machinery directly and have a way to tell it what to\ndo.  Hence, introduce a structure to group these variables, so that\nthe API can take them as a single replay_options parameter.  The only\nexception is the variable \"me\" -- remove it since it not an\nindependent option, and can be inferred from the action.\n\nUnfortunately, this patch introduces a minor regression.  Parsing\nstrategy-option violates a C89 rule: Initializers cannot refer to\nvariables whose address is not known at compile time.  Currently, this\nrule is violated by some other parts of Git as well, and it is\npossible to get GCC to report these instances using the \"-std=c89\n-pedantic\" option.\n\nInspired-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Jonathan Nieder <jrnieder@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c |  201 ++++++++++++++++++++++++++++++------------------------\n 1 files changed, 113 insertions(+), 88 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex d6c9f1d..50f72de 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -35,76 +35,94 @@ static const char * const cherry_pick_usage[] = {\n \tNULL\n };\n \n-static int edit, record_origin, no_commit, mainline, signoff, allow_ff;\n-static enum { REVERT, CHERRY_PICK } action;\n-static int commit_argc;\n-static const char **commit_argv;\n-static int allow_rerere_auto;\n-\n-static const char *me;\n-\n-/* Merge strategy. */\n-static const char *strategy;\n-static const char **xopts;\n-static size_t xopts_nr, xopts_alloc;\n+enum replay_action { REVERT, CHERRY_PICK };\n+\n+struct replay_opts {\n+\tenum replay_action action;\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+\tint commit_argc;\n+\tconst char **commit_argv;\n+\n+\t/* Merge strategy */\n+\tconst char *strategy;\n+\tconst char **xopts;\n+\tsize_t xopts_nr, xopts_alloc;\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 == 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(void)\n+static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n {\n-\treturn action == REVERT ? revert_usage : cherry_pick_usage;\n+\treturn opts->action == REVERT ? revert_usage : cherry_pick_usage;\n }\n \n static int option_parse_x(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n+\tstruct replay_opts **opts_ptr = opt->value;\n+\tstruct replay_opts *opts = *opts_ptr;\n+\n \tif (unset)\n \t\treturn 0;\n \n-\tALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);\n-\txopts[xopts_nr++] = xstrdup(arg);\n+\tALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);\n+\topts->xopts[opts->xopts_nr++] = xstrdup(arg);\n \treturn 0;\n }\n \n-static void parse_args(int argc, const char **argv)\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();\n+\tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tint noop;\n \tstruct option options[] = {\n-\t\tOPT_BOOLEAN('n', \"no-commit\", &no_commit, \"don't automatically commit\"),\n-\t\tOPT_BOOLEAN('e', \"edit\", &edit, \"edit the commit message\"),\n+\t\tOPT_BOOLEAN('n', \"no-commit\", &opts->no_commit, \"don't automatically commit\"),\n+\t\tOPT_BOOLEAN('e', \"edit\", &opts->edit, \"edit the commit message\"),\n \t\t{ OPTION_BOOLEAN, 'r', NULL, &noop, NULL, \"no-op (backward compatibility)\",\n \t\t  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 0 },\n-\t\tOPT_BOOLEAN('s', \"signoff\", &signoff, \"add Signed-off-by:\"),\n-\t\tOPT_INTEGER('m', \"mainline\", &mainline, \"parent number\"),\n-\t\tOPT_RERERE_AUTOUPDATE(&allow_rerere_auto),\n-\t\tOPT_STRING(0, \"strategy\", &strategy, \"strategy\", \"merge strategy\"),\n-\t\tOPT_CALLBACK('X', \"strategy-option\", &xopts, \"option\",\n+\t\tOPT_BOOLEAN('s', \"signoff\", &opts->signoff, \"add Signed-off-by:\"),\n+\t\tOPT_INTEGER('m', \"mainline\", &opts->mainline, \"parent number\"),\n+\t\tOPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),\n+\t\tOPT_STRING(0, \"strategy\", &opts->strategy, \"strategy\", \"merge strategy\"),\n+\t\tOPT_CALLBACK('X', \"strategy-option\", &opts, \"option\",\n \t\t\t\"option for merge strategy\", option_parse_x),\n \t\tOPT_END(),\n \t\tOPT_END(),\n \t\tOPT_END(),\n \t};\n \n-\tif (action == CHERRY_PICK) {\n+\tif (opts->action == CHERRY_PICK) {\n \t\tstruct option cp_extra[] = {\n-\t\t\tOPT_BOOLEAN('x', NULL, &record_origin, \"append commit name\"),\n-\t\t\tOPT_BOOLEAN(0, \"ff\", &allow_ff, \"allow fast-forward\"),\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 \t\t\tOPT_END(),\n \t\t};\n \t\tif (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))\n \t\t\tdie(_(\"program error\"));\n \t}\n \n-\tcommit_argc = parse_options(argc, argv, NULL, options, usage_str,\n-\t\t\t\t    PARSE_OPT_KEEP_ARGV0 |\n-\t\t\t\t    PARSE_OPT_KEEP_UNKNOWN);\n-\tif (commit_argc < 2)\n+\topts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,\n+\t\t\t\t\tPARSE_OPT_KEEP_ARGV0 |\n+\t\t\t\t\tPARSE_OPT_KEEP_UNKNOWN);\n+\tif (opts->commit_argc < 2)\n \t\tusage_with_options(usage_str, options);\n \n-\tcommit_argv = argv;\n+\topts->commit_argv = argv;\n }\n \n struct commit_message {\n@@ -240,20 +258,20 @@ static struct tree *empty_tree(void)\n \treturn tree;\n }\n \n-static NORETURN void die_dirty_index(const char *me)\n+static NORETURN void die_dirty_index(struct replay_opts *opts)\n {\n \tif (read_cache_unmerged()) {\n-\t\tdie_resolve_conflict(me);\n+\t\tdie_resolve_conflict(action_name(opts));\n \t} else {\n \t\tif (advice_commit_before_merge) {\n-\t\t\tif (action == REVERT)\n+\t\t\tif (opts->action == REVERT)\n \t\t\t\tdie(_(\"Your local changes would be overwritten by revert.\\n\"\n \t\t\t\t\t  \"Please, commit your changes or stash them to proceed.\"));\n \t\t\telse\n \t\t\t\tdie(_(\"Your local changes would be overwritten by cherry-pick.\\n\"\n \t\t\t\t\t  \"Please, commit your changes or stash them to proceed.\"));\n \t\t} else {\n-\t\t\tif (action == REVERT)\n+\t\t\tif (opts->action == REVERT)\n \t\t\t\tdie(_(\"Your local changes would be overwritten by revert.\\n\"));\n \t\t\telse\n \t\t\t\tdie(_(\"Your local changes would be overwritten by cherry-pick.\\n\"));\n@@ -274,7 +292,8 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)\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      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@@ -295,7 +314,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tnext_tree = next ? next->tree : empty_tree();\n \tbase_tree = base ? base->tree : empty_tree();\n \n-\tfor (xopt = xopts; xopt != xopts + xopts_nr; xopt++)\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@@ -306,7 +325,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\"), me);\n+\t\tdie(_(\"%s: Unable to write new index file\"), action_name(opts));\n \trollback_lock_file(&index_lock);\n \n \tif (!clean) {\n@@ -335,7 +354,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\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)\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@@ -343,9 +362,9 @@ static int run_git_commit(const char *defmsg)\n \n \targs[i++] = \"commit\";\n \targs[i++] = \"-n\";\n-\tif (signoff)\n+\tif (opts->signoff)\n \t\targs[i++] = \"-s\";\n-\tif (!edit) {\n+\tif (!opts->edit) {\n \t\targs[i++] = \"-F\";\n \t\targs[i++] = defmsg;\n \t}\n@@ -354,7 +373,7 @@ static int run_git_commit(const char *defmsg)\n \treturn run_command_v_opt(args, RUN_GIT_CMD);\n }\n \n-static int do_pick_commit(struct commit *commit)\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@@ -364,7 +383,7 @@ static int do_pick_commit(struct commit *commit)\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tint res;\n \n-\tif (no_commit) {\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@@ -377,7 +396,7 @@ static int do_pick_commit(struct commit *commit)\n \t\tif (get_sha1(\"HEAD\", head))\n \t\t\tdie (_(\"You do not have a valid HEAD\"));\n \t\tif (index_differs_from(\"HEAD\", 0))\n-\t\t\tdie_dirty_index(me);\n+\t\t\tdie_dirty_index(opts);\n \t}\n \tdiscard_cache();\n \n@@ -389,32 +408,32 @@ static int do_pick_commit(struct commit *commit)\n \t\tint cnt;\n \t\tstruct commit_list *p;\n \n-\t\tif (!mainline)\n+\t\tif (!opts->mainline)\n \t\t\tdie(_(\"Commit %s is a merge but no -m option was given.\"),\n \t\t\t    sha1_to_hex(commit->object.sha1));\n \n \t\tfor (cnt = 1, p = commit->parents;\n-\t\t     cnt != mainline && p;\n+\t\t     cnt != opts->mainline && p;\n \t\t     cnt++)\n \t\t\tp = p->next;\n-\t\tif (cnt != mainline || !p)\n+\t\tif (cnt != opts->mainline || !p)\n \t\t\tdie(_(\"Commit %s does not have parent %d\"),\n-\t\t\t    sha1_to_hex(commit->object.sha1), mainline);\n+\t\t\t    sha1_to_hex(commit->object.sha1), opts->mainline);\n \t\tparent = p->item;\n-\t} else if (0 < mainline)\n+\t} else if (0 < opts->mainline)\n \t\tdie(_(\"Mainline was specified but commit %s is not a merge.\"),\n \t\t    sha1_to_hex(commit->object.sha1));\n \telse\n \t\tparent = commit->parents->item;\n \n-\tif (allow_ff && parent && !hashcmp(parent->object.sha1, head))\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\tdie(_(\"%s: cannot parse parent commit %s\"),\n-\t\t    me, sha1_to_hex(parent->object.sha1));\n+\t\t    action_name(opts), sha1_to_hex(parent->object.sha1));\n \n \tif (get_message(commit, &msg) != 0)\n \t\tdie(_(\"Cannot get commit message for %s\"),\n@@ -429,7 +448,7 @@ static int do_pick_commit(struct commit *commit)\n \n \tdefmsg = git_pathdup(\"MERGE_MSG\");\n \n-\tif (action == REVERT) {\n+\tif (opts->action == REVERT) {\n \t\tbase = commit;\n \t\tbase_label = msg.label;\n \t\tnext = parent;\n@@ -463,18 +482,18 @@ static int do_pick_commit(struct commit *commit)\n \t\t\tstrbuf_addstr(&msgbuf, p);\n \t\t}\n \n-\t\tif (record_origin) {\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\tif (!no_commit)\n+\t\tif (!opts->no_commit)\n \t\t\twrite_cherry_pick_head(commit);\n \t}\n \n-\tif (!strategy || !strcmp(strategy, \"recursive\") || action == REVERT) {\n+\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n-\t\t\t\t\t head, &msgbuf);\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@@ -484,23 +503,23 @@ static int do_pick_commit(struct commit *commit)\n \n \t\tcommit_list_insert(base, &common);\n \t\tcommit_list_insert(next, &remotes);\n-\t\tres = try_merge_command(strategy, xopts_nr, xopts, common,\n-\t\t\t\t\tsha1_to_hex(head), 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 \tif (res) {\n-\t\terror(action == REVERT\n+\t\terror(opts->action == 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();\n-\t\trerere(allow_rerere_auto);\n+\t\trerere(opts->allow_rerere_auto);\n \t} else {\n-\t\tif (!no_commit)\n-\t\t\tres = run_git_commit(defmsg);\n+\t\tif (!opts->no_commit)\n+\t\t\tres = run_git_commit(defmsg, opts);\n \t}\n \n \tfree_message(&msg);\n@@ -509,18 +528,18 @@ static int do_pick_commit(struct commit *commit)\n \treturn res;\n }\n \n-static void prepare_revs(struct rev_info *revs)\n+static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)\n {\n \tint argc;\n \n \tinit_revisions(revs, NULL);\n \trevs->no_walk = 1;\n-\tif (action != REVERT)\n+\tif (opts->action != REVERT)\n \t\trevs->reverse = 1;\n \n-\targc = setup_revisions(commit_argc, commit_argv, revs, NULL);\n+\targc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);\n \tif (argc > 1)\n-\t\tusage(*revert_or_cherry_pick_usage());\n+\t\tusage(*revert_or_cherry_pick_usage(opts));\n \n \tif (prepare_revision_walk(revs))\n \t\tdie(_(\"revision walk setup failed\"));\n@@ -529,48 +548,48 @@ static void prepare_revs(struct rev_info *revs)\n \t\tdie(_(\"empty commit set passed\"));\n }\n \n-static void read_and_refresh_cache(const char *me)\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\"), me);\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\"), me);\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-static int revert_or_cherry_pick(int argc, const char **argv)\n+static int revert_or_cherry_pick(int argc, const char **argv,\n+\t\t\t\tstruct replay_opts *opts)\n {\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \n \tgit_config(git_default_config, NULL);\n-\tme = action == REVERT ? \"revert\" : \"cherry-pick\";\n-\tsetenv(GIT_REFLOG_ACTION, me, 0);\n-\tparse_args(argc, argv);\n+\tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n+\tparse_args(argc, argv, opts);\n \n-\tif (allow_ff) {\n-\t\tif (signoff)\n+\tif (opts->allow_ff) {\n+\t\tif (opts->signoff)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --signoff\"));\n-\t\tif (no_commit)\n+\t\tif (opts->no_commit)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --no-commit\"));\n-\t\tif (record_origin)\n+\t\tif (opts->record_origin)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with -x\"));\n-\t\tif (edit)\n+\t\tif (opts->edit)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --edit\"));\n \t}\n \n-\tread_and_refresh_cache(me);\n+\tread_and_refresh_cache(opts);\n \n-\tprepare_revs(&revs);\n+\tprepare_revs(&revs, opts);\n \n \twhile ((commit = get_revision(&revs))) {\n-\t\tint res = do_pick_commit(commit);\n+\t\tint res = do_pick_commit(commit, opts);\n \t\tif (res)\n \t\t\treturn res;\n \t}\n@@ -580,14 +599,20 @@ static int revert_or_cherry_pick(int argc, const char **argv)\n \n int cmd_revert(int argc, const char **argv, const char *prefix)\n {\n+\tstruct replay_opts opts;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n-\t\tedit = 1;\n-\taction = REVERT;\n-\treturn revert_or_cherry_pick(argc, argv);\n+\t\topts.edit = 1;\n+\topts.action = REVERT;\n+\treturn revert_or_cherry_pick(argc, argv, &opts);\n }\n \n int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n {\n-\taction = CHERRY_PICK;\n-\treturn revert_or_cherry_pick(argc, argv);\n+\tstruct replay_opts opts;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.action = CHERRY_PICK;\n+\treturn revert_or_cherry_pick(argc, argv, &opts);\n }\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172909","messageId":"1312454356-3070-9-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 08/18] revert: Separate cmdline parsing from functional code","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:06Z","receivedAt":"2011-08-04T10:39:06Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Currently, revert_or_cherry_pick sets up a default git config, parses\ncommand-line arguments, before preparing to pick commits.  This makes\nfor a bad API as the central worry of callers is to assert whether or\nnot a conflict occured while cherry picking.  The current API is like:\n\n  if (revert_or_cherry_pick(argc, argv, opts) < 0)\n     print \"Something failed, we're not sure what\"\n\nSimplify and rename revert_or_cherry_pick to pick_commits so that it\nonly has the responsibility of setting up the revision walker and\npicking commits in a loop.  Transfer the remaining work to its\ncallers.  Now, the API is simplified as:\n\n  if (parse_args(argc, argv, opts) < 0)\n     print \"Can't parse arguments\"\n  if (pick_commits(opts) < 0)\n     print \"Error encountered in picking machinery\"\n\nLater in the series, pick_commits will also serve as the starting\npoint for continuing a cherry-pick or revert.\n\nInspired-by: Christian Couder <chriscool@tuxfamily.org>\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c |   14 +++++++-------\n 1 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 50f72de..90fe2ef 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -563,16 +563,12 @@ static void read_and_refresh_cache(struct replay_opts *opts)\n \trollback_lock_file(&index_lock);\n }\n \n-static int revert_or_cherry_pick(int argc, const char **argv,\n-\t\t\t\tstruct replay_opts *opts)\n+static int pick_commits(struct replay_opts *opts)\n {\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \n-\tgit_config(git_default_config, NULL);\n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n-\tparse_args(argc, argv, opts);\n-\n \tif (opts->allow_ff) {\n \t\tif (opts->signoff)\n \t\t\tdie(_(\"cherry-pick --ff cannot be used with --signoff\"));\n@@ -605,7 +601,9 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \tif (isatty(0))\n \t\topts.edit = 1;\n \topts.action = REVERT;\n-\treturn revert_or_cherry_pick(argc, argv, &opts);\n+\tgit_config(git_default_config, NULL);\n+\tparse_args(argc, argv, &opts);\n+\treturn pick_commits(&opts);\n }\n \n int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n@@ -614,5 +612,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.action = CHERRY_PICK;\n-\treturn revert_or_cherry_pick(argc, argv, &opts);\n+\tgit_config(git_default_config, NULL);\n+\tparse_args(argc, argv, &opts);\n+\treturn pick_commits(&opts);\n }\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172910","messageId":"1312454356-3070-10-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 09/18] revert: Don't create invalid replay_opts in parse_args","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:07Z","receivedAt":"2011-08-04T10:39:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The \"--ff\" command-line option cannot be used with some other\ncommand-line options.  However, parse_args still parses these\nincompatible options into a replay_opts structure for use by the rest\nof the program.  Although pick_commits, the current gatekeeper to the\ncherry-pick machinery, checks the validity of the replay_opts\nstructure before before starting its operation, there will be multiple\nentry points to the cherry-pick machinery in future.  To futureproof\nthe code and catch these errors in one place, make sure that an\ninvalid replay_opts structure is not created by parse_args in the\nfirst place.  We still check the replay_opts structure for validity in\npick_commits, but this is an assert() now to emphasize that it's the\ncaller's responsibility to get it right.\n\nInspired-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Jonathan Nieder <jrnieder@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c |   38 +++++++++++++++++++++++++++-----------\n 1 files changed, 27 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 90fe2ef..f75c9cb 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -86,9 +86,26 @@ static int option_parse_x(const struct option *opt,\n \treturn 0;\n }\n \n+static void verify_opt_compatible(const char *me, const char *base_opt, ...)\n+{\n+\tconst char *this_opt;\n+\tva_list ap;\n+\n+\tva_start(ap, base_opt);\n+\twhile ((this_opt = va_arg(ap, const char *))) {\n+\t\tif (va_arg(ap, int))\n+\t\t\tbreak;\n+\t}\n+\tva_end(ap);\n+\n+\tif (this_opt)\n+\t\tdie(_(\"%s: %s cannot be used with %s\"), me, this_opt, base_opt);\n+}\n+\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 \tint noop;\n \tstruct option options[] = {\n \t\tOPT_BOOLEAN('n', \"no-commit\", &opts->no_commit, \"don't automatically commit\"),\n@@ -122,6 +139,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \tif (opts->commit_argc < 2)\n \t\tusage_with_options(usage_str, options);\n \n+\tif (opts->allow_ff)\n+\t\tverify_opt_compatible(me, \"--ff\",\n+\t\t\t\t\"--signoff\", opts->signoff,\n+\t\t\t\t\"--no-commit\", opts->no_commit,\n+\t\t\t\t\"-x\", opts->record_origin,\n+\t\t\t\t\"--edit\", opts->edit,\n+\t\t\t\tNULL);\n \topts->commit_argv = argv;\n }\n \n@@ -569,17 +593,9 @@ static int pick_commits(struct replay_opts *opts)\n \tstruct commit *commit;\n \n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n-\tif (opts->allow_ff) {\n-\t\tif (opts->signoff)\n-\t\t\tdie(_(\"cherry-pick --ff cannot be used with --signoff\"));\n-\t\tif (opts->no_commit)\n-\t\t\tdie(_(\"cherry-pick --ff cannot be used with --no-commit\"));\n-\t\tif (opts->record_origin)\n-\t\t\tdie(_(\"cherry-pick --ff cannot be used with -x\"));\n-\t\tif (opts->edit)\n-\t\t\tdie(_(\"cherry-pick --ff cannot be used with --edit\"));\n-\t}\n-\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 \tprepare_revs(&revs, opts);\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172912","messageId":"1312454356-3070-11-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 10/18] revert: Save data for continuing after conflict resolution","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:08Z","receivedAt":"2011-08-04T10:39:08Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Ever since v1.7.2-rc1~4^2~7 (revert: allow cherry-picking more than\none commit, 2010-06-02), a single invocation of \"git cherry-pick\" or\n\"git revert\" can perform picks of several individual commits.  To\nimplement features like \"--continue\" to continue the whole operation,\nwe will need to store some information about the state and the plan at\nthe beginning.  Introduce a \".git/sequencer/head\" file to store this\nstate, and \".git/sequencer/todo\" file to store the plan.  The head\nfile contains the SHA-1 of the HEAD before the start of the operation,\nand the todo file contains an instruction sheet whose format is\ninspired by the format of the \"rebase -i\" instruction sheet.  As a\nresult, a typical todo file looks like:\n\n  pick 8537f0e submodule add: test failure when url is not configured\n  pick 4d68932 submodule add: allow relative repository path\n  pick f22a17e submodule add: clean up duplicated code\n  pick 59a5775 make copy_ref globally available\n\nSince SHA-1 hex is abbreviated using an find_unique_abbrev(), it is\nunambiguous.  This does not guarantee that there will be no ambiguity\nwhen more objects are added to the repository.\n\nThese two files alone are not enough to implement a \"--continue\" that\nremembers the command-line options specified; later patches in the\nseries save them too.\n\nThese new files are unrelated to the existing .git/CHERRY_PICK_HEAD,\nwhich will still be useful while committing after a conflict\nresolution.\n\nInspired-by: Christian Couder <chriscool@tuxfamily.org>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c                |  134 +++++++++++++++++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh |   48 ++++++++++++++\n 2 files changed, 178 insertions(+), 4 deletions(-)\n create mode 100755 t/t3510-cherry-pick-sequence.sh\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex f75c9cb..54df8a2 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -13,6 +13,7 @@\n #include \"rerere.h\"\n #include \"merge-recursive.h\"\n #include \"refs.h\"\n+#include \"dir.h\"\n \n /*\n  * This implements the builtins revert and cherry-pick.\n@@ -60,6 +61,10 @@ struct replay_opts {\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n+#define SEQ_DIR         \"sequencer\"\n+#define SEQ_HEAD_FILE   \"sequencer/head\"\n+#define SEQ_TODO_FILE   \"sequencer/todo\"\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n@@ -587,10 +592,116 @@ static void read_and_refresh_cache(struct replay_opts *opts)\n \trollback_lock_file(&index_lock);\n }\n \n-static int pick_commits(struct replay_opts *opts)\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+struct commit_list **commit_list_append(struct commit *commit,\n+\t\t\t\t\tstruct 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+\tstruct commit_message msg = { NULL, NULL, NULL, NULL, NULL };\n+\tconst char *sha1_abbrev = NULL;\n+\tconst char *action_str = opts->action == REVERT ? \"revert\" : \"pick\";\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\tif (get_message(cur->item, &msg))\n+\t\t\treturn error(_(\"Cannot get commit message for %s\"), sha1_abbrev);\n+\t\tstrbuf_addf(buf, \"%s %s %s\\n\", action_str, sha1_abbrev, msg.subject);\n+\t}\n+\treturn 0;\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 rev_info revs;\n \tstruct commit *commit;\n+\tstruct commit_list **next;\n+\n+\tprepare_revs(&revs, opts);\n+\n+\tnext = todo_list;\n+\twhile ((commit = get_revision(&revs)))\n+\t\tnext = commit_list_append(commit, next);\n+}\n+\n+static void create_seq_dir(void)\n+{\n+\tconst char *seq_dir = git_path(SEQ_DIR);\n+\n+\tif (!(file_exists(seq_dir) && is_directory(seq_dir))\n+\t\t&& mkdir(seq_dir, 0777) < 0)\n+\t\tdie_errno(_(\"Could not create sequencer directory '%s'.\"), seq_dir);\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 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 int pick_commits(struct replay_opts *opts)\n+{\n+\tstruct commit_list *todo_list = NULL;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tunsigned char sha1[20];\n+\tstruct commit_list *cur;\n+\tint res;\n \n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \tif (opts->allow_ff)\n@@ -598,14 +709,29 @@ static int pick_commits(struct replay_opts *opts)\n \t\t\t\topts->record_origin || opts->edit));\n \tread_and_refresh_cache(opts);\n \n-\tprepare_revs(&revs, opts);\n+\twalk_revs_populate_todo(&todo_list, opts);\n+\tcreate_seq_dir();\n+\tif (get_sha1(\"HEAD\", sha1)) {\n+\t\tif (opts->action == REVERT)\n+\t\t\tdie(_(\"Can't revert as initial commit\"));\n+\t\tdie(_(\"Can't cherry-pick into empty head\"));\n+\t}\n+\tsave_head(sha1_to_hex(sha1));\n \n-\twhile ((commit = get_revision(&revs))) {\n-\t\tint res = do_pick_commit(commit, opts);\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+\tstrbuf_addf(&buf, \"%s\", git_path(SEQ_DIR));\n+\tremove_dir_recursively(&buf, 0);\n+\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nnew file mode 100755\nindex 0000000..a2c70ad\n--- /dev/null\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -0,0 +1,48 @@\n+#!/bin/sh\n+\n+test_description='Test cherry-pick continuation features\n+\n+  + anotherpick: rewrites foo to d\n+  + picked: rewrites foo to c\n+  + unrelatedpick: rewrites unrelated to reallyunrelated\n+  + base: rewrites foo to b\n+  + initial: writes foo as a, unrelated as unrelated\n+\n+'\n+\n+. ./test-lib.sh\n+\n+pristine_detach () {\n+\trm -rf .git/sequencer &&\n+\tgit checkout -f \"$1^0\" &&\n+\tgit read-tree -u --reset HEAD &&\n+\tgit clean -d -f -f -q -x\n+}\n+\n+test_expect_success setup '\n+\techo unrelated >unrelated &&\n+\tgit add unrelated &&\n+\ttest_commit initial foo a &&\n+\ttest_commit base foo b &&\n+\ttest_commit unrelatedpick unrelated reallyunrelated &&\n+\ttest_commit picked foo c &&\n+\ttest_commit anotherpick foo d &&\n+\tgit config advice.detachedhead false\n+\n+'\n+\n+test_expect_success 'cherry-pick persists data on failure' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..anotherpick &&\n+\ttest_path_is_dir .git/sequencer &&\n+\ttest_path_is_file .git/sequencer/head &&\n+\ttest_path_is_file .git/sequencer/todo\n+'\n+\n+test_expect_success 'cherry-pick cleans up sequencer state upon success' '\n+\tpristine_detach initial &&\n+\tgit cherry-pick initial..picked &&\n+\ttest_path_is_missing .git/sequencer\n+'\n+\n+test_done\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172911","messageId":"1312454356-3070-12-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 11/18] revert: Save command-line options for continuing operation","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:09Z","receivedAt":"2011-08-04T10:39:09Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"In the same spirit as \".git/sequencer/head\" and \".git/sequencer/todo\",\nintroduce \".git/sequencer/opts\" to persist the replay_opts structure\nfor continuing after a conflict resolution.  Use the gitconfig format\nfor this file so that it looks like:\n\n  [options]\n\t  signoff = true\n\t  record-origin = true\n\t  mainline = 1\n\t  strategy = recursive\n\t  strategy-option = patience\n\t  strategy-option = ours\n\nHelped-by: Jonathan Nieder <jrnieder@gmail.com>\nHelped-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c                |   33 +++++++++++++++++++++++++++++++++\n t/t3510-cherry-pick-sequence.sh |   29 +++++++++++++++++++++++++++--\n 2 files changed, 60 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 54df8a2..308c1ce 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -64,6 +64,7 @@ struct replay_opts {\n #define SEQ_DIR         \"sequencer\"\n #define SEQ_HEAD_FILE   \"sequencer/head\"\n #define SEQ_TODO_FILE   \"sequencer/todo\"\n+#define SEQ_OPTS_FILE   \"sequencer/opts\"\n \n static const char *action_name(const struct replay_opts *opts)\n {\n@@ -695,6 +696,37 @@ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)\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_opts *opts)\n {\n \tstruct commit_list *todo_list = NULL;\n@@ -717,6 +749,7 @@ static int pick_commits(struct replay_opts *opts)\n \t\tdie(_(\"Can't cherry-pick into empty head\"));\n \t}\n \tsave_head(sha1_to_hex(sha1));\n+\tsave_opts(opts);\n \n \tfor (cur = todo_list; cur; cur = cur->next) {\n \t\tsave_todo(cur, opts);\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex a2c70ad..8ee0001 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -33,10 +33,35 @@ test_expect_success setup '\n \n test_expect_success 'cherry-pick persists data on failure' '\n \tpristine_detach initial &&\n-\ttest_must_fail git cherry-pick base..anotherpick &&\n+\ttest_must_fail git cherry-pick -s base..anotherpick &&\n \ttest_path_is_dir .git/sequencer &&\n \ttest_path_is_file .git/sequencer/head &&\n-\ttest_path_is_file .git/sequencer/todo\n+\ttest_path_is_file .git/sequencer/todo &&\n+\ttest_path_is_file .git/sequencer/opts\n+'\n+\n+test_expect_success 'cherry-pick persists opts correctly' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&\n+\ttest_path_is_dir .git/sequencer &&\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+\techo \"true\" >expect &&\n+\tgit config --file=.git/sequencer/opts --get-all options.signoff >actual &&\n+\ttest_cmp expect actual &&\n+\techo \"1\" >expect &&\n+\tgit config --file=.git/sequencer/opts --get-all options.mainline >actual &&\n+\ttest_cmp expect actual &&\n+\techo \"recursive\" >expect &&\n+\tgit config --file=.git/sequencer/opts --get-all options.strategy >actual &&\n+\ttest_cmp expect actual &&\n+\tcat >expect <<-\\EOF &&\n+\tpatience\n+\tours\n+\tEOF\n+\tgit config --file=.git/sequencer/opts --get-all options.strategy-option >actual &&\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'cherry-pick cleans up sequencer state upon success' '\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172913","messageId":"1312454356-3070-13-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 12/18] revert: Make pick_commits functionally act on a commit list","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:10Z","receivedAt":"2011-08-04T10:39:10Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Apart from its central objective of calling into the picking\nmechanism, pick_commits creates a sequencer directory, prepares a todo\nlist, and even acts upon the \"--reset\" subcommand.  This makes for a\nbad API since the central worry of callers is to figure out whether or\nnot any conflicts were encountered during the cherry picking.  The\ncurrent API is like:\n\n  if (pick_commits(opts) < 0)\n     print \"Something failed, we're not sure what\"\n\nSo, change pick_commits so that it's only responsible for picking\ncommits in a loop and reporting any errors, leaving the rest to a new\nfunction called pick_revisions.  Consequently, the API of pick_commits\nbecomes much clearer:\n\n  act_on_subcommand(opts->subcommand);\n  todo_list = prepare_todo_list();\n  if (pick_commits(todo_list, opts) < 0)\n     print \"Error encountered while picking commits\"\n\nNow, callers can easily call-in to the cherry-picking machinery by\nconstructing an arbitrary todo list along with some options.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c |   43 ++++++++++++++++++++++++++++---------------\n 1 files changed, 28 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 308c1ce..f088e1c 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -727,11 +727,9 @@ static void save_opts(struct replay_opts *opts)\n \t}\n }\n \n-static int pick_commits(struct replay_opts *opts)\n+static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n {\n-\tstruct commit_list *todo_list = NULL;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tunsigned char sha1[20];\n \tstruct commit_list *cur;\n \tint res;\n \n@@ -741,16 +739,6 @@ static int pick_commits(struct replay_opts *opts)\n \t\t\t\topts->record_origin || opts->edit));\n \tread_and_refresh_cache(opts);\n \n-\twalk_revs_populate_todo(&todo_list, opts);\n-\tcreate_seq_dir();\n-\tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REVERT)\n-\t\t\tdie(_(\"Can't revert as initial commit\"));\n-\t\tdie(_(\"Can't cherry-pick into empty head\"));\n-\t}\n-\tsave_head(sha1_to_hex(sha1));\n-\tsave_opts(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@@ -768,6 +756,31 @@ static int pick_commits(struct replay_opts *opts)\n \treturn 0;\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+\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+\twalk_revs_populate_todo(&todo_list, opts);\n+\tcreate_seq_dir();\n+\tif (get_sha1(\"HEAD\", sha1)) {\n+\t\tif (opts->action == REVERT)\n+\t\t\tdie(_(\"Can't revert as initial commit\"));\n+\t\tdie(_(\"Can't cherry-pick into empty head\"));\n+\t}\n+\tsave_head(sha1_to_hex(sha1));\n+\tsave_opts(opts);\n+\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@@ -778,7 +791,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \topts.action = REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\treturn pick_commits(&opts);\n+\treturn pick_revisions(&opts);\n }\n \n int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n@@ -789,5 +802,5 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n \topts.action = CHERRY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\treturn pick_commits(&opts);\n+\treturn pick_revisions(&opts);\n }\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172914","messageId":"1312454356-3070-14-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 13/18] revert: Introduce --reset to remove sequencer state","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:11Z","receivedAt":"2011-08-04T10:39:11Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"To explicitly remove the sequencer state for a fresh cherry-pick or\nrevert invocation, introduce a new subcommand called \"--reset\" to\nremove the sequencer state.\n\nTake the opportunity to publicly expose the sequencer paths, and a\ngeneric function called \"remove_sequencer_state\" that various git\nprograms can use to remove the sequencer state in a uniform manner;\n\"git reset\" uses it later in this series.  Introducing this public API\nis also in line with our long-term goal of eventually factoring out\nfunctions from revert.c into a generic commit sequencer.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Documentation/git-cherry-pick.txt |    5 +++\n Documentation/git-revert.txt      |    5 +++\n Documentation/sequencer.txt       |    4 ++\n Makefile                          |    2 +\n builtin/revert.c                  |   62 +++++++++++++++++++++++++-----------\n sequencer.c                       |   19 +++++++++++\n sequencer.h                       |   20 ++++++++++++\n t/t3510-cherry-pick-sequence.sh   |   14 ++++++++-\n 8 files changed, 111 insertions(+), 20 deletions(-)\n create mode 100644 Documentation/sequencer.txt\n create mode 100644 sequencer.c\n create mode 100644 sequencer.h\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex 6c9c2cb..12a85ba 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...\n+'git cherry-pick' --reset\n \n DESCRIPTION\n -----------\n@@ -110,6 +111,10 @@ effect to your index in a row.\n \tPass the merge strategy-specific option through to the\n \tmerge strategy.  See linkgit:git-merge[1] for details.\n \n+SEQUENCER SUBCOMMANDS\n+---------------------\n+include::sequencer.txt[]\n+\n EXAMPLES\n --------\n git cherry-pick master::\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 3d0a7d1..31c85b4 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...\n+'git revert' --reset\n \n DESCRIPTION\n -----------\n@@ -91,6 +92,10 @@ effect to your index in a row.\n \tPass the merge strategy-specific option through to the\n \tmerge strategy.  See linkgit:git-merge[1] for details.\n \n+SEQUENCER SUBCOMMANDS\n+---------------------\n+include::sequencer.txt[]\n+\n EXAMPLES\n --------\n git revert HEAD~3::\ndiff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt\nnew file mode 100644\nindex 0000000..16ce88c\n--- /dev/null\n+++ b/Documentation/sequencer.txt\n@@ -0,0 +1,4 @@\n+--reset::\n+\tForget about the current operation in progress.  Can be used\n+\tto clear the sequencer state after a failed cherry-pick or\n+\trevert.\ndiff --git a/Makefile b/Makefile\nindex 4ed7996..afd7673 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -554,6 +554,7 @@ LIB_H += rerere.h\n LIB_H += resolve-undo.h\n LIB_H += revision.h\n LIB_H += run-command.h\n+LIB_H += sequencer.h\n LIB_H += sha1-array.h\n LIB_H += sha1-lookup.h\n LIB_H += sideband.h\n@@ -658,6 +659,7 @@ LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\n LIB_OBJS += server-info.o\n LIB_OBJS += setup.o\n+LIB_OBJS += sequencer.o\n LIB_OBJS += sha1-array.o\n LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1_file.o\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex f088e1c..a8accd6 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -14,6 +14,7 @@\n #include \"merge-recursive.h\"\n #include \"refs.h\"\n #include \"dir.h\"\n+#include \"sequencer.h\"\n \n /*\n  * This implements the builtins revert and cherry-pick.\n@@ -28,18 +29,22 @@\n \n static const char * const revert_usage[] = {\n \t\"git revert [options] <commit-ish>\",\n+\t\"git revert <subcommand>\",\n \tNULL\n };\n \n static const char * const cherry_pick_usage[] = {\n \t\"git cherry-pick [options] <commit-ish>\",\n+\t\"git cherry-pick <subcommand>\",\n \tNULL\n };\n \n enum replay_action { REVERT, CHERRY_PICK };\n+enum replay_subcommand { REPLAY_NONE, REPLAY_RESET };\n \n struct replay_opts {\n \tenum replay_action action;\n+\tenum replay_subcommand subcommand;\n \n \t/* Boolean options */\n \tint edit;\n@@ -61,11 +66,6 @@ struct replay_opts {\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n-#define SEQ_DIR         \"sequencer\"\n-#define SEQ_HEAD_FILE   \"sequencer/head\"\n-#define SEQ_TODO_FILE   \"sequencer/todo\"\n-#define SEQ_OPTS_FILE   \"sequencer/opts\"\n-\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n@@ -113,7 +113,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n \tint noop;\n+\tint reset = 0;\n \tstruct option options[] = {\n+\t\tOPT_BOOLEAN(0, \"reset\", &reset, \"forget the current operation\"),\n \t\tOPT_BOOLEAN('n', \"no-commit\", &opts->no_commit, \"don't automatically commit\"),\n \t\tOPT_BOOLEAN('e', \"edit\", &opts->edit, \"edit the commit message\"),\n \t\t{ OPTION_BOOLEAN, 'r', NULL, &noop, NULL, \"no-op (backward compatibility)\",\n@@ -142,7 +144,27 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \topts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,\n \t\t\t\t\tPARSE_OPT_KEEP_ARGV0 |\n \t\t\t\t\tPARSE_OPT_KEEP_UNKNOWN);\n-\tif (opts->commit_argc < 2)\n+\n+\t/* Set the subcommand */\n+\tif (reset)\n+\t\topts->subcommand = REPLAY_RESET;\n+\telse\n+\t\topts->subcommand = REPLAY_NONE;\n+\n+\t/* Check for incompatible command line arguments */\n+\tif (opts->subcommand == REPLAY_RESET) {\n+\t\tverify_opt_compatible(me, \"--reset\",\n+\t\t\t\t\"--no-commit\", opts->no_commit,\n+\t\t\t\t\"--signoff\", opts->signoff,\n+\t\t\t\t\"--mainline\", opts->mainline,\n+\t\t\t\t\"--strategy\", opts->strategy ? 1 : 0,\n+\t\t\t\t\"--strategy-option\", opts->xopts ? 1 : 0,\n+\t\t\t\t\"-x\", opts->record_origin,\n+\t\t\t\t\"--ff\", opts->allow_ff,\n+\t\t\t\tNULL);\n+\t}\n+\n+\telse if (opts->commit_argc < 2)\n \t\tusage_with_options(usage_str, options);\n \n \tif (opts->allow_ff)\n@@ -729,7 +751,6 @@ static void save_opts(struct replay_opts *opts)\n \n static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct commit_list *cur;\n \tint res;\n \n@@ -750,9 +771,7 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\n \t * Sequence of picks finished successfully; cleanup by\n \t * removing the .git/sequencer directory\n \t */\n-\tstrbuf_addf(&buf, \"%s\", git_path(SEQ_DIR));\n-\tremove_dir_recursively(&buf, 0);\n-\tstrbuf_release(&buf);\n+\tremove_sequencer_state(1);\n \treturn 0;\n }\n \n@@ -768,16 +787,21 @@ static int pick_revisions(struct replay_opts *opts)\n \t * cherry-pick should be handled differently from an existing\n \t * one that is being continued\n \t */\n-\twalk_revs_populate_todo(&todo_list, opts);\n-\tcreate_seq_dir();\n-\tif (get_sha1(\"HEAD\", sha1)) {\n-\t\tif (opts->action == REVERT)\n-\t\t\tdie(_(\"Can't revert as initial commit\"));\n-\t\tdie(_(\"Can't cherry-pick into empty head\"));\n+\tif (opts->subcommand == REPLAY_RESET) {\n+\t\tremove_sequencer_state(1);\n+\t\treturn 0;\n+\t} else {\n+\t\t/* Start a new cherry-pick/ revert sequence */\n+\t\twalk_revs_populate_todo(&todo_list, opts);\n+\t\tcreate_seq_dir();\n+\t\tif (get_sha1(\"HEAD\", sha1)) {\n+\t\t\tif (opts->action == REVERT)\n+\t\t\t\tdie(_(\"Can't revert as initial commit\"));\n+\t\t\tdie(_(\"Can't cherry-pick into empty head\"));\n+\t\t}\n+\t\tsave_head(sha1_to_hex(sha1));\n+\t\tsave_opts(opts);\n \t}\n-\tsave_head(sha1_to_hex(sha1));\n-\tsave_opts(opts);\n-\n \treturn pick_commits(todo_list, opts);\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nnew file mode 100644\nindex 0000000..bc2c046\n--- /dev/null\n+++ b/sequencer.c\n@@ -0,0 +1,19 @@\n+#include \"cache.h\"\n+#include \"sequencer.h\"\n+#include \"strbuf.h\"\n+#include \"dir.h\"\n+\n+void remove_sequencer_state(int aggressive)\n+{\n+\tstruct strbuf seq_dir = STRBUF_INIT;\n+\tstruct strbuf seq_old_dir = STRBUF_INIT;\n+\n+\tstrbuf_addf(&seq_dir, \"%s\", git_path(SEQ_DIR));\n+\tstrbuf_addf(&seq_old_dir, \"%s\", git_path(SEQ_OLD_DIR));\n+\tremove_dir_recursively(&seq_old_dir, 0);\n+\trename(git_path(SEQ_DIR), git_path(SEQ_OLD_DIR));\n+\tif (aggressive)\n+\t\tremove_dir_recursively(&seq_old_dir, 0);\n+\tstrbuf_release(&seq_dir);\n+\tstrbuf_release(&seq_old_dir);\n+}\ndiff --git a/sequencer.h b/sequencer.h\nnew file mode 100644\nindex 0000000..905d295\n--- /dev/null\n+++ b/sequencer.h\n@@ -0,0 +1,20 @@\n+#ifndef SEQUENCER_H\n+#define SEQUENCER_H\n+\n+#define SEQ_DIR\t\t\"sequencer\"\n+#define SEQ_OLD_DIR\t\"sequencer-old\"\n+#define SEQ_HEAD_FILE\t\"sequencer/head\"\n+#define SEQ_TODO_FILE\t\"sequencer/todo\"\n+#define SEQ_OPTS_FILE\t\"sequencer/opts\"\n+\n+/*\n+ * Removes SEQ_OLD_DIR and renames SEQ_DIR to SEQ_OLD_DIR, ignoring\n+ * any errors.  Intended to be used by 'git reset'.\n+ *\n+ * With the aggressive flag, it additionally removes SEQ_OLD_DIR,\n+ * ignoring any errors.  Inteded to be used by the sequencer's\n+ * '--reset' subcommand.\n+ */\n+void remove_sequencer_state(int aggressive);\n+\n+#endif\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 8ee0001..fd69865 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -13,7 +13,7 @@ test_description='Test cherry-pick continuation features\n . ./test-lib.sh\n \n pristine_detach () {\n-\trm -rf .git/sequencer &&\n+\tgit cherry-pick --reset &&\n \tgit checkout -f \"$1^0\" &&\n \tgit read-tree -u --reset HEAD &&\n \tgit clean -d -f -f -q -x\n@@ -70,4 +70,16 @@ test_expect_success 'cherry-pick cleans up sequencer state upon success' '\n \ttest_path_is_missing .git/sequencer\n '\n \n+test_expect_success '--reset does not complain when no cherry-pick is in progress' '\n+\tpristine_detach initial &&\n+\tgit cherry-pick --reset\n+'\n+\n+test_expect_success '--reset cleans up sequencer state' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..picked &&\n+\tgit cherry-pick --reset &&\n+\ttest_path_is_missing .git/sequencer\n+'\n+\n test_done\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172915","messageId":"1312454356-3070-15-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 14/18] reset: Make reset remove the sequencer state","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:12Z","receivedAt":"2011-08-04T10:39:12Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Years of muscle memory have trained users to use \"git reset --hard\" to\nremove the branch state after any sort operation.  Make it also remove\nthe sequencer state to facilitate this established workflow:\n\n  $ git cherry-pick foo..bar\n  ... conflict encountered ...\n  $ git reset --hard # Oops, I didn't mean that\n  $ git cherry-pick quux..bar\n  ... cherry-pick succeeded ...\n\nGuard against accidental removal of the sequencer state by providing\none level of \"undo\".  In the first \"reset\" invocation,\n\".git/sequencer\" is moved to \".git/sequencer-old\"; it is completely\nremoved only in the second invocation.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n branch.c                 |    2 ++\n t/7106-reset-sequence.sh |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 46 insertions(+), 0 deletions(-)\n create mode 100755 t/7106-reset-sequence.sh\n\ndiff --git a/branch.c b/branch.c\nindex c0c865a..d06aec4 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -3,6 +3,7 @@\n #include \"refs.h\"\n #include \"remote.h\"\n #include \"commit.h\"\n+#include \"sequencer.h\"\n \n struct tracking {\n \tstruct refspec spec;\n@@ -228,4 +229,5 @@ void remove_branch_state(void)\n \tunlink(git_path(\"MERGE_MSG\"));\n \tunlink(git_path(\"MERGE_MODE\"));\n \tunlink(git_path(\"SQUASH_MSG\"));\n+\tremove_sequencer_state(0);\n }\ndiff --git a/t/7106-reset-sequence.sh b/t/7106-reset-sequence.sh\nnew file mode 100755\nindex 0000000..4956caa\n--- /dev/null\n+++ b/t/7106-reset-sequence.sh\n@@ -0,0 +1,44 @@\n+#!/bin/sh\n+\n+test_description='Test interaction of reset --hard with sequencer\n+\n+  + anotherpick: rewrites foo to d\n+  + picked: rewrites foo to c\n+  + unrelatedpick: rewrites unrelated to reallyunrelated\n+  + base: rewrites foo to b\n+  + initial: writes foo as a, unrelated as unrelated\n+'\n+\n+. ./test-lib.sh\n+\n+pristine_detach () {\n+\tgit cherry-pick --reset &&\n+\tgit checkout -f \"$1^0\" &&\n+\tgit read-tree -u --reset HEAD &&\n+\tgit clean -d -f -f -q -x\n+}\n+\n+test_expect_success setup '\n+\techo unrelated >unrelated &&\n+\tgit add unrelated &&\n+\ttest_commit initial foo a &&\n+\ttest_commit base foo b &&\n+\ttest_commit unrelatedpick unrelated reallyunrelated &&\n+\ttest_commit picked foo c &&\n+\ttest_commit anotherpick foo d &&\n+\tgit config advice.detachedhead false\n+\n+'\n+\n+test_expect_success 'reset --hard cleans up sequencer state, providing one-level undo' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..anotherpick &&\n+\ttest_path_is_dir .git/sequencer &&\n+\tgit reset --hard &&\n+\ttest_path_is_missing .git/sequencer &&\n+\ttest_path_is_dir .git/sequencer-old &&\n+\tgit reset --hard &&\n+\ttest_path_is_missing .git/sequencer-old\n+'\n+\n+test_done\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172916","messageId":"1312454356-3070-16-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 15/18] revert: Remove sequencer state when no commits are pending","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:13Z","receivedAt":"2011-08-04T10:39:13Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"When cherry-pick or revert is called on a list of commits, and a\nconflict encountered somewhere in the middle, the data in\n\".git/sequencer\" is required to continue the operation.  However, when\na conflict is encountered in the very last commit, the user will have\nto \"continue\" after resolving the conflict and committing just so that\nthe sequencer state is removed.  This is how the current \"rebase -i\"\nscript works as well.\n\n  $ git cherry-pick foo..bar\n  ... conflict encountered while picking \"bar\" ...\n  $ echo \"resolved\" >problematicfile\n  $ git add problematicfile\n  $ git commit\n  $ git cherry-pick --continue # This would be a no-op\n\nChange this so that the sequencer state is cleared when a conflict is\nencountered in the last commit.  Incidentally, this patch makes sure\nthat some existing tests don't break when features like \"--reset\" and\n\"--continue\" are implemented later in the series.\n\nA better way to implement this feature is to get the last \"git commit\"\nto remove the sequencer state.  However, that requires tighter\ncoupling between \"git commit\" and the sequencer, a goal that can be\npursued once the sequencer is made more general.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nAcked-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c                |   12 +++++++++++-\n t/t3510-cherry-pick-sequence.sh |   24 ++++++++++++++++++++++++\n 2 files changed, 35 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex a8accd6..000806c 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -763,8 +763,18 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)\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\tif (res) {\n+\t\t\tif (!cur->next)\n+\t\t\t\t/*\n+\t\t\t\t * An error was encountered while\n+\t\t\t\t * picking the last commit; the\n+\t\t\t\t * sequencer state is useless now --\n+\t\t\t\t * the user simply needs to resolve\n+\t\t\t\t * the conflict and commit\n+\t\t\t\t */\n+\t\t\t\tremove_sequencer_state(0);\n \t\t\treturn res;\n+\t\t}\n \t}\n \n \t/*\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex fd69865..a414086 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -82,4 +82,28 @@ test_expect_success '--reset cleans up sequencer state' '\n \ttest_path_is_missing .git/sequencer\n '\n \n+test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..picked &&\n+\ttest_path_is_missing .git/sequencer &&\n+\techo \"resolved\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\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\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.6.351.gb35ac.dirty\n"},{"id":"172917","messageId":"1312454356-3070-17-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 16/18] revert: Don't implicitly stomp pending sequencer operation","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:14Z","receivedAt":"2011-08-04T10:39:14Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Protect the user from forgetting about a pending sequencer operation\nby immediately erroring out when an existing cherry-pick or revert\noperation is in progress like:\n\n  $ git cherry-pick foo\n  ... conflict ...\n  $ git cherry-pick moo\n  error: .git/sequencer already exists\n  hint: A cherry-pick or revert is in progress\n  hint: Use --reset to forget about it\n  fatal: cherry-pick failed\n\nA naive version of this would break the following established ways of\nworking:\n\n  $ git cherry-pick foo\n  ... conflict ...\n  $ git reset --hard  # I actually meant \"moo\" when I said \"foo\"\n  $ git cherry-pick moo\n\n  $ git cherry-pick foo\n  ... conflict ...\n  $ git commit # commit the resolution\n  $ git cherry-pick moo # New operation\n\nHowever, the previous patches \"reset: Make reset remove the sequencer\nstate\" and \"revert: Remove sequencer state when no commits are\npending\" make sure that this does not happen.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c                |   30 +++++++++++++++++++++++++-----\n t/t3510-cherry-pick-sequence.sh |    9 +++++++++\n 2 files changed, 34 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 000806c..19b6739 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -66,6 +66,15 @@ struct replay_opts {\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n+static void fatal(const char *advice, ...)\n+{\n+\tva_list params;\n+\n+\tva_start(params, advice);\n+\tvreportf(\"fatal: \", advice, params);\n+\tva_end(params);\n+}\n+\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n@@ -673,13 +682,15 @@ static void walk_revs_populate_todo(struct commit_list **todo_list,\n \t\tnext = commit_list_append(commit, next);\n }\n \n-static void create_seq_dir(void)\n+static int create_seq_dir(void)\n {\n \tconst char *seq_dir = git_path(SEQ_DIR);\n \n-\tif (!(file_exists(seq_dir) && is_directory(seq_dir))\n-\t\t&& mkdir(seq_dir, 0777) < 0)\n+\tif (file_exists(seq_dir))\n+\t\treturn error(_(\"%s already exists.\"), seq_dir);\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@@ -801,9 +812,18 @@ static int pick_revisions(struct replay_opts *opts)\n \t\tremove_sequencer_state(1);\n \t\treturn 0;\n \t} else {\n-\t\t/* Start a new cherry-pick/ revert sequence */\n+\t\t/*\n+\t\t * Start a new cherry-pick/ revert sequence; but\n+\t\t * first, make sure that an existing one isn't in\n+\t\t * progress\n+\t\t */\n+\n \t\twalk_revs_populate_todo(&todo_list, opts);\n-\t\tcreate_seq_dir();\n+\t\tif (create_seq_dir() < 0) {\n+\t\t\tfatal(_(\"A cherry-pick or revert is in progress.\"));\n+\t\t\tadvise(_(\"Use --reset to forget about it\"));\n+\t\t\texit(128);\n+\t\t}\n \t\tif (get_sha1(\"HEAD\", sha1)) {\n \t\t\tif (opts->action == REVERT)\n \t\t\t\tdie(_(\"Can't revert as initial commit\"));\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex a414086..566a15e 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -106,4 +106,13 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..anotherpick &&\n+\ttest-chmtime -v +0 .git/sequencer >expect &&\n+\ttest_must_fail git cherry-pick unrelatedpick &&\n+\ttest-chmtime -v +0 .git/sequencer >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172919","messageId":"1312454356-3070-18-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 17/18] revert: Introduce --continue to continue the operation","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:15Z","receivedAt":"2011-08-04T10:39:15Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Introduce a new \"git cherry-pick --continue\" command which uses the\ninformation in \".git/sequencer\" to continue a cherry-pick that stopped\nbecause of a conflict or other error.  It works by dropping the first\ninstruction from .git/sequencer/todo and performing the remaining\ncherry-picks listed there, with options (think \"-s\" and \"-X\") from the\ninitial command listed in \".git/sequencer/opts\".\n\nSo now you can do:\n\n  $ git cherry-pick -Xpatience foo..bar\n  ... description conflict in commit moo ...\n  $ git cherry-pick --continue\n  error: 'cherry-pick' is not possible because you have unmerged files.\n  fatal: failed to resume cherry-pick\n  $ echo resolved >conflictingfile\n  $ git add conflictingfile && git commit\n  $ git cherry-pick --continue; # resumes with the commit after \"moo\"\n\nDuring the \"git commit\" stage, CHERRY_PICK_HEAD will aid by providing\nthe commit message from the conflicting \"moo\" commit.  Note that the\ncherry-pick mechanism has no control at this stage, so the user is\nfree to violate anything that was specified during the first\ncherry-pick invocation.  For example, if \"-x\" was specified during the\nfirst cherry-pick invocation, the user is free to edit out the message\nduring commit time.  Note that the \"--signoff\" option specified at\ncherry-pick invocation time is not reflected in the commit message\nprovided by CHERRY_PICK_HEAD; the user must take care to add\n\"--signoff\" during the \"git commit\" invocation.\n\nHelped-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Documentation/git-cherry-pick.txt |    1 +\n Documentation/git-revert.txt      |    1 +\n Documentation/sequencer.txt       |    5 +\n builtin/revert.c                  |  188 ++++++++++++++++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   |   96 +++++++++++++++++++\n 5 files changed, 287 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex 12a85ba..b4a2d74 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...\n 'git cherry-pick' --reset\n+'git cherry-pick' --continue\n \n DESCRIPTION\n -----------\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 31c85b4..68f8e68 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -10,6 +10,7 @@ SYNOPSIS\n [verse]\n 'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...\n 'git revert' --reset\n+'git revert' --continue\n \n DESCRIPTION\n -----------\ndiff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt\nindex 16ce88c..3e6df33 100644\n--- a/Documentation/sequencer.txt\n+++ b/Documentation/sequencer.txt\n@@ -2,3 +2,8 @@\n \tForget about the current operation in progress.  Can be used\n \tto clear the sequencer state after a failed cherry-pick or\n \trevert.\n+\n+--continue::\n+\tContinue the operation in progress using the information in\n+\t'.git/sequencer'.  Can be used to continue after resolving\n+\tconflicts in a failed cherry-pick or revert.\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 19b6739..4f0d8f1 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 enum replay_action { REVERT, CHERRY_PICK };\n-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET };\n+enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };\n \n struct replay_opts {\n \tenum replay_action action;\n@@ -117,14 +117,37 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)\n \t\tdie(_(\"%s: %s cannot be used with %s\"), me, this_opt, base_opt);\n }\n \n+static void verify_opt_mutually_compatible(const char *me, ...)\n+{\n+\tconst char *opt1, *opt2;\n+\tva_list ap;\n+\n+\tva_start(ap, me);\n+\twhile ((opt1 = va_arg(ap, const char *))) {\n+\t\tif (va_arg(ap, int))\n+\t\t\tbreak;\n+\t}\n+\tif (opt1) {\n+\t\twhile ((opt2 = va_arg(ap, const char *))) {\n+\t\t\tif (va_arg(ap, int))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (opt1 && opt2)\n+\t\tdie(_(\"%s: %s cannot be used with %s\"),\tme, opt1, opt2);\n+}\n+\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 \tint noop;\n \tint reset = 0;\n+\tint contin = 0;\n \tstruct option options[] = {\n \t\tOPT_BOOLEAN(0, \"reset\", &reset, \"forget the current operation\"),\n+\t\tOPT_BOOLEAN(0, \"continue\", &contin, \"continue the current operation\"),\n \t\tOPT_BOOLEAN('n', \"no-commit\", &opts->no_commit, \"don't automatically commit\"),\n \t\tOPT_BOOLEAN('e', \"edit\", &opts->edit, \"edit the commit message\"),\n \t\t{ OPTION_BOOLEAN, 'r', NULL, &noop, NULL, \"no-op (backward compatibility)\",\n@@ -154,15 +177,29 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)\n \t\t\t\t\tPARSE_OPT_KEEP_ARGV0 |\n \t\t\t\t\tPARSE_OPT_KEEP_UNKNOWN);\n \n+\t/* Check for incompatible subcommands */\n+\tverify_opt_mutually_compatible(me,\n+\t\t\t\t\"--reset\", reset,\n+\t\t\t\t\"--continue\", contin,\n+\t\t\t\tNULL);\n+\n \t/* Set the subcommand */\n \tif (reset)\n \t\topts->subcommand = REPLAY_RESET;\n+\telse if (contin)\n+\t\topts->subcommand = REPLAY_CONTINUE;\n \telse\n \t\topts->subcommand = REPLAY_NONE;\n \n \t/* Check for incompatible command line arguments */\n-\tif (opts->subcommand == REPLAY_RESET) {\n-\t\tverify_opt_compatible(me, \"--reset\",\n+\tif (opts->subcommand != REPLAY_NONE) {\n+\t\tchar *this_operation;\n+\t\tif (opts->subcommand == REPLAY_RESET)\n+\t\t\tthis_operation = \"--reset\";\n+\t\telse\n+\t\t\tthis_operation = \"--continue\";\n+\n+\t\tverify_opt_compatible(me, this_operation,\n \t\t\t\t\"--no-commit\", opts->no_commit,\n \t\t\t\t\"--signoff\", opts->signoff,\n \t\t\t\t\"--mainline\", opts->mainline,\n@@ -668,6 +705,137 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,\n \treturn 0;\n }\n \n+static struct commit *parse_insn_line(char *start, struct replay_opts *opts)\n+{\n+\tunsigned char commit_sha1[20];\n+\tchar sha1_abbrev[40];\n+\tenum replay_action action;\n+\tint insn_len = 0;\n+\tchar *p, *q;\n+\n+\tif (!prefixcmp(start, \"pick \")) {\n+\t\taction = CHERRY_PICK;\n+\t\tinsn_len = strlen(\"pick\");\n+\t\tp = start + insn_len + 1;\n+\t} else if (!prefixcmp(start, \"revert \")) {\n+\t\taction = REVERT;\n+\t\tinsn_len = strlen(\"revert\");\n+\t\tp = start + insn_len + 1;\n+\t} else\n+\t\treturn NULL;\n+\n+\tq = strchr(p, ' ');\n+\tif (!q)\n+\t\treturn NULL;\n+\tq++;\n+\n+\tstrlcpy(sha1_abbrev, p, q - p);\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 == 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 (get_sha1(sha1_abbrev, commit_sha1) < 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\tcommit = parse_insn_line(p, 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 = strchrnul(p, '\\n');\n+\t\tif (*p)\n+\t\t\tp++;\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@@ -811,6 +979,15 @@ static int pick_revisions(struct replay_opts *opts)\n \tif (opts->subcommand == REPLAY_RESET) {\n \t\tremove_sequencer_state(1);\n \t\treturn 0;\n+\t} else if (opts->subcommand == REPLAY_CONTINUE) {\n+\t\tif (!file_exists(git_path(SEQ_TODO_FILE)))\n+\t\t\tgoto error;\n+\t\tread_populate_opts(&opts);\n+\t\tread_populate_todo(&todo_list, opts);\n+\n+\t\t/* Verify that the conflict has been resolved */\n+\t\tif (!index_differs_from(\"HEAD\", 0))\n+\t\t\ttodo_list = todo_list->next;\n \t} else {\n \t\t/*\n \t\t * Start a new cherry-pick/ revert sequence; but\n@@ -821,7 +998,8 @@ static int pick_revisions(struct replay_opts *opts)\n \t\twalk_revs_populate_todo(&todo_list, opts);\n \t\tif (create_seq_dir() < 0) {\n \t\t\tfatal(_(\"A cherry-pick or revert is in progress.\"));\n-\t\t\tadvise(_(\"Use --reset to forget about it\"));\n+\t\t\tadvise(_(\"Use --continue to continue the operation\"));\n+\t\t\tadvise(_(\"or --reset to forget about it\"));\n \t\t\texit(128);\n \t\t}\n \t\tif (get_sha1(\"HEAD\", sha1)) {\n@@ -833,6 +1011,8 @@ static int pick_revisions(struct replay_opts *opts)\n \t\tsave_opts(opts);\n \t}\n \treturn pick_commits(todo_list, opts);\n+error:\n+\tdie(_(\"No %s in progress\"), action_name(opts));\n }\n \n int cmd_revert(int argc, const char **argv, const char *prefix)\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 566a15e..78f3f01 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -115,4 +115,100 @@ test_expect_success 'cherry-pick does not implicitly stomp an existing operation\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--continue complains when no cherry-pick is in progress' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick --continue\n+'\n+\n+test_expect_success '--continue complains when there are unresolved conflicts' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..anotherpick &&\n+\ttest_must_fail git cherry-pick --continue\n+'\n+\n+test_expect_success '--continue continues after conflicts are resolved' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..anotherpick &&\n+\techo \"c\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\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\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_expect_success '--continue respects opts' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick -x base..anotherpick &&\n+\techo \"c\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\n+\tgit cherry-pick --continue &&\n+\ttest_path_is_missing .git/sequencer &&\n+\tgit cat-file commit HEAD >anotherpick_msg &&\n+\tgit cat-file commit HEAD~1 >picked_msg &&\n+\tgit cat-file commit HEAD~2 >unrelatedpick_msg &&\n+\tgit cat-file commit HEAD~3 >initial_msg &&\n+\ttest_must_fail grep \"cherry picked from\" initial_msg &&\n+\tgrep \"cherry picked from\" unrelatedpick_msg &&\n+\tgrep \"cherry picked from\" picked_msg &&\n+\tgrep \"cherry picked from\" anotherpick_msg\n+'\n+\n+test_expect_success '--signoff is not automatically propogated to resolved conflict' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick --signoff base..anotherpick &&\n+\techo \"c\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\n+\tgit cherry-pick --continue &&\n+\ttest_path_is_missing .git/sequencer &&\n+\tgit cat-file commit HEAD >anotherpick_msg &&\n+\tgit cat-file commit HEAD~1 >picked_msg &&\n+\tgit cat-file commit HEAD~2 >unrelatedpick_msg &&\n+\tgit cat-file commit HEAD~3 >initial_msg &&\n+\ttest_must_fail grep \"Signed-off-by:\" initial_msg &&\n+\tgrep \"Signed-off-by:\" unrelatedpick_msg &&\n+\ttest_must_fail grep \"Signed-off-by:\" picked_msg &&\n+\tgrep \"Signed-off-by:\" anotherpick_msg\n+'\n+\n+test_expect_success 'malformed instruction sheet 1' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick base..anotherpick &&\n+\techo \"resolved\" >foo &&\n+\tgit add foo &&\n+\tgit commit &&\n+\tsed \"s/pick /pick/\" .git/sequencer/todo >new_sheet &&\n+\tcp new_sheet .git/sequencer/todo &&\n+\ttest_must_fail git cherry-pick --continue\n+'\n+\n+test_expect_success 'malformed instruction sheet 2' '\n+\tpristine_detach initial &&\n+\ttest_must_fail 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_must_fail git cherry-pick --continue\n+'\n+\n test_done\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"172918","messageId":"1312454356-3070-19-git-send-email-artagnon@gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 18/18] revert: Propagate errors upwards from do_pick_commit","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-04T10:39:16Z","receivedAt":"2011-08-04T10:39:16Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Currently, revert_or_cherry_pick can fail in two ways.  If it\nencounters a conflict, it returns a positive number indicating the\nintended exit status for the git wrapper to pass on; for all other\nerrors, it calls die().  The latter behavior is inconsiderate towards\ncallers, as it denies them the opportunity to recover from errors and\ndo other things.\n\nAfter this patch, revert_or_cherry_pick will still return a positive\nreturn value to indicate an exit status for conflicts as before, while\nfor some other errors, it will print an error message and return -1\ninstead of die()-ing.  The cmd_revert and cmd_cherry_pick are adjusted\nto handle the fatal errors by die()-ing themselves.\n\nWhile the full benefits of this patch will only be seen once all the\n\"die\" calls are replaced with calls to \"error\", its immediate impact\nis to change some \"fatal:\" messages to say \"error:\" and to add a new\n\"fatal: cherry-pick failed\" message at the end when the operation\nfails.\n\nInspired-by: Christian Couder <chriscool@tuxfamily.org>\nMentored-by: Jonathan Nieder <jrnieder@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n builtin/revert.c |   86 +++++++++++++++++++++++++-----------------------------\n 1 files changed, 40 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 4f0d8f1..8b452e8 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -66,15 +66,6 @@ struct replay_opts {\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n-static void fatal(const char *advice, ...)\n-{\n-\tva_list params;\n-\n-\tva_start(params, advice);\n-\tvreportf(\"fatal: \", advice, params);\n-\tva_end(params);\n-}\n-\n static const char *action_name(const struct replay_opts *opts)\n {\n \treturn opts->action == REVERT ? \"revert\" : \"cherry-pick\";\n@@ -356,25 +347,20 @@ static struct tree *empty_tree(void)\n \treturn tree;\n }\n \n-static NORETURN void die_dirty_index(struct replay_opts *opts)\n+static int error_dirty_index(struct replay_opts *opts)\n {\n-\tif (read_cache_unmerged()) {\n-\t\tdie_resolve_conflict(action_name(opts));\n-\t} else {\n-\t\tif (advice_commit_before_merge) {\n-\t\t\tif (opts->action == REVERT)\n-\t\t\t\tdie(_(\"Your local changes would be overwritten by revert.\\n\"\n-\t\t\t\t\t  \"Please, commit your changes or stash them to proceed.\"));\n-\t\t\telse\n-\t\t\t\tdie(_(\"Your local changes would be overwritten by cherry-pick.\\n\"\n-\t\t\t\t\t  \"Please, commit your changes or stash them to proceed.\"));\n-\t\t} else {\n-\t\t\tif (opts->action == REVERT)\n-\t\t\t\tdie(_(\"Your local changes would be overwritten by revert.\\n\"));\n-\t\t\telse\n-\t\t\t\tdie(_(\"Your local changes would be overwritten by cherry-pick.\\n\"));\n-\t\t}\n-\t}\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 == 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@@ -492,9 +478,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\t\tdie (_(\"Your index file is unmerged.\"));\n \t} else {\n \t\tif (get_sha1(\"HEAD\", head))\n-\t\t\tdie (_(\"You do not have a valid HEAD\"));\n+\t\t\treturn error(_(\"You do not have a valid HEAD\"));\n \t\tif (index_differs_from(\"HEAD\", 0))\n-\t\t\tdie_dirty_index(opts);\n+\t\t\treturn error_dirty_index(opts);\n \t}\n \tdiscard_cache();\n \n@@ -507,20 +493,20 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \t\tstruct commit_list *p;\n \n \t\tif (!opts->mainline)\n-\t\t\tdie(_(\"Commit %s is a merge but no -m option was given.\"),\n-\t\t\t    sha1_to_hex(commit->object.sha1));\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\tdie(_(\"Commit %s does not have parent %d\"),\n-\t\t\t    sha1_to_hex(commit->object.sha1), opts->mainline);\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\tdie(_(\"Mainline was specified but commit %s is not a merge.\"),\n-\t\t    sha1_to_hex(commit->object.sha1));\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@@ -530,12 +516,12 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\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\tdie(_(\"%s: cannot parse parent commit %s\"),\n-\t\t    action_name(opts), sha1_to_hex(parent->object.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\tdie(_(\"Cannot get commit message for %s\"),\n-\t\t\t\tsha1_to_hex(commit->object.sha1));\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@@ -997,27 +983,28 @@ static int pick_revisions(struct replay_opts *opts)\n \n \t\twalk_revs_populate_todo(&todo_list, opts);\n \t\tif (create_seq_dir() < 0) {\n-\t\t\tfatal(_(\"A cherry-pick or revert is in progress.\"));\n+\t\t\terror(_(\"A cherry-pick or revert is in progress.\"));\n \t\t\tadvise(_(\"Use --continue to continue the operation\"));\n \t\t\tadvise(_(\"or --reset to forget about it\"));\n-\t\t\texit(128);\n+\t\t\treturn -1;\n \t\t}\n \t\tif (get_sha1(\"HEAD\", sha1)) {\n \t\t\tif (opts->action == REVERT)\n-\t\t\t\tdie(_(\"Can't revert as initial commit\"));\n-\t\t\tdie(_(\"Can't cherry-pick into empty head\"));\n+\t\t\t\treturn error(_(\"Can't revert as initial commit\"));\n+\t\t\treturn error(_(\"Can't cherry-pick into empty head\"));\n \t\t}\n \t\tsave_head(sha1_to_hex(sha1));\n \t\tsave_opts(opts);\n \t}\n \treturn pick_commits(todo_list, opts);\n error:\n-\tdie(_(\"No %s in progress\"), action_name(opts));\n+\treturn error(_(\"No %s in progress\"), action_name(opts));\n }\n \n int cmd_revert(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts;\n+\tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n \tif (isatty(0))\n@@ -1025,16 +1012,23 @@ int cmd_revert(int argc, const char **argv, const char *prefix)\n \topts.action = REVERT;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\treturn pick_revisions(&opts);\n+\tres = pick_revisions(&opts);\n+\tif (res < 0)\n+\t\tdie(_(\"revert failed\"));\n+\treturn res;\n }\n \n int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n {\n \tstruct replay_opts opts;\n+\tint res;\n \n \tmemset(&opts, 0, sizeof(opts));\n \topts.action = CHERRY_PICK;\n \tgit_config(git_default_config, NULL);\n \tparse_args(argc, argv, &opts);\n-\treturn pick_revisions(&opts);\n+\tres = pick_revisions(&opts);\n+\tif (res < 0)\n+\t\tdie(_(\"cherry-pick failed\"));\n+\treturn res;\n }\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"173121","messageId":"CAP8UFD0FfPqFpp1Fvg+ESkt1euqua6n_WPa3BSoyBEkNrOrFvQ@mail.gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-15-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 14/18] reset: Make reset remove the sequencer state","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2011-08-08T07:27:28Z","receivedAt":"2011-08-08T07:27:28Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Thursday 04 August 2011 12:39:12 Ramkumar Ramachandra wrote:\n>\n>  branch.c                 |    2 ++\n>  t/7106-reset-sequence.sh |   44\n> ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46\n> insertions(+), 0 deletions(-)\n>  create mode 100755 t/7106-reset-sequence.sh\n\nThe name of the new test file should be \"t7106-reset-sequence.sh\" instead of\n\"7106-reset-sequence.sh\".\n\n> diff --git a/t/7106-reset-sequence.sh b/t/7106-reset-sequence.sh\n> new file mode 100755\n> index 0000000..4956caa\n> --- /dev/null\n> +++ b/t/7106-reset-sequence.sh\n\nThanks,\nChristian.\n"},{"id":"173122","messageId":"CAP8UFD0izFOW0xHQB8ZT3+bbTjGtSm-ZWHs6AWdJoEneRX32mw@mail.gmail.com","threadId":"28021","inReplyTo":"1312454356-3070-18-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 17/18] revert: Introduce --continue to continue the operation","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2011-08-08T07:31:03Z","receivedAt":"2011-08-08T07:31:03Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Hi Ram,\n\nOn Thursday 04 August 2011 12:39:15 Ramkumar Ramachandra wrote:\n>\n> +test_expect_success '--signoff is not automatically propogated to resolved\n> conflict' '\n\ns/propogated/propagated/\n\nI found nothing else from another look at the tests in the series so you can\nadd an \"Acked-by: Christian Couder <chriscool@tuxfamily.org>\" or \"Signed-of-\nby: Christian Couder <chriscool@tuxfamily.org>\".\n\nThanks,\nChristian.\n"},{"id":"173123","messageId":"CALkWK0nGp3pAE1vqqHfEqX-vons15YOxJKBVVz7o4ffs11Zudw@mail.gmail.com","threadId":"28021","inReplyTo":"CAP8UFD0FfPqFpp1Fvg+ESkt1euqua6n_WPa3BSoyBEkNrOrFvQ@mail.gmail.com","subject":"Re: [PATCH 14/18] reset: Make reset remove the sequencer state","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-08T08:20:22Z","receivedAt":"2011-08-08T08:20:22Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Christian,\n\nChristian Couder writes:\n> On Thursday 04 August 2011 12:39:12 Ramkumar Ramachandra wrote:\n>>  create mode 100755 t/7106-reset-sequence.sh\n>\n> The name of the new test file should be \"t7106-reset-sequence.sh\" instead of\n> \"7106-reset-sequence.sh\".\n\nUgh, right.\nJunio: Could you kindly squash the following patch in before merging\nthe series into 'next'?  I hope it's not too late.\n\np.s- I'm pleasantly surprised to see no diff.\n\n--8<--\nSubject: [PATCH] fixup! reset: Make reset remove the sequencer state\n\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n ...6-reset-sequence.sh => t7106-reset-sequence.sh} |    0\n 1 files changed, 0 insertions(+), 0 deletions(-)\n rename t/{7106-reset-sequence.sh => t7106-reset-sequence.sh} (100%)\n\ndiff --git a/t/7106-reset-sequence.sh b/t/t7106-reset-sequence.sh\nsimilarity index 100%\nrename from t/7106-reset-sequence.sh\nrename to t/t7106-reset-sequence.sh\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"173124","messageId":"CALkWK0mKTXUxW=PT_wHEVAzUSgPdZ1s18-9ndZKJqSf6NwVH7w@mail.gmail.com","threadId":"28021","inReplyTo":"CAP8UFD0izFOW0xHQB8ZT3+bbTjGtSm-ZWHs6AWdJoEneRX32mw@mail.gmail.com","subject":"Re: [PATCH 17/18] revert: Introduce --continue to continue the operation","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-08T08:24:04Z","receivedAt":"2011-08-08T08:24:04Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Christian,\n\nChristian Couder writes:\n> On Thursday 04 August 2011 12:39:15 Ramkumar Ramachandra wrote:\n>>\n>> +test_expect_success '--signoff is not automatically propogated to resolved\n>> conflict' '\n>\n> s/propogated/propagated/\n>\n> I found nothing else from another look at the tests in the series so you can\n> add an \"Acked-by: Christian Couder <chriscool@tuxfamily.org>\" or \"Signed-of-\n> by: Christian Couder <chriscool@tuxfamily.org>\".\n\nThanks for being so thorough! :)\nJunio: Here's another small fixup patch for your convenience.\n\n--8<--\nSubject: [PATCH] fixup! revert: Introduce --continue to continue the\n operation\n\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n t/t3510-cherry-pick-sequence.sh |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 78f3f01..3bca2b3 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -171,7 +171,7 @@ test_expect_success '--continue respects opts' '\n        grep \"cherry picked from\" anotherpick_msg\n '\n\n-test_expect_success '--signoff is not automatically propogated to\nresolved conflict' '\n+test_expect_success '--signoff is not automatically propagated to\nresolved conflict' '\n        pristine_detach initial &&\n        test_must_fail git cherry-pick --signoff base..anotherpick &&\n        echo \"c\" >foo &&\n-- \n1.7.6.351.gb35ac.dirty\n"},{"id":"173153","messageId":"7vobzzwvug.fsf@alter.siamese.dyndns.org","threadId":"28021","inReplyTo":"CALkWK0mKTXUxW=PT_wHEVAzUSgPdZ1s18-9ndZKJqSf6NwVH7w@mail.gmail.com","subject":"Re: [PATCH 17/18] revert: Introduce --continue to continue the operation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-08-08T16:28:32Z","receivedAt":"2011-08-08T16:28:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ramkumar Ramachandra <artagnon@gmail.com> writes:\n\n> Junio: Here's another small fixup patch for your convenience.\n\nNot very convenient I am afraid ;-)\n\n> --8<--\n\nThis seems to be too short for \"am -c\"; have you tried applying it\nyourself first before sending it out?\n\n> Subject: [PATCH] fixup! revert: Introduce --continue to continue the\n>  operation\n>\n>\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n> ---\n>  t/t3510-cherry-pick-sequence.sh |    2 +-\n>  1 files changed, 1 insertions(+), 1 deletions(-)\n>\n> diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\n> index 78f3f01..3bca2b3 100755\n> --- a/t/t3510-cherry-pick-sequence.sh\n> +++ b/t/t3510-cherry-pick-sequence.sh\n> @@ -171,7 +171,7 @@ test_expect_success '--continue respects opts' '\n>         grep \"cherry picked from\" anotherpick_msg\n>  '\n>\n> -test_expect_success '--signoff is not automatically propogated to\n> resolved conflict' '\n> +test_expect_success '--signoff is not automatically propagated to\n> resolved conflict' '\n\nLinewrapped.\n\nI'll squash this manually, so no need to resend. Thanks.\n"},{"id":"173196","messageId":"CALkWK0my7_Ocd9eFfGVcJreOJVkC_OyaUo1Q_U7o0mW04ab-YQ@mail.gmail.com","threadId":"28021","inReplyTo":"7vobzzwvug.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 17/18] revert: Introduce --continue to continue the operation","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-08-09T05:26:07Z","receivedAt":"2011-08-09T05:26:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Junio,\n\nJunio C Hamano writes:\n> Ramkumar Ramachandra <artagnon@gmail.com> writes:\n>\n>> Junio: Here's another small fixup patch for your convenience.\n>\n> Not very convenient I am afraid ;-)\n\nHm, I actually thought about this a bit.  Due to the volume of patches\nyou have to pick up and apply from emails everyday, I figured that you\nmust have optimized your email-patch application pipeline to such a\nridiculous extent that it's probably faster for you to apply patches\nthan even typing out a word.  A quick interactive rebase with\nautosquash will then fix rr/revert-cherry-pick-continue, no?\n\n>> --8<--\n>\n> This seems to be too short for \"am -c\"; have you tried applying it\n> yourself first before sending it out?\n\nNo, I haven't tried this.  Will try it and patch \"am -c\" if necessary,\nbecause I like this minimal scissor (picked it up from Jonathan I\nthink).\n\n>> -test_expect_success '--signoff is not automatically propogated to\n>> resolved conflict' '\n>> +test_expect_success '--signoff is not automatically propagated to\n>> resolved conflict' '\n>\n> Linewrapped.\n\nUgh.  Classic GMail web interface mistake.  I sometimes risk sending\nsmall patches without git-send-email, and this is what happens.\nNothing helps: mutt's single-threaded'ness is starting to get on my\nnerves now.\n\n> I'll squash this manually, so no need to resend. Thanks.\n\nThanks.\n\n-- Ram\n"}]}