{"thread":{"id":"44955","subject":"[PATCH 0/5] sequencer: allow skipping commits","startedAt":"2017-01-23T22:52:56Z","lastAt":"2017-01-23T22:53:05Z","messageCount":6,"participants":["Giuseppe Bilotta"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"310044","messageId":"20170123225221.3659-1-giuseppe.bilotta@gmail.com","threadId":"44955","inReplyTo":null,"subject":"[PATCH 0/5] sequencer: allow skipping commits","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2017-01-23T22:52:16Z","receivedAt":"2017-01-23T22:52:56Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"This series introduces a few options to the sequencer,\nto allow skipping unwanted/unnecessary commits.\n\nThe first patch is just cleanup. The second fixes a potential issue\nabout sequencing options not being correctly remembered across\ninterruptions.\n\nThe next two introduce cherry-pick options to skip empty (or only\nredundant) commits. The two options are introduced separately because\nof the complexity associated with the possible combinations that can be\nhad.\n\nThe last commit allows --skip as a reset + --continue, to quickly skip\nthe current commit during a failed cherry-pick or revert (for example\nbecause a better version of the commit was already merged).\n\nGiuseppe Bilotta (5):\n  sequencer: sort options load/save by struct position\n  sequencer: save/load all options\n  cherry-pick: option to skip empty commits\n  cherry-pick: allow skipping only redundant commits\n  sequencer: allow to --skip current commit\n\n Documentation/git-cherry-pick.txt |  10 +++\n Documentation/sequencer.txt       |  10 ++-\n builtin/revert.c                  |  24 +++++-\n sequencer.c                       | 163 ++++++++++++++++++++++++++++++--------\n sequencer.h                       |   4 +-\n 5 files changed, 176 insertions(+), 35 deletions(-)\n\n-- \n2.11.0.616.gd72966cf44.dirty\n\n"},{"id":"310045","messageId":"20170123225221.3659-6-giuseppe.bilotta@gmail.com","threadId":"44955","inReplyTo":"20170123225221.3659-1-giuseppe.bilotta@gmail.com","subject":"[PATCH 5/5] sequencer: allow to --skip current commit","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2017-01-23T22:52:21Z","receivedAt":"2017-01-23T22:52:59Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"If a sequencing gets interrupted (by a conflict or an empty commit or\nwhatever), the user can now opt to just skip it passing the `--skip`\ncommand line option, which acts like a `--continue`, except that the\ncurrent commit gets skipped.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n Documentation/sequencer.txt | 10 +++++++++-\n builtin/revert.c            |  7 +++++--\n sequencer.c                 | 32 ++++++++++++++++++++++++++++----\n sequencer.h                 |  2 +-\n 4 files changed, 43 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt\nindex 5747f442f2..095d6cd732 100644\n--- a/Documentation/sequencer.txt\n+++ b/Documentation/sequencer.txt\n@@ -1,7 +1,15 @@\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.\n+\tconflicts in a failed cherry-pick or revert.  Use `--skip`\n+\tinstead if the current commit should be ignored.\n+\n+--skip::\n+\tSkips the current commit, and then continues the operation\n+\tin progress using the information in '.git/sequencer'.i\n+\tCan be used to continue to a cherry-pick or rever that was\n+\tinterrupted by an empty commit, or by a commit that conflicts\n+\tand for which the resolution is to discard the commit.\n \n --quit::\n \tForget about the current operation in progress.  Can be used\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex aca8a1d9d0..dece0bebf7 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -79,6 +79,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n+\t\tOPT_CMDMODE(0, \"skip\", &cmd, N_(\"resume revert or cherry-pick sequence, skipping this commit\"), 's'),\n \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n@@ -127,6 +128,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \t\t\tthis_operation = \"--quit\";\n \t\telse if (cmd == 'c')\n \t\t\tthis_operation = \"--continue\";\n+\t\telse if (cmd == 's')\n+\t\t\tthis_operation = \"--skip\";\n \t\telse {\n \t\t\tassert(cmd == 'a');\n \t\t\tthis_operation = \"--abort\";\n@@ -188,8 +191,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \n \tif (cmd == 'q')\n \t\treturn sequencer_remove_state(opts);\n-\tif (cmd == 'c')\n-\t\treturn sequencer_continue(opts);\n+\tif (cmd == 'c' || cmd == 's')\n+\t\treturn sequencer_continue(opts, cmd);\n \tif (cmd == 'a')\n \t\treturn sequencer_rollback(opts);\n \treturn sequencer_pick_revisions(opts);\ndiff --git a/sequencer.c b/sequencer.c\nindex 333d9112de..cfe8c06989 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1359,21 +1359,45 @@ static int continue_single_pick(void)\n \treturn run_command_v_opt(argv, RUN_GIT_CMD);\n }\n \n-int sequencer_continue(struct replay_opts *opts)\n+/*\n+ * Continue the sequencing, after either committing\n+ * (cmd == 'c') or skipping (cmd == 's') the current\n+ * commit.\n+ */\n+int sequencer_continue(struct replay_opts *opts, char cmd)\n {\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n+\tint single, res;\n \n \tif (read_and_refresh_cache(opts))\n \t\treturn -1;\n \n-\tif (!file_exists(get_todo_path(opts)))\n-\t\treturn continue_single_pick();\n+\tif (!file_exists(get_todo_path(opts))) {\n+\t\tif (cmd == 'c') {\n+\t\t\treturn continue_single_pick();\n+\t\t} else {\n+\t\t\tassert(cmd == 's');\n+\t\t\t/* Skipping the only commit is equivalent to an abort */\n+\t\t\treturn sequencer_rollback(opts);\n+\t\t}\n+\t}\n \tif (read_populate_opts(opts))\n \t\treturn -1;\n \tif ((res = read_populate_todo(&todo_list, opts)))\n \t\tgoto release_todo_list;\n \n+\t/* If we were asked to skip this commit, rollback\n+\t * and continue with the next */\n+\tif (cmd == 's') {\n+\t\tif ((res = rollback_single_pick()))\n+\t\t\tgoto release_todo_list;\n+\t\tdiscard_cache();\n+\t\tif ((res = read_cache()) < 0)\n+\t\t\tgoto release_todo_list;\n+\t\tprintf(\"index unchanged: %d\\n\", is_index_unchanged());\n+\t\tgoto skip_this_commit;\n+\t}\n+\n \t/* check if there is something to commit */\n \tres = is_index_unchanged();\n \tif (res < 0)\ndiff --git a/sequencer.h b/sequencer.h\nindex f8b8bd0063..afc4bb4e6c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -41,7 +41,7 @@ struct replay_opts {\n #define REPLAY_OPTS_INIT { -1 }\n \n int sequencer_pick_revisions(struct replay_opts *opts);\n-int sequencer_continue(struct replay_opts *opts);\n+int sequencer_continue(struct replay_opts *opts, char cmd);\n int sequencer_rollback(struct replay_opts *opts);\n int sequencer_remove_state(struct replay_opts *opts);\n \n-- \n2.11.0.616.gd72966cf44.dirty\n\n"},{"id":"310046","messageId":"20170123225221.3659-4-giuseppe.bilotta@gmail.com","threadId":"44955","inReplyTo":"20170123225221.3659-1-giuseppe.bilotta@gmail.com","subject":"[PATCH 3/5] cherry-pick: option to skip empty commits","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2017-01-23T22:52:19Z","receivedAt":"2017-01-23T22:53:01Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"This allows cherry-picking a set of commits, some of which may be\nredundant, without stopping to ask for the user intervention.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n Documentation/git-cherry-pick.txt |  4 ++++\n builtin/revert.c                  |  1 +\n sequencer.c                       | 45 +++++++++++++++++++++++++++++++--------\n sequencer.h                       |  1 +\n 4 files changed, 42 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex d35d771fc8..ffced816d6 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -138,6 +138,10 @@ effect to your index in a row.\n \texamine the commit. This option overrides that behavior and\n \tcreates an empty commit object.  Implies `--allow-empty`.\n \n+--skip-empty::\n+\tThis option causes empty and redundant cherry-picked commits to\n+\tbe skipped without requesting the user intervention.\n+\n --strategy=<strategy>::\n \tUse the given merge strategy.  Should only be used once.\n \tSee the MERGE STRATEGIES section in linkgit:git-merge[1]\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 4ca5b51544..ffdd367f99 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -102,6 +102,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \t\t\tOPT_BOOL(0, \"allow-empty\", &opts->allow_empty, N_(\"preserve initially empty commits\")),\n \t\t\tOPT_BOOL(0, \"allow-empty-message\", &opts->allow_empty_message, N_(\"allow commits with empty messages\")),\n \t\t\tOPT_BOOL(0, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"keep redundant, empty commits\")),\n+\t\t\tOPT_BOOL(0, \"skip-empty\", &opts->skip_empty, N_(\"skip redundant, empty commits\")),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\ndiff --git a/sequencer.c b/sequencer.c\nindex 3d2f61c979..9c01310162 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -550,22 +550,32 @@ static int is_original_commit_empty(struct commit *commit)\n \n /*\n  * Do we run \"git commit\" with \"--allow-empty\"?\n+ *\n+ * Or do we just skip this empty commit?\n+ *\n+ * Returns 1 if a commit should be done with --allow-empty,\n+ *         0 if a commit should be done without --allow-empty,\n+ *         2 if no commit should be done at all (skip empty commit)\n+ *         negative values in case of error\n+ *\n  */\n-static int allow_empty(struct replay_opts *opts, struct commit *commit)\n+static int allow_or_skip_empty(struct replay_opts *opts, struct commit *commit)\n {\n \tint index_unchanged, empty_commit;\n \n \t/*\n-\t * Three cases:\n+\t * Four cases:\n \t *\n-\t * (1) we do not allow empty at all and error out.\n+\t * (1) we do not allow empty at all and error out;\n \t *\n-\t * (2) we allow ones that were initially empty, but\n+\t * (2) we skip empty commits altogether;\n+\t *\n+\t * (3) we allow ones that were initially empty, but\n \t * forbid the ones that become empty;\n \t *\n-\t * (3) we allow both.\n+\t * (4) we allow both.\n \t */\n-\tif (!opts->allow_empty)\n+\tif (!opts->allow_empty && !opts->skip_empty)\n \t\treturn 0; /* let \"git commit\" barf as necessary */\n \n \tindex_unchanged = is_index_unchanged();\n@@ -574,6 +584,9 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n \n+\tif (opts->skip_empty)\n+\t\treturn 2;\n+\n \tif (opts->keep_redundant_commits)\n \t\treturn 1;\n \n@@ -612,7 +625,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res, unborn = 0, allow;\n+\tint res = 0, unborn = 0, allow;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -771,12 +784,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \n-\tallow = allow_empty(opts, commit);\n+\tallow = allow_or_skip_empty(opts, commit);\n \tif (allow < 0) {\n \t\tres = allow;\n \t\tgoto leave;\n \t}\n-\tif (!opts->no_commit)\n+\t/* allow == 2 means skip this commit */\n+\tif (allow != 2 && !opts->no_commit)\n \t\tres = run_git_commit(opts->edit ? NULL : git_path_merge_msg(),\n \t\t\t\t     opts, allow, opts->edit, 0, 0);\n \n@@ -993,6 +1007,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->allow_empty_message = git_config_bool_or_int(key, value, &error_flag);\n \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n \t\topts->keep_redundant_commits = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.skip-empty\"))\n+\t\topts->skip_empty = 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.gpg-sign\"))\n@@ -1249,6 +1265,8 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-empty-message\", \"true\");\n \tif (opts->keep_redundant_commits)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.keep-redundant-commits\", \"true\");\n+\tif (opts->skip_empty)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.skip-empty\", \"true\");\n \tif (opts->mainline) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n@@ -1322,6 +1340,14 @@ int sequencer_continue(struct replay_opts *opts)\n \tif ((res = read_populate_todo(&todo_list, opts)))\n \t\tgoto release_todo_list;\n \n+\t/* check if there is something to commit */\n+\tres = is_index_unchanged();\n+\tif (res < 0)\n+\t\tgoto release_todo_list;\n+\n+\tif (res && opts->skip_empty)\n+\t\tgoto skip_this_commit;\n+\n \t/* Verify that the conflict has been resolved */\n \tif (file_exists(git_path_cherry_pick_head()) ||\n \t    file_exists(git_path_revert_head())) {\n@@ -1333,6 +1359,7 @@ int sequencer_continue(struct replay_opts *opts)\n \t\tres = error_dirty_index(opts);\n \t\tgoto release_todo_list;\n \t}\n+skip_this_commit:\n \ttodo_list.current++;\n \tres = pick_commits(&todo_list, opts);\n release_todo_list:\ndiff --git a/sequencer.h b/sequencer.h\nindex 7a513c576b..c747cfcfc7 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -23,6 +23,7 @@ struct replay_opts {\n \tint allow_empty;\n \tint allow_empty_message;\n \tint keep_redundant_commits;\n+\tint skip_empty;\n \n \tint mainline;\n \n-- \n2.11.0.616.gd72966cf44.dirty\n\n"},{"id":"310047","messageId":"20170123225221.3659-2-giuseppe.bilotta@gmail.com","threadId":"44955","inReplyTo":"20170123225221.3659-1-giuseppe.bilotta@gmail.com","subject":"[PATCH 1/5] sequencer: sort options load/save by struct position","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2017-01-23T22:52:17Z","receivedAt":"2017-01-23T22:53:02Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"No functional change. The order in which options are serialized and\nreloaded is now the same in which they appear in the replay_opts\nstructure. This makes it easier to spot when we forget to\nserialize/reload an option value.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n sequencer.c | 24 ++++++++++++------------\n 1 file changed, 12 insertions(+), 12 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9adb7bbf1d..672c81b559 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -975,22 +975,22 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\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.no-commit\"))\n+\t\topts->no_commit = 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.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_dup(&opts->strategy, key, value);\n \telse if (!strcmp(key, \"options.gpg-sign\"))\n \t\tgit_config_string_dup(&opts->gpg_sign, key, value);\n+\telse if (!strcmp(key, \"options.strategy\"))\n+\t\tgit_config_string_dup(&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@@ -1223,14 +1223,14 @@ static int save_opts(struct replay_opts *opts)\n \tconst char *opts_file = git_path_opts_file();\n \tint res = 0;\n \n-\tif (opts->no_commit)\n-\t\tres |= git_config_set_in_file_gently(opts_file, \"options.no-commit\", \"true\");\n \tif (opts->edit)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.edit\", \"true\");\n-\tif (opts->signoff)\n-\t\tres |= git_config_set_in_file_gently(opts_file, \"options.signoff\", \"true\");\n \tif (opts->record_origin)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.record-origin\", \"true\");\n+\tif (opts->no_commit)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.no-commit\", \"true\");\n+\tif (opts->signoff)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.signoff\", \"true\");\n \tif (opts->allow_ff)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-ff\", \"true\");\n \tif (opts->mainline) {\n@@ -1239,10 +1239,10 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.mainline\", buf.buf);\n \t\tstrbuf_release(&buf);\n \t}\n+\tif (opts->gpg_sign)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.gpg-sign\", opts->gpg_sign);\n \tif (opts->strategy)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.strategy\", opts->strategy);\n-\tif (opts->gpg_sign)\n-\t\tres |= git_config_set_in_file_gently(opts_file, \"options.gpg-sign\", opts->gpg_sign);\n \tif (opts->xopts) {\n \t\tint i;\n \t\tfor (i = 0; i < opts->xopts_nr; i++)\n-- \n2.11.0.616.gd72966cf44.dirty\n\n"},{"id":"310048","messageId":"20170123225221.3659-5-giuseppe.bilotta@gmail.com","threadId":"44955","inReplyTo":"20170123225221.3659-1-giuseppe.bilotta@gmail.com","subject":"[PATCH 4/5] cherry-pick: allow skipping only redundant commits","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2017-01-23T22:52:20Z","receivedAt":"2017-01-23T22:53:03Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"This allows the preservation of originally empty commits with the\ncombination of flags --allow-empty --skip-redundant-commits.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n Documentation/git-cherry-pick.txt |  8 ++++-\n builtin/revert.c                  | 18 +++++++++++-\n sequencer.c                       | 62 ++++++++++++++++++++++++++++++---------\n sequencer.h                       |  1 +\n 4 files changed, 73 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex ffced816d6..147e0cde0c 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -138,9 +138,15 @@ effect to your index in a row.\n \texamine the commit. This option overrides that behavior and\n \tcreates an empty commit object.  Implies `--allow-empty`.\n \n+--skip-redundant-commits::\n+\tRedundant commits will be skipped altogether. This does not\n+\tinfluence commits that were originally empty (see\n+\t`--allow-empty` and `--skip-empty`).\n+\n --skip-empty::\n \tThis option causes empty and redundant cherry-picked commits to\n-\tbe skipped without requesting the user intervention.\n+\tbe skipped without requesting the user intervention. Implies\n+\t`--skip-redundant-commits`.\n \n --strategy=<strategy>::\n \tUse the given merge strategy.  Should only be used once.\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex ffdd367f99..aca8a1d9d0 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -102,7 +102,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \t\t\tOPT_BOOL(0, \"allow-empty\", &opts->allow_empty, N_(\"preserve initially empty commits\")),\n \t\t\tOPT_BOOL(0, \"allow-empty-message\", &opts->allow_empty_message, N_(\"allow commits with empty messages\")),\n \t\t\tOPT_BOOL(0, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"keep redundant, empty commits\")),\n-\t\t\tOPT_BOOL(0, \"skip-empty\", &opts->skip_empty, N_(\"skip redundant, empty commits\")),\n+\t\t\tOPT_BOOL(0, \"skip-empty\", &opts->skip_empty, N_(\"skip both redundant and initially empty commits\")),\n+\t\t\tOPT_BOOL(0, \"skip-redundant-commits\", &opts->skip_redundant_commits, N_(\"skip redundant commits\")),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\n@@ -115,6 +116,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \t/* implies allow_empty */\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n+\t/* implies skip_redundant_commits */\n+\tif (opts->skip_empty)\n+\t\topts->skip_redundant_commits = 1;\n \n \t/* Check for incompatible command line arguments */\n \tif (cmd) {\n@@ -147,6 +151,18 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \t\t\t\t\"--edit\", opts->edit,\n \t\t\t\tNULL);\n \n+\tif (opts->keep_redundant_commits)\n+\t\tverify_opt_compatible(me, \"--keep-redundant-commits\",\n+\t\t\t\t\"--skip-empty\", opts->skip_empty,\n+\t\t\t\t\"--skip-redundant-commits\", opts->skip_redundant_commits,\n+\t\t\t\tNULL);\n+\n+\tif (opts->keep_redundant_commits)\n+\t\tverify_opt_compatible(me, \"--skip-empty\",\n+\t\t\t\t\"--allow-empty\", opts->allow_empty,\n+\t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n+\t\t\t\tNULL);\n+\n \tif (cmd) {\n \t\topts->revs = NULL;\n \t} else {\ndiff --git a/sequencer.c b/sequencer.c\nindex 9c01310162..333d9112de 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -563,40 +563,70 @@ static int allow_or_skip_empty(struct replay_opts *opts, struct commit *commit)\n {\n \tint index_unchanged, empty_commit;\n \n-\t/*\n-\t * Four cases:\n+\t/* We have four options:\n \t *\n-\t * (1) we do not allow empty at all and error out;\n+\t * --allow-empty (AE)\n+\t * --keep-redundant-commits (KR)\n+\t * --skip-empty (SE)\n+\t * --skip-redundant-commits (SR)\n \t *\n-\t * (2) we skip empty commits altogether;\n+\t * Additionally, if KR, then AE. And if SE, then SR.\n+\t * \n+\t * We have three possible states:\n+\t * Not Empty (NE)\n+\t * Originally Empty (OE)\n+\t * made REdundant (RE) (originally not empty)\n \t *\n-\t * (3) we allow ones that were initially empty, but\n-\t * forbid the ones that become empty;\n+\t * NE always gets committed. The other two depend on the combination\n+\t * of flags.\n \t *\n-\t * (4) we allow both.\n+\t *              OE outcome | RE outcome | AE  KR  SE  SR\n+\t *     Case 0:  0 (error)    0 (error)     0   0   0   0\n+\t *     Case 1:  1 (allow)    0 (error)     1   0   0   0\n+\t * N/A Case 2:  2 (skip)     0 (error)     0   0   1   0\n+\t * N/A Case 3:  0 (error)    1 (keep)      0   1   0   0\n+\t *     Case 4:  1 (allow)    1 (keep)      1   1   0   0\n+\t * N/A Case 5:  2 (skip)     1 (keep)      0   1   1   0\n+\t *     Case 6:  0 (error)    2 (skip)      0   0   0   1\n+\t *     Case 7:  1 (allow)    2 (skip)      1   0   0   1\n+\t *     Case 8:  2 (skip )    2 (skip)      0   0   1   1\n+\t *\n+\t * TODO should we allow Case 2? If so, how?\n \t */\n-\tif (!opts->allow_empty && !opts->skip_empty)\n+\n+\t/* Case 0 */\n+\tif (!opts->allow_empty && !opts->skip_redundant_commits)\n \t\treturn 0; /* let \"git commit\" barf as necessary */\n \n \tindex_unchanged = is_index_unchanged();\n \tif (index_unchanged < 0)\n \t\treturn index_unchanged;\n+\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n \n-\tif (opts->skip_empty)\n-\t\treturn 2;\n+\t/* Here we know that the commit is either OE or RE */\n \n+\t/* Case 4, we don't care, result is 'allow' for both cases */\n \tif (opts->keep_redundant_commits)\n \t\treturn 1;\n \n+\t/* Case 8, we don't care, result is 'skip' for both cases */\n+\tif (opts->skip_empty)\n+\t\treturn 2;\n+\n+\t/* Now we must differentiate between OE and RE,\n+\t * for Case 1, 6, 7 */\n \tempty_commit = is_original_commit_empty(commit);\n \tif (empty_commit < 0)\n \t\treturn empty_commit;\n-\tif (!empty_commit)\n-\t\treturn 0;\n-\telse\n-\t\treturn 1;\n+\n+\t/* An OE will return 1 if AE, 0 otherwise */\n+\tif (empty_commit)\n+\t\treturn opts->allow_empty;\n+\n+\t/* An RE will return 2 if SR, 0 otherwise */\n+\treturn 2*opts->skip_redundant_commits;\n }\n \n enum todo_command {\n@@ -1009,6 +1039,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->keep_redundant_commits = git_config_bool_or_int(key, value, &error_flag);\n \telse if (!strcmp(key, \"options.skip-empty\"))\n \t\topts->skip_empty = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.skip-redundant-commits\"))\n+\t\topts->skip_redundant_commits = 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.gpg-sign\"))\n@@ -1267,6 +1299,8 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.keep-redundant-commits\", \"true\");\n \tif (opts->skip_empty)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.skip-empty\", \"true\");\n+\tif (opts->skip_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.skip-redundant-commits\", \"true\");\n \tif (opts->mainline) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\ndiff --git a/sequencer.h b/sequencer.h\nindex c747cfcfc7..f8b8bd0063 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -24,6 +24,7 @@ struct replay_opts {\n \tint allow_empty_message;\n \tint keep_redundant_commits;\n \tint skip_empty;\n+\tint skip_redundant_commits;;\n \n \tint mainline;\n \n-- \n2.11.0.616.gd72966cf44.dirty\n\n"},{"id":"310049","messageId":"20170123225221.3659-3-giuseppe.bilotta@gmail.com","threadId":"44955","inReplyTo":"20170123225221.3659-1-giuseppe.bilotta@gmail.com","subject":"[PATCH 2/5] sequencer: save/load all options","fromName":"Giuseppe Bilotta","fromEmail":"giuseppe.bilotta@gmail.com","sentAt":"2017-01-23T22:52:18Z","receivedAt":"2017-01-23T22:53:05Z","isPatch":true,"sender":{"key":"giuseppe.bilotta@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1464?v=4"},"body":"Add the missing replay_opts to save_opts and populate_opts, so that an\ninterrupted cherry-pick will continue with the same setup it had before\nthe interruption.\n\nSigned-off-by: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>\n---\n sequencer.c | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 672c81b559..3d2f61c979 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -985,6 +985,14 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->signoff = 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.rerere-autoupdate\"))\n+\t\topts->allow_rerere_auto = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.allow-empty\"))\n+\t\topts->allow_empty = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.allow-empty-message\"))\n+\t\topts->allow_empty_message = git_config_bool_or_int(key, value, &error_flag);\n+\telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n+\t\topts->keep_redundant_commits = 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.gpg-sign\"))\n@@ -1233,6 +1241,14 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.signoff\", \"true\");\n \tif (opts->allow_ff)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-ff\", \"true\");\n+\tif (opts->allow_rerere_auto)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.rerere-autoupdate\", \"true\");\n+\tif (opts->allow_empty)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-empty\", \"true\");\n+\tif (opts->allow_empty_message)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-empty-message\", \"true\");\n+\tif (opts->keep_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file, \"options.keep-redundant-commits\", \"true\");\n \tif (opts->mainline) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstrbuf_addf(&buf, \"%d\", opts->mainline);\n-- \n2.11.0.616.gd72966cf44.dirty\n\n"}]}