{"thread":{"id":"60764","subject":"[PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","startedAt":"2024-01-19T06:13:28Z","lastAt":"2024-03-27T16:37:33Z","messageCount":118,"participants":["brianmlyles@gmail.com","Kristoffer Haugsbakk","Brian Lyles","Junio C Hamano","Phillip Wood","Elijah Newren","Jean-Noël AVILA","phillip.wood123@gmail.com","Dirk Gouders"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"487042","messageId":"20240119060721.3734775-2-brianmlyles@gmail.com","threadId":"60764","inReplyTo":null,"subject":"[PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-19T05:59:17Z","receivedAt":"2024-01-19T06:13:28Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"From: Brian Lyles <brianmlyles@gmail.com>\n\nPreviously, a consumer of the sequencer that wishes to take advantage of\neither the `keep_redundant_commits` or `drop_redundant_commits` feature\nmust also specify `allow_empty`.\n\nThe only consumer of `drop_redundant_commits` is `git-rebase`, which\nalready allows empty commits by default and simply always enables\n`allow_empty`. `keep_redundant_commits` was also consumed by\n`git-cherry-pick`, which had to specify `allow-empty` when\n`keep_redundant_commits` was specified in order for the sequencer's\n`allow_empty()` to actually respect `keep_redundant_commits`.\n\nThe latter is an interesting case: As noted in the docs, this means that\n`--keep-redundant-commits` implies `--allow-empty`, despite the two\nhaving distinct, non-overlapping meanings:\n\n- `allow_empty` refers specifically to commits which start empty, as\n  indicated by the documentation for `--allow-empty` within\n  `git-cherry-pick`:\n\n  \"Note also, that use of this option only keeps commits that were\n  initially empty (i.e. the commit recorded the same tree as its\n  parent). Commits which are made empty due to a previous commit are\n  dropped. To force the inclusion of those commits use\n  --keep-redundant-commits.\"\n\n- `keep_redundant_commits` refers specifically to commits that do not\n  start empty, but become empty due to the content already existing in\n  the target history. This is indicated by the documentation for\n  `--keep-redundant-commits` within `git-cherry-pick`:\n\n  \"If a commit being cherry picked duplicates a commit already in the\n  current history, it will become empty. By default these redundant\n  commits cause cherry-pick to stop so the user can examine the commit.\n  This option overrides that behavior and creates an empty commit\n  object. Implies --allow-empty.\"\n\nThis implication of `--allow-empty` therefore seems incorrect: One\nshould be able to keep a commit that becomes empty without also being\nforced to pick commits that start as empty. However, today, the\nfollowing series of commands would result in both the commit that became\nempty and the commit that started empty being picked despite only\n`--keep-redundant-commits` being specified:\n\n    git init\n    echo \"a\" >test\n    git add test\n    git commit -m \"Initial commit\"\n    echo \"b\" >test\n    git commit -am \"a -> b\"\n    git commit --allow-empty -m \"empty\"\n    git cherry-pick --keep-redundant-commits HEAD^ HEAD\n\nThe same cherry-pick with `--allow-empty` would fail on the redundant\ncommit, and with neither option would fail on the empty commit.\n\nIn a future commit, an `--empty` option will be added to\n`git-cherry-pick`, meaning that `drop_redundant_commits` will be\navailable in that command. For that to be possible with the current\nimplementation of the sequencer's `allow_empty()`, `git-cherry-pick`\nwould need to specify `allow_empty` with `drop_redundant_commits` as\nwell, which is an even less intuitive implication of `--allow-empty`: in\norder to prevent redundant commits automatically, initially-empty\ncommits would need to be kept automatically.\n\nInstead, this commit rewrites the `allow_empty()` logic to remove the\nover-arching requirement that `allow_empty` be specified in order to\nreach any of the keep/drop behaviors. Only if the commit was originally\nempty will `allow_empty` have an effect.\n\nFor some amount of backwards compatibility with the existing code and\ntests, I have opted to preserve the behavior of returning 0 when:\n\n- `allow_empty` is specified, and\n- either `is_index_unchanged` or `is_original_commit_empty` indicates an\n  error\n\nThis is primarily out of caution -- I am not positive what downstream\nimpacts this might have.\n\nNote that this commit is a breaking change: `--keep-redundant-commits`\nwill no longer imply `--allow-empty`. It would be possible to maintain\nthe current behavior of `--keep-redundant-commits` implying\n`--allow-empty` if it were needed to avoid a breaking change, but I\nbelieve that decoupling them entirely is the correct behavior.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nDisclaimer: This is my first contribution to the git project, and thus\nmy first attempt at submitting a patch via `git-send-email`. It is also\nthe first time I've touched worked in C in over a decade, and I really\ndidn't work with it much before that either. I welcome any and all\nfeedback on what I may have gotten wrong regarding the patch submission\nprocess, the code changes, or my commit messages.\n\nThis is the first in a series of commits that aims to introduce an\n`--empty` option to `git-cherry-pick` that provides the same flexibility\nas the `--empty` options for `git-rebase` and `git-am`, as well as\nimprove the consistency in the values and documentation for this option\nacross the three commands.\n\nThe main thing that may be controversial with this particular commit is\nthat I am proposing a breaking change. As described in the above\nmessage, I do not think that it makes sense to tie `--allow-empty` and\n`--keep-redundant-commits` together since they appear to be intended to\nwork with different types of empty commits. That being said, if it is\ndeemed unacceptable to make this breaking change, we can consider an\nalternative approach where we maintain the behavior of\n`--keep-redundant-commits` implying `--allow-empty`, while preventing\nthe need for the future `--empty=drop` to have that same implication.\n\n Documentation/git-cherry-pick.txt | 10 +++++++---\n builtin/revert.c                  |  4 ----\n sequencer.c                       | 18 ++++++++++--------\n t/t3505-cherry-pick-empty.sh      |  5 +++++\n 4 files changed, 22 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex fdcad3d200..806295a730 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -131,8 +131,8 @@ effect to your index in a row.\n \teven without this option.  Note also, that use of this option only\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n-\tprevious commit are dropped.  To force the inclusion of those commits\n-\tuse `--keep-redundant-commits`.\n+\tprevious commit will cause the cherry-pick to fail.  To force the\n+\tinclusion of those commits use `--keep-redundant-commits`.\n \n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n@@ -144,7 +144,11 @@ effect to your index in a row.\n \tcurrent history, it will become empty.  By default these\n \tredundant commits cause `cherry-pick` to stop so the user can\n \texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object.  Implies `--allow-empty`.\n+\tcreates an empty commit object. Note that use of this option only\n+\tresults in an empty commit when the commit was not initially empty,\n+\tbut rather became empty due to a previous commit. Commits that were\n+\tinitially empty will cause the cherry-pick to fail. To force the\n+\tinclusion of those commits use `--allow-empty`.\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 e6f9a1ad26..b2cfde7a87 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -136,10 +136,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n \n-\t/* implies allow_empty */\n-\tif (opts->keep_redundant_commits)\n-\t\topts->allow_empty = 1;\n-\n \tif (cleanup_arg) {\n \t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n \t\topts->explicit_cleanup = 1;\ndiff --git a/sequencer.c b/sequencer.c\nindex d584cac8ed..582bde8d46 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1739,22 +1739,24 @@ static int allow_empty(struct repository *r,\n \t *\n \t * (4) we allow both.\n \t */\n-\tif (!opts->allow_empty)\n-\t\treturn 0; /* let \"git commit\" barf as necessary */\n-\n \tindex_unchanged = is_index_unchanged(r);\n-\tif (index_unchanged < 0)\n+\tif (index_unchanged < 0) {\n+\t\tif (!opts->allow_empty)\n+\t\t\treturn 0;\n \t\treturn index_unchanged;\n+\t}\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n \n-\tif (opts->keep_redundant_commits)\n-\t\treturn 1;\n-\n \toriginally_empty = is_original_commit_empty(commit);\n-\tif (originally_empty < 0)\n+\tif (originally_empty < 0) {\n+\t\tif (!opts->allow_empty)\n+\t\t\treturn 0;\n \t\treturn originally_empty;\n+\t}\n \tif (originally_empty)\n+\t\treturn opts->allow_empty;\n+\telse if (opts->keep_redundant_commits)\n \t\treturn 1;\n \telse if (opts->drop_redundant_commits)\n \t\treturn 2;\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex eba3c38d5a..6adfd25351 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -59,6 +59,11 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '\n \ttest_must_fail git cherry-pick empty-change-branch\n '\n \n+test_expect_success 'cherry pick an empty non-ff commit with --keep-redundant-commits' '\n+\tgit checkout main &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits empty-change-branch\n+'\n+\n test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '\n \tgit checkout main &&\n \tgit cherry-pick --allow-empty empty-change-branch\n-- \n2.41.0\n\n"},{"id":"487043","messageId":"20240119060721.3734775-3-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`","fromName":"","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-19T05:59:18Z","receivedAt":"2024-01-19T06:13:30Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"From: Brian Lyles <brianmlyles@gmail.com>\n\nBoth of these pages document very similar `--empty` options, but with\ndifferent styles. This commit aims to make them more consistent.\n\nIn a future commit, we'll be documenting a new `--empty` option for\n`git-cherry-pick`, making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-am.txt     | 18 ++++++++++++------\n Documentation/git-rebase.txt | 17 ++++++++++++-----\n 2 files changed, 24 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-am.txt b/Documentation/git-am.txt\nindex e080458d6c..77df5e606a 100644\n--- a/Documentation/git-am.txt\n+++ b/Documentation/git-am.txt\n@@ -67,12 +67,18 @@ OPTIONS\n \tThis flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).\n \n --empty=(stop|drop|keep)::\n-\tBy default, or when the option is set to 'stop', the command\n-\terrors out on an input e-mail message lacking a patch\n-\tand stops in the middle of the current am session. When this\n-\toption is set to 'drop', skip such an e-mail message instead.\n-\tWhen this option is set to 'keep', create an empty commit,\n-\trecording the contents of the e-mail message as its log.\n+\tHow to handle an e-mail message lacking a patch:\n++\n+--\n+`stop`;;\n+\tThe command will fail, stopping in the middle of the current `am`\n+\tsession. This is the default behavior.\n+`drop`;;\n+\tThe e-mail message will be skipped.\n+`keep`;;\n+\tAn empty commit will be created, with the contents of the e-mail\n+\tmessage as its log.\n+--\n \n -m::\n --message-id::\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex b4526ca246..3ee85f6d86 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -293,13 +293,20 @@ See also INCOMPATIBLE OPTIONS below.\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n-\tupstream changes).  With drop (the default), commits that\n-\tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask (implied by `--interactive`), the rebase will halt when\n-\tan empty commit is applied allowing you to choose whether to\n-\tdrop it, edit files more, or just commit the empty changes.\n+\tupstream changes):\n++\n+--\n+`drop`;;\n+\tThe empty commit will be dropped. This is the default behavior.\n+`keep`;;\n+\tThe empty commit will be kept.\n+`ask`;;\n+\tThe rebase will halt when the empty commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `--interactive` is specified.\n \tOther options, like `--exec`, will use the default of drop unless\n \t`-i`/`--interactive` is explicitly specified.\n+--\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\n-- \n2.41.0\n\n"},{"id":"487044","messageId":"20240119060721.3734775-4-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`","fromName":"","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-19T05:59:19Z","receivedAt":"2024-01-19T06:13:31Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"From: Brian Lyles <brianmlyles@gmail.com>\n\nWhen `git-am` got its own `--empty` option in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09), `stop` was used\ninstead of `ask`. `stop` is a more accurate term for describing what\nreally happens, and consistency is good. This commit updates\n`git-rebase` to also use `stop`, while keeping `ask` as a deprecated\nsynonym.\n\nIn a future commit, we'll be adding a new `--empty` option for\n`git-cherry-pick` as well, making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt |  8 +++++---\n builtin/rebase.c             | 12 ++++++------\n t/t3424-rebase-empty.sh      | 17 ++++++++++++++---\n 3 files changed, 25 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 3ee85f6d86..fe74d0c367 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,7 +289,7 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(drop|keep|ask)::\n+--empty=(drop|keep|stop)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n@@ -300,12 +300,14 @@ See also INCOMPATIBLE OPTIONS below.\n \tThe empty commit will be dropped. This is the default behavior.\n `keep`;;\n \tThe empty commit will be kept.\n-`ask`;;\n+`stop`;;\n \tThe rebase will halt when the empty commit is applied, allowing you to\n \tchoose whether to drop it, edit files more, or just commit the empty\n \tchanges. This option is implied when `--interactive` is specified.\n \tOther options, like `--exec`, will use the default of drop unless\n \t`-i`/`--interactive` is explicitly specified.\n+`ask`;;\n+\tA deprecated synonym of `stop`.\n --\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n@@ -702,7 +704,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(drop|keep|ask)` option for changing the behavior\n+also has an `--empty=(drop|keep|stop)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 043c65dccd..1fb9d8263d 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -62,7 +62,7 @@ enum empty_type {\n \tEMPTY_UNSPECIFIED = -1,\n \tEMPTY_DROP,\n \tEMPTY_KEEP,\n-\tEMPTY_ASK\n+\tEMPTY_STOP\n };\n \n enum action {\n@@ -963,10 +963,10 @@ static enum empty_type parse_empty_value(const char *value)\n \t\treturn EMPTY_DROP;\n \telse if (!strcasecmp(value, \"keep\"))\n \t\treturn EMPTY_KEEP;\n-\telse if (!strcasecmp(value, \"ask\"))\n-\t\treturn EMPTY_ASK;\n+\telse if (!strcasecmp(value, \"stop\") || !strcasecmp(value, \"ask\"))\n+\t\treturn EMPTY_STOP;\n \n-\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"ask\\\".\"), value);\n+\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"stop\\\".\"), value);\n }\n \n static int parse_opt_keep_empty(const struct option *opt, const char *arg,\n@@ -1145,7 +1145,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"instead of ignoring them\"),\n \t\t\t      1, PARSE_OPT_HIDDEN),\n \t\tOPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),\n-\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|ask)\",\n+\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|stop)\",\n \t\t\t       N_(\"how to handle commits that become empty\"),\n \t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\tOPT_CALLBACK_F('k', \"keep-empty\", &options, NULL,\n@@ -1562,7 +1562,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (options.empty == EMPTY_UNSPECIFIED) {\n \t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n-\t\t\toptions.empty = EMPTY_ASK;\n+\t\t\toptions.empty = EMPTY_STOP;\n \t\telse if (options.exec.nr > 0)\n \t\t\toptions.empty = EMPTY_KEEP;\n \t\telse\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 5e1045a0af..e3ddec88a2 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'rebase --merge --empty=stop' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --merge --empty=stop upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'rebase --merge --empty=ask' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --merge --empty=ask upstream &&\n@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive --empty=ask' '\n+test_expect_success 'rebase --interactive --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --interactive --empty=ask upstream &&\n+\ttest_must_fail git rebase --interactive --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive uses default of --empty=ask' '\n+test_expect_success 'rebase --interactive uses default of --empty=stop' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --interactive upstream &&\n \n-- \n2.41.0\n\n"},{"id":"487045","messageId":"20240119060721.3734775-5-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-19T05:59:20Z","receivedAt":"2024-01-19T06:13:33Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"From: Brian Lyles <brianmlyles@gmail.com>\n\nAs with `git-rebase` and `git-am`, `git-cherry-pick` can result in a\ncommit being made redundant if the content from the picked commit is\nalready present in the target history. However, `git-cherry-pick` does\nnot have the same options available that `git-rebase` and `git-am` have.\n\nThere are three things that can be done with these redundant commits:\ndrop them, keep them, or have the cherry-pick stop and wait for the user\nto take an action. `git-rebase` has the `--empty` option added in commit\ne98c4269c8 (rebase (interactive-backend): fix handling of commits that\nbecome empty, 2020-02-15), which handles all three of these scenarios.\nSimilarly, `git-am` got its own `--empty` in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09).\n\n`git-cherry-pick`, on the other hand, only supports two of the three\npossiblities: Keep the redundant commits via `--keep-redundant-commits`,\nor have the cherry-pick fail by not specifying that option. There is no\nway to automatically drop redundant commits.\n\nIn order to bring `git-cherry-pick` more in-line with `git-rebase` and\n`git-am`, this commit adds an `--empty` option to `git-cherry-pick`. It\nhas the same three options (keep, drop, and stop), and largely behaves\nthe same. The notable difference is that for `git-cherry-pick`, the\ndefault will be `stop`, which maintains the current behavior when the\noption is not specified.\n\nThe `--keep-redundant-commits` option will be documented as a deprecated\nsynonym of `--empty=keep`, and will be supported for backwards\ncompatibility for the time being.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-cherry-pick.txt | 28 ++++++++++++++++++-------\n builtin/revert.c                  | 35 ++++++++++++++++++++++++++++++-\n sequencer.c                       |  6 ++++++\n t/t3505-cherry-pick-empty.sh      | 26 ++++++++++++++++++++++-\n 4 files changed, 86 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex 806295a730..8c20a10d4b 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -132,23 +132,37 @@ effect to your index in a row.\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n \tprevious commit will cause the cherry-pick to fail.  To force the\n-\tinclusion of those commits use `--keep-redundant-commits`.\n+\tinclusion of those commits use `--empty=keep`.\n \n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n \tThis option overrides that behavior, allowing commits with empty\n \tmessages to be cherry picked.\n \n---keep-redundant-commits::\n-\tIf a commit being cherry picked duplicates a commit already in the\n-\tcurrent history, it will become empty.  By default these\n-\tredundant commits cause `cherry-pick` to stop so the user can\n-\texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object. Note that use of this option only\n+--empty=(stop|drop|keep)::\n+\tHow to handle commits being cherry-picked that are redundant with\n+\tchanges already in the current history.\n++\n+-- \n+`stop`;;\n+\tThe cherry-pick will stop when the empty commit is applied, allowing\n+\tyou to examine the commit. This is the default behavior.\n+`drop`;;\n+\tThe empty commit will be dropped.\n+`keep`;;\n+\tThe empty commit will be kept. Note that use of this option only\n \tresults in an empty commit when the commit was not initially empty,\n \tbut rather became empty due to a previous commit. Commits that were\n \tinitially empty will cause the cherry-pick to fail. To force the\n \tinclusion of those commits use `--allow-empty`.\n+--\n++\n+Note that commits which start empty will cause the cherry-pick to fail (unless\n+`--allow-empty` is specified).\n++\n+\n+--keep-redundant-commits::\n+\tDeprecated synonym for `--empty=keep`.\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 b2cfde7a87..1491c45e26 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -45,6 +45,30 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n+enum empty_action {\n+\tSTOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of a cherry-pick */\n+\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n+\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n+};\n+\n+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n+{\n+\tint *opt_value = opt->value;\n+\n+\tBUG_ON_OPT_NEG(unset);\n+\n+\tif (!strcmp(arg, \"stop\"))\n+\t\t*opt_value = STOP_ON_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"drop\"))\n+\t\t*opt_value = DROP_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"keep\"))\n+\t\t*opt_value = KEEP_EMPTY_COMMIT;\n+\telse\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n+\n+\treturn 0;\n+}\n+\n static int option_parse_m(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -87,6 +111,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n \tconst char *cleanup_arg = NULL;\n+\tenum empty_action empty_opt;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n@@ -116,7 +141,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\tOPT_BOOL(0, \"ff\", &opts->allow_ff, N_(\"allow fast-forward\")),\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, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"deprecated: use --empty=keep instead\")),\n+\t\t\tOPT_CALLBACK_F(0, \"empty\", &empty_opt, \"(stop|drop|keep)\",\n+\t\t\t\t       N_(\"how to handle commits that become empty\"),\n+\t\t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\n@@ -136,6 +164,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n \n+\tif (opts->action == REPLAY_PICK) {\n+\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n+\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n+\t}\n+\n \tif (cleanup_arg) {\n \t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n \t\topts->explicit_cleanup = 1;\ndiff --git a/sequencer.c b/sequencer.c\nindex 582bde8d46..c49c27c795 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2934,6 +2934,9 @@ static int populate_opts_cb(const char *key, const char *value,\n \telse if (!strcmp(key, \"options.allow-empty-message\"))\n \t\topts->allow_empty_message =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n+\telse if (!strcmp(key, \"options.drop-redundant-commits\"))\n+\t\topts->drop_redundant_commits =\n+\t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n \t\topts->keep_redundant_commits =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n@@ -3478,6 +3481,9 @@ static int save_opts(struct replay_opts *opts)\n \tif (opts->allow_empty_message)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.allow-empty-message\", \"true\");\n+\tif (opts->drop_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file,\n+\t\t\t\t\"options.drop-redundant-commits\", \"true\");\n \tif (opts->keep_redundant_commits)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.keep-redundant-commits\", \"true\");\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex 6adfd25351..ae0cf7886a 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -89,7 +89,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n \tgit commit -m \"add file2 on the side\"\n '\n \n-test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n \tgit reset --hard &&\n \tgit checkout fork^0 &&\n \ttest_must_fail git cherry-pick main\n@@ -104,4 +104,28 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cherry-pick a no-op with --empty=ask' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\ttest_must_fail git cherry-pick --empty=ask main\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=drop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=drop main &&\n+\tgit show -s --format=%s >actual &&\n+\techo \"add file2 on the side\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=keep' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=keep main &&\n+\tgit show -s --format=%s >actual &&\n+\techo \"add file2 on main\" >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.41.0\n\n"},{"id":"487151","messageId":"06eb93d7-7113-4583-9860-28a6a6d528a2@app.fastmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-5-brianmlyles@gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-01-20T20:24:02Z","receivedAt":"2024-01-20T20:24:47Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"Hi\n\nOn Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n> ---keep-redundant-commits::\n> -\tIf a commit being cherry picked duplicates a commit already in the\n> -\tcurrent history, it will become empty.  By default these\n> -\tredundant commits cause `cherry-pick` to stop so the user can\n> -\texamine the commit. This option overrides that behavior and\n> -\tcreates an empty commit object. Note that use of this option only\n> +--empty=(stop|drop|keep)::\n> +\tHow to handle commits being cherry-picked that are redundant with\n> +\tchanges already in the current history.\n> ++\n> +-- \n\nTrailing whitespace on this line.\n\n-- \nKristoffer Haugsbakk\n"},{"id":"487157","messageId":"d6361ecf-82bc-46c6-adfe-3c6ab25d39b2@app.fastmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-01-20T21:38:25Z","receivedAt":"2024-01-20T21:38:48Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"Initial discussion (proposal): https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/\n\nOn Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n>\n> Previously, a consumer of the sequencer that wishes to take advantage of\n> either the `keep_redundant_commits` or `drop_redundant_commits` feature\n> must also specify `allow_empty`.\n\nPreviously to this change? It is preferred to describe what the code\ncurrently does without this change in the present tense.[1] The change\nitself uses the imperative mood.[2]\n\n† 1: SubmittingPatches, “The problem statement that describes the status\n    quo …”\n† 2: SubmittingPatches, “Describe your changes in imperative mood […] as\n    if you are giving orders to the codebase to change its behavior.”\n\n> The only consumer of `drop_redundant_commits` is `git-rebase`, which\n> already allows empty commits by default and simply always enables\n> `allow_empty`. `keep_redundant_commits` was also consumed by\n> `git-cherry-pick`, which had to specify `allow-empty` when\n> `keep_redundant_commits` was specified in order for the sequencer's\n> `allow_empty()` to actually respect `keep_redundant_commits`.\n>\n> The latter is an interesting case: As noted in the docs, this means that\n> `--keep-redundant-commits` implies `--allow-empty`, despite the two\n> having distinct, non-overlapping meanings:\n\nHuh. I’m used to the git-rebase(1) behavior and I definitely would have\njust assumed that git-cherry-pick(1) behaves the same. :) Nice catch.\n\n> This implication of `--allow-empty` therefore seems incorrect: One\n> should be able to keep a commit that becomes empty without also being\n> forced to pick commits that start as empty. However, today, the\n> following series of commands would result in both the commit that became\n> empty and the commit that started empty being picked despite only\n> `--keep-redundant-commits` being specified:\n\nNice description of the current problem. All of it.\n\n> In a future commit, an `--empty` option will be added to\n> `git-cherry-pick`, meaning that `drop_redundant_commits` will be\n> available in that command. For that to be possible with the current\n> implementation of the sequencer's `allow_empty()`, `git-cherry-pick`\n> would need to specify `allow_empty` with `drop_redundant_commits` as\n> well, which is an even less intuitive implication of `--allow-empty`: in\n> order to prevent redundant commits automatically, initially-empty\n> commits would need to be kept automatically.\n>\n> Instead, this commit rewrites the `allow_empty()` logic to remove the\n> over-arching requirement that `allow_empty` be specified in order to\n> reach any of the keep/drop behaviors. Only if the commit was originally\n> empty will `allow_empty` have an effect.\n\nIn general, phrases like “this commit <verb>” or “this patch <verb>” can\nbe rewritten to the “commanding” style (see [2]).[3] But here you’re\nstarting a new paragraph after having talked about a future commit, so\nusing the commanding style might be stylistically difficult to pull off\nwithout breaking the flow of the text.\n\nAnd “this [commit][patch] <verb>” seems to be used with some regularity in\nany case.\n\n🔗 3: https://lore.kernel.org/git/xmqqedeqienh.fsf@gitster.g/\n\n> Disclaimer: This is my first contribution to the git project, and thus\n> my first attempt at submitting a patch via `git-send-email`. It is also\n> the first time I've touched worked in C in over a decade, and I really\n> didn't work with it much before that either. I welcome any and all\n> feedback on what I may have gotten wrong regarding the patch submission\n> process, the code changes, or my commit messages.\n\nThis part (after the commit message) looks like the cover letter for the\nseries (the four patches). `SubmittingPatches` recommends submitting that\nin a dedicated email message (for series that have more than one\npatch). Maybe this cover letter style is just an alternative that is\nequally accepted. But most series use a separate cover letter message for\nwhat it’s worth.\n\nCheers\n\n-- \nKristoffer Haugsbakk\n"},{"id":"487182","messageId":"CAHPHrSf3-r4G6X-19GMhMzec5kZbopV+ZvmQ5HHBYPNgH6XZCg@mail.gmail.com","threadId":"60764","inReplyTo":"d6361ecf-82bc-46c6-adfe-3c6ab25d39b2@app.fastmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-21T18:19:56Z","receivedAt":"2024-01-21T18:20:34Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Sat, Jan 20, 2024 at 3:38 PM Kristoffer Haugsbakk\n<code@khaugsbakk.name> wrote:\n\nHi Kristoffer,\nThank you for taking some time to review my patch.\n\n> Initial discussion (proposal): https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/\n\nI ought to have included this in a cover letter -- thanks for linking it\nhere, and I will include this with v2.\n\n> On Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:\n> > From: Brian Lyles <brianmlyles@gmail.com>\n> >\n> > Previously, a consumer of the sequencer that wishes to take advantage of\n> > either the `keep_redundant_commits` or `drop_redundant_commits` feature\n> > must also specify `allow_empty`.\n>\n> Previously to this change? It is preferred to describe what the code\n> currently does without this change in the present tense.[1] The change\n> itself uses the imperative mood.[2]\n>\n> † 1: SubmittingPatches, “The problem statement that describes the status\n>     quo …”\n> † 2: SubmittingPatches, “Describe your changes in imperative mood […] as\n>     if you are giving orders to the codebase to change its behavior.”\n\nI appreciate the stylistic feedback here. I will tweak this for v2 of\nthe patch.\n\n> > In a future commit, an `--empty` option will be added to\n> > `git-cherry-pick`, meaning that `drop_redundant_commits` will be\n> > available in that command. For that to be possible with the current\n> > implementation of the sequencer's `allow_empty()`, `git-cherry-pick`\n> > would need to specify `allow_empty` with `drop_redundant_commits` as\n> > well, which is an even less intuitive implication of `--allow-empty`: in\n> > order to prevent redundant commits automatically, initially-empty\n> > commits would need to be kept automatically.\n> >\n> > Instead, this commit rewrites the `allow_empty()` logic to remove the\n> > over-arching requirement that `allow_empty` be specified in order to\n> > reach any of the keep/drop behaviors. Only if the commit was originally\n> > empty will `allow_empty` have an effect.\n>\n> In general, phrases like “this commit <verb>” or “this patch <verb>” can\n> be rewritten to the “commanding” style (see [2]).[3] But here you’re\n> starting a new paragraph after having talked about a future commit, so\n> using the commanding style might be stylistically difficult to pull off\n> without breaking the flow of the text.\n>\n> And “this [commit][patch] <verb>” seems to be used with some regularity in\n> any case.\n>\n> 🔗 3: https://lore.kernel.org/git/xmqqedeqienh.fsf@gitster.g/\n\nI think I should be able to adjust this to a more commanding style\nwithout breaking the flow. I'll give that a shot in v2 as well.\n\n> > Disclaimer: This is my first contribution to the git project, and thus\n> > my first attempt at submitting a patch via `git-send-email`. It is also\n> > the first time I've touched worked in C in over a decade, and I really\n> > didn't work with it much before that either. I welcome any and all\n> > feedback on what I may have gotten wrong regarding the patch submission\n> > process, the code changes, or my commit messages.\n>\n> This part (after the commit message) looks like the cover letter for the\n> series (the four patches). `SubmittingPatches` recommends submitting that\n> in a dedicated email message (for series that have more than one\n> patch). Maybe this cover letter style is just an alternative that is\n> equally accepted. But most series use a separate cover letter message for\n> what it’s worth.\n\nThat makes sense -- when I send v2, I can pull this part out into a\nseparate cover letter email.\n\nI will hold off on sending out v2 for a few days to give others a chance\nto weigh in on this series.\n\nThanks again,\nBrian Lyles\n"},{"id":"487183","messageId":"CAHPHrSddqTEjtfhgVJ=vmRhbtuwXcGEiE1KFZqR1QhKw-HDtSg@mail.gmail.com","threadId":"60764","inReplyTo":"06eb93d7-7113-4583-9860-28a6a6d528a2@app.fastmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-21T18:28:11Z","receivedAt":"2024-01-21T18:28:49Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Sat, Jan 20, 2024 at 2:24 PM Kristoffer Haugsbakk\n<code@khaugsbakk.name> wrote:\n\n> On Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:\n> > From: Brian Lyles <brianmlyles@gmail.com>\n> > ---keep-redundant-commits::\n> > -     If a commit being cherry picked duplicates a commit already in the\n> > -     current history, it will become empty.  By default these\n> > -     redundant commits cause `cherry-pick` to stop so the user can\n> > -     examine the commit. This option overrides that behavior and\n> > -     creates an empty commit object. Note that use of this option only\n> > +--empty=(stop|drop|keep)::\n> > +     How to handle commits being cherry-picked that are redundant with\n> > +     changes already in the current history.\n> > ++\n> > +--\n>\n> Trailing whitespace on this line.\n\nThank you -- This will be corrected with v2.\n\nIs the sample pre-commit hook the ideal way to prevent this in the\nfuture? Or is there some config I could set globally to enforce this\nacross repositories? I was having a little trouble finding a good way to\naccomplish this globally.\n\nThanks,\nBrian Lyles\n"},{"id":"487185","messageId":"ca810226-d626-4437-95da-f6a071aff961@app.fastmail.com","threadId":"60764","inReplyTo":"CAHPHrSddqTEjtfhgVJ=vmRhbtuwXcGEiE1KFZqR1QhKw-HDtSg@mail.gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-01-21T22:05:17Z","receivedAt":"2024-01-21T22:05:39Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Sun, Jan 21, 2024, at 19:28, Brian Lyles wrote:\n> Is the sample pre-commit hook the ideal way to prevent this in the\n> future? Or is there some config I could set globally to enforce this\n> across repositories? I was having a little trouble finding a good way to\n> accomplish this globally.\n\nI don’t know of any global config. So a pre-commit hook is probably the\nsafest bet. Personally I set all my editors to remove trailing space and\nthey very seldom mess it up. :)\n\n-- \nKristoffer Haugsbakk\n"},{"id":"487186","messageId":"xmqq8r4ie2ce.fsf@gitster.g","threadId":"60764","inReplyTo":"CAHPHrSddqTEjtfhgVJ=vmRhbtuwXcGEiE1KFZqR1QhKw-HDtSg@mail.gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-21T22:41:37Z","receivedAt":"2024-01-21T22:41:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brian Lyles <brianmlyles@gmail.com> writes:\n\n>> Trailing whitespace on this line.\n>\n> Thank you -- This will be corrected with v2.\n>\n> Is the sample pre-commit hook the ideal way to prevent this in the\n> future?\n\nThe example we ship in templates/hooks--pre-commit.sample in our\nsource has such a check at the end of it.\n\n"},{"id":"487194","messageId":"d0d6626c-7181-44ce-ae50-f9cb57381ebd@gmail.com","threadId":"60764","inReplyTo":"CAHPHrSddqTEjtfhgVJ=vmRhbtuwXcGEiE1KFZqR1QhKw-HDtSg@mail.gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-22T10:40:07Z","receivedAt":"2024-01-22T10:40:10Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 21/01/2024 18:28, Brian Lyles wrote:\n> Is the sample pre-commit hook the ideal way to prevent this in the\n> future? Or is there some config I could set globally to enforce this\n> across repositories? I was having a little trouble finding a good way to\n> accomplish this globally.\n\nIf you want to run the same hooks in all your repositories then you can \nrun 'git config --global core.hooksPath <my-hooks-path>' and git will \nlook for hooks in 'my-hooks-path' instead of '.git/hooks'. It makes it \ntricky to run different linters in different projects though.\n\nI'll try and take a proper look at these patches in the next couple of days.\n\nBest Wishes\n\nPhillip\n"},{"id":"487224","messageId":"10838549-c364-429b-a086-68a41b7369de@app.fastmail.com","threadId":"60764","inReplyTo":"CAHPHrSddqTEjtfhgVJ=vmRhbtuwXcGEiE1KFZqR1QhKw-HDtSg@mail.gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-01-22T20:55:13Z","receivedAt":"2024-01-22T20:55:35Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Sun, Jan 21, 2024, at 19:28, Brian Lyles wrote:\n> Thank you -- This will be corrected with v2.\n>\n> Is the sample pre-commit hook the ideal way to prevent this in the\n> future? Or is there some config I could set globally to enforce this\n> across repositories? I was having a little trouble finding a good way to\n> accomplish this globally.\n>\n> Thanks,\n> Brian Lyles\n\nOh, and this thread reminded me https://lore.kernel.org/git/xmqqle8hrtcs.fsf@gitster.g/T/#t\n\nthat editorconfig[1] has this option:\n\n```\ntrim_trailing_whitespace = true\n```\n\nSo I guess that should be enough for all editors that respect this\nconfig (although I haven’t tested it).\n\n🔗 1: https://editorconfig.org/\n\n-- \nKristoffer Haugsbakk\n"},{"id":"487245","messageId":"CAHPHrSd8rLj_TDE11dYQW+51--8YC4rumnfT+v2bYr+K7AMQrQ@mail.gmail.com","threadId":"60764","inReplyTo":"10838549-c364-429b-a086-68a41b7369de@app.fastmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-23T05:23:17Z","receivedAt":"2024-01-23T05:23:55Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Sun, Jan 21, 2024 at 4:05 PM Kristoffer Haugsbakk\n<code@khaugsbakk.name> wrote:\n\n> I don’t know of any global config. So a pre-commit hook is probably the\n> safest bet. Personally I set all my editors to remove trailing space and\n> they very seldom mess it up. :)\n\nFair point -- Apparently this was a gap in my nvim config.\nEasily added.\n\nOn Mon, Jan 22, 2024 at 2:55 PM Kristoffer Haugsbakk\n<code@khaugsbakk.name> wrote:\n\n> Oh, and this thread reminded me https://lore.kernel.org/git/xmqqle8hrtcs.fsf@gitster.g/T/#t\n>\n> that editorconfig[1] has this option:\n>\n> ```\n> trim_trailing_whitespace = true\n> ```\n>\n> So I guess that should be enough for all editors that respect this\n> config (although I haven’t tested it).\n>\n> 🔗 1: https://editorconfig.org/\n\nIs there a good reason that this should not just be added to the\n`.editorconfig` in this repository? Would a patch for this be welcome?\n\nI do see that there are ~130 files with trailing whitespace in maint\ntoday, though I suspect that most of those are not intentional.\n\nBrian Lyles\n"},{"id":"487248","messageId":"6475ad33-423e-43ef-aa75-3ff5539a04d1@app.fastmail.com","threadId":"60764","inReplyTo":"CAHPHrSd8rLj_TDE11dYQW+51--8YC4rumnfT+v2bYr+K7AMQrQ@mail.gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Kristoffer Haugsbakk","fromEmail":"code@khaugsbakk.name","sentAt":"2024-01-23T07:11:12Z","receivedAt":"2024-01-23T07:11:33Z","isPatch":true,"sender":{"key":"code@khaugsbakk.name","avatar":"https://avatars.githubusercontent.com/u/2229597?v=4"},"body":"On Tue, Jan 23, 2024, at 06:23, Brian Lyles wrote:\n>> Oh, and this thread reminded me https://lore.kernel.org/git/xmqqle8hrtcs.fsf@gitster.g/T/#t\n>>\n>> that editorconfig[1] has this option:\n>>\n>> ```\n>> trim_trailing_whitespace = true\n>> ```\n>> […]\n>\n> Is there a good reason that this should not just be added to the\n> `.editorconfig` in this repository? Would a patch for this be welcome?\n\nI was thinking the same thing when I saw that other thread. It doesn’t\nhurt to try.\n\nI was also curious about some subtleties like: does it dictate enforcing\nthis for all the lines of a touched files or only the modified ones?\nBecause the latter is much more useful.\n\n-- \nKristoffer Haugsbakk\n"},{"id":"487255","messageId":"7bf5036b-9f55-4451-a13c-8a2c815dfbb7@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-23T14:23:52Z","receivedAt":"2024-01-23T14:23:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nLet me start by saying that overall I'm impressed with the quality of \nthis submission. I've left quite a few comments but for a first patch \nseries it is very good.\n\nOn 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n> \n> Previously, a consumer of the sequencer that wishes to take advantage of\n> either the `keep_redundant_commits` or `drop_redundant_commits` feature\n> must also specify `allow_empty`.\n> \n> The only consumer of `drop_redundant_commits` is `git-rebase`, which\n> already allows empty commits by default and simply always enables\n> `allow_empty`. `keep_redundant_commits` was also consumed by\n> `git-cherry-pick`, which had to specify `allow-empty` when\n> `keep_redundant_commits` was specified in order for the sequencer's\n> `allow_empty()` to actually respect `keep_redundant_commits`.\n\nI think it might be more persuasive to start the commit message by \nexplaining what user visible change you're trying to make and why rather \nthan concentrating on the implementation details.\n\n> The latter is an interesting case: As noted in the docs, this means that\n> `--keep-redundant-commits` implies `--allow-empty`, despite the two\n> having distinct, non-overlapping meanings:\n> \n> - `allow_empty` refers specifically to commits which start empty, as\n>    indicated by the documentation for `--allow-empty` within\n>    `git-cherry-pick`:\n> \n>    \"Note also, that use of this option only keeps commits that were\n>    initially empty (i.e. the commit recorded the same tree as its\n>    parent). Commits which are made empty due to a previous commit are\n>    dropped. To force the inclusion of those commits use\n>    --keep-redundant-commits.\"\n> \n> - `keep_redundant_commits` refers specifically to commits that do not\n>    start empty, but become empty due to the content already existing in\n>    the target history. This is indicated by the documentation for\n>    `--keep-redundant-commits` within `git-cherry-pick`:\n> \n>    \"If a commit being cherry picked duplicates a commit already in the\n>    current history, it will become empty. By default these redundant\n>    commits cause cherry-pick to stop so the user can examine the commit.\n>    This option overrides that behavior and creates an empty commit\n>    object. Implies --allow-empty.\"\n> \n> This implication of `--allow-empty` therefore seems incorrect: One\n> should be able to keep a commit that becomes empty without also being\n> forced to pick commits that start as empty.\n\nDo you have a practical example of where you want to keep the commits \nthat become empty but not the ones that start empty? I agree there is a \ndistinction but I think the common case is that the user wants to keep \nboth types of empty commit or none. I'm not against giving the user the \noption to keep one or the other if it is useful but I'm wary of changing \nthe default.\n\n> However, today, the\n> following series of commands would result in both the commit that became\n> empty and the commit that started empty being picked despite only\n> `--keep-redundant-commits` being specified:\n> \n>      git init\n>      echo \"a\" >test\n>      git add test\n>      git commit -m \"Initial commit\"\n>      echo \"b\" >test\n>      git commit -am \"a -> b\"\n>      git commit --allow-empty -m \"empty\"\n>      git cherry-pick --keep-redundant-commits HEAD^ HEAD\n> \n> The same cherry-pick with `--allow-empty` would fail on the redundant\n> commit, and with neither option would fail on the empty commit.\n> \n> In a future commit, an `--empty` option will be added to\n> `git-cherry-pick`, meaning that `drop_redundant_commits` will be\n> available in that command. For that to be possible with the current\n> implementation of the sequencer's `allow_empty()`, `git-cherry-pick`\n> would need to specify `allow_empty` with `drop_redundant_commits` as\n> well, which is an even less intuitive implication of `--allow-empty`: in\n> order to prevent redundant commits automatically, initially-empty\n> commits would need to be kept automatically.\n>\n> Instead, this commit rewrites the `allow_empty()` logic to remove the\n> over-arching requirement that `allow_empty` be specified in order to\n> reach any of the keep/drop behaviors. Only if the commit was originally\n> empty will `allow_empty` have an effect.\n\nrebase always sets \"opts->allow_empty = 1\" in \nbuiltin/rebase.c:get_replay_opts() and if the user passes \n--no-keep-empty drops commits that start empty from the list of commits \nto be picked. This is slightly confusing but is more efficient as we \ndon't do waste time trying to pick a commit we're going to drop. Can we \ndo something similar for \"git cherry-pick\"? When cherry-picking a \nsequence of commits I think it should just work because the code is \nshared with rebase, for a single commit we'd need to add a test to see \nif it is empty in single_pick() before calling pick_commits().\n\n> For some amount of backwards compatibility with the existing code and\n> tests, I have opted to preserve the behavior of returning 0 when:\n> \n> - `allow_empty` is specified, and\n> - either `is_index_unchanged` or `is_original_commit_empty` indicates an\n>    error\n\nI'm not sure that is a good idea as it is hiding an error that we didn't \nhit before because we returned early.\n\n> This is primarily out of caution -- I am not positive what downstream\n> impacts this might have.\n> \n> Note that this commit is a breaking change: `--keep-redundant-commits`\n> will no longer imply `--allow-empty`. It would be possible to maintain\n> the current behavior of `--keep-redundant-commits` implying\n> `--allow-empty` if it were needed to avoid a breaking change, but I\n> believe that decoupling them entirely is the correct behavior.\n\nThank you for being clear about the change in behavior, as I said above \nI'm wary of changing the default unless there is a compelling reason but \nI'm happy to support\n\n     git cherry-pick --keep-redundant-commits --no-allow-empty\n\nif it is needed.\n\n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n> \n> Disclaimer: This is my first contribution to the git project, and thus\n> my first attempt at submitting a patch via `git-send-email`. It is also\n> the first time I've touched worked in C in over a decade, and I really\n> didn't work with it much before that either. I welcome any and all\n> feedback on what I may have gotten wrong regarding the patch submission\n> process, the code changes, or my commit messages.\n\nAs others have mentioned I think it would be useful to have a \ncover-letter where we can discuss the aim of the patch series \nindependently of the individual patches.\n\n> This is the first in a series of commits that aims to introduce an\n> `--empty` option to `git-cherry-pick` that provides the same flexibility\n> as the `--empty` options for `git-rebase` and `git-am`, as well as\n> improve the consistency in the values and documentation for this option\n> across the three commands.\n\nI think that is a good aim\n\n> The main thing that may be controversial with this particular commit is\n> that I am proposing a breaking change. As described in the above\n> message, I do not think that it makes sense to tie `--allow-empty` and\n> `--keep-redundant-commits` together since they appear to be intended to\n> work with different types of empty commits. That being said, if it is\n> deemed unacceptable to make this breaking change, we can consider an\n> alternative approach where we maintain the behavior of\n> `--keep-redundant-commits` implying `--allow-empty`, while preventing\n> the need for the future `--empty=drop` to have that same implication.\n\nAs I said above I think it would be worth looking at what \"git rebase\" \ndoes to see if we can do the same thing for \"git cherry-pick\".\n\n > [...]> +test_expect_success 'cherry pick an empty non-ff commit with \n--keep-redundant-commits' '\n> +\tgit checkout main &&\n> +\ttest_must_fail git cherry-pick --keep-redundant-commits empty-change-branch\n\nWhen using test_must_fail it is a good idea to check the error message \nto make sure that it's failing for the reason we expect (see patch 4).\n\nBest Wishes\n\nPhillip\n"},{"id":"487256","messageId":"192892be-262c-487e-bb1d-6e50c01e2d66@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-3-brianmlyles@gmail.com","subject":"Re: [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-23T14:24:15Z","receivedAt":"2024-01-23T14:24:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n> \n> Both of these pages document very similar `--empty` options, but with\n> different styles. This commit aims to make them more consistent.\n\nI think that's reasonable though the options they are worded as doing \ndifferent things. For \"am\" it talks about the patch being empty - i.e. a \npatch of an empty commit whereas for \"rebase\" the option applies to \nnon-empty commits that become empty. What does \"am\" do if you try to \napply a patch whose changes are already present?\n\nIf you're aiming for consistency then it would be worth listing the \npossible values in the same order for each command.\n\n\n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index b4526ca246..3ee85f6d86 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -293,13 +293,20 @@ See also INCOMPATIBLE OPTIONS below.\n>   \tHow to handle commits that are not empty to start and are not\n>   \tclean cherry-picks of any upstream commit, but which become\n>   \tempty after rebasing (because they contain a subset of already\n> -\tupstream changes).  With drop (the default), commits that\n> -\tbecome empty are dropped.  With keep, such commits are kept.\n> -\tWith ask (implied by `--interactive`), the rebase will halt when\n> -\tan empty commit is applied allowing you to choose whether to\n> -\tdrop it, edit files more, or just commit the empty changes.\n> +\tupstream changes):\n> ++\n> +--\n> +`drop`;;\n> +\tThe empty commit will be dropped. This is the default behavior.\n\nI think it would be clearer to say \"The commit\" - I found \"The empty \ncommit\" confusing as the commit that is being picked isn't empty.\n\n> +`keep`;;\n> +\tThe empty commit will be kept.\n> +`ask`;;\n> +\tThe rebase will halt when the empty commit is applied, allowing you to\n> +\tchoose whether to drop it, edit files more, or just commit the empty\n> +\tchanges. This option is implied when `--interactive` is specified.\n>   \tOther options, like `--exec`, will use the default of drop unless\n>   \t`-i`/`--interactive` is explicitly specified.\n\nThanks for adding a bit more detail about the default, however it looks \nto me like we keep commits that become empty when --exec is specified\n\n\tif (options.empty == EMPTY_UNSPECIFIED) {\n\t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n\t\t\toptions.empty = EMPTY_STOP;\n\t\telse if (options.exec.nr > 0)\n\t\t\toptions.empty = EMPTY_KEEP;\n\t\telse\n\t\t\toptions.empty = EMPTY_DROP;\n\t}\n\nOff the top of my head I'm not sure why or if that is a good idea.\n\nBest Wishes\n\nPhillip\n"},{"id":"487257","messageId":"7032ae96-602f-499d-8430-a5dc3864d1bb@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-4-brianmlyles@gmail.com","subject":"Re: [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-23T14:24:53Z","receivedAt":"2024-01-23T14:24:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n> \n> When `git-am` got its own `--empty` option in 7c096b8d61 (am: support\n> --empty=<option> to handle empty patches, 2021-12-09), `stop` was used\n> instead of `ask`. `stop` is a more accurate term for describing what\n> really happens,\n\nI can see your reasoning but I think of stopping as git's way of asking \nwhat to do so I'm not sure if \"stop\" is better than \"ask\". I don't know \nhow we ended up with two different terms - the prior art is \"ask\" so \nmaybe we should change \"am --empty\" instead. Lets see what others think.\n\nIt would be helpful to mention the tests in the commit message - we end \nup with a mixture of \"--empty=ask\" and \"--empty=stop\" I assume that is \nby design\n\n> and consistency is good. This commit updates\n> `git-rebase` to also use `stop`, while keeping `ask` as a deprecated\n> synonym.\n\nIf we're deprecating \"ask\" do we want to print a warning recommending \n\"stop\" instead?\n\nBest Wishes\n\nPhillip\n"},{"id":"487258","messageId":"b897771e-c60e-4e41-bfae-3bcfdd832be1@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-5-brianmlyles@gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-23T14:25:43Z","receivedAt":"2024-01-23T14:25:44Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\n\nOn 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n> \n> As with `git-rebase` and `git-am`, `git-cherry-pick` can result in a\n> commit being made redundant if the content from the picked commit is\n> already present in the target history. However, `git-cherry-pick` does\n> not have the same options available that `git-rebase` and `git-am` have.\n> \n> There are three things that can be done with these redundant commits:\n> drop them, keep them, or have the cherry-pick stop and wait for the user\n> to take an action. `git-rebase` has the `--empty` option added in commit\n> e98c4269c8 (rebase (interactive-backend): fix handling of commits that\n> become empty, 2020-02-15), which handles all three of these scenarios.\n> Similarly, `git-am` got its own `--empty` in 7c096b8d61 (am: support\n> --empty=<option> to handle empty patches, 2021-12-09).\n> \n> `git-cherry-pick`, on the other hand, only supports two of the three\n> possiblities: Keep the redundant commits via `--keep-redundant-commits`,\n> or have the cherry-pick fail by not specifying that option. There is no\n> way to automatically drop redundant commits.\n> \n> In order to bring `git-cherry-pick` more in-line with `git-rebase` and\n> `git-am`, this commit adds an `--empty` option to `git-cherry-pick`. It\n> has the same three options (keep, drop, and stop), and largely behaves\n> the same. The notable difference is that for `git-cherry-pick`, the\n> default will be `stop`, which maintains the current behavior when the\n> option is not specified.\n\nThanks for the well explained commit message\n\n> The `--keep-redundant-commits` option will be documented as a deprecated\n> synonym of `--empty=keep`, and will be supported for backwards\n> compatibility for the time being.\n\nI'm not sure if we need to deprecate it as in \"it will be removed in the \nfuture\" or just reduce it prominence in favor of --empty\n\n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n>   Documentation/git-cherry-pick.txt | 28 ++++++++++++++++++-------\n>   builtin/revert.c                  | 35 ++++++++++++++++++++++++++++++-\n>   sequencer.c                       |  6 ++++++\n>   t/t3505-cherry-pick-empty.sh      | 26 ++++++++++++++++++++++-\n>   4 files changed, 86 insertions(+), 9 deletions(-)\n> \n> diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\n> index 806295a730..8c20a10d4b 100644\n> --- a/Documentation/git-cherry-pick.txt\n> +++ b/Documentation/git-cherry-pick.txt\n> @@ -132,23 +132,37 @@ effect to your index in a row.\n>   \tkeeps commits that were initially empty (i.e. the commit recorded the\n>   \tsame tree as its parent).  Commits which are made empty due to a\n>   \tprevious commit will cause the cherry-pick to fail.  To force the\n> -\tinclusion of those commits use `--keep-redundant-commits`.\n> +\tinclusion of those commits use `--empty=keep`.\n>   \n>   --allow-empty-message::\n>   \tBy default, cherry-picking a commit with an empty message will fail.\n>   \tThis option overrides that behavior, allowing commits with empty\n>   \tmessages to be cherry picked.\n>   \n> ---keep-redundant-commits::\n> -\tIf a commit being cherry picked duplicates a commit already in the\n> -\tcurrent history, it will become empty.  By default these\n> -\tredundant commits cause `cherry-pick` to stop so the user can\n> -\texamine the commit. This option overrides that behavior and\n> -\tcreates an empty commit object. Note that use of this option only\n> +--empty=(stop|drop|keep)::\n> +\tHow to handle commits being cherry-picked that are redundant with\n> +\tchanges already in the current history.\n> ++\n> +--\n> +`stop`;;\n\nI'm still on the fence about \"stop\" vs \"ask\". I see in your tests you've \naccidentally used \"ask\" which makes me wonder if that is the more \nfamiliar term for users who probably use \"git rebase\" more often than \n\"git am\".\n\n> +\tThe cherry-pick will stop when the empty commit is applied, allowing\n> +\tyou to examine the commit. This is the default behavior.\n> +`drop`;;\n> +\tThe empty commit will be dropped.\n> +`keep`;;\n> +\tThe empty commit will be kept. Note that use of this option only\n>   \tresults in an empty commit when the commit was not initially empty,\n>   \tbut rather became empty due to a previous commit. Commits that were\n>   \tinitially empty will cause the cherry-pick to fail. To force the\n>   \tinclusion of those commits use `--allow-empty`.\n> +--\n> ++\n> +Note that commits which start empty will cause the cherry-pick to fail (unless\n> +`--allow-empty` is specified).\n> ++\n> +\n> +--keep-redundant-commits::\n> +\tDeprecated synonym for `--empty=keep`.\n>   \n>   --strategy=<strategy>::\n>   \tUse the given merge strategy.  Should only be used once.\n> diff --git a/builtin/revert.c b/builtin/revert.c\n> index b2cfde7a87..1491c45e26 100644\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -45,6 +45,30 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n>   \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n>   }\n>   \n> +enum empty_action {\n> +\tSTOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of a cherry-pick */\n> +\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n> +\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n> +};\n> +\n> +static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n> +{\n> +\tint *opt_value = opt->value;\n> +\n> +\tBUG_ON_OPT_NEG(unset);\n> +\n> +\tif (!strcmp(arg, \"stop\"))\n> +\t\t*opt_value = STOP_ON_EMPTY_COMMIT;\n> +\telse if (!strcmp(arg, \"drop\"))\n> +\t\t*opt_value = DROP_EMPTY_COMMIT;\n> +\telse if (!strcmp(arg, \"keep\"))\n> +\t\t*opt_value = KEEP_EMPTY_COMMIT;\n> +\telse\n> +\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n> +\n> +\treturn 0;\n> +}\n> +\n>   static int option_parse_m(const struct option *opt,\n>   \t\t\t  const char *arg, int unset)\n>   {\n> @@ -87,6 +111,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n>   \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n>   \tconst char *me = action_name(opts);\n>   \tconst char *cleanup_arg = NULL;\n> +\tenum empty_action empty_opt;\n>   \tint cmd = 0;\n>   \tstruct option base_options[] = {\n>   \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n> @@ -116,7 +141,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n>   \t\t\tOPT_BOOL(0, \"ff\", &opts->allow_ff, N_(\"allow fast-forward\")),\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, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"deprecated: use --empty=keep instead\")),\n> +\t\t\tOPT_CALLBACK_F(0, \"empty\", &empty_opt, \"(stop|drop|keep)\",\n> +\t\t\t\t       N_(\"how to handle commits that become empty\"),\n> +\t\t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n>   \t\t\tOPT_END(),\n>   \t\t};\n>   \t\toptions = parse_options_concat(options, cp_extra);\n> @@ -136,6 +164,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n>   \tprepare_repo_settings(the_repository);\n>   \tthe_repository->settings.command_requires_full_index = 0;\n>   \n> +\tif (opts->action == REPLAY_PICK) {\n> +\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n> +\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n> +\t}\n\nThe code changes look good but I think we want to update \nverify_opt_compatible() to check for \"--empty\" being combined with \n\"--continue\" etc. as well.\n\n>   \tif (cleanup_arg) {\n>   \t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n>   \t\topts->explicit_cleanup = 1;\n> diff --git a/sequencer.c b/sequencer.c\n> index 582bde8d46..c49c27c795 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -2934,6 +2934,9 @@ static int populate_opts_cb(const char *key, const char *value,\n>   \telse if (!strcmp(key, \"options.allow-empty-message\"))\n>   \t\topts->allow_empty_message =\n>   \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n> +\telse if (!strcmp(key, \"options.drop-redundant-commits\"))\n> +\t\topts->drop_redundant_commits =\n> +\t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n>   \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n>   \t\topts->keep_redundant_commits =\n>   \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n> @@ -3478,6 +3481,9 @@ static int save_opts(struct replay_opts *opts)\n>   \tif (opts->allow_empty_message)\n>   \t\tres |= git_config_set_in_file_gently(opts_file,\n>   \t\t\t\t\"options.allow-empty-message\", \"true\");\n> +\tif (opts->drop_redundant_commits)\n> +\t\tres |= git_config_set_in_file_gently(opts_file,\n> +\t\t\t\t\"options.drop-redundant-commits\", \"true\");\n\nIt is good that we're saving the option - it would be good to add a test \nto check that we remember --empty after stopping for a conflict resolution.\n\n>   \tif (opts->keep_redundant_commits)\n>   \t\tres |= git_config_set_in_file_gently(opts_file,\n>   \t\t\t\t\"options.keep-redundant-commits\", \"true\");\n> diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\n> index 6adfd25351..ae0cf7886a 100755\n> --- a/t/t3505-cherry-pick-empty.sh\n> +++ b/t/t3505-cherry-pick-empty.sh\n> @@ -89,7 +89,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n>   \tgit commit -m \"add file2 on the side\"\n>   '\n>   \n> -test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n> +test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n>   \tgit reset --hard &&\n>   \tgit checkout fork^0 &&\n>   \ttest_must_fail git cherry-pick main\n> @@ -104,4 +104,28 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n>   \ttest_cmp expect actual\n>   '\n>   \n> +test_expect_success 'cherry-pick a no-op with --empty=ask' '\n> +\tgit reset --hard &&\n> +\tgit checkout fork^0 &&\n> +\ttest_must_fail git cherry-pick --empty=ask main\n\nThis is an example of why it is a good idea to check the error message \nwhen using \"test_must_fail\" as here the test will fail due to a bad \nvalue passed to \"--empty\" rather than for the reason we want the test to \ncheck. It would be good to add a separate test to check that we reject \ninvalid \"--empty\" values.\n\n> +'\n> +\n> +test_expect_success 'cherry-pick a no-op with --empty=drop' '\n> +\tgit reset --hard &&\n> +\tgit checkout fork^0 &&\n> +\tgit cherry-pick --empty=drop main &&\n> +\tgit show -s --format=%s >actual &&\n> +\techo \"add file2 on the side\" >expect &&\n> +\ttest_cmp expect actual\n\nI think you could simplify this by using test_commit_message\n\nBest Wishes\n\nPhillip\n\n"},{"id":"487270","messageId":"xmqqa5owot07.fsf@gitster.g","threadId":"60764","inReplyTo":"CAHPHrSd8rLj_TDE11dYQW+51--8YC4rumnfT+v2bYr+K7AMQrQ@mail.gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-23T17:32:24Z","receivedAt":"2024-01-23T17:32:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brian Lyles <brianmlyles@gmail.com> writes:\n\n> I do see that there are ~130 files with trailing whitespace in maint\n> today, though I suspect that most of those are not intentional.\n\nI got curious and took a look at files that has hits with \"lines\nthat end with SP or HT\":\n\n    $ git grep -l -e '[  ]$'\n\nThere are some that can be cleaned up, but many of them are\nintentional.\n\nFor example, CODE_OF_CONDUCT.md has these two (shown with $$$)\nI think can be removed without breaking markdown:\n\n    Community Impact Guidelines were inspired by $$$\n    [Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\n    For answers to common questions about this code of conduct, see the FAQ at\n    [https://www.contributor-covenant.org/faq][FAQ]. Translations are available $$$\n    at [https://www.contributor-covenant.org/translations][translations].\n\nThe one in Documentation/user-manual.txt is on borderline.  They\nappear in a sample output from a command the user is typing (again,\n$$$ shows where the SP at the end of line is):\n\n    diff --git a/init-db.c b/init-db.c\n    index 65898fa..b002dc6 100644\n    --- a/init-db.c\n    +++ b/init-db.c\n    @@ -7,7 +7,7 @@\n     $$$\n     int main(int argc, char **argv)\n    ...\n\nAs its purpose is to show human readers what they see in their\nterminal should _look_ like, we _could_ do without the single space\non these otherwise empty lines, which denotes an empty line that\nhasn't changed in the diff output.  But it would no longer match\nbyte-for-byte with what we are trying to illustrate.\n\nThere are many hits from the above grep in t/t[0-9][0-9][0-9][0-9]/\ndirectories; these are canonical/expected output used in tests and\nthe spaces at the end of these lines are expected.\n"},{"id":"487274","messageId":"xmqq8r4gnd3c.fsf@gitster.g","threadId":"60764","inReplyTo":"b897771e-c60e-4e41-bfae-3bcfdd832be1@gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-23T18:01:27Z","receivedAt":"2024-01-23T18:01:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Thanks for the well explained commit message\n\n;-)\n\n>> The `--keep-redundant-commits` option will be documented as a deprecated\n>> synonym of `--empty=keep`, and will be supported for backwards\n>> compatibility for the time being.\n>\n> I'm not sure if we need to deprecate it as in \"it will be removed in\n> the future\" or just reduce it prominence in favor of --empty\n\nTrue, especially since --empty=keep is much less descriptive and the\npart after \"note that ...\" below will take a long time before\nsticking in readers' brain.\n\n>> +--empty=(stop|drop|keep)::\n>> +\tHow to handle commits being cherry-picked that are redundant with\n>> +\tchanges already in the current history.\n\nIt might make it easier to understand if we moved the description in\n'keep' that says something about --allow-empty here, as it should\napply to other two choices if I understand correctly.  Let me try:\n\n    This option specifies how a commit that is not originally empty\n    but becomes a no-op when cherry-picked due to earlier changes\n    already applied or already in the current history.  Regardless\n    of this this option, `cherry-pick` will fail on a commit that is\n    empty in the original history---see `--allow-empty` to pass them\n    intact.\n\nor something.  Then the description of `keep` can become just as\nshort as other two, e.g. a single sentence \"The commit that becomes\nempty will be kept\".\n\n>> ...\n>> +\tThe cherry-pick will stop when the empty commit is applied, allowing\n>> +\tyou to examine the commit. This is the default behavior.\n>> +`drop`;;\n>> +\tThe empty commit will be dropped.\n>> +`keep`;;\n>> +\tThe empty commit will be kept. Note that use of this option only\n>>   \tresults in an empty commit when the commit was not initially empty,\n>>   \tbut rather became empty due to a previous commit. Commits that were\n>>   \tinitially empty will cause the cherry-pick to fail. To force the\n>>   \tinclusion of those commits use `--allow-empty`.\n>> +--\n>> ++\n>> +Note that commits which start empty will cause the cherry-pick to fail (unless\n>> +`--allow-empty` is specified).\n"},{"id":"487275","messageId":"xmqqy1cfnca7.fsf@gitster.g","threadId":"60764","inReplyTo":"7bf5036b-9f55-4451-a13c-8a2c815dfbb7@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-23T18:18:56Z","receivedAt":"2024-01-23T18:19:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> This implication of `--allow-empty` therefore seems incorrect: One\n>> should be able to keep a commit that becomes empty without also being\n>> forced to pick commits that start as empty.\n>\n> Do you have a practical example of where you want to keep the commits\n> that become empty but not the ones that start empty? I agree there is\n> a distinction but I think the common case is that the user wants to\n> keep both types of empty commit or none. I'm not against giving the\n> user the option to keep one or the other if it is useful but I'm wary\n> of changing the default.\n\nThis may not a new issue introduced by this series, but one thing I\nwould be worried about the usability of the keep-redundant is that I\nknow it takes more than one tries of cherry-picking of the same\nseries, at least to me, to get a series right.  The initial attempt\nmay make some commit empty and thanks to --keep-redundant they will\nbe kept, but I'll inevitably find more things I need to tweak and\ncherry-pick the resulting series, possibly on a different base.  And\nto this second round of cherry-pick, these \"were not, but now have\nbecome empty\" commits appear empty from the start.\n"},{"id":"487277","messageId":"xmqqmssvnb8d.fsf_-_@gitster.g","threadId":"60764","inReplyTo":"xmqqa5owot07.fsf@gitster.g","subject":"Subject: [PATCH] CoC: whitespace fix","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-23T18:41:38Z","receivedAt":"2024-01-23T18:41:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> For example, CODE_OF_CONDUCT.md has these two (shown with $$$)\n> I think can be removed without breaking markdown:\n>\n>     Community Impact Guidelines were inspired by $$$\n>     [Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n>\n>     For answers to common questions about this code of conduct, see the FAQ at\n>     [https://www.contributor-covenant.org/faq][FAQ]. Translations are available $$$\n>     at [https://www.contributor-covenant.org/translations][translations].\n\n\nBefore I forget...\n\n------ >8 ----------- >8 ----------- >8 ----------- >8 ------\nSubject: [PATCH] CoC: whitespace fix\n\nFix two lines with trailing whitespaces.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n CODE_OF_CONDUCT.md | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md\nindex 0215b1fd4c..e58917c50a 100644\n--- a/CODE_OF_CONDUCT.md\n+++ b/CODE_OF_CONDUCT.md\n@@ -130,11 +130,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],\n version 2.0, available at\n [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].\n \n-Community Impact Guidelines were inspired by \n+Community Impact Guidelines were inspired by\n [Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n \n For answers to common questions about this code of conduct, see the FAQ at\n-[https://www.contributor-covenant.org/faq][FAQ]. Translations are available \n+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available\n at [https://www.contributor-covenant.org/translations][translations].\n \n [homepage]: https://www.contributor-covenant.org\n-- \n2.43.0-386-ge02ecfcc53\n\n"},{"id":"487278","messageId":"xmqqede7navt.fsf@gitster.g","threadId":"60764","inReplyTo":"xmqqa5owot07.fsf@gitster.g","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-23T18:49:10Z","receivedAt":"2024-01-23T18:49:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I got curious and took a look at files that has hits with \"lines\n> that end with SP or HT\":\n>\n>     $ git grep -l -e '[  ]$'\n\nAnother more interest way to check is to do this:\n\n    $ git diff --check $(git hash-object --stdin -t tree </dev/null)\n\nwhich will not just catch trailing whitespaces but other whitespace\nerrors.  Good thing is that the projects can even customize the rules\nused for various paths using the attributes mechanism.\n\nHere is a sample patch based on what the above command line found.\n\n------ >8 ----------- >8 ----------- >8 ----------- >8 ------\nSubject: [PATCH] docs: a few whitespace fixes\n\nSome documentation files have 8-space indented lines where other\nindented lines in the same list use a single HT to indent.  As\nDocumentation/.gitattributes says *.txt files use the default\nwhitespace rules, let's fix some of them as a practice.\n\nNote that git-add documentation has other instances of space\nindented lines, but they are samples of manually aligned output\nof the \"git add -i\" command and it would be better to keep them\nas-is.  Which in turn may mean we may want to loosen the whitespace\nrules for some parts of the documentation files, but we currently do\nnot have such a feature.  The attribute based whitespace rules apply\nto the whole file.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/diff-format.txt | 8 ++++----\n Documentation/git-add.txt     | 2 +-\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git c/Documentation/diff-format.txt w/Documentation/diff-format.txt\nindex a3ae8747a2..bcaf9ca608 100644\n--- c/Documentation/diff-format.txt\n+++ w/Documentation/diff-format.txt\n@@ -8,16 +8,16 @@ These commands all compare two sets of things; what is\n compared differs:\n \n git-diff-index <tree-ish>::\n-        compares the <tree-ish> and the files on the filesystem.\n+\tcompares the <tree-ish> and the files on the filesystem.\n \n git-diff-index --cached <tree-ish>::\n-        compares the <tree-ish> and the index.\n+\tcompares the <tree-ish> and the index.\n \n git-diff-tree [-r] <tree-ish-1> <tree-ish-2> [<pattern>...]::\n-        compares the trees named by the two arguments.\n+\tcompares the trees named by the two arguments.\n \n git-diff-files [<pattern>...]::\n-        compares the index and the files on the filesystem.\n+\tcompares the index and the files on the filesystem.\n \n The \"git-diff-tree\" command begins its output by printing the hash of\n what is being compared. After that, all the commands print one output\ndiff --git c/Documentation/git-add.txt w/Documentation/git-add.txt\nindex ed44c1cb31..e1a5c27acd 100644\n--- c/Documentation/git-add.txt\n+++ w/Documentation/git-add.txt\n@@ -73,7 +73,7 @@ in linkgit:gitglossary[7].\n \n -v::\n --verbose::\n-        Be verbose.\n+\tBe verbose.\n \n -f::\n --force::\n\n"},{"id":"487292","messageId":"CABPp-BEJN_gvBucfndfFbUUM+=0EwjZ=oWqGd4XsJBS9gNtoYA@mail.gmail.com","threadId":"60764","inReplyTo":"xmqqmssvnb8d.fsf_-_@gitster.g","subject":"Re: Subject: [PATCH] CoC: whitespace fix","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2024-01-24T03:06:30Z","receivedAt":"2024-01-24T03:06:45Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jan 23, 2024 at 10:41 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> > For example, CODE_OF_CONDUCT.md has these two (shown with $$$)\n> > I think can be removed without breaking markdown:\n> >\n> >     Community Impact Guidelines were inspired by $$$\n> >     [Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n> >\n> >     For answers to common questions about this code of conduct, see the FAQ at\n> >     [https://www.contributor-covenant.org/faq][FAQ]. Translations are available $$$\n> >     at [https://www.contributor-covenant.org/translations][translations].\n>\n>\n> Before I forget...\n>\n> ------ >8 ----------- >8 ----------- >8 ----------- >8 ------\n> Subject: [PATCH] CoC: whitespace fix\n>\n> Fix two lines with trailing whitespaces.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  CODE_OF_CONDUCT.md | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md\n> index 0215b1fd4c..e58917c50a 100644\n> --- a/CODE_OF_CONDUCT.md\n> +++ b/CODE_OF_CONDUCT.md\n> @@ -130,11 +130,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],\n>  version 2.0, available at\n>  [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].\n>\n> -Community Impact Guidelines were inspired by\n> +Community Impact Guidelines were inspired by\n>  [Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n>\n>  For answers to common questions about this code of conduct, see the FAQ at\n> -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available\n> +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available\n>  at [https://www.contributor-covenant.org/translations][translations].\n>\n>  [homepage]: https://www.contributor-covenant.org\n> --\n\nI'm always happy to see trailing whitespace removed.  :-)   LGTM.\n"},{"id":"487322","messageId":"7229fe62-cf9b-4829-80ec-c6b44c52163e@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-24T11:01:23Z","receivedAt":"2024-01-24T11:01:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nHere are some code comments now I've realized why we need to change it\n\nOn 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> From: Brian Lyles <brianmlyles@gmail.com>\n >   Documentation/git-cherry-pick.txt | 10 +++++++---\n >   builtin/revert.c                  |  4 ----\n >   sequencer.c                       | 18 ++++++++++--------\n >   t/t3505-cherry-pick-empty.sh      |  5 +++++\n >   4 files changed, 22 insertions(+), 15 deletions(-)\n >\n > diff --git a/Documentation/git-cherry-pick.txt \nb/Documentation/git-cherry-pick.txt\n > index fdcad3d200..806295a730 100644\n > --- a/Documentation/git-cherry-pick.txt\n > +++ b/Documentation/git-cherry-pick.txt\n > @@ -131,8 +131,8 @@ effect to your index in a row.\n >       even without this option.  Note also, that use of this option only\n >       keeps commits that were initially empty (i.e. the commit \nrecorded the\n >       same tree as its parent).  Commits which are made empty due to a\n > -    previous commit are dropped.  To force the inclusion of those \ncommits\n > -    use `--keep-redundant-commits`.\n > +    previous commit will cause the cherry-pick to fail.  To force the\n > +    inclusion of those commits use `--keep-redundant-commits`.\n >\n >   --allow-empty-message::\n >       By default, cherry-picking a commit with an empty message will \nfail.\n > @@ -144,7 +144,11 @@ effect to your index in a row.\n >       current history, it will become empty.  By default these\n >       redundant commits cause `cherry-pick` to stop so the user can\n >       examine the commit. This option overrides that behavior and\n > -    creates an empty commit object.  Implies `--allow-empty`.\n > +    creates an empty commit object. Note that use of this option only\n > +    results in an empty commit when the commit was not initially empty,\n > +    but rather became empty due to a previous commit. Commits that were\n > +    initially empty will cause the cherry-pick to fail. To force the\n > +    inclusion of those commits use `--allow-empty`.\n >\n >   --strategy=<strategy>::\n >       Use the given merge strategy.  Should only be used once.\n > diff --git a/builtin/revert.c b/builtin/revert.c\n > index e6f9a1ad26..b2cfde7a87 100644\n > --- a/builtin/revert.c\n > +++ b/builtin/revert.c\n > @@ -136,10 +136,6 @@ static int run_sequencer(int argc, const char \n**argv, const char *prefix,\n >       prepare_repo_settings(the_repository);\n >       the_repository->settings.command_requires_full_index = 0;\n >\n > -    /* implies allow_empty */\n > -    if (opts->keep_redundant_commits)\n > -        opts->allow_empty = 1;\n\nI'm wary of this, especially after Juino's comments in \nhttps://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/\n\nThe documentation changes look if we do decide to change the default.\n\n >       if (cleanup_arg) {\n >           opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n >           opts->explicit_cleanup = 1;\n > diff --git a/sequencer.c b/sequencer.c\n > index d584cac8ed..582bde8d46 100644\n > --- a/sequencer.c\n > +++ b/sequencer.c\n > @@ -1739,22 +1739,24 @@ static int allow_empty(struct repository *r,\n >        *\n >        * (4) we allow both.\n >        */\n\nThe comment above should be updated if we change the behavior of this \nfunction.\n\n > -    if (!opts->allow_empty)\n > -        return 0; /* let \"git commit\" barf as necessary */\n > -\n\nHere we stop returning early if allow_empty is not set - this allows us \nto apply allow_empty only to commits that start empty below.\n\n >       index_unchanged = is_index_unchanged(r);\n > -    if (index_unchanged < 0)\n > +    if (index_unchanged < 0) {\n > +        if (!opts->allow_empty)\n > +            return 0;\n >           return index_unchanged;\n > +    }\n\nI don't think we want to hide the error here or below from \noriginally_empty()\n\n >       if (!index_unchanged)\n >           return 0; /* we do not have to say --allow-empty */\n >\n > -    if (opts->keep_redundant_commits)\n > -        return 1;\n > -\n\nWe move this check so that we do not unconditionally keep commits that \nare initially empty when opts->keep_redundant_commits is set.\n\n >       originally_empty = is_original_commit_empty(commit);\n > -    if (originally_empty < 0)\n > +    if (originally_empty < 0) {\n > +        if (!opts->allow_empty)\n > +            return 0;\n >           return originally_empty;\n > +    }\n >       if (originally_empty)\n > +        return opts->allow_empty;\n\nNow opts->allow_empty only applies to commits that were originally empty\n\n > +    else if (opts->keep_redundant_commits)\n >           return 1;\n\nThis ensures we keep commits that become empty when \nopts->redundant_commits is set.\n\nThe changes to allow_empty() all looks good apart from hiding errors \nfrom index_unchanged() and originally_empty()\n\nBest Wishes\n\nPhillip\n"},{"id":"487323","messageId":"ec05c0e9-20ad-42ab-8858-cc4ae0847d5a@gmail.com","threadId":"60764","inReplyTo":"7bf5036b-9f55-4451-a13c-8a2c815dfbb7@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-24T11:01:35Z","receivedAt":"2024-01-24T11:01:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 23/01/2024 14:23, Phillip Wood wrote:\n\n> rebase always sets \"opts->allow_empty = 1\" in \n> builtin/rebase.c:get_replay_opts() and if the user passes \n> --no-keep-empty drops commits that start empty from the list of commits \n> to be picked. This is slightly confusing but is more efficient as we \n> don't do waste time trying to pick a commit we're going to drop. Can we \n> do something similar for \"git cherry-pick\"? When cherry-picking a \n> sequence of commits I think it should just work because the code is \n> shared with rebase, for a single commit we'd need to add a test to see \n> if it is empty in single_pick() before calling pick_commits().\n\nHaving thought about this again I don't think we can reuse the same \napproach as rebase because cherry-pick and rebase have different \nbehaviors. \"git rebase --no-keep-empty\" drops empty commits whereas \"git \ncherry-pick\" wants to error out if it sees an empty commit. So I think \nyour approach of reworking allow_empty() in the sequencer is the right \napproach.\n\nSorry for the confusion\n\nPhillip\n\n"},{"id":"487455","messageId":"CAHPHrSdOVoBPR9vJou_Bxmq=4QW_z6nhnzxfmZ1Am0i-GJuz4g@mail.gmail.com","threadId":"60764","inReplyTo":"192892be-262c-487e-bb1d-6e50c01e2d66@gmail.com","subject":"Re: [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-27T21:22:31Z","receivedAt":"2024-01-27T21:23:10Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> > From: Brian Lyles <brianmlyles@gmail.com>\n> >\n> > Both of these pages document very similar `--empty` options, but with\n> > different styles. This commit aims to make them more consistent.\n>\n> I think that's reasonable though the options they are worded as doing\n> different things. For \"am\" it talks about the patch being empty - i.e. a\n> patch of an empty commit whereas for \"rebase\" the option applies to\n> non-empty commits that become empty. What does \"am\" do if you try to\n> apply a patch whose changes are already present?\n\nHm -- as you mention, this does appear to have a different meaning for\ngit-am(1) than it does for git-rebase(1). Regardless of the `--empty`\nvalue passed to git-am(1), a non-empty patch that is already present\nappears to error and stop.\n\nThat is an unfortunate difference. I think that my updated version of\nthe git-am(1) docs is still easier to read, and preserves the original\nmeaning. So I'm inclined to say that it's still an improvement worth\nmaking, and perhaps my commit message should just clarify that.\nThoughts?\n\n> If you're aiming for consistency then it would be worth listing the\n> possible values in the same order for each command.\n\nThat makes sense. I had initially maintained the existing order in which\nthese were documented, keeping the default option first. I think that\nthe updated layout makes the order less relevant by making it easier to\nread and identify the default anyway.\n\nI could see alphabetical being better, though with the changes later in\nthis series we'd end up with the deprecated `ask` being first or\nout-or-order at the end. What are your thoughts on the ideal order for\nthese?\n\n> > diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> > index b4526ca246..3ee85f6d86 100644\n> > --- a/Documentation/git-rebase.txt\n> > +++ b/Documentation/git-rebase.txt\n> > @@ -293,13 +293,20 @@ See also INCOMPATIBLE OPTIONS below.\n> >       How to handle commits that are not empty to start and are not\n> >       clean cherry-picks of any upstream commit, but which become\n> >       empty after rebasing (because they contain a subset of already\n> > -     upstream changes).  With drop (the default), commits that\n> > -     become empty are dropped.  With keep, such commits are kept.\n> > -     With ask (implied by `--interactive`), the rebase will halt when\n> > -     an empty commit is applied allowing you to choose whether to\n> > -     drop it, edit files more, or just commit the empty changes.\n> > +     upstream changes):\n> > ++\n> > +--\n> > +`drop`;;\n> > +     The empty commit will be dropped. This is the default behavior.\n>\n> I think it would be clearer to say \"The commit\" - I found \"The empty\n> commit\" confusing as the commit that is being picked isn't empty.\n\nI could see that -- I'll adjust this for v2 of the patch.\n\n> > +`keep`;;\n> > +     The empty commit will be kept.\n> > +`ask`;;\n> > +     The rebase will halt when the empty commit is applied, allowing you to\n> > +     choose whether to drop it, edit files more, or just commit the empty\n> > +     changes. This option is implied when `--interactive` is specified.\n> >       Other options, like `--exec`, will use the default of drop unless\n> >       `-i`/`--interactive` is explicitly specified.\n>\n> Thanks for adding a bit more detail about the default, however it looks\n> to me like we keep commits that become empty when --exec is specified\n>\n>         if (options.empty == EMPTY_UNSPECIFIED) {\n>                 if (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n>                         options.empty = EMPTY_STOP;\n>                 else if (options.exec.nr > 0)\n>                         options.empty = EMPTY_KEEP;\n>                 else\n>                         options.empty = EMPTY_DROP;\n>         }\n>\n> Off the top of my head I'm not sure why or if that is a good idea.\n\nThe two lines indicating this behavior are actually pre-existing -- I\ndid not change them in this patch and thus didn't even think to fact\ncheck them.\n\nUpon testing this, I've confirmed that you are correct about the actual\nbehavior. I will address this in a separate commit in v2.\n\nThanks,\nBrian Lyles\n"},{"id":"487456","messageId":"CAHPHrSefHb7KddWNS4NS2bAFG9DFfKZ=Ue499+EqDT3myS_tEA@mail.gmail.com","threadId":"60764","inReplyTo":"7032ae96-602f-499d-8430-a5dc3864d1bb@gmail.com","subject":"Re: [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-27T21:49:32Z","receivedAt":"2024-01-27T21:50:11Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> > From: Brian Lyles <brianmlyles@gmail.com>\n> >\n> > When `git-am` got its own `--empty` option in 7c096b8d61 (am: support\n> > --empty=<option> to handle empty patches, 2021-12-09), `stop` was used\n> > instead of `ask`. `stop` is a more accurate term for describing what\n> > really happens,\n>\n> I can see your reasoning but I think of stopping as git's way of asking\n> what to do so I'm not sure if \"stop\" is better than \"ask\". I don't know\n> how we ended up with two different terms - the prior art is \"ask\" so\n> maybe we should change \"am --empty\" instead. Lets see what others think.\n\nThe suggestion to use 'stop' instead of 'ask' for rebase was initially\nElijah's[1], which I agreed with. I am certainly open to others'\nopinions here though, and am content with whatever is decided. I am\nmostly aiming for consistency between git-rebase(1), git-am(1), and\nultimately git-cherry-pick(1).\n\n[1]: https://lore.kernel.org/git/CABPp-BGJfvBhO_zEX8nLoa8WNsjmwvtZ2qOjmYm9iPoZg4SwPw@mail.gmail.com/\n\n> It would be helpful to mention the tests in the commit message - we end\n> up with a mixture of \"--empty=ask\" and \"--empty=stop\" I assume that is\n> by design\n\nYou are correct -- the intent being to ensure that `--ask` continues\nworking for as long as it is supported. I'll add this to the message in\nv2.\n\n> > and consistency is good. This commit updates\n> > `git-rebase` to also use `stop`, while keeping `ask` as a deprecated\n> > synonym.\n>\n> If we're deprecating \"ask\" do we want to print a warning recommending\n> \"stop\" instead?\n\nThat makes sense -- I will include a warning for this in v2.\n\nThanks,\nBrian Lyles\n"},{"id":"487457","messageId":"CAHPHrSeKY2Ou9VCq6rtADTOwycc0KCTPaCCwtqf94yLi0wj0OQ@mail.gmail.com","threadId":"60764","inReplyTo":"7229fe62-cf9b-4829-80ec-c6b44c52163e@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-27T23:30:02Z","receivedAt":"2024-01-27T23:30:41Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"[+cc Junio]\n\nHi Phillip\n\nOn Tue, Jan 23, 2024 at 8:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Brian\n>\n> Let me start by saying that overall I'm impressed with the quality of\n> this submission. I've left quite a few comments but for a first patch\n> series it is very good.\n\nThank you for the kind words!\n\n> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> > From: Brian Lyles <brianmlyles@gmail.com>\n> >\n> > Previously, a consumer of the sequencer that wishes to take advantage of\n> > either the `keep_redundant_commits` or `drop_redundant_commits` feature\n> > must also specify `allow_empty`.\n> >\n> > The only consumer of `drop_redundant_commits` is `git-rebase`, which\n> > already allows empty commits by default and simply always enables\n> > `allow_empty`. `keep_redundant_commits` was also consumed by\n> > `git-cherry-pick`, which had to specify `allow-empty` when\n> > `keep_redundant_commits` was specified in order for the sequencer's\n> > `allow_empty()` to actually respect `keep_redundant_commits`.\n>\n> I think it might be more persuasive to start the commit message by\n> explaining what user visible change you're trying to make and why rather\n> than concentrating on the implementation details.\n\nI struggled a bit with this initially because the motivation behind the\nchange in this particular commit was driven by a technical issue in my\nmind. The side-effect with git-cherry-pick(s) `--allow-empty` and\n`--keep-redundant-commits` was mildly problematic, but less concerning\nthat the future problem that we'd have once git-cherry-pick(1) got the\nmore robust `--empty` option in a later commit in this series.\n\nI think my problem came down to this commit trying to solve two problems\nat once -- the underlying technical concern _and_ the git-cherry-pick(1)\nbehavior.\n\nIn v2, I intend to break this commit into two:\n\n- Update `allow_empty()` to not require `allow_empty`, but without\n  actually changing any consumers (and thus without making any\n  functional change)\n- Update git-cherry-pick(1) such that `--keep-redundant-commits` no\n  longer implies `--allow-empty`.\n\nThis allows me to better justify the technical change technically and\nthe functional change functionally, while also making it easier to drop\nthe functional change if we decide that a breaking change is not\nwarranted to address this.\n\n> Do you have a practical example of where you want to keep the commits\n> that become empty but not the ones that start empty? I agree there is a\n> distinction but I think the common case is that the user wants to keep\n> both types of empty commit or none. I'm not against giving the user the\n> option to keep one or the other if it is useful but I'm wary of changing\n> the default.\n\nThat practical example is documented in the initial discussion[1], which\nI should have ought to have linked in a cover letter for this series\n(and will do so in v2). I'll avoid copying the details here, but we'd\nvery much like to be able to programmatically drop the commits that\nbecome empty when doing the automated cherry-pick described there.\n\n[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/\n\n> rebase always sets \"opts->allow_empty = 1\" in\n> builtin/rebase.c:get_replay_opts() and if the user passes\n> --no-keep-empty drops commits that start empty from the list of commits\n> to be picked. This is slightly confusing but is more efficient as we\n> don't do waste time trying to pick a commit we're going to drop. Can we\n> do something similar for \"git cherry-pick\"? When cherry-picking a\n> sequence of commits I think it should just work because the code is\n> shared with rebase, for a single commit we'd need to add a test to see\n> if it is empty in single_pick() before calling pick_commits().\n\nJust noting here for future readers here that you sent a followup email\nindicating this was not viable:\n\n> On Wed, Jan 24, 2024 at 5:01 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Having thought about this again I don't think we can reuse the same\n> approach as rebase because cherry-pick and rebase have different\n> behaviors. \"git rebase --no-keep-empty\" drops empty commits whereas \"git\n> cherry-pick\" wants to error out if it sees an empty commit. So I think\n> your approach of reworking allow_empty() in the sequencer is the right\n> approach.\n>\n> Sorry for the confusion\n\nNo worries. If you have any suggestions for how I might better explain\nthese changes in the commit message, please let me know.\n\n> > For some amount of backwards compatibility with the existing code and\n> > tests, I have opted to preserve the behavior of returning 0 when:\n> >\n> > - `allow_empty` is specified, and\n> > - either `is_index_unchanged` or `is_original_commit_empty` indicates an\n> >    error\n>\n> I'm not sure that is a good idea as it is hiding an error that we didn't\n> hit before because we returned early.\n\nI think you're right -- Previously the error could not have been hit,\nbut now it can. An error is still an error, and we should handle it\nregardless of how `allow_empty` was set. I'll address this in v2 by\nsimply returning the error.\n\n> > Note that this commit is a breaking change: `--keep-redundant-commits`\n> > will no longer imply `--allow-empty`. It would be possible to maintain\n> > the current behavior of `--keep-redundant-commits` implying\n> > `--allow-empty` if it were needed to avoid a breaking change, but I\n> > believe that decoupling them entirely is the correct behavior.\n>\n> Thank you for being clear about the change in behavior, as I said above\n> I'm wary of changing the default unless there is a compelling reason but\n> I'm happy to support\n>\n>      git cherry-pick --keep-redundant-commits --no-allow-empty\n>\n> if it is needed.\n\nI totally understand being wary here.\n\nI've certainly convinced myself that having the future `--empty=drop`\nbehavior introduced later in this patch should not imply\n`--allow-empty`.\n\nI also _think_ that the existing behavior of `--keep-redundant-commits`\nis probably technically not ideal or correct, but could be convinced\nthat changing it now is not worthwhile. I will defer to group consensus\nhere.\n\n> > Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> > ---\n> >\n> > Disclaimer: This is my first contribution to the git project, and thus\n> > my first attempt at submitting a patch via `git-send-email`. It is also\n> > the first time I've touched worked in C in over a decade, and I really\n> > didn't work with it much before that either. I welcome any and all\n> > feedback on what I may have gotten wrong regarding the patch submission\n> > process, the code changes, or my commit messages.\n>\n> As others have mentioned I think it would be useful to have a\n> cover-letter where we can discuss the aim of the patch series\n> independently of the individual patches.\n\nAbsolutely, will do in v2.\n\n>  > [...]> +test_expect_success 'cherry pick an empty non-ff commit with\n> --keep-redundant-commits' '\n> > +     git checkout main &&\n> > +     test_must_fail git cherry-pick --keep-redundant-commits empty-change-branch\n>\n> When using test_must_fail it is a good idea to check the error message\n> to make sure that it's failing for the reason we expect (see patch 4).\n\nThanks for the tip, I'll add this in v2.\n\nOn Wed, Jan 24, 2024 at 5:01 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> Here are some code comments now I've realized why we need to change it\n>\n> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n> > From: Brian Lyles <brianmlyles@gmail.com>\n>  >   Documentation/git-cherry-pick.txt | 10 +++++++---\n>  >   builtin/revert.c                  |  4 ----\n>  >   sequencer.c                       | 18 ++++++++++--------\n>  >   t/t3505-cherry-pick-empty.sh      |  5 +++++\n>  >   4 files changed, 22 insertions(+), 15 deletions(-)\n>  >\n>  > diff --git a/Documentation/git-cherry-pick.txt\n> b/Documentation/git-cherry-pick.txt\n>  > index fdcad3d200..806295a730 100644\n>  > --- a/Documentation/git-cherry-pick.txt\n>  > +++ b/Documentation/git-cherry-pick.txt\n>  > @@ -131,8 +131,8 @@ effect to your index in a row.\n>  >       even without this option.  Note also, that use of this option only\n>  >       keeps commits that were initially empty (i.e. the commit\n> recorded the\n>  >       same tree as its parent).  Commits which are made empty due to a\n>  > -    previous commit are dropped.  To force the inclusion of those\n> commits\n>  > -    use `--keep-redundant-commits`.\n>  > +    previous commit will cause the cherry-pick to fail.  To force the\n>  > +    inclusion of those commits use `--keep-redundant-commits`.\n>  >\n>  >   --allow-empty-message::\n>  >       By default, cherry-picking a commit with an empty message will\n> fail.\n>  > @@ -144,7 +144,11 @@ effect to your index in a row.\n>  >       current history, it will become empty.  By default these\n>  >       redundant commits cause `cherry-pick` to stop so the user can\n>  >       examine the commit. This option overrides that behavior and\n>  > -    creates an empty commit object.  Implies `--allow-empty`.\n>  > +    creates an empty commit object. Note that use of this option only\n>  > +    results in an empty commit when the commit was not initially empty,\n>  > +    but rather became empty due to a previous commit. Commits that were\n>  > +    initially empty will cause the cherry-pick to fail. To force the\n>  > +    inclusion of those commits use `--allow-empty`.\n>  >\n>  >   --strategy=<strategy>::\n>  >       Use the given merge strategy.  Should only be used once.\n>  > diff --git a/builtin/revert.c b/builtin/revert.c\n>  > index e6f9a1ad26..b2cfde7a87 100644\n>  > --- a/builtin/revert.c\n>  > +++ b/builtin/revert.c\n>  > @@ -136,10 +136,6 @@ static int run_sequencer(int argc, const char\n> **argv, const char *prefix,\n>  >       prepare_repo_settings(the_repository);\n>  >       the_repository->settings.command_requires_full_index = 0;\n>  >\n>  > -    /* implies allow_empty */\n>  > -    if (opts->keep_redundant_commits)\n>  > -        opts->allow_empty = 1;\n>\n> I'm wary of this, especially after Juino's comments in\n> https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/\n\nAs noted above, I've split this commit into two, and am open to\ndiscussing dropping the functional change to `--keep-redundant-commits`\n\n>  >       if (cleanup_arg) {\n>  >           opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n>  >           opts->explicit_cleanup = 1;\n>  > diff --git a/sequencer.c b/sequencer.c\n>  > index d584cac8ed..582bde8d46 100644\n>  > --- a/sequencer.c\n>  > +++ b/sequencer.c\n>  > @@ -1739,22 +1739,24 @@ static int allow_empty(struct repository *r,\n>  >        *\n>  >        * (4) we allow both.\n>  >        */\n>\n> The comment above should be updated if we change the behavior of this\n> function.\n\nOf course, good catch.\n\n> I don't think we want to hide the error here or below from\n> originally_empty()\n>\n>  >       if (!index_unchanged)\n>  >           return 0; /* we do not have to say --allow-empty */\n>  >\n>  > -    if (opts->keep_redundant_commits)\n>  > -        return 1;\n>  > -\n\nAgreed, will address in v2 as mentioned above.\n\nThanks,\nBrian Lyles\n"},{"id":"487458","messageId":"CAHPHrSf+joHe6ikErHLgWrk-_qjSROS-dXCHagxWGDAF=2deDg@mail.gmail.com","threadId":"60764","inReplyTo":"b897771e-c60e-4e41-bfae-3bcfdd832be1@gmail.com","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-27T23:56:31Z","receivedAt":"2024-01-27T23:57:10Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"[+cc Junio]\n\nOn Tue, Jan 23, 2024 at 8:25 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Brian\n>\n>\n> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n>\n> > The `--keep-redundant-commits` option will be documented as a deprecated\n> > synonym of `--empty=keep`, and will be supported for backwards\n> > compatibility for the time being.\n>\n> I'm not sure if we need to deprecate it as in \"it will be removed in the\n> future\" or just reduce it prominence in favor of --empty\n\nThis is also related to Junio's comment:\n\n> On Tue, Jan 23, 2024 at 12:01 PM Junio C Hamano <gitster@pobox.com>\nwrote:\n>\n> True, especially since --empty=keep is much less descriptive and the\n> part after \"note that ...\" below will take a long time before\n> sticking in readers' brain.\n\nMy primary motivation here was simply for consistency with `--empty` for\nboth git-rebase(1) and git-am(1). In theory, I am not opposed to\nupdating this patch to instead simply add a `--drop-redundant-commits`\noption if we feel that provides better usability. However, I think that\nthe consistency would be better.\n\nI will happily defer to the group consensus here, with the options I see\nbeing:\n\n1. No deprecation: just make `--keep-redundant-commits` a synonym of\n  `--empty=keep`\n2. Soft deprecation: Give a warning when `--keep-redundant-commits` is\n  used\n3. Add `--drop-redundant-commits` instead of `--empty`\n\nMy preference would be 2, followed by 1 and then 3.\n\n> I'm still on the fence about \"stop\" vs \"ask\". I see in your tests you've\n> accidentally used \"ask\" which makes me wonder if that is the more\n> familiar term for users who probably use \"git rebase\" more often than\n> \"git am\".\n\nOh, thank you for catching that. The cause here was actually because I\nhad initially written these tests prior to adding the \"ask -> stop\"\nchange rather than familiarity. I simply failed to update the tests\nafter moving things around.\n\n> The code changes look good but I think we want to update\n> verify_opt_compatible() to check for \"--empty\" being combined with\n> \"--continue\" etc. as well.\n\nIt looks like `--keep-redundant-commits` was not being included in these\nchecks previously. I suspect that to be an oversight though.\n\nI can add this for v2.\n\n>\n> >       if (cleanup_arg) {\n> >               opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n> >               opts->explicit_cleanup = 1;\n> > diff --git a/sequencer.c b/sequencer.c\n> > index 582bde8d46..c49c27c795 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -2934,6 +2934,9 @@ static int populate_opts_cb(const char *key, const char *value,\n> >       else if (!strcmp(key, \"options.allow-empty-message\"))\n> >               opts->allow_empty_message =\n> >                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n> > +     else if (!strcmp(key, \"options.drop-redundant-commits\"))\n> > +             opts->drop_redundant_commits =\n> > +                     git_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n> >       else if (!strcmp(key, \"options.keep-redundant-commits\"))\n> >               opts->keep_redundant_commits =\n> >                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n> > @@ -3478,6 +3481,9 @@ static int save_opts(struct replay_opts *opts)\n> >       if (opts->allow_empty_message)\n> >               res |= git_config_set_in_file_gently(opts_file,\n> >                               \"options.allow-empty-message\", \"true\");\n> > +     if (opts->drop_redundant_commits)\n> > +             res |= git_config_set_in_file_gently(opts_file,\n> > +                             \"options.drop-redundant-commits\", \"true\");\n>\n> It is good that we're saving the option - it would be good to add a test\n> to check that we remember --empty after stopping for a conflict resolution.\n\nI can add a test for this in v2\n\n> >       if (opts->keep_redundant_commits)\n> >               res |= git_config_set_in_file_gently(opts_file,\n> >                               \"options.keep-redundant-commits\", \"true\");\n> > diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\n> > index 6adfd25351..ae0cf7886a 100755\n> > --- a/t/t3505-cherry-pick-empty.sh\n> > +++ b/t/t3505-cherry-pick-empty.sh\n> > @@ -89,7 +89,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n> >       git commit -m \"add file2 on the side\"\n> >   '\n> >\n> > -test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n> > +test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n> >       git reset --hard &&\n> >       git checkout fork^0 &&\n> >       test_must_fail git cherry-pick main\n> > @@ -104,4 +104,28 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n> >       test_cmp expect actual\n> >   '\n> >\n> > +test_expect_success 'cherry-pick a no-op with --empty=ask' '\n> > +     git reset --hard &&\n> > +     git checkout fork^0 &&\n> > +     test_must_fail git cherry-pick --empty=ask main\n>\n> This is an example of why it is a good idea to check the error message\n> when using \"test_must_fail\" as here the test will fail due to a bad\n> value passed to \"--empty\" rather than for the reason we want the test to\n> check. It would be good to add a separate test to check that we reject\n> invalid \"--empty\" values.\n\nAn excellent catch, thank you. Will be addressed in v2\n\n> > +'\n> > +\n> > +test_expect_success 'cherry-pick a no-op with --empty=drop' '\n> > +     git reset --hard &&\n> > +     git checkout fork^0 &&\n> > +     git cherry-pick --empty=drop main &&\n> > +     git show -s --format=%s >actual &&\n> > +     echo \"add file2 on the side\" >expect &&\n> > +     test_cmp expect actual\n>\n> I think you could simplify this by using test_commit_message\n\nThanks for pointing that function out -- you're absolutely right. Will\nbe addressed in v2.\n\n\nThanks for the review,\nBrian Lyles\n"},{"id":"487459","messageId":"CAHPHrScPSSCLC4FjUWoboOV_FX4c7huJSFsGWeYk+O21+AYE7w@mail.gmail.com","threadId":"60764","inReplyTo":"xmqq8r4gnd3c.fsf@gitster.g","subject":"Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-28T00:07:36Z","receivedAt":"2024-01-28T00:08:14Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Junio\n\nOn Tue, Jan 23, 2024 at 12:01 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n>\n> > Thanks for the well explained commit message\n>\n> ;-)\n>\n> >> The `--keep-redundant-commits` option will be documented as a deprecated\n> >> synonym of `--empty=keep`, and will be supported for backwards\n> >> compatibility for the time being.\n> >\n> > I'm not sure if we need to deprecate it as in \"it will be removed in\n> > the future\" or just reduce it prominence in favor of --empty\n>\n> True, especially since --empty=keep is much less descriptive and the\n> part after \"note that ...\" below will take a long time before\n> sticking in readers' brain.\n\nI responded to this in my reply to Phillip, and CC'd you there.\n\n> >> +--empty=(stop|drop|keep)::\n> >> +    How to handle commits being cherry-picked that are redundant with\n> >> +    changes already in the current history.\n>\n> It might make it easier to understand if we moved the description in\n> 'keep' that says something about --allow-empty here, as it should\n> apply to other two choices if I understand correctly.  Let me try:\n>\n>     This option specifies how a commit that is not originally empty\n>     but becomes a no-op when cherry-picked due to earlier changes\n>     already applied or already in the current history.  Regardless\n>     of this this option, `cherry-pick` will fail on a commit that is\n>     empty in the original history---see `--allow-empty` to pass them\n>     intact.\n>\n> or something.  Then the description of `keep` can become just as\n> short as other two, e.g. a single sentence \"The commit that becomes\n> empty will be kept\".\n\nThank you for this suggestion. You are correct that the difference\nbetween `--empty` and `allow-empty` is relevant regardless of the option\nselected by the user.\n\nIn fact, this entire tidbit is somewhat duplicative with the note I\nalready added after the options:\n\n> Note that commits which start empty will cause the cherry-pick to fail\n> (unless `--allow-empty` is specified).\n\nI'll clean this up in v2. Here's what I am thinking currently:\n\n> --empty=(stop|drop|keep)::\n>     How to handle commits being cherry-picked that are redundant with\n>     changes already in the current history.\n> +\n> --\n> `stop`;;\n>     The cherry-pick will stop when the commit is applied, allowing\n>     you to examine the commit. This is the default behavior.\n> `drop`;;\n>     The commit will be dropped.\n> `keep`;;\n>     The commit will be kept.\n> --\n> +\n> Note that this option species how to handle a commit that was not initially\n> empty, but rather became empty due to a previous commit. Commits that were\n> initially empty will cause the cherry-pick to fail. To force the inclusion of\n> those commits, use `--allow-empty`.\n> +\n\nThank you,\nBrian Lyles\n"},{"id":"487466","messageId":"CAHPHrSf=UkR9+hMfb7pp5Y6uHqa2pBrEf+cTLJv=z=BOFdL3rw@mail.gmail.com","threadId":"60764","inReplyTo":"CAHPHrSeKY2Ou9VCq6rtADTOwycc0KCTPaCCwtqf94yLi0wj0OQ@mail.gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-01-28T16:36:42Z","receivedAt":"2024-01-28T16:37:21Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"[+cc Junio]\n\nOn Sat, Jan 27, 2024 at 5:30 PM Brian Lyles <brianmlyles@gmail.com> wrote:\n\n> > > For some amount of backwards compatibility with the existing code and\n> > > tests, I have opted to preserve the behavior of returning 0 when:\n> > >\n> > > - `allow_empty` is specified, and\n> > > - either `is_index_unchanged` or `is_original_commit_empty` indicates an\n> > >    error\n> >\n> > I'm not sure that is a good idea as it is hiding an error that we didn't\n> > hit before because we returned early.\n>\n> I think you're right -- Previously the error could not have been hit,\n> but now it can. An error is still an error, and we should handle it\n> regardless of how `allow_empty` was set. I'll address this in v2 by\n> simply returning the error.\n\nAs I dig into this more, I'm noticing that this may have unintended side\neffects that I'm unsure of. After making this change, I noticed a couple\nof failures in the cherry-pick test suite. The others may be a knock-on\nof this initial failure:\n\n    expecting success of 3501.8 'cherry-pick on unborn branch':\n            git checkout --orphan unborn &&\n            git rm --cached -r . &&\n            rm -rf * &&\n            git cherry-pick initial &&\n            git diff --quiet initial &&\n            test_cmp_rev ! initial HEAD\n\n    A       extra_file\n    Switched to a new branch 'unborn'\n    rm 'extra_file'\n    rm 'spoo'\n    error: could not resolve HEAD commit\n    fatal: cherry-pick failed\n    not ok 8 - cherry-pick on unborn branch\n    #\n    #               git checkout --orphan unborn &&\n    #               git rm --cached -r . &&\n    #               rm -rf * &&\n    #               git cherry-pick initial &&\n    #               git diff --quiet initial &&\n    #               test_cmp_rev ! initial HEAD\n    #\n\nIt looks like this is caused specifically by not hiding the error from\n`index_unchanged`\n\n    index_unchanged = is_index_unchanged(r);\n    if (index_unchanged < 0) {\n        return index_unchanged;\n    }\n\nAt this point, my inexperience with the git codebase and these more edge\ncase scenarios starts to show. I'm unsure how to best approach this. It\nseems that exposing these errors when `allow_empty` is not set causes\nthe entire cherry-pick to fail in situations where it would not\npreviously. Here is what that same test looks like prior to any of my\nchanges from this series:\n\n    expecting success of 3501.8 'cherry-pick on unborn branch':\n            git checkout --orphan unborn &&\n            git rm --cached -r . &&\n            rm -rf * &&\n            git cherry-pick initial &&\n            git diff --quiet initial &&\n            test_cmp_rev ! initial HEAD\n\n    A       extra_file\n    Switched to a new branch 'unborn'\n    rm 'extra_file'\n    rm 'spoo'\n    [unborn 38e6d75] initial\n     Author: A U Thor <author@example.com>\n     Date: Thu Apr 7 15:13:13 2005 -0700\n     1 file changed, 15 insertions(+)\n     create mode 100644 oops\n    ok 8 - cherry-pick on unborn branch\n\nConceptually I definitely agree that it seems odd to hide these errors\njust because `allow_empty` was not set, but I fear this to be a breaking\nchange for which I don't have a good understanding of the impact.\n\nAny guidance here would be appreciated.\n\nThank you,\nBrian Lyles\n"},{"id":"487498","messageId":"b5213705-4cd6-40ef-8c5f-32b214534b8b@gmail.com","threadId":"60764","inReplyTo":"CAHPHrSf=UkR9+hMfb7pp5Y6uHqa2pBrEf+cTLJv=z=BOFdL3rw@mail.gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-01-29T10:55:26Z","receivedAt":"2024-01-29T10:55:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 28/01/2024 16:36, Brian Lyles wrote:\n> [+cc Junio]\n> \n> On Sat, Jan 27, 2024 at 5:30 PM Brian Lyles <brianmlyles@gmail.com> wrote:\n> \n>>>> For some amount of backwards compatibility with the existing code and\n>>>> tests, I have opted to preserve the behavior of returning 0 when:\n>>>>\n>>>> - `allow_empty` is specified, and\n>>>> - either `is_index_unchanged` or `is_original_commit_empty` indicates an\n>>>>     error\n>>>\n>>> I'm not sure that is a good idea as it is hiding an error that we didn't\n>>> hit before because we returned early.\n>>\n>> I think you're right -- Previously the error could not have been hit,\n>> but now it can. An error is still an error, and we should handle it\n>> regardless of how `allow_empty` was set. I'll address this in v2 by\n>> simply returning the error.\n> \n> As I dig into this more, I'm noticing that this may have unintended side\n> effects that I'm unsure of. After making this change, I noticed a couple\n> of failures in the cherry-pick test suite. The others may be a knock-on\n> of this initial failure:\n> \n>      expecting success of 3501.8 'cherry-pick on unborn branch':\n>              git checkout --orphan unborn &&\n>              git rm --cached -r . &&\n>              rm -rf * &&\n>              git cherry-pick initial &&\n>              git diff --quiet initial &&\n>              test_cmp_rev ! initial HEAD\n> \n>      A       extra_file\n>      Switched to a new branch 'unborn'\n>      rm 'extra_file'\n>      rm 'spoo'\n>      error: could not resolve HEAD commit\n>      fatal: cherry-pick failed\n>      not ok 8 - cherry-pick on unborn branch\n>      #\n>      #               git checkout --orphan unborn &&\n>      #               git rm --cached -r . &&\n>      #               rm -rf * &&\n>      #               git cherry-pick initial &&\n>      #               git diff --quiet initial &&\n>      #               test_cmp_rev ! initial HEAD\n>      #\n> \n> It looks like this is caused specifically by not hiding the error from\n> `index_unchanged`\n\nOh dear, that's a pain. I haven't checked but suspect we already hit \nthis when running\n\n     git cherry-pick --allow-empty\n\non an orphan checkout. In do_pick_commit() we treat an error reading \nHEAD as an unborn branch so I think we could do the same here. If the \nbranch is unborn then we can use the_hash_algo->empty_tree as the tree \nto compare to.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"487733","messageId":"8ff4650c-f84f-41bd-a46c-3b845ff29b70@gmail.com","threadId":"60764","inReplyTo":"CAHPHrSeKY2Ou9VCq6rtADTOwycc0KCTPaCCwtqf94yLi0wj0OQ@mail.gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-01T10:57:15Z","receivedAt":"2024-02-01T10:57:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 27/01/2024 23:30, Brian Lyles wrote:\n> On Tue, Jan 23, 2024 at 8:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n>>> From: Brian Lyles <brianmlyles@gmail.com>\n>>>\n>>> Previously, a consumer of the sequencer that wishes to take advantage of\n>>> either the `keep_redundant_commits` or `drop_redundant_commits` feature\n>>> must also specify `allow_empty`.\n>>>\n>>> The only consumer of `drop_redundant_commits` is `git-rebase`, which\n>>> already allows empty commits by default and simply always enables\n>>> `allow_empty`. `keep_redundant_commits` was also consumed by\n>>> `git-cherry-pick`, which had to specify `allow-empty` when\n>>> `keep_redundant_commits` was specified in order for the sequencer's\n>>> `allow_empty()` to actually respect `keep_redundant_commits`.\n>>\n>> I think it might be more persuasive to start the commit message by\n>> explaining what user visible change you're trying to make and why rather\n>> than concentrating on the implementation details.\n> \n> I struggled a bit with this initially because the motivation behind the\n> change in this particular commit was driven by a technical issue in my\n> mind. The side-effect with git-cherry-pick(s) `--allow-empty` and\n> `--keep-redundant-commits` was mildly problematic, but less concerning\n> that the future problem that we'd have once git-cherry-pick(1) got the\n> more robust `--empty` option in a later commit in this series.\n> \n> I think my problem came down to this commit trying to solve two problems\n> at once -- the underlying technical concern _and_ the git-cherry-pick(1)\n> behavior.\n> \n> In v2, I intend to break this commit into two:\n> \n> - Update `allow_empty()` to not require `allow_empty`, but without\n>    actually changing any consumers (and thus without making any\n>    functional change)\n> - Update git-cherry-pick(1) such that `--keep-redundant-commits` no\n>    longer implies `--allow-empty`.\n> \n> This allows me to better justify the technical change technically and\n> the functional change functionally, while also making it easier to drop\n> the functional change if we decide that a breaking change is not\n> warranted to address this.\n\nThat sounds like a good strategy. I'm wondering if we should not change \nthe behavior of `--keep-redundant-commits` to avoid breaking existing \nusers but have `--empty=keep|drop` not imply `--allow-empty`. What ever \nwe do we'll annoy someone. It is confusing to have subtly different \nbehaviors for `--keep-redundant-commits` and `--empty=keep` but it \navoids breaking existing users. If we change `--keep-redundant-commits` \nwe potentially upset existing users but we don't confuse others with the \nsubtle difference between the two.\n\n>> Do you have a practical example of where you want to keep the commits\n>> that become empty but not the ones that start empty? I agree there is a\n>> distinction but I think the common case is that the user wants to keep\n>> both types of empty commit or none. I'm not against giving the user the\n>> option to keep one or the other if it is useful but I'm wary of changing\n>> the default.\n> \n> That practical example is documented in the initial discussion[1], which\n> I should have ought to have linked in a cover letter for this series\n> (and will do so in v2). I'll avoid copying the details here, but we'd\n> very much like to be able to programmatically drop the commits that\n> become empty when doing the automated cherry-pick described there.\n> \n> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/\n\nMaybe I've missed something but that seems to be an argument for \nimplementing `--empty=drop` which is completely reasonable but doesn't \nexplain why someone using `--keep-redundant-commits` would want to keep \nthe commits that become empty while dropping the commits that start empty.\n\n> [...]\n>> Thank you for being clear about the change in behavior, as I said above\n>> I'm wary of changing the default unless there is a compelling reason but\n>> I'm happy to support\n>>\n>>       git cherry-pick --keep-redundant-commits --no-allow-empty\n>>\n>> if it is needed.\n> \n> I totally understand being wary here.\n> \n> I've certainly convinced myself that having the future `--empty=drop`\n> behavior introduced later in this patch should not imply\n> `--allow-empty`.\n\nI agree with that\n\n> I also _think_ that the existing behavior of `--keep-redundant-commits`\n> is probably technically not ideal or correct, but could be convinced\n> that changing it now is not worthwhile. I will defer to group consensus\n> here.\n\nThere is definitely an argument that the existing behavior conflates the \ntwo flavors of empty commit. I think one can also argue that the \nconflation is beneficial as most of the time users don't care about the \ndistinction when using `--keep-redundant-commits` and so it is not worth \nchanging the behavior of the existing option.\n\nI sounds like you've got a good plan for v2, I look forward to reading it.\n\nBest Wishes\n\nPhillip\n"},{"id":"487742","messageId":"0ed4a9b2-2318-4424-8173-42c0e61c8dda@gmail.com","threadId":"60764","inReplyTo":"CAHPHrSdOVoBPR9vJou_Bxmq=4QW_z6nhnzxfmZ1Am0i-GJuz4g@mail.gmail.com","subject":"Re: [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-01T14:02:28Z","receivedAt":"2024-02-01T14:02:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 27/01/2024 21:22, Brian Lyles wrote:\n> On Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n>>> From: Brian Lyles <brianmlyles@gmail.com>\n>>>\n>>> Both of these pages document very similar `--empty` options, but with\n>>> different styles. This commit aims to make them more consistent.\n>>\n>> I think that's reasonable though the options they are worded as doing\n>> different things. For \"am\" it talks about the patch being empty - i.e. a\n>> patch of an empty commit whereas for \"rebase\" the option applies to\n>> non-empty commits that become empty. What does \"am\" do if you try to\n>> apply a patch whose changes are already present?\n> \n> Hm -- as you mention, this does appear to have a different meaning for\n> git-am(1) than it does for git-rebase(1). Regardless of the `--empty`\n> value passed to git-am(1), a non-empty patch that is already present\n> appears to error and stop.\n> \n> That is an unfortunate difference. I think that my updated version of\n> the git-am(1) docs is still easier to read, and preserves the original\n> meaning. So I'm inclined to say that it's still an improvement worth\n> making, and perhaps my commit message should just clarify that.\n> Thoughts?\n\nYes I agree the change is worthwhile but I think it would benefit from \nan updated commit message.\n\n>> If you're aiming for consistency then it would be worth listing the\n>> possible values in the same order for each command.\n> \n> That makes sense. I had initially maintained the existing order in which\n> these were documented, keeping the default option first. I think that\n> the updated layout makes the order less relevant by making it easier to\n> read and identify the default anyway.\n> \n> I could see alphabetical being better, though with the changes later in\n> this series we'd end up with the deprecated `ask` being first or\n> out-or-order at the end. What are your thoughts on the ideal order for\n> these?\n\nAlphabetical sounds reasonable, we could sort on the non deprecated \nnames with stop and ask grouped together\n\ndrop;;\n     ...\nkeep;;\n     ...\nstop;;\nask;;\n     ...\n     `ask` is a deprecated synonym of `stop`\n\n>>> +`keep`;;\n>>> +     The empty commit will be kept.\n>>> +`ask`;;\n>>> +     The rebase will halt when the empty commit is applied, allowing you to\n>>> +     choose whether to drop it, edit files more, or just commit the empty\n>>> +     changes. This option is implied when `--interactive` is specified.\n>>>        Other options, like `--exec`, will use the default of drop unless\n>>>        `-i`/`--interactive` is explicitly specified.\n>>\n>> Thanks for adding a bit more detail about the default, however it looks\n>> to me like we keep commits that become empty when --exec is specified\n>>\n>>          if (options.empty == EMPTY_UNSPECIFIED) {\n>>                  if (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n>>                          options.empty = EMPTY_STOP;\n>>                  else if (options.exec.nr > 0)\n>>                          options.empty = EMPTY_KEEP;\n>>                  else\n>>                          options.empty = EMPTY_DROP;\n>>          }\n>>\n>> Off the top of my head I'm not sure why or if that is a good idea.\n> \n> The two lines indicating this behavior are actually pre-existing -- I\n> did not change them in this patch and thus didn't even think to fact\n> check them.\n> \n> Upon testing this, I've confirmed that you are correct about the actual\n> behavior. I will address this in a separate commit in v2.\n\nThanks, I'd missed that those were context lines in the diff.\n\nBest Wishes\n\nPhillip\n"},{"id":"487743","messageId":"20b6c81a-b1cf-4d10-980b-94698d7e62aa@gmail.com","threadId":"60764","inReplyTo":"CAHPHrSefHb7KddWNS4NS2bAFG9DFfKZ=Ue499+EqDT3myS_tEA@mail.gmail.com","subject":"Re: [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-01T14:02:35Z","receivedAt":"2024-02-01T14:02:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 27/01/2024 21:49, Brian Lyles wrote:\n> On Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:\n>>> From: Brian Lyles <brianmlyles@gmail.com>\n>>>\n>>> When `git-am` got its own `--empty` option in 7c096b8d61 (am: support\n>>> --empty=<option> to handle empty patches, 2021-12-09), `stop` was used\n>>> instead of `ask`. `stop` is a more accurate term for describing what\n>>> really happens,\n>>\n>> I can see your reasoning but I think of stopping as git's way of asking\n>> what to do so I'm not sure if \"stop\" is better than \"ask\". I don't know\n>> how we ended up with two different terms - the prior art is \"ask\" so\n>> maybe we should change \"am --empty\" instead. Lets see what others think.\n> \n> The suggestion to use 'stop' instead of 'ask' for rebase was initially\n> Elijah's[1], which I agreed with. I am certainly open to others'\n> opinions here though, and am content with whatever is decided. I am\n> mostly aiming for consistency between git-rebase(1), git-am(1), and\n> ultimately git-cherry-pick(1).\n> \n> [1]: https://lore.kernel.org/git/CABPp-BGJfvBhO_zEX8nLoa8WNsjmwvtZ2qOjmYm9iPoZg4SwPw@mail.gmail.com/\n\nThanks for the link, that is useful context\n\n>> It would be helpful to mention the tests in the commit message - we end\n>> up with a mixture of \"--empty=ask\" and \"--empty=stop\" I assume that is\n>> by design\n> \n> You are correct -- the intent being to ensure that `--ask` continues\n> working for as long as it is supported. I'll add this to the message in\n> v2.\n\nThat makes sense,\n\nThanks\n\nPhillip\n"},{"id":"488329","messageId":"17b26644b51ba1ec.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"8ff4650c-f84f-41bd-a46c-3b845ff29b70@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T04:34:14Z","receivedAt":"2024-02-10T04:34:17Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Thu, Feb 1, 2024 at 4:57 AM Phillip Wood <phillip.wood123@gmail.com>\nwrote:\n\n> That sounds like a good strategy. I'm wondering if we should not change \n> the behavior of `--keep-redundant-commits` to avoid breaking existing \n> users but have `--empty=keep|drop` not imply `--allow-empty`. What ever \n> we do we'll annoy someone. It is confusing to have subtly different \n> behaviors for `--keep-redundant-commits` and `--empty=keep` but it \n> avoids breaking existing users. If we change `--keep-redundant-commits` \n> we potentially upset existing users but we don't confuse others with the \n> subtle difference between the two.\n\nI am starting to come around to this approach for this particular series\njust to avoid anything potentially controversial holding it up.\n\n>>> Do you have a practical example of where you want to keep the commits\n>>> that become empty but not the ones that start empty? I agree there is a\n>>> distinction but I think the common case is that the user wants to keep\n>>> both types of empty commit or none. I'm not against giving the user the\n>>> option to keep one or the other if it is useful but I'm wary of changing\n>>> the default.\n>> \n>> That practical example is documented in the initial discussion[1], which\n>> I should have ought to have linked in a cover letter for this series\n>> (and will do so in v2). I'll avoid copying the details here, but we'd\n>> very much like to be able to programmatically drop the commits that\n>> become empty when doing the automated cherry-pick described there.\n>> \n>> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/\n> \n> Maybe I've missed something but that seems to be an argument for \n> implementing `--empty=drop` which is completely reasonable but doesn't \n> explain why someone using `--keep-redundant-commits` would want to keep \n> the commits that become empty while dropping the commits that start empty.\n\nNope, you didn't miss something -- I just didn't read properly.\n\nI don't have a concrete example here, but the behavior *feels* quite odd\nto me. But I suppose that's not a good enough reason to make a breaking\nchange.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"488331","messageId":"17b26a6fdb07139f.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"b5213705-4cd6-40ef-8c5f-32b214534b8b@gmail.com","subject":"Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T05:50:37Z","receivedAt":"2024-02-10T05:50:40Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Mon, Jan 29, 2024 at 4:55 AM Phillip Wood <phillip.wood123@gmail.com>\nwrote:\n\n>>>> I'm not sure that is a good idea as it is hiding an error that we didn't\n>>>> hit before because we returned early.\n>>>\n>>> I think you're right -- Previously the error could not have been hit,\n>>> but now it can. An error is still an error, and we should handle it\n>>> regardless of how `allow_empty` was set. I'll address this in v2 by\n>>> simply returning the error.\n>> \n>> As I dig into this more, I'm noticing that this may have unintended side\n>> effects that I'm unsure of. After making this change, I noticed a couple\n>> of failures in the cherry-pick test suite. The others may be a knock-on\n>> of this initial failure:\n>> \n>>      expecting success of 3501.8 'cherry-pick on unborn branch':\n>>              git checkout --orphan unborn &&\n>>              git rm --cached -r . &&\n>>              rm -rf * &&\n>>              git cherry-pick initial &&\n>>              git diff --quiet initial &&\n>>              test_cmp_rev ! initial HEAD\n>> \n>>      A       extra_file\n>>      Switched to a new branch 'unborn'\n>>      rm 'extra_file'\n>>      rm 'spoo'\n>>      error: could not resolve HEAD commit\n>>      fatal: cherry-pick failed\n>>      not ok 8 - cherry-pick on unborn branch\n>>      #\n>>      #               git checkout --orphan unborn &&\n>>      #               git rm --cached -r . &&\n>>      #               rm -rf * &&\n>>      #               git cherry-pick initial &&\n>>      #               git diff --quiet initial &&\n>>      #               test_cmp_rev ! initial HEAD\n>>      #\n>> \n>> It looks like this is caused specifically by not hiding the error from\n>> `index_unchanged`\n> \n> Oh dear, that's a pain. I haven't checked but suspect we already hit \n> this when running\n> \n>      git cherry-pick --allow-empty\n> \n> on an orphan checkout. In do_pick_commit() we treat an error reading \n> HEAD as an unborn branch so I think we could do the same here. If the \n> branch is unborn then we can use the_hash_algo->empty_tree as the tree \n> to compare to.\n\nYou're correct that `git cherry-pick --allow-empty` on an unborn branch\nhits the same error today, and that using `empty_tree` seems to resolve\nit. Thank you for this tip, I'll include a fix and corresponding test as\na separate commit in v2.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"488335","messageId":"20240210074859.552497-1-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 0/8] cherry-pick: add `--empty`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:48Z","receivedAt":"2024-02-10T07:49:32Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The ultimate goal of this series is to allow git-cherry-pick(1) to\nautomatically drop redundant commits. The mechanism chosen is an\n`--empty` option that provides the same flexibility as the `--empty`\noptions for git-rebase(1) and git-am(1).\n\nSome secondary goals are to improve the consistency in the values and\ndocumentation for this option across the three commands.\n\nSee \"Does extending `--empty` to git-cherry-pick make sense?\" [1] for\nsome context for why this option is desired in git-cherry-pick(1).\n\n[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com\n\nAlong the way, I (with some help from Elijah and Phillip) found a few\nother things in the docs and related sequencer code to clean up.\n\nBrian Lyles (8):\n  docs: address inaccurate `--empty` default with `--exec`\n  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n  rebase: update `--empty=ask` to `--empty=drop`\n  sequencer: treat error reading HEAD as unborn branch\n  sequencer: do not require `allow_empty` for redundant commit options\n  cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`\n  cherry-pick: enforce `--keep-redundant-commits` incompatibility\n  cherry-pick: add `--empty` for more robust redundant commit handling\n\n Documentation/git-am.txt                    | 20 ++++---\n Documentation/git-cherry-pick.txt           | 30 +++++++---\n Documentation/git-rebase.txt                | 26 ++++++---\n builtin/rebase.c                            | 16 +++--\n builtin/revert.c                            | 40 +++++++++++--\n sequencer.c                                 | 65 +++++++++++----------\n t/t3424-rebase-empty.sh                     | 55 ++++++++++++++++-\n t/t3501-revert-cherry-pick.sh               | 11 ++++\n t/t3505-cherry-pick-empty.sh                | 29 ++++++++-\n t/t3510-cherry-pick-sequence.sh             | 40 +++++++++++++\n t/t3515-cherry-pick-incompatible-options.sh | 48 +++++++++++++++\n 11 files changed, 312 insertions(+), 68 deletions(-)\n create mode 100755 t/t3515-cherry-pick-incompatible-options.sh\n\n-- \n2.43.0\n\n"},{"id":"488336","messageId":"20240210074859.552497-2-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 1/8] docs: address inaccurate `--empty` default with `--exec`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:49Z","receivedAt":"2024-02-10T07:49:33Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The documentation for git-rebase(1) indicates that using the `--exec`\noption will use `--empty=drop`. This is inaccurate: when `--interactive`\nis not explicitly provided, `--exec` results in `--empty=keep`\nbehaviors.\n\nCorrectly indicate the behavior of `--exec` using `--empty=keep` when\n`--interactive` is not specified.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\nReported-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nThis commit was not present in v1. It is a fix to some existing\ndocumentation that Phillip noticed was incorrect.\n\n\n Documentation/git-rebase.txt | 10 +++++-----\n t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 150734fb39..9d7397b696 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes).  With drop (the default), commits that\n \tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask (implied by `--interactive`), the rebase will halt when\n-\tan empty commit is applied allowing you to choose whether to\n-\tdrop it, edit files more, or just commit the empty changes.\n-\tOther options, like `--exec`, will use the default of drop unless\n-\t`-i`/`--interactive` is explicitly specified.\n+\tWith ask, the rebase will halt when an empty commit is applied\n+\tallowing you to choose whether to drop it, edit files more, or just\n+\tcommit the empty changes.\n+\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n+\tOtherwise, when the `--exec` option is used, the default becomes keep.\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 5e1045a0af..73ff35ced2 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '\n \ttest_path_is_missing .git/MERGE_MSG\n '\n\n+test_expect_success 'rebase --exec --empty=drop' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=drop upstream &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=keep upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec uses default of --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=ask' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.43.0\n\n"},{"id":"488337","messageId":"20240210074859.552497-3-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 2/8] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:50Z","receivedAt":"2024-02-10T07:49:35Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Both of these pages document very similar `--empty` options, but with\ndifferent styles. The exact behavior of these `--empty` options differs\nsomewhat, but consistent styling in the docs is still beneficial. This\ncommit aims to make them more consistent.\n\nBreak the possible values for `--empty` into separate sections for\nreadability. Alphabetical order is chosen for consistency.\n\nIn a future commit, we'll be documenting a new `--empty` option for\ngit-cherry-pick(1), making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nChanges since v1:\n- Options are now listed in alphabetical order per Phillip's\n  recommendation.\n\n\n Documentation/git-am.txt     | 20 +++++++++++++-------\n Documentation/git-rebase.txt | 25 ++++++++++++++++---------\n 2 files changed, 29 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-am.txt b/Documentation/git-am.txt\nindex e080458d6c..f852e0ba79 100644\n--- a/Documentation/git-am.txt\n+++ b/Documentation/git-am.txt\n@@ -66,13 +66,19 @@ OPTIONS\n --quoted-cr=<action>::\n \tThis flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).\n\n---empty=(stop|drop|keep)::\n-\tBy default, or when the option is set to 'stop', the command\n-\terrors out on an input e-mail message lacking a patch\n-\tand stops in the middle of the current am session. When this\n-\toption is set to 'drop', skip such an e-mail message instead.\n-\tWhen this option is set to 'keep', create an empty commit,\n-\trecording the contents of the e-mail message as its log.\n+--empty=(drop|keep|stop)::\n+\tHow to handle an e-mail message lacking a patch:\n++\n+--\n+`drop`;;\n+\tThe e-mail message will be skipped.\n+`keep`;;\n+\tAn empty commit will be created, with the contents of the e-mail\n+\tmessage as its log.\n+`stop`;;\n+\tThe command will fail, stopping in the middle of the current `am`\n+\tsession. This is the default behavior.\n+--\n\n -m::\n --message-id::\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 9d7397b696..68cdebd2aa 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n\n---empty=(drop|keep|ask)::\n+--empty=(ask|drop|keep)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n-\tupstream changes).  With drop (the default), commits that\n-\tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask, the rebase will halt when an empty commit is applied\n-\tallowing you to choose whether to drop it, edit files more, or just\n-\tcommit the empty changes.\n-\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n-\tOtherwise, when the `--exec` option is used, the default becomes keep.\n+\tupstream changes):\n++\n+--\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified.\n+`drop`;;\n+\tThe commit will be dropped. This is the default behavior.\n+`keep`;;\n+\tThe commit will be kept. This option is implied when `--exec` is\n+\tspecified unless `-i`/`--interactive` is also specified.\n+--\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\n@@ -698,7 +705,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(drop|keep|ask)` option for changing the behavior\n+also has an `--empty=(ask|drop|keep)` option for changing the behavior\n of handling commits that become empty.\n\n Directory rename detection\n-- \n2.43.0\n\n"},{"id":"488338","messageId":"20240210074859.552497-4-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:51Z","receivedAt":"2024-02-10T07:49:37Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09), `stop` was used\ninstead of `ask`. `stop` is a more accurate term for describing what\nreally happens, and consistency is good.\n\nUpdate git-rebase(1) to also use `stop`, while keeping `ask` as a\ndeprecated synonym. Update the tests to primarily use `stop`, but also\nensure that `ask` is still allowed.\n\nIn a future commit, we'll be adding a new `--empty` option for\ngit-cherry-pick(1) as well, making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\nReported-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/git-rebase.txt | 15 ++++++++-------\n builtin/rebase.c             | 16 ++++++++++------\n t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----\n 3 files changed, 34 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 68cdebd2aa..6f64084a95 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(ask|drop|keep)::\n+--empty=(drop|keep|stop)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes):\n +\n --\n-`ask`;;\n-\tThe rebase will halt when the commit is applied, allowing you to\n-\tchoose whether to drop it, edit files more, or just commit the empty\n-\tchanges. This option is implied when `-i`/`--interactive` is\n-\tspecified.\n `drop`;;\n \tThe commit will be dropped. This is the default behavior.\n `keep`;;\n \tThe commit will be kept. This option is implied when `--exec` is\n \tspecified unless `-i`/`--interactive` is also specified.\n+`stop`;;\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified. `ask` is a deprecated synonym of `stop`.\n --\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n@@ -705,7 +706,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(ask|drop|keep)` option for changing the behavior\n+also has an `--empty=(drop|keep|stop)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 4084a6abb8..3b9bb2fa06 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -58,7 +58,7 @@ enum empty_type {\n \tEMPTY_UNSPECIFIED = -1,\n \tEMPTY_DROP,\n \tEMPTY_KEEP,\n-\tEMPTY_ASK\n+\tEMPTY_STOP\n };\n \n enum action {\n@@ -954,10 +954,14 @@ static enum empty_type parse_empty_value(const char *value)\n \t\treturn EMPTY_DROP;\n \telse if (!strcasecmp(value, \"keep\"))\n \t\treturn EMPTY_KEEP;\n-\telse if (!strcasecmp(value, \"ask\"))\n-\t\treturn EMPTY_ASK;\n+\telse if (!strcasecmp(value, \"stop\"))\n+\t\treturn EMPTY_STOP;\n+\telse if (!strcasecmp(value, \"ask\")) {\n+\t\twarning(_(\"--empty=ask is deprecated; use '--empty=stop' instead.\"));\n+\t\treturn EMPTY_STOP;\n+\t}\n \n-\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"ask\\\".\"), value);\n+\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"stop\\\".\"), value);\n }\n \n static int parse_opt_keep_empty(const struct option *opt, const char *arg,\n@@ -1136,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"instead of ignoring them\"),\n \t\t\t      1, PARSE_OPT_HIDDEN),\n \t\tOPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),\n-\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|ask)\",\n+\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|stop)\",\n \t\t\t       N_(\"how to handle commits that become empty\"),\n \t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\tOPT_CALLBACK_F('k', \"keep-empty\", &options, NULL,\n@@ -1553,7 +1557,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (options.empty == EMPTY_UNSPECIFIED) {\n \t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n-\t\t\toptions.empty = EMPTY_ASK;\n+\t\t\toptions.empty = EMPTY_STOP;\n \t\telse if (options.exec.nr > 0)\n \t\t\toptions.empty = EMPTY_KEEP;\n \t\telse\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 73ff35ced2..1ee6b00fd5 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'rebase --merge --empty=stop' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --merge --empty=stop upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'rebase --merge --empty=ask' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --merge --empty=ask upstream &&\n@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive --empty=ask' '\n+test_expect_success 'rebase --interactive --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --interactive --empty=ask upstream &&\n+\ttest_must_fail git rebase --interactive --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive uses default of --empty=ask' '\n+test_expect_success 'rebase --interactive uses default of --empty=stop' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --interactive upstream &&\n \n@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --exec --empty=ask' '\n+test_expect_success 'rebase --exec --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n-- \n2.43.0\n\n"},{"id":"488339","messageId":"20240210074859.552497-5-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:52Z","receivedAt":"2024-02-10T07:49:38Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When using git-cherry-pick(1) with `--allow-empty` while on an unborn\nbranch, an error is thrown. This is inconsistent with the same\ncherry-pick when `--allow-empty` is not specified.\n\nTreat a failure reading HEAD as an unborn branch in\n`is_index_unchanged`. This is consistent with other sequencer logic such\nas `do_pick_commit`. When on an unborn branch, use the `empty_tree` as\nthe tree to compare against.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n\nThis is another new commit that was not present in v1.\n\nSee this comment[1] from Phillip for context.\n\n[1]: https://lore.kernel.org/git/b5213705-4cd6-40ef-8c5f-32b214534b8b@gmail.com/\n\n\n sequencer.c                   | 36 ++++++++++++++++++++---------------\n t/t3501-revert-cherry-pick.sh | 11 +++++++++++\n 2 files changed, 32 insertions(+), 15 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3cc88d8a80..b1b19512de 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -769,30 +769,36 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)\n \n static int is_index_unchanged(struct repository *r)\n {\n-\tstruct object_id head_oid, *cache_tree_oid;\n+\tstruct object_id head_oid, *cache_tree_oid, head_tree_oid;\n \tstruct commit *head_commit;\n \tstruct index_state *istate = r->index;\n \n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n-\t\treturn error(_(\"could not resolve HEAD commit\"));\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n+\t\t/*\n+\t\t * Treat an error reading HEAD as an unborn branch.\n+\t\t */\n+\t\thead_tree_oid = *the_hash_algo->empty_tree;\n+\t} else {\n+\t\thead_commit = lookup_commit(r, &head_oid);\n \n-\thead_commit = lookup_commit(r, &head_oid);\n+\t\t/*\n+\t\t * If head_commit is NULL, check_commit, called from\n+\t\t * lookup_commit, would have indicated that head_commit is not\n+\t\t * a commit object already.  repo_parse_commit() will return failure\n+\t\t * without further complaints in such a case.  Otherwise, if\n+\t\t * the commit is invalid, repo_parse_commit() will complain.  So\n+\t\t * there is nothing for us to say here.  Just return failure.\n+\t\t */\n+\t\tif (repo_parse_commit(r, head_commit))\n+\t\t\treturn -1;\n \n-\t/*\n-\t * If head_commit is NULL, check_commit, called from\n-\t * lookup_commit, would have indicated that head_commit is not\n-\t * a commit object already.  repo_parse_commit() will return failure\n-\t * without further complaints in such a case.  Otherwise, if\n-\t * the commit is invalid, repo_parse_commit() will complain.  So\n-\t * there is nothing for us to say here.  Just return failure.\n-\t */\n-\tif (repo_parse_commit(r, head_commit))\n-\t\treturn -1;\n+\t\thead_tree_oid = *get_commit_tree_oid(head_commit);\n+\t}\n \n \tif (!(cache_tree_oid = get_cache_tree_oid(istate)))\n \t\treturn -1;\n \n-\treturn oideq(cache_tree_oid, get_commit_tree_oid(head_commit));\n+\treturn oideq(cache_tree_oid, &head_tree_oid);\n }\n \n static int write_author_script(const char *message)\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex aeab689a98..390e0ed186 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -112,6 +112,17 @@ test_expect_success 'cherry-pick on unborn branch' '\n \ttest_cmp_rev ! initial HEAD\n '\n \n+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n+\tgit checkout main &&\n+\tgit branch -D unborn &&\n+\tgit checkout --orphan unborn &&\n+\tgit rm --cached -r . &&\n+\trm -rf * &&\n+\tgit cherry-pick initial --allow-empty &&\n+\tgit diff --quiet initial &&\n+\ttest_cmp_rev ! initial HEAD\n+'\n+\n test_expect_success 'cherry-pick \"-\" to pick from previous branch' '\n \tgit checkout unborn &&\n \ttest_commit to-pick actual content &&\n-- \n2.43.0\n\n"},{"id":"488340","messageId":"20240210074859.552497-6-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:53Z","receivedAt":"2024-02-10T07:49:40Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"A consumer of the sequencer that wishes to take advantage of either the\n`keep_redundant_commits` or `drop_redundant_commits` feature must also\nspecify `allow_empty`. However, these refer to two distinct types of\nempty commits:\n\n- `allow_empty` refers specifically to commits which start empty\n- `keep_redundant_commits` refers specifically to commits that do not\n  start empty, but become empty due to the content already existing in\n  the target history\n\nConceptually, there is no reason that the behavior for handling one of\nthese should be entangled with the other. It is particularly unintuitive\nto require `allow_empty` in order for `drop_redundant_commits` to have\nan effect: in order to prevent redundant commits automatically,\ninitially-empty commits would need to be kept automatically as well.\n\nInstead, rewrite the `allow_empty()` logic to remove the over-arching\nrequirement that `allow_empty` be specified in order to reach any of the\nkeep/drop behaviors. Only if the commit was originally empty will\n`allow_empty` have an effect.\n\nNote that no behavioral changes should result from this commit -- it\nmerely sets the stage for future commits. In one such future commit, an\n`--empty` option will be added to git-cherry-pick(1), meaning that\n`drop_redundant_commits` will be used by that command.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nThis is the first half of the first commit[1] in v1, which has now been\nsplit up. While the next commit may be considered somewhat\ncontroversial, this part of the change should not be.\n\n[1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/\n\n sequencer.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b1b19512de..3f41863dae 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1725,34 +1725,25 @@ static int allow_empty(struct repository *r,\n \tint index_unchanged, originally_empty;\n\n \t/*\n-\t * Four cases:\n+\t * For a commit that is initially empty, allow_empty determines if it\n+\t * should be kept or not\n \t *\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, and\n-\t *     just drop the ones that become empty\n-\t *\n-\t * (3) we allow ones that were initially empty, but\n-\t *     halt for the ones that become empty;\n-\t *\n-\t * (4) we allow both.\n+\t * For a commit that becomes empty, keep_redundant_commits and\n+\t * drop_redundant_commits determine whether the commit should be kept or\n+\t * dropped. If neither is specified, halt.\n \t */\n-\tif (!opts->allow_empty)\n-\t\treturn 0; /* let \"git commit\" barf as necessary */\n-\n \tindex_unchanged = is_index_unchanged(r);\n \tif (index_unchanged < 0)\n \t\treturn index_unchanged;\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n\n-\tif (opts->keep_redundant_commits)\n-\t\treturn 1;\n-\n \toriginally_empty = is_original_commit_empty(commit);\n \tif (originally_empty < 0)\n \t\treturn originally_empty;\n \tif (originally_empty)\n+\t\treturn opts->allow_empty;\n+\telse if (opts->keep_redundant_commits)\n \t\treturn 1;\n \telse if (opts->drop_redundant_commits)\n \t\treturn 2;\n-- \n2.43.0\n\n"},{"id":"488341","messageId":"20240210074859.552497-7-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:54Z","receivedAt":"2024-02-10T07:49:42Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"As noted in the git-cherry-pick(1) docs, `--keep-redundant-commits`\nimplies `--allow-empty`, despite the two having distinct,\nnon-overlapping meanings:\n\n- `allow_empty` refers specifically to commits which start empty, as\n  indicated by the documentation for `--allow-empty` within\n  git-cherry-pick(1):\n\n  \"Note also, that use of this option only keeps commits that were\n  initially empty (i.e. the commit recorded the same tree as its\n  parent). Commits which are made empty due to a previous commit are\n  dropped. To force the inclusion of those commits use\n  --keep-redundant-commits.\"\n\n- `keep_redundant_commits` refers specifically to commits that do not\n  start empty, but become empty due to the content already existing in\n  the target history. This is indicated by the documentation for\n  `--keep-redundant-commits` within git-cherry-pick(1):\n\n  \"If a commit being cherry picked duplicates a commit already in the\n  current history, it will become empty. By default these redundant\n  commits cause cherry-pick to stop so the user can examine the commit.\n  This option overrides that behavior and creates an empty commit\n  object. Implies --allow-empty.\"\n\nThis implication of `--allow-empty` therefore seems incorrect: One\nshould be able to keep a commit that becomes empty without also being\nforced to pick commits that start as empty. However, the following\nseries of commands results in both the commit that became empty and the\ncommit that started empty being picked, despite only\n`--keep-redundant-commits` being specified:\n\n    git init\n    echo \"a\" >test\n    git add test\n    git commit -m \"Initial commit\"\n    echo \"b\" >test\n    git commit -am \"a -> b\"\n    git commit --allow-empty -m \"empty\"\n    git cherry-pick --keep-redundant-commits HEAD^ HEAD\n\nThe same cherry-pick with `--allow-empty` would fail on the redundant\ncommit, and with neither option would fail on the empty commit.\n\nDo not imply `--allow-empty` when using `--keep-redundant-commits` with\ngit-cherry-pick(1).\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nThis is the second half of the first commit[1] in v1, which has now been\nsplit up.\n\nThis commit proposes a breaking change, albeit one that seems correct\nand relatively minor to me. If this change is deemed too controversial,\nI am prepared to drop it from the series. See Junio's[2] and\nPhillip's[3] comments on v1 for additional context.\n\n[1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/\n[2]: https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/\n[3]: https://lore.kernel.org/git/8ff4650c-f84f-41bd-a46c-3b845ff29b70@gmail.com/\n\n Documentation/git-cherry-pick.txt | 10 +++++++---\n builtin/revert.c                  |  4 ----\n t/t3505-cherry-pick-empty.sh      |  6 ++++++\n 3 files changed, 13 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex fdcad3d200..c88bb88822 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -131,8 +131,8 @@ effect to your index in a row.\n \teven without this option.  Note also, that use of this option only\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n-\tprevious commit are dropped.  To force the inclusion of those commits\n-\tuse `--keep-redundant-commits`.\n+\tprevious commit will cause the cherry-pick to fail.  To force the\n+\tinclusion of those commits, use `--keep-redundant-commits`.\n\n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n@@ -144,7 +144,11 @@ effect to your index in a row.\n \tcurrent history, it will become empty.  By default these\n \tredundant commits cause `cherry-pick` to stop so the user can\n \texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object.  Implies `--allow-empty`.\n+\tcreates an empty commit object. Note that use of this option only\n+\tresults in an empty commit when the commit was not initially empty,\n+\tbut rather became empty due to a previous commit. Commits that were\n+\tinitially empty will cause the cherry-pick to fail. To force the\n+\tinclusion of those commits use `--allow-empty`.\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 89821bab95..d83977e36e 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -134,10 +134,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n\n-\t/* implies allow_empty */\n-\tif (opts->keep_redundant_commits)\n-\t\topts->allow_empty = 1;\n-\n \tif (cleanup_arg) {\n \t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n \t\topts->explicit_cleanup = 1;\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex eba3c38d5a..2709cfc677 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -59,6 +59,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '\n \ttest_must_fail git cherry-pick empty-change-branch\n '\n\n+test_expect_success 'cherry pick an empty non-ff commit with --keep-redundant-commits' '\n+\tgit checkout main &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits empty-change-branch 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output\n+'\n+\n test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '\n \tgit checkout main &&\n \tgit cherry-pick --allow-empty empty-change-branch\n-- \n2.43.0\n\n"},{"id":"488342","messageId":"20240210074859.552497-8-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:55Z","receivedAt":"2024-02-10T07:49:43Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When `--keep-redundant-commits` was added in  b27cfb0d8d\n(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was\nnot marked as incompatible with the various operations needed to\ncontinue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and\n`--quit`).\n\nEnforce this incompatibility via `verify_opt_compatible` like we do for\nthe other various options.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nThis commit was not present in v1 either. It addresses an existing issue\nthat I noticed after Phillip pointed out the same deficiency for my new\n`--empty` option introduced in the ultimate commit in this series.\n\n[1]: https://lore.kernel.org/git/CAHPHrSf+joHe6ikErHLgWrk-_qjSROS-dXCHagxWGDAF=2deDg@mail.gmail.com/\n\n builtin/revert.c                            |  1 +\n t/t3515-cherry-pick-incompatible-options.sh | 34 +++++++++++++++++++++\n 2 files changed, 35 insertions(+)\n create mode 100755 t/t3515-cherry-pick-incompatible-options.sh\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex d83977e36e..48c426f277 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -163,6 +163,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--ff\", opts->allow_ff,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n+\t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n \t\t\t\tNULL);\n \t}\n \ndiff --git a/t/t3515-cherry-pick-incompatible-options.sh b/t/t3515-cherry-pick-incompatible-options.sh\nnew file mode 100755\nindex 0000000000..6100ab64fd\n--- /dev/null\n+++ b/t/t3515-cherry-pick-incompatible-options.sh\n@@ -0,0 +1,34 @@\n+#!/bin/sh\n+\n+test_description='test if cherry-pick detects and aborts on incompatible options'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\n+\techo first > file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m \"first\" &&\n+\n+\techo second > file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m \"second\"\n+'\n+\n+test_expect_success '--keep-redundant-commits is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_done\n-- \n2.43.0\n\n"},{"id":"488343","messageId":"20240210074859.552497-9-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-10T07:43:56Z","receivedAt":"2024-02-10T07:49:45Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a\ncommit being made redundant if the content from the picked commit is\nalready present in the target history. However, git-cherry-pick(1) does\nnot have the same options available that git-rebase(1) and git-am(1) have.\n\nThere are three things that can be done with these redundant commits:\ndrop them, keep them, or have the cherry-pick stop and wait for the user\nto take an action. git-rebase(1) has the `--empty` option added in commit\ne98c4269c8 (rebase (interactive-backend): fix handling of commits that\nbecome empty, 2020-02-15), which handles all three of these scenarios.\nSimilarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09).\n\ngit-cherry-pick(1), on the other hand, only supports two of the three\npossiblities: Keep the redundant commits via `--keep-redundant-commits`,\nor have the cherry-pick fail by not specifying that option. There is no\nway to automatically drop redundant commits.\n\nIn order to bring git-cherry-pick(1) more in-line with git-rebase(1) and\ngit-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It\nhas the same three options (keep, drop, and stop), and largely behaves\nthe same. The notable difference is that for git-cherry-pick(1), the\ndefault will be `stop`, which maintains the current behavior when the\noption is not specified.\n\nThe `--keep-redundant-commits` option will be documented as a deprecated\nsynonym of `--empty=keep`, and will be supported for backwards\ncompatibility for the time being.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-cherry-pick.txt           | 32 +++++++++++------\n builtin/revert.c                            | 37 ++++++++++++++++++-\n sequencer.c                                 |  6 ++++\n t/t3505-cherry-pick-empty.sh                | 23 +++++++++++-\n t/t3510-cherry-pick-sequence.sh             | 40 +++++++++++++++++++++\n t/t3515-cherry-pick-incompatible-options.sh | 14 ++++++++\n 6 files changed, 140 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex c88bb88822..a444d960b1 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -132,23 +132,35 @@ effect to your index in a row.\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n \tprevious commit will cause the cherry-pick to fail.  To force the\n-\tinclusion of those commits, use `--keep-redundant-commits`.\n+\tinclusion of those commits, use `--empty=keep`.\n\n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n \tThis option overrides that behavior, allowing commits with empty\n \tmessages to be cherry picked.\n\n+--empty=(drop|keep|stop)::\n+\tHow to handle commits being cherry-picked that are redundant with\n+\tchanges already in the current history.\n++\n+--\n+`drop`;;\n+\tThe commit will be dropped.\n+`keep`;;\n+\tThe commit will be kept.\n+`stop`;;\n+\tThe cherry-pick will stop when the commit is applied, allowing\n+\tyou to examine the commit. This is the default behavior.\n+--\n++\n+Note that this option species how to handle a commit that was not initially\n+empty, but rather became empty due to a previous commit. Commits that were\n+initially empty will cause the cherry-pick to fail. To force the inclusion of\n+those commits, use `--allow-empty`.\n++\n+\n --keep-redundant-commits::\n-\tIf a commit being cherry picked duplicates a commit already in the\n-\tcurrent history, it will become empty.  By default these\n-\tredundant commits cause `cherry-pick` to stop so the user can\n-\texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object. Note that use of this option only\n-\tresults in an empty commit when the commit was not initially empty,\n-\tbut rather became empty due to a previous commit. Commits that were\n-\tinitially empty will cause the cherry-pick to fail. To force the\n-\tinclusion of those commits use `--allow-empty`.\n+\tDeprecated synonym for `--empty=keep`.\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 48c426f277..27efb6284b 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n\n+enum empty_action {\n+\tEMPTY_COMMIT_UNSPECIFIED = 0,\n+\tSTOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */\n+\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n+\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n+};\n+\n+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n+{\n+\tint *opt_value = opt->value;\n+\n+\tBUG_ON_OPT_NEG(unset);\n+\n+\tif (!strcmp(arg, \"stop\"))\n+\t\t*opt_value = STOP_ON_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"drop\"))\n+\t\t*opt_value = DROP_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"keep\"))\n+\t\t*opt_value = KEEP_EMPTY_COMMIT;\n+\telse\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n+\n+\treturn 0;\n+}\n+\n static int option_parse_m(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n \tconst char *cleanup_arg = NULL;\n+\tenum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\tOPT_BOOL(0, \"ff\", &opts->allow_ff, N_(\"allow fast-forward\")),\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, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"deprecated: use --empty=keep instead\")),\n+\t\t\tOPT_CALLBACK_F(0, \"empty\", &empty_opt, \"(stop|drop|keep)\",\n+\t\t\t\t       N_(\"how to handle commits that become empty\"),\n+\t\t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\n@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n\n+\tif (opts->action == REPLAY_PICK) {\n+\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n+\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n+\t}\n+\n \tif (cleanup_arg) {\n \t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n \t\topts->explicit_cleanup = 1;\n@@ -164,6 +198,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n \t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n+\t\t\t\t\"--empty\", empty_opt != EMPTY_COMMIT_UNSPECIFIED,\n \t\t\t\tNULL);\n \t}\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3f41863dae..509a5244d2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2921,6 +2921,9 @@ static int populate_opts_cb(const char *key, const char *value,\n \telse if (!strcmp(key, \"options.allow-empty-message\"))\n \t\topts->allow_empty_message =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n+\telse if (!strcmp(key, \"options.drop-redundant-commits\"))\n+\t\topts->drop_redundant_commits =\n+\t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n \t\topts->keep_redundant_commits =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n@@ -3465,6 +3468,9 @@ static int save_opts(struct replay_opts *opts)\n \tif (opts->allow_empty_message)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.allow-empty-message\", \"true\");\n+\tif (opts->drop_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file,\n+\t\t\t\t\"options.drop-redundant-commits\", \"true\");\n \tif (opts->keep_redundant_commits)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.keep-redundant-commits\", \"true\");\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex 2709cfc677..669416c158 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -90,7 +90,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n \tgit commit -m \"add file2 on the side\"\n '\n\n-test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n \tgit reset --hard &&\n \tgit checkout fork^0 &&\n \ttest_must_fail git cherry-pick main\n@@ -105,4 +105,25 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n \ttest_cmp expect actual\n '\n\n+test_expect_success 'cherry-pick a no-op with --empty=stop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\ttest_must_fail git cherry-pick --empty=stop main 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=drop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=drop main &&\n+\ttest_commit_message HEAD -m \"add file2 on the side\"\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=keep' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=keep main &&\n+\ttest_commit_message HEAD -m \"add file2 on main\"\n+'\n+\n test_done\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 72020a51c4..5f6c45dfe3 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -90,6 +90,46 @@ test_expect_success 'cherry-pick persists opts correctly' '\n \ttest_cmp expect actual\n '\n\n+test_expect_success 'cherry-pick persists --empty=stop correctly' '\n+\tpristine_detach initial &&\n+\t# to make sure that the session to cherry-pick a sequence\n+\t# gets interrupted, use a high-enough number that is larger\n+\t# than the number of parents of any commit we have created\n+\tmainline=4 &&\n+\ttest_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&\n+\ttest_path_is_file .git/sequencer/opts &&\n+\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&\n+\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=drop correctly' '\n+\tpristine_detach initial &&\n+\t# to make sure that the session to cherry-pick a sequence\n+\t# gets interrupted, use a high-enough number that is larger\n+\t# than the number of parents of any commit we have created\n+\tmainline=4 &&\n+\ttest_expect_code 128 git cherry-pick -s -m $mainline --empty=drop initial..anotherpick &&\n+\ttest_path_is_file .git/sequencer/opts &&\n+\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&\n+\techo \"true\" >expect &&\n+\tgit config --file=.git/sequencer/opts --get-all options.drop-redundant-commits >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=keep correctly' '\n+\tpristine_detach initial &&\n+\t# to make sure that the session to cherry-pick a sequence\n+\t# gets interrupted, use a high-enough number that is larger\n+\t# than the number of parents of any commit we have created\n+\tmainline=4 &&\n+\ttest_expect_code 128 git cherry-pick -s -m $mainline --empty=keep initial..anotherpick &&\n+\ttest_path_is_file .git/sequencer/opts &&\n+\techo \"true\" >expect &&\n+\tgit config --file=.git/sequencer/opts --get-all options.keep-redundant-commits >actual &&\n+\ttest_cmp expect actual &&\n+\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits\n+'\n+\n test_expect_success 'revert persists opts correctly' '\n \tpristine_detach initial &&\n \t# to make sure that the session to revert a sequence\ndiff --git a/t/t3515-cherry-pick-incompatible-options.sh b/t/t3515-cherry-pick-incompatible-options.sh\nindex 6100ab64fd..b2780fdbf3 100755\n--- a/t/t3515-cherry-pick-incompatible-options.sh\n+++ b/t/t3515-cherry-pick-incompatible-options.sh\n@@ -31,4 +31,18 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '\n \tgit cherry-pick --abort\n '\n\n+test_expect_success '--empty is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n test_done\n-- \n2.43.0\n\n"},{"id":"488382","messageId":"17b2b5fb5acd8fad.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"20240210074859.552497-4-brianmlyles@gmail.com","subject":"Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-11T04:54:59Z","receivedAt":"2024-02-11T04:55:02Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"I just noticed that I incorrectly specified `--empty=drop` in the\nsubject of this commit. It should read \"rebase: update `--empty=ask` to\n`--empty=stop`\". This will need to be corrected in a v3 reroll.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"488422","messageId":"2345401.ElGaqSPkdT@cayenne","threadId":"60764","inReplyTo":"20240210074859.552497-9-brianmlyles@gmail.com","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2024-02-11T20:50:04Z","receivedAt":"2024-02-11T20:50:13Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Hello,\n\nThere's a typo.\n\nOn Saturday, 10 February 2024 08:43:56 CET Brian Lyles wrote:\n> +--\n> ++\n> +Note that this option species how to handle a commit that was not initially\n\nthis option *specifies*\n\n> +empty, but rather became empty due to a previous commit. Commits that were\n> +initially empty will cause the cherry-pick to fail. To force the inclusion of\n> +those commits, use `--allow-empty`.\n> ++\n> +\n>  --keep-redundant-commits::\n> -\tIf a commit being cherry picked duplicates a commit already in the\n> -\tcurrent history, it will become empty.  By default these\n> -\tredundant commits cause `cherry-pick` to stop so the user can\n> -\texamine the commit. This option overrides that behavior and\n> -\tcreates an empty commit object. Note that use of this option only\n> -\tresults in an empty commit when the commit was not initially empty,\n> -\tbut rather became empty due to a previous commit. Commits that were\n> -\tinitially empty will cause the cherry-pick to fail. To force the\n> -\tinclusion of those commits use `--allow-empty`.\n> +\tDeprecated synonym for `--empty=keep`.\n> \n>  --strategy=<strategy>::\n>  \tUse the given merge strategy.  Should only be used once.\n\nOtherwise, documentation looks good to me.\n\nJN\n\n\n"},{"id":"488426","messageId":"17b2f9b0a891ad00.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"2345401.ElGaqSPkdT@cayenne","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-12T01:35:45Z","receivedAt":"2024-02-12T01:35:48Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Sun, Feb 11, 2024 at 2:50 PM Jean-Noël AVILA <jn.avila@free.fr>\nwrote:\n\n> Hello,\n> \n> There's a typo.\n> \n> On Saturday, 10 February 2024 08:43:56 CET Brian Lyles wrote:\n>> +--\n>> ++\n>> +Note that this option species how to handle a commit that was not initially\n> \n> this option *specifies*\n\nThank you for catching this, I'll correct it in v3.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"488635","messageId":"7fc9a39a-f09b-4a81-b987-c5dd0ebca793@gmail.com","threadId":"60764","inReplyTo":"17b2b5fb5acd8fad.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-14T11:05:08Z","receivedAt":"2024-02-14T11:05:09Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 11/02/2024 04:54, Brian Lyles wrote:\n> I just noticed that I incorrectly specified `--empty=drop` in the\n> subject of this commit. It should read \"rebase: update `--empty=ask` to\n> `--empty=stop`\". This will need to be corrected in a v3 reroll.\n\nThanks for flagging that, I'm afraid it will probably be next week \nbefore I take a proper look at these\n\nBest Wishes\n\nPhillip\n\n\n"},{"id":"489160","messageId":"9f16544e-b6cc-414f-81e5-aac9e076f8df@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-4-brianmlyles@gmail.com","subject":"Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:34:22Z","receivedAt":"2024-02-22T16:34:25Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support\n> --empty=<option> to handle empty patches, 2021-12-09), `stop` was used\n> instead of `ask`. `stop` is a more accurate term for describing what\n> really happens, and consistency is good.\n> \n> Update git-rebase(1) to also use `stop`, while keeping `ask` as a\n> deprecated synonym. Update the tests to primarily use `stop`, but also\n> ensure that `ask` is still allowed.\n> \n> In a future commit, we'll be adding a new `--empty` option for\n> git-cherry-pick(1) as well, making the consistency even more relevant.\n\nI'm sightly nervous of deprecating \"ask\" as the warnings have the \npotential to annoy users but it would be good to use consistent \nterminology so it may well be worth it. This patch the previous ones \nlook good apart from one minor issue ...\n\n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> Reported-by: Elijah Newren <newren@gmail.com>\n\nI think we normally put Reported-by: and Helped-by: etc above the patch \nauthors Signed-off-by: trailer.\n\nBest Wishes\n\nPhillip\n\n> ---\n>   Documentation/git-rebase.txt | 15 ++++++++-------\n>   builtin/rebase.c             | 16 ++++++++++------\n>   t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----\n>   3 files changed, 34 insertions(+), 18 deletions(-)\n> \n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index 68cdebd2aa..6f64084a95 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n>   +\n>   See also INCOMPATIBLE OPTIONS below.\n>   \n> ---empty=(ask|drop|keep)::\n> +--empty=(drop|keep|stop)::\n>   \tHow to handle commits that are not empty to start and are not\n>   \tclean cherry-picks of any upstream commit, but which become\n>   \tempty after rebasing (because they contain a subset of already\n>   \tupstream changes):\n>   +\n>   --\n> -`ask`;;\n> -\tThe rebase will halt when the commit is applied, allowing you to\n> -\tchoose whether to drop it, edit files more, or just commit the empty\n> -\tchanges. This option is implied when `-i`/`--interactive` is\n> -\tspecified.\n>   `drop`;;\n>   \tThe commit will be dropped. This is the default behavior.\n>   `keep`;;\n>   \tThe commit will be kept. This option is implied when `--exec` is\n>   \tspecified unless `-i`/`--interactive` is also specified.\n> +`stop`;;\n> +`ask`;;\n> +\tThe rebase will halt when the commit is applied, allowing you to\n> +\tchoose whether to drop it, edit files more, or just commit the empty\n> +\tchanges. This option is implied when `-i`/`--interactive` is\n> +\tspecified. `ask` is a deprecated synonym of `stop`.\n>   --\n>   +\n>   Note that commits which start empty are kept (unless `--no-keep-empty`\n> @@ -705,7 +706,7 @@ be dropped automatically with `--no-keep-empty`).\n>   Similar to the apply backend, by default the merge backend drops\n>   commits that become empty unless `-i`/`--interactive` is specified (in\n>   which case it stops and asks the user what to do).  The merge backend\n> -also has an `--empty=(ask|drop|keep)` option for changing the behavior\n> +also has an `--empty=(drop|keep|stop)` option for changing the behavior\n>   of handling commits that become empty.\n>   \n>   Directory rename detection\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 4084a6abb8..3b9bb2fa06 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -58,7 +58,7 @@ enum empty_type {\n>   \tEMPTY_UNSPECIFIED = -1,\n>   \tEMPTY_DROP,\n>   \tEMPTY_KEEP,\n> -\tEMPTY_ASK\n> +\tEMPTY_STOP\n>   };\n>   \n>   enum action {\n> @@ -954,10 +954,14 @@ static enum empty_type parse_empty_value(const char *value)\n>   \t\treturn EMPTY_DROP;\n>   \telse if (!strcasecmp(value, \"keep\"))\n>   \t\treturn EMPTY_KEEP;\n> -\telse if (!strcasecmp(value, \"ask\"))\n> -\t\treturn EMPTY_ASK;\n> +\telse if (!strcasecmp(value, \"stop\"))\n> +\t\treturn EMPTY_STOP;\n> +\telse if (!strcasecmp(value, \"ask\")) {\n> +\t\twarning(_(\"--empty=ask is deprecated; use '--empty=stop' instead.\"));\n> +\t\treturn EMPTY_STOP;\n> +\t}\n>   \n> -\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"ask\\\".\"), value);\n> +\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"stop\\\".\"), value);\n>   }\n>   \n>   static int parse_opt_keep_empty(const struct option *opt, const char *arg,\n> @@ -1136,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t\t\t \"instead of ignoring them\"),\n>   \t\t\t      1, PARSE_OPT_HIDDEN),\n>   \t\tOPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),\n> -\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|ask)\",\n> +\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|stop)\",\n>   \t\t\t       N_(\"how to handle commits that become empty\"),\n>   \t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n>   \t\tOPT_CALLBACK_F('k', \"keep-empty\", &options, NULL,\n> @@ -1553,7 +1557,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \n>   \tif (options.empty == EMPTY_UNSPECIFIED) {\n>   \t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n> -\t\t\toptions.empty = EMPTY_ASK;\n> +\t\t\toptions.empty = EMPTY_STOP;\n>   \t\telse if (options.exec.nr > 0)\n>   \t\t\toptions.empty = EMPTY_KEEP;\n>   \t\telse\n> diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\n> index 73ff35ced2..1ee6b00fd5 100755\n> --- a/t/t3424-rebase-empty.sh\n> +++ b/t/t3424-rebase-empty.sh\n> @@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '\n>   \ttest_cmp expect actual\n>   '\n>   \n> +test_expect_success 'rebase --merge --empty=stop' '\n> +\tgit checkout -B testing localmods &&\n> +\ttest_must_fail git rebase --merge --empty=stop upstream &&\n> +\n> +\tgit rebase --skip &&\n> +\n> +\ttest_write_lines D C B A >expect &&\n> +\tgit log --format=%s >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   test_expect_success 'rebase --merge --empty=ask' '\n>   \tgit checkout -B testing localmods &&\n>   \ttest_must_fail git rebase --merge --empty=ask upstream &&\n> @@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '\n>   \ttest_cmp expect actual\n>   '\n>   \n> -test_expect_success 'rebase --interactive --empty=ask' '\n> +test_expect_success 'rebase --interactive --empty=stop' '\n>   \tgit checkout -B testing localmods &&\n> -\ttest_must_fail git rebase --interactive --empty=ask upstream &&\n> +\ttest_must_fail git rebase --interactive --empty=stop upstream &&\n>   \n>   \tgit rebase --skip &&\n>   \n> @@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '\n>   \ttest_cmp expect actual\n>   '\n>   \n> -test_expect_success 'rebase --interactive uses default of --empty=ask' '\n> +test_expect_success 'rebase --interactive uses default of --empty=stop' '\n>   \tgit checkout -B testing localmods &&\n>   \ttest_must_fail git rebase --interactive upstream &&\n>   \n> @@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '\n>   \ttest_cmp expect actual\n>   '\n>   \n> -test_expect_success 'rebase --exec --empty=ask' '\n> +test_expect_success 'rebase --exec --empty=stop' '\n>   \tgit checkout -B testing localmods &&\n> -\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n> +\ttest_must_fail git rebase --exec \"true\" --empty=stop upstream &&\n>   \n>   \tgit rebase --skip &&\n>   \n"},{"id":"489161","messageId":"08073d69-18f4-40c9-90a5-23db914c163e@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-5-brianmlyles@gmail.com","subject":"Re: [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:34:48Z","receivedAt":"2024-02-22T16:34:50Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> When using git-cherry-pick(1) with `--allow-empty` while on an unborn\n> branch, an error is thrown. This is inconsistent with the same\n> cherry-pick when `--allow-empty` is not specified.\n> \n> Treat a failure reading HEAD as an unborn branch in\n> `is_index_unchanged`. This is consistent with other sequencer logic such\n> as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as\n> the tree to compare against.\n> \n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n> \n> This is another new commit that was not present in v1.\n> \n> See this comment[1] from Phillip for context.\n> \n> [1]: https://lore.kernel.org/git/b5213705-4cd6-40ef-8c5f-32b214534b8b@gmail.com/\n\nThanks for fixing this and adding a test, I've left a few small comments \nbelow.\n\n>   static int is_index_unchanged(struct repository *r)\n>   {\n> -\tstruct object_id head_oid, *cache_tree_oid;\n> +\tstruct object_id head_oid, *cache_tree_oid, head_tree_oid;\n\nI think we can make `head_tree_oid` a pointer like cache_tree_oid and \navoid de-referencing `the_hash_algo->empty_tree` and the return value of \n`get_commit_tree_oid()`. I think the only reason to copy it would be if \nthe underlying object had a shorter lifetime than `head_tree_oid` but I \ndon't think that's the case.\n\n> +test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n> +\tgit checkout main &&\n\nI'm a bit confused by this - are we already on the branch \"unborn\" and \nso need to move away from it to delete it?\n\n> +\tgit branch -D unborn &&\n> +\tgit checkout --orphan unborn &&\n> +\tgit rm --cached -r . &&\n> +\trm -rf * &&\n\n\"git switch --orphan\" leaves us with an empty index and working copy \nwithout having to remove the files ourselves.\n\n> +\tgit cherry-pick initial --allow-empty &&\n> +\tgit diff --quiet initial &&\n\nI'd drop \"--quiet\" here as it makes debugging easier if we can see the \ndiff if the test fails.\n\n> +\ttest_cmp_rev ! initial HEAD\n> +'\n\nBest Wishes\n\nPhillip\n"},{"id":"489162","messageId":"84db20bc-94d0-43be-b4e0-f3f9245de63f@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-6-brianmlyles@gmail.com","subject":"Re: [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:35:14Z","receivedAt":"2024-02-22T16:35:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> A consumer of the sequencer that wishes to take advantage of either the\n> `keep_redundant_commits` or `drop_redundant_commits` feature must also\n> specify `allow_empty`. However, these refer to two distinct types of\n> empty commits:\n> \n> - `allow_empty` refers specifically to commits which start empty\n> - `keep_redundant_commits` refers specifically to commits that do not\n>    start empty, but become empty due to the content already existing in\n>    the target history\n> \n> Conceptually, there is no reason that the behavior for handling one of\n> these should be entangled with the other. It is particularly unintuitive\n> to require `allow_empty` in order for `drop_redundant_commits` to have\n> an effect: in order to prevent redundant commits automatically,\n> initially-empty commits would need to be kept automatically as well.\n> \n> Instead, rewrite the `allow_empty()` logic to remove the over-arching\n> requirement that `allow_empty` be specified in order to reach any of the\n> keep/drop behaviors. Only if the commit was originally empty will\n> `allow_empty` have an effect.\n> \n> Note that no behavioral changes should result from this commit -- it\n> merely sets the stage for future commits.\n\nThanks for clarifying that. I think splitting this change out is a good \nidea. The patch looks good.\n\nBest Wishes\n\nPhillip\n\n> In one such future commit, an\n> `--empty` option will be added to git-cherry-pick(1), meaning that\n> `drop_redundant_commits` will be used by that command.\n> \n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n> \n> This is the first half of the first commit[1] in v1, which has now been\n> split up. While the next commit may be considered somewhat\n> controversial, this part of the change should not be.\n> \n> [1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/\n> \n>   sequencer.c | 23 +++++++----------------\n>   1 file changed, 7 insertions(+), 16 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index b1b19512de..3f41863dae 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1725,34 +1725,25 @@ static int allow_empty(struct repository *r,\n>   \tint index_unchanged, originally_empty;\n> \n>   \t/*\n> -\t * Four cases:\n> +\t * For a commit that is initially empty, allow_empty determines if it\n> +\t * should be kept or not\n>   \t *\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, and\n> -\t *     just drop the ones that become empty\n> -\t *\n> -\t * (3) we allow ones that were initially empty, but\n> -\t *     halt for the ones that become empty;\n> -\t *\n> -\t * (4) we allow both.\n> +\t * For a commit that becomes empty, keep_redundant_commits and\n> +\t * drop_redundant_commits determine whether the commit should be kept or\n> +\t * dropped. If neither is specified, halt.\n>   \t */\n> -\tif (!opts->allow_empty)\n> -\t\treturn 0; /* let \"git commit\" barf as necessary */\n> -\n>   \tindex_unchanged = is_index_unchanged(r);\n>   \tif (index_unchanged < 0)\n>   \t\treturn index_unchanged;\n>   \tif (!index_unchanged)\n>   \t\treturn 0; /* we do not have to say --allow-empty */\n> \n> -\tif (opts->keep_redundant_commits)\n> -\t\treturn 1;\n> -\n>   \toriginally_empty = is_original_commit_empty(commit);\n>   \tif (originally_empty < 0)\n>   \t\treturn originally_empty;\n>   \tif (originally_empty)\n> +\t\treturn opts->allow_empty;\n> +\telse if (opts->keep_redundant_commits)\n>   \t\treturn 1;\n>   \telse if (opts->drop_redundant_commits)\n>   \t\treturn 2;\n"},{"id":"489163","messageId":"3f276e10-7b03-4480-a157-47a7648e7f19@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-7-brianmlyles@gmail.com","subject":"Re: [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:35:29Z","receivedAt":"2024-02-22T16:35:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> As noted in the git-cherry-pick(1) docs, `--keep-redundant-commits`\n> implies `--allow-empty`, despite the two having distinct,\n> non-overlapping meanings:\n> \n> - `allow_empty` refers specifically to commits which start empty, as\n>    indicated by the documentation for `--allow-empty` within\n>    git-cherry-pick(1):\n> \n>    \"Note also, that use of this option only keeps commits that were\n>    initially empty (i.e. the commit recorded the same tree as its\n>    parent). Commits which are made empty due to a previous commit are\n>    dropped. To force the inclusion of those commits use\n>    --keep-redundant-commits.\"\n> \n> - `keep_redundant_commits` refers specifically to commits that do not\n>    start empty, but become empty due to the content already existing in\n>    the target history. This is indicated by the documentation for\n>    `--keep-redundant-commits` within git-cherry-pick(1):\n> \n>    \"If a commit being cherry picked duplicates a commit already in the\n>    current history, it will become empty. By default these redundant\n>    commits cause cherry-pick to stop so the user can examine the commit.\n>    This option overrides that behavior and creates an empty commit\n>    object. Implies --allow-empty.\"\n> \n> This implication of `--allow-empty` therefore seems incorrect: One\n> should be able to keep a commit that becomes empty without also being\n> forced to pick commits that start as empty. However, the following\n> series of commands results in both the commit that became empty and the\n> commit that started empty being picked, despite only\n> `--keep-redundant-commits` being specified:\n> \n>      git init\n>      echo \"a\" >test\n>      git add test\n>      git commit -m \"Initial commit\"\n>      echo \"b\" >test\n>      git commit -am \"a -> b\"\n>      git commit --allow-empty -m \"empty\"\n>      git cherry-pick --keep-redundant-commits HEAD^ HEAD\n> \n> The same cherry-pick with `--allow-empty` would fail on the redundant\n> commit, and with neither option would fail on the empty commit.\n> \n> Do not imply `--allow-empty` when using `--keep-redundant-commits` with\n> git-cherry-pick(1).\n>\n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n> \n> This is the second half of the first commit[1] in v1, which has now been\n> split up.\n> \n> This commit proposes a breaking change, albeit one that seems correct\n> and relatively minor to me. If this change is deemed too controversial,\n> I am prepared to drop it from the series. See Junio's[2] and\n> Phillip's[3] comments on v1 for additional context.\n\nI agree that if we were starting from scratch there would be no reason \nto tie --apply-empty and --keep-redundant-commits together but I'm not \nsure it is worth the disruption of changing it now. We're about to add \nempty=keep which won't imply --allow-empty for anyone who wants that \nbehavior and I still tend to think the practical effect of implying \n--allow-empty with --keep-redundant-commits is largely beneficial as I'm \nskeptical that users want to keep commits that become empty but not the \nones that started empty.\n\nBest Wishes\n\nPhillip\n\n> [1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/\n> [2]: https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/\n> [3]: https://lore.kernel.org/git/8ff4650c-f84f-41bd-a46c-3b845ff29b70@gmail.com/\n> \n>   Documentation/git-cherry-pick.txt | 10 +++++++---\n>   builtin/revert.c                  |  4 ----\n>   t/t3505-cherry-pick-empty.sh      |  6 ++++++\n>   3 files changed, 13 insertions(+), 7 deletions(-)\n> \n> diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\n> index fdcad3d200..c88bb88822 100644\n> --- a/Documentation/git-cherry-pick.txt\n> +++ b/Documentation/git-cherry-pick.txt\n> @@ -131,8 +131,8 @@ effect to your index in a row.\n>   \teven without this option.  Note also, that use of this option only\n>   \tkeeps commits that were initially empty (i.e. the commit recorded the\n>   \tsame tree as its parent).  Commits which are made empty due to a\n> -\tprevious commit are dropped.  To force the inclusion of those commits\n> -\tuse `--keep-redundant-commits`.\n> +\tprevious commit will cause the cherry-pick to fail.  To force the\n> +\tinclusion of those commits, use `--keep-redundant-commits`.\n> \n>   --allow-empty-message::\n>   \tBy default, cherry-picking a commit with an empty message will fail.\n> @@ -144,7 +144,11 @@ effect to your index in a row.\n>   \tcurrent history, it will become empty.  By default these\n>   \tredundant commits cause `cherry-pick` to stop so the user can\n>   \texamine the commit. This option overrides that behavior and\n> -\tcreates an empty commit object.  Implies `--allow-empty`.\n> +\tcreates an empty commit object. Note that use of this option only\n> +\tresults in an empty commit when the commit was not initially empty,\n> +\tbut rather became empty due to a previous commit. Commits that were\n> +\tinitially empty will cause the cherry-pick to fail. To force the\n> +\tinclusion of those commits use `--allow-empty`.\n> \n>   --strategy=<strategy>::\n>   \tUse the given merge strategy.  Should only be used once.\n> diff --git a/builtin/revert.c b/builtin/revert.c\n> index 89821bab95..d83977e36e 100644\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -134,10 +134,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n>   \tprepare_repo_settings(the_repository);\n>   \tthe_repository->settings.command_requires_full_index = 0;\n> \n> -\t/* implies allow_empty */\n> -\tif (opts->keep_redundant_commits)\n> -\t\topts->allow_empty = 1;\n> -\n>   \tif (cleanup_arg) {\n>   \t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n>   \t\topts->explicit_cleanup = 1;\n> diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\n> index eba3c38d5a..2709cfc677 100755\n> --- a/t/t3505-cherry-pick-empty.sh\n> +++ b/t/t3505-cherry-pick-empty.sh\n> @@ -59,6 +59,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '\n>   \ttest_must_fail git cherry-pick empty-change-branch\n>   '\n> \n> +test_expect_success 'cherry pick an empty non-ff commit with --keep-redundant-commits' '\n> +\tgit checkout main &&\n> +\ttest_must_fail git cherry-pick --keep-redundant-commits empty-change-branch 2>output &&\n> +\ttest_grep \"The previous cherry-pick is now empty\" output\n> +'\n> +\n>   test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '\n>   \tgit checkout main &&\n>   \tgit cherry-pick --allow-empty empty-change-branch\n\n"},{"id":"489164","messageId":"8c2eec1b-9a59-4739-a903-b2e8955f3ff5@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-8-brianmlyles@gmail.com","subject":"Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:35:54Z","receivedAt":"2024-02-22T16:35:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> When `--keep-redundant-commits` was added in  b27cfb0d8d\n> (git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was\n> not marked as incompatible with the various operations needed to\n> continue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and\n> `--quit`).\n> \n> Enforce this incompatibility via `verify_opt_compatible` like we do for\n> the other various options.\n> \n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n> \n> This commit was not present in v1 either. It addresses an existing issue\n> that I noticed after Phillip pointed out the same deficiency for my new\n> `--empty` option introduced in the ultimate commit in this series.\n\nWell spotted, do we really need a new test file just for this though? I \nwonder if the new test would be better off living in \nt3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh\n\nBest Wishes\n\nPhillip\n\n> [1]: https://lore.kernel.org/git/CAHPHrSf+joHe6ikErHLgWrk-_qjSROS-dXCHagxWGDAF=2deDg@mail.gmail.com/\n> \n>   builtin/revert.c                            |  1 +\n>   t/t3515-cherry-pick-incompatible-options.sh | 34 +++++++++++++++++++++\n>   2 files changed, 35 insertions(+)\n>   create mode 100755 t/t3515-cherry-pick-incompatible-options.sh\n> \n> diff --git a/builtin/revert.c b/builtin/revert.c\n> index d83977e36e..48c426f277 100644\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -163,6 +163,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n>   \t\t\t\t\"--ff\", opts->allow_ff,\n>   \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n>   \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n> +\t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n>   \t\t\t\tNULL);\n>   \t}\n>   \n> diff --git a/t/t3515-cherry-pick-incompatible-options.sh b/t/t3515-cherry-pick-incompatible-options.sh\n> new file mode 100755\n> index 0000000000..6100ab64fd\n> --- /dev/null\n> +++ b/t/t3515-cherry-pick-incompatible-options.sh\n> @@ -0,0 +1,34 @@\n> +#!/bin/sh\n> +\n> +test_description='test if cherry-pick detects and aborts on incompatible options'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success setup '\n> +\n> +\techo first > file1 &&\n> +\tgit add file1 &&\n> +\ttest_tick &&\n> +\tgit commit -m \"first\" &&\n> +\n> +\techo second > file1 &&\n> +\tgit add file1 &&\n> +\ttest_tick &&\n> +\tgit commit -m \"second\"\n> +'\n> +\n> +test_expect_success '--keep-redundant-commits is incompatible with operations' '\n> +\ttest_must_fail git cherry-pick HEAD 2>output &&\n> +\ttest_grep \"The previous cherry-pick is now empty\" output &&\n> +\ttest_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&\n> +\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue\" output &&\n> +\ttest_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&\n> +\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip\" output &&\n> +\ttest_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&\n> +\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort\" output &&\n> +\ttest_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&\n> +\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit\" output &&\n> +\tgit cherry-pick --abort\n> +'\n> +\n> +test_done\n\n"},{"id":"489165","messageId":"bb23026e-72c5-40a0-bd5f-356a03efa5aa@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-9-brianmlyles@gmail.com","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:36:05Z","receivedAt":"2024-02-22T16:36:07Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a\n> commit being made redundant if the content from the picked commit is\n> already present in the target history. However, git-cherry-pick(1) does\n> not have the same options available that git-rebase(1) and git-am(1) have.\n> \n> There are three things that can be done with these redundant commits:\n> drop them, keep them, or have the cherry-pick stop and wait for the user\n> to take an action. git-rebase(1) has the `--empty` option added in commit\n> e98c4269c8 (rebase (interactive-backend): fix handling of commits that\n> become empty, 2020-02-15), which handles all three of these scenarios.\n> Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support\n> --empty=<option> to handle empty patches, 2021-12-09).\n> \n> git-cherry-pick(1), on the other hand, only supports two of the three\n> possiblities: Keep the redundant commits via `--keep-redundant-commits`,\n> or have the cherry-pick fail by not specifying that option. There is no\n> way to automatically drop redundant commits.\n> \n> In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and\n> git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It\n> has the same three options (keep, drop, and stop), and largely behaves\n> the same. The notable difference is that for git-cherry-pick(1), the\n> default will be `stop`, which maintains the current behavior when the\n> option is not specified.\n> \n> The `--keep-redundant-commits` option will be documented as a deprecated\n> synonym of `--empty=keep`, and will be supported for backwards\n> compatibility for the time being.\n\nI'm leaning towards leaving `--keep-redundant-commits` alone. That \nintroduces an inconsistency between `--keep-redundant-commits` and \n`--empty=keep` as the latter does not imply `--allow-empty` but it does \navoid breaking existing users. We could document \n`--keep-redundant-commits` as predating `--empty` and behaving like \n`--empty=keep --allow-empty`. The documentation and implementation of \nthe new option look good modulo the typo that has already been pointed \nout and a couple of small comments below.\n\n> +enum empty_action {\n> +\tEMPTY_COMMIT_UNSPECIFIED = 0,\n\nWe tend to use -1 for unspecified options\n\n> +\tSTOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */\n> +\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n> +\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n> +};\n\n\n> +test_expect_success 'cherry-pick persists --empty=stop correctly' '\n> +\tpristine_detach initial &&\n> +\t# to make sure that the session to cherry-pick a sequence\n> +\t# gets interrupted, use a high-enough number that is larger\n> +\t# than the number of parents of any commit we have created\n> +\tmainline=4 &&\n> +\ttest_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&\n> +\ttest_path_is_file .git/sequencer/opts &&\n> +\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&\n> +\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits\n> +'\n\nThanks for adding these tests to check that the --empty option persists. \nUsually for tests like this we prefer to check the user visible behavior \nrather than the implementation details (I suspect we have some older \ntests that do the latter). To check the behavior we usually arrange for \na merge conflict but using -m is a creative alternative, then we'd run \n\"git cherry-pick --continue\" and check that the commits that become \nempty have been preserved or dropped or that the cherry-pick stops.\n\nBest Wishes\n\nPhillip\n"},{"id":"489167","messageId":"ad547799-07f1-4def-8e20-7e37a662c58f@gmail.com","threadId":"60764","inReplyTo":"20240210074859.552497-1-brianmlyles@gmail.com","subject":"Re: [PATCH v2 0/8] cherry-pick: add `--empty`","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-22T16:39:26Z","receivedAt":"2024-02-22T16:39:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/02/2024 07:43, Brian Lyles wrote:\n> The ultimate goal of this series is to allow git-cherry-pick(1) to\n> automatically drop redundant commits. The mechanism chosen is an\n> `--empty` option that provides the same flexibility as the `--empty`\n> options for git-rebase(1) and git-am(1).\n> \n> Some secondary goals are to improve the consistency in the values and\n> documentation for this option across the three commands.\n> \n> See \"Does extending `--empty` to git-cherry-pick make sense?\" [1] for\n> some context for why this option is desired in git-cherry-pick(1).\n> \n> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com\n> \n> Along the way, I (with some help from Elijah and Phillip) found a few\n> other things in the docs and related sequencer code to clean up.\n\nThanks for the revised patches - they are looking good and were a \npleasant read. I've left a few small comments, my main concern is the \nchange to `--keep-redundant-commits` in patch 6 which I'm not sure is \nreally worth the disruption.\n\nBest Wishes\n\nPhillip\n\n> Brian Lyles (8):\n>    docs: address inaccurate `--empty` default with `--exec`\n>    docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n>    rebase: update `--empty=ask` to `--empty=drop`\n>    sequencer: treat error reading HEAD as unborn branch\n>    sequencer: do not require `allow_empty` for redundant commit options\n>    cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`\n>    cherry-pick: enforce `--keep-redundant-commits` incompatibility\n>    cherry-pick: add `--empty` for more robust redundant commit handling\n> \n>   Documentation/git-am.txt                    | 20 ++++---\n>   Documentation/git-cherry-pick.txt           | 30 +++++++---\n>   Documentation/git-rebase.txt                | 26 ++++++---\n>   builtin/rebase.c                            | 16 +++--\n>   builtin/revert.c                            | 40 +++++++++++--\n>   sequencer.c                                 | 65 +++++++++++----------\n>   t/t3424-rebase-empty.sh                     | 55 ++++++++++++++++-\n>   t/t3501-revert-cherry-pick.sh               | 11 ++++\n>   t/t3505-cherry-pick-empty.sh                | 29 ++++++++-\n>   t/t3510-cherry-pick-sequence.sh             | 40 +++++++++++++\n>   t/t3515-cherry-pick-incompatible-options.sh | 48 +++++++++++++++\n>   11 files changed, 312 insertions(+), 68 deletions(-)\n>   create mode 100755 t/t3515-cherry-pick-incompatible-options.sh\n> \n"},{"id":"489182","messageId":"xmqq34tke4m7.fsf@gitster.g","threadId":"60764","inReplyTo":"9f16544e-b6cc-414f-81e5-aac9e076f8df@gmail.com","subject":"Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-02-22T18:27:44Z","receivedAt":"2024-02-22T18:27:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n>> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n>> Reported-by: Elijah Newren <newren@gmail.com>\n>\n> I think we normally put Reported-by: and Helped-by: etc above the\n> patch authors Signed-off-by: trailer.\n\nTrue.\n\nTeaching how to fish, the rule is to try emulating chronological\norder of events.  Elijah reported and then the patch was written,\nand the \"seal on the envelope\" is what the author's sign-off\nattached at the end of the chain of events.\n"},{"id":"489183","messageId":"xmqqwmqwcpf7.fsf@gitster.g","threadId":"60764","inReplyTo":"3f276e10-7b03-4480-a157-47a7648e7f19@gmail.com","subject":"Re: [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-02-22T18:41:16Z","receivedAt":"2024-02-22T18:41:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> ... I still tend to think the practical effect of implying\n> --allow-empty with --keep-redundant-commits is largely beneficial as\n> I'm skeptical that users want to keep commits that become empty but\n> not the ones that started empty.\n\nI share that feeling exactly.\n\nThere are good reasons to keep a commit that starts as empty (as\nmuch as creating an empty commit in the first place), so if\nanything, a more common workflow element would be to drop the ones\nthat have become unnecessary (e.g. because the upstream already\nadded a change that is equivalent to what is being picked) while\nkeeping the ones that are empty from the start (e.g. in some\nworkflows an empty commit can be used as a container of\nmetainfo---you can imagine that in an N-commit chain leading to the\ntip of a topic branch forked from the master branch, the topmost\ncommit is an empty one with the cover letter being prepared, so that\nthe resulting topic branch can be either (1) made into a patch\nseries with an advanced version of \"git format-patch\" that knows how\nto use such an empty top commit in the cover letter message, or (2)\nmerged to the mainline via a pull request, with an advanced version\nof \"git merge\" that notices the empty commit at the tip, and makes a\nmerge with the commit topic~1 while using the empty top commit to\nwrite the message for the merge commit.\n\nI do not quite see a good reason to do the opposite, dropping\ncommits that started out as empty but keeping the ones that have\nbecome empty.  Such a behaviour has additional downside that after\nsuch a cherry-pick, when you cherry-pick the resulting history onto\nyet another base, your precious \"were not empty but have become so\nduring the initial cherry-pick\" commits will appear as commits that\nwere empty from the start.  So I do not see much reason to allow the\ndecoupling, even with the new \"empty=keep\" thing that does not imply\n\"allow-empty\".\n\nThanks.\n\n"},{"id":"489215","messageId":"17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"08073d69-18f4-40c9-90a5-23db914c163e@gmail.com","subject":"Re: [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-23T05:28:48Z","receivedAt":"2024-02-23T05:28:51Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Thu, Feb 22, 2024 at 10:34 AM <phillip.wood123@gmail.com> wrote:\n\n>>   static int is_index_unchanged(struct repository *r)\n>>   {\n>> -\tstruct object_id head_oid, *cache_tree_oid;\n>> +\tstruct object_id head_oid, *cache_tree_oid, head_tree_oid;\n> \n> I think we can make `head_tree_oid` a pointer like cache_tree_oid and \n> avoid de-referencing `the_hash_algo->empty_tree` and the return value of \n> `get_commit_tree_oid()`. I think the only reason to copy it would be if \n> the underlying object had a shorter lifetime than `head_tree_oid` but I \n> don't think that's the case.\n\nMakes sense -- I'll update this in v3.\n\n>> +test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n>> +\tgit checkout main &&\n> \n> I'm a bit confused by this - are we already on the branch \"unborn\" and \n> so need to move away from it to delete it?\n\nYes, the previous test leaves us on that branch. In v3, I will update\nthis to instead just use `git checkout --detach`, as that does seem a\nlittle less confusing than switching to some other branch that is only\nrelevant because it's not `unborn`. If there is a cleaner way to do\nthis, I'd be happy to switch to it.\n\n>> +\tgit branch -D unborn &&\n>> +\tgit checkout --orphan unborn &&\n>> +\tgit rm --cached -r . &&\n>> +\trm -rf * &&\n> \n> \"git switch --orphan\" leaves us with an empty index and working copy \n> without having to remove the files ourselves.\n\nThanks for pointing this out. Using git-switch(1) here instead of\ngit-checkout(1) allows us to drop the `rm -rf *` call form both the\nexisting 'cherry-pick on unborn branch' test as well as my new test. It\nappears that the `git rm --cached -r .` call is still necessary in the\nexisting test.\n\n>> +\tgit cherry-pick initial --allow-empty &&\n>> +\tgit diff --quiet initial &&\n>\n> I'd drop \"--quiet\" here as it makes debugging easier if we can see the \n> diff if the test fails.\n\nThis makes sense. In v3, I will update this new test as well as the\nexisting test to not use `--quiet`.\n\nCombining the above suggestions, here's the version of the existing and\nnew tests that I intend to use in v3. Let me know if this isn't what you\nhad in mind!\n\n    test_expect_success 'cherry-pick on unborn branch' '\n    \tgit switch --orphan unborn &&\n    \tgit rm --cached -r . &&\n    \tgit cherry-pick initial &&\n    \tgit diff initial &&\n    \ttest_cmp_rev ! initial HEAD\n    '\n    \n    test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n    \tgit checkout --detach &&\n    \tgit branch -D unborn &&\n    \tgit switch --orphan unborn &&\n    \tgit cherry-pick initial --allow-empty &&\n    \tgit diff initial &&\n    \ttest_cmp_rev ! initial HEAD\n    '\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"489216","messageId":"17b669c4bfe6602f.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"8c2eec1b-9a59-4739-a903-b2e8955f3ff5@gmail.com","subject":"Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-23T06:23:22Z","receivedAt":"2024-02-23T06:23:25Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n\n> Well spotted, do we really need a new test file just for this though? I \n> wonder if the new test would be better off living in \n> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh\n\nI was modelling this off of 't3422-rebase-incompatible-options.sh'.\nAdditionally, I do feel like these tests are only tangentially related\nto the tests that actually exercise the features themselves. Notably,\nthe setup requirements are drastically different (simpler) since the\ntest should fail long before any setup actually matters. For that\nreason, I think a separate file where other future tests for\nincompatible options can also live does make sense.\n\nIs there any particular downside to the new file that I am unaware of?\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"489218","messageId":"17b66baf4d8c4255.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"bb23026e-72c5-40a0-bd5f-356a03efa5aa@gmail.com","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-23T06:58:29Z","receivedAt":"2024-02-23T06:58:31Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"\nOn Thu, Feb 22, 2024 at 10:36 AM <phillip.wood123@gmail.com> wrote:\n\n> I'm leaning towards leaving `--keep-redundant-commits` alone. That \n> introduces an inconsistency between `--keep-redundant-commits` and \n> `--empty=keep` as the latter does not imply `--allow-empty` but it does \n> avoid breaking existing users. We could document \n> `--keep-redundant-commits` as predating `--empty` and behaving like \n> `--empty=keep --allow-empty`. The documentation and implementation of \n> the new option look good modulo the typo that has already been pointed \n> out and a couple of small comments below.\n\nI think I'm on board with leaving `--keep-redundant-commits` alone. I'm\non the fence about having `--empty=keep` imply `--allow-empty` after\nseeing Junio's concerns. I laid out the options that I see in a reply to\npatch 6/8[1] and would appreciate input there. I'll adjust the details\nof this commit in v3 based on what we decide there.\n\n[1]: https://lore.kernel.org/git/17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk/\n> \n>> +enum empty_action {\n>> +\tEMPTY_COMMIT_UNSPECIFIED = 0,\n> \n> We tend to use -1 for unspecified options\n\nThanks, I'll update this in v3.\n\n>> +test_expect_success 'cherry-pick persists --empty=stop correctly' '\n>> +\tpristine_detach initial &&\n>> +\t# to make sure that the session to cherry-pick a sequence\n>> +\t# gets interrupted, use a high-enough number that is larger\n>> +\t# than the number of parents of any commit we have created\n>> +\tmainline=4 &&\n>> +\ttest_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&\n>> +\ttest_path_is_file .git/sequencer/opts &&\n>> +\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&\n>> +\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits\n>> +'\n> \n> Thanks for adding these tests to check that the --empty option persists. \n> Usually for tests like this we prefer to check the user visible behavior \n> rather than the implementation details (I suspect we have some older \n> tests that do the latter). To check the behavior we usually arrange for \n> a merge conflict but using -m is a creative alternative, then we'd run \n> \"git cherry-pick --continue\" and check that the commits that become \n> empty have been preserved or dropped or that the cherry-pick stops.\n\nIndeed, I was modelling these new tests after other existing tests in\nthis file. While I agree with you in theory, I am hesitant to make these\nnew tests drastically different from the existing tests that are testing\nthe same mechanisms (and appear to be very intentionally testing that\nthe options are persisted in that config file). I'm also hesitant to\nupdate the existing tests as part of this series (primarily due to a\nlack of familiarity, and partially to avoid scope creep of the series).\n\nHow concerned are you about the current implementation? Does it make\nsense to you to defer this suggestion to a future series that cleans up\nthe tests to do more user-oriented checks?\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"489245","messageId":"xmqqjzmvulgu.fsf@gitster.g","threadId":"60764","inReplyTo":"17b669c4bfe6602f.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-02-23T17:41:37Z","receivedAt":"2024-02-23T17:41:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Brian Lyles\" <brianmlyles@gmail.com> writes:\n\n>> Well spotted, do we really need a new test file just for this though? I \n>> wonder if the new test would be better off living in \n>> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh\n>\n> I was modelling this off of 't3422-rebase-incompatible-options.sh'.\n> Additionally, I do feel like these tests are only tangentially related\n> to the tests that actually exercise the features themselves. Notably,\n> the setup requirements are drastically different (simpler) since the\n> test should fail long before any setup actually matters.\n\nIt is exactly the reason why we do not want to waste a scarce\nresource, the test file number, when we do not have to.\n\nSanity checking the command line options and failing when they do\nnot make sense is a small part of what a command needs to do; you\nare exactly right to say \"they do not really exercise the main\nfeature\".\n\nAs these \"we expect this to fail\" tests do not depend on and do not\nhave to modify the state of the test repository, we do not even have\nto do any extra set-up over what the existing test script already\nestablishes, no?  No additional set-up is even better than a simpler\nbut non-zero set-up we need to newly do, right?\n"},{"id":"489313","messageId":"62247a1c-0249-4ce1-8626-fe97b89c23dc@gmail.com","threadId":"60764","inReplyTo":"17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-25T16:57:09Z","receivedAt":"2024-02-25T16:57:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 23/02/2024 05:28, Brian Lyles wrote:\n> On Thu, Feb 22, 2024 at 10:34 AM <phillip.wood123@gmail.com> wrote:\n>>> +test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n>>> +\tgit checkout main &&\n>>\n>> I'm a bit confused by this - are we already on the branch \"unborn\" and\n>> so need to move away from it to delete it?\n> \n> Yes, the previous test leaves us on that branch. In v3, I will update\n> this to instead just use `git checkout --detach`, as that does seem a\n> little less confusing than switching to some other branch that is only\n> relevant because it's not `unborn`. If there is a cleaner way to do\n> this, I'd be happy to switch to it.\n\nI think \"git checkout --detach\" is probably the best we can do. It would \nbe nice to be able to do \"git switch -C --orphan unborn\" but \"-C\" does \nnot work with \"--orphan\"\n\n>>> +\tgit branch -D unborn &&\n>>> +\tgit checkout --orphan unborn &&\n>>> +\tgit rm --cached -r . &&\n>>> +\trm -rf * &&\n>>\n>> \"git switch --orphan\" leaves us with an empty index and working copy\n>> without having to remove the files ourselves.\n> \n> Thanks for pointing this out. Using git-switch(1) here instead of\n> git-checkout(1) allows us to drop the `rm -rf *` call form both the\n> existing 'cherry-pick on unborn branch' test as well as my new test. It\n> appears that the `git rm --cached -r .` call is still necessary in the\n> existing test.\n\nIt looks like the previous test 'revert forbidden on dirty working tree' \nfails to clean up properly and so \"git switch\" is carrying the \nuncommitted changes across to the new orphan branch. I think that \"git \nswitch --discard-changes --orphan unborn\" ought to clean the worktree \nand index but it doesn't seem to work.\n\n>>> +\tgit cherry-pick initial --allow-empty &&\n>>> +\tgit diff --quiet initial &&\n>>\n>> I'd drop \"--quiet\" here as it makes debugging easier if we can see the\n>> diff if the test fails.\n> \n> This makes sense. In v3, I will update this new test as well as the\n> existing test to not use `--quiet`.\n> \n> Combining the above suggestions, here's the version of the existing and\n> new tests that I intend to use in v3. Let me know if this isn't what you\n> had in mind!\n> \n>      test_expect_success 'cherry-pick on unborn branch' '\n>      \tgit switch --orphan unborn &&\n>      \tgit rm --cached -r . &&\n>      \tgit cherry-pick initial &&\n>      \tgit diff initial &&\n>      \ttest_cmp_rev ! initial HEAD\n>      '\n>      \n>      test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n>      \tgit checkout --detach &&\n>      \tgit branch -D unborn &&\n>      \tgit switch --orphan unborn &&\n>      \tgit cherry-pick initial --allow-empty &&\n>      \tgit diff initial &&\n>      \ttest_cmp_rev ! initial HEAD\n>      '\n\nThese look good\n\nThanks\n\nPhillip\n"},{"id":"489314","messageId":"83070e02-8e6b-43d2-819d-2272fe895c75@gmail.com","threadId":"60764","inReplyTo":"17b66baf4d8c4255.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-25T16:57:37Z","receivedAt":"2024-02-25T16:57:42Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 23/02/2024 06:58, Brian Lyles wrote:\n> \n> On Thu, Feb 22, 2024 at 10:36 AM <phillip.wood123@gmail.com> wrote:\n> \n>> I'm leaning towards leaving `--keep-redundant-commits` alone. That\n>> introduces an inconsistency between `--keep-redundant-commits` and\n>> `--empty=keep` as the latter does not imply `--allow-empty` but it does\n>> avoid breaking existing users. We could document\n>> `--keep-redundant-commits` as predating `--empty` and behaving like\n>> `--empty=keep --allow-empty`. The documentation and implementation of\n>> the new option look good modulo the typo that has already been pointed\n>> out and a couple of small comments below.\n> \n> I think I'm on board with leaving `--keep-redundant-commits` alone. I'm\n> on the fence about having `--empty=keep` imply `--allow-empty` after\n> seeing Junio's concerns. I laid out the options that I see in a reply to\n> patch 6/8[1] and would appreciate input there. I'll adjust the details\n> of this commit in v3 based on what we decide there.\n\nI'll take a look at that in the next couple of days\n\n> [1]: https://lore.kernel.org/git/17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk/\n>>\n>>> +enum empty_action {\n>>> +\tEMPTY_COMMIT_UNSPECIFIED = 0,\n>>\n>> We tend to use -1 for unspecified options\n> \n> Thanks, I'll update this in v3.\n> \n>>> +test_expect_success 'cherry-pick persists --empty=stop correctly' '\n>>> +\tpristine_detach initial &&\n>>> +\t# to make sure that the session to cherry-pick a sequence\n>>> +\t# gets interrupted, use a high-enough number that is larger\n>>> +\t# than the number of parents of any commit we have created\n>>> +\tmainline=4 &&\n>>> +\ttest_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&\n>>> +\ttest_path_is_file .git/sequencer/opts &&\n>>> +\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&\n>>> +\ttest_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits\n>>> +'\n>>\n>> Thanks for adding these tests to check that the --empty option persists.\n>> Usually for tests like this we prefer to check the user visible behavior\n>> rather than the implementation details (I suspect we have some older\n>> tests that do the latter). To check the behavior we usually arrange for\n>> a merge conflict but using -m is a creative alternative, then we'd run\n>> \"git cherry-pick --continue\" and check that the commits that become\n>> empty have been preserved or dropped or that the cherry-pick stops.\n> \n> Indeed, I was modelling these new tests after other existing tests in\n> this file. While I agree with you in theory, I am hesitant to make these\n> new tests drastically different from the existing tests that are testing\n> the same mechanisms (and appear to be very intentionally testing that\n> the options are persisted in that config file). I'm also hesitant to\n> update the existing tests as part of this series (primarily due to a\n> lack of familiarity, and partially to avoid scope creep of the series).\n\nI certainly don't think it should be up to you to update the existing \ntests. I'm not sure adding more tests in the same pattern is a good idea \nthough. Apart from the fact that they are testing an implementation \ndetail rather than the user facing behavior they don't actually check \nthat the option is respected by \"git cherry-pick --continue\", only that \nwe save it when stopping for a conflict resolution.\n\n> How concerned are you about the current implementation? Does it make\n> sense to you to defer this suggestion to a future series that cleans up\n> the tests to do more user-oriented checks?\n\nI think adding tests that follow a pattern we want to change is just \nstoring up work for the future and makes it less likely we'll improve \nthings because it will be more work to do so.\n\nBest Wishes\n\nPhillip\n\n"},{"id":"489315","messageId":"f10cc07a-0e77-462c-bc86-ca0452c20a1c@gmail.com","threadId":"60764","inReplyTo":"17b669c4bfe6602f.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-25T16:58:11Z","receivedAt":"2024-02-25T16:58:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 23/02/2024 06:23, Brian Lyles wrote:\n> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> \n>> Well spotted, do we really need a new test file just for this though? I\n>> wonder if the new test would be better off living in\n>> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh\n> \n> I was modelling this off of 't3422-rebase-incompatible-options.sh'.\n\nThe rebase case is more complicated due to different options being \nsupported by the two different backends. Thankfully here we only have to \nworry about options that are incompatible with \"--continue/--abort\" and \nso adding \"--continue rejects --foo\" into the file that tests option \n\"--foo\" keeps everything together.\n\n> Additionally, I do feel like these tests are only tangentially related\n> to the tests that actually exercise the features themselves. Notably,\n> the setup requirements are drastically different (simpler) since the\n> test should fail long before any setup actually matters. For that\n> reason, I think a separate file where other future tests for\n> incompatible options can also live does make sense.\n> \n> Is there any particular downside to the new file that I am unaware of?\n\nThe main downside is that it spreads the tests for a particular option \nover several test files. There is also an overhead in setting up the \nrepository at the start of each test file.\n\nBest Wishes\n\nPhillip\n"},{"id":"489341","messageId":"17b748518feffc50.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"83070e02-8e6b-43d2-819d-2272fe895c75@gmail.com","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-26T02:21:38Z","receivedAt":"2024-02-26T02:21:40Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Sun, Feb 25, 2024 at 10:57 AM <phillip.wood123@gmail.com> wrote:\n\n>> How concerned are you about the current implementation? Does it make\n>> sense to you to defer this suggestion to a future series that cleans up\n>> the tests to do more user-oriented checks?\n> \n> I think adding tests that follow a pattern we want to change is just \n> storing up work for the future and makes it less likely we'll improve \n> things because it will be more work to do so.\n\nThat's fair. I'll rework these in v3. Thanks!\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"489342","messageId":"17b74aa40b428632.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"f10cc07a-0e77-462c-bc86-ca0452c20a1c@gmail.com","subject":"Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-26T03:04:12Z","receivedAt":"2024-02-26T03:04:14Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Sun, Feb 25, 2024 at 10:58 AM <phillip.wood123@gmail.com> wrote:\n\n> Hi Brian\n> \n> On 23/02/2024 06:23, Brian Lyles wrote:\n>> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>> \n>>> Well spotted, do we really need a new test file just for this though? I\n>>> wonder if the new test would be better off living in\n>>> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh\n>> \n>> I was modelling this off of 't3422-rebase-incompatible-options.sh'.\n> \n> The rebase case is more complicated due to different options being \n> supported by the two different backends. Thankfully here we only have to \n> worry about options that are incompatible with \"--continue/--abort\" and \n> so adding \"--continue rejects --foo\" into the file that tests option \n> \"--foo\" keeps everything together.\n> \n>> Additionally, I do feel like these tests are only tangentially related\n>> to the tests that actually exercise the features themselves. Notably,\n>> the setup requirements are drastically different (simpler) since the\n>> test should fail long before any setup actually matters. For that\n>> reason, I think a separate file where other future tests for\n>> incompatible options can also live does make sense.\n>> \n>> Is there any particular downside to the new file that I am unaware of?\n> \n> The main downside is that it spreads the tests for a particular option \n> over several test files. There is also an overhead in setting up the \n> repository at the start of each test file.\n\nThat makes sense. I'll move these tests into\n`t3505-cherry-pick-empty.sh` for v3, along with the corresponding\nincompatibility tests for `--empty` introduced in the ultimate commit\nfor the series.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"489343","messageId":"17b74c2ffa1884ed.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"83070e02-8e6b-43d2-819d-2272fe895c75@gmail.com","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-02-26T03:32:32Z","receivedAt":"2024-02-26T03:32:34Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip and Junio\n\nOn Sun, Feb 25, 2024 at 10:57 AM <phillip.wood123@gmail.com> wrote:\n\n> On 23/02/2024 06:58, Brian Lyles wrote:\n\n>> I think I'm on board with leaving `--keep-redundant-commits` alone. I'm\n>> on the fence about having `--empty=keep` imply `--allow-empty` after\n>> seeing Junio's concerns. I laid out the options that I see in a reply to\n>> patch 6/8[1] and would appreciate input there. I'll adjust the details\n>> of this commit in v3 based on what we decide there.\n> \n> I'll take a look at that in the next couple of days\n>\n>> [1]: https://lore.kernel.org/git/17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk/\n\nI'm not quite sure what happened here, but it seems that:\n\n- The above link is to the wrong email, and\n- The email I meant to link to isn't showing up in the archive for some\n  reason, despite clearly showing as sent in my mailbox\n\nApologies for the confusion -- I'm not sure what happened.\n\nIn an attempt to keep this conversation on track, I've copied my\noriginal attempted reply to Phillip's[2] and Junio's[3] replies below.\n\n[2]: https://lore.kernel.org/git/3f276e10-7b03-4480-a157-47a7648e7f19@gmail.com/\n[3]: https://lore.kernel.org/git/xmqqwmqwcpf7.fsf@gitster.g/\n\nOn Fri, Feb 23, 2024 at 12:08 AM Brian Lyles <brianmlyles@gmail.com> wrote:\n>\n> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> > I agree that if we were starting from scratch there would be no reason\n> > to tie --apply-empty and --keep-redundant-commits together but I'm not\n> > sure it is worth the disruption of changing it now. We're about to add\n> > empty=keep which won't imply --allow-empty for anyone who wants that\n> > behavior and I still tend to think the practical effect of implying\n> > --allow-empty with --keep-redundant-commits is largely beneficial as I'm\n> > skeptical that users want to keep commits that become empty but not the\n> > ones that started empty.\n>\n> I think that's fair. I am okay dropping this potentially disruptive\n> change.\n>\n> It sounds like you are on board with `--empty=keep` not having the same\n> implication?\n>\n> That said...\n>\n> On Thu, Feb 22, 2024 at 12:41 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> > I do not quite see a good reason to do the opposite, dropping\n> > commits that started out as empty but keeping the ones that have\n> > become empty.  Such a behaviour has additional downside that after\n> > such a cherry-pick, when you cherry-pick the resulting history onto\n> > yet another base, your precious \"were not empty but have become so\n> > during the initial cherry-pick\" commits will appear as commits that\n> > were empty from the start.  So I do not see much reason to allow the\n> > decoupling, even with the new \"empty=keep\" thing that does not imply\n> > \"allow-empty\".\n>\n> Junio -- can you clarify this part?\n>\n> > So I do not see much reason to allow the decoupling, even with the new\n> > \"empty=keep\" thing that does not imply \"allow-empty\"\n>\n> I'm not 100% sure if you are saying that you want `--empty=keep` to\n> *also* imply `--allow-empty`, or that you simply want\n> `--keep-redundant-commits` to continue implying `--allow-empty`\n> *despite* the new `--empty=keep` no implying the same.\n>\n> On the one hand, I agree with Phillip's sentiment of \"if we were\n> starting from scratch there would be no reason to tie --apply-empty and\n> --keep-redundant-commits together\" (though your points perhaps provide\n> such a reason). On the other, if both `--keep-redundant-commits` and\n> `--empty=keep` behave the same way, it makes sense to soft-deprecate\n> `--keep-redundant-commits` as I have currently done later in this\n> series. If they do not behave the same way, that deprecation makes less\n> sense and we have two very similar (but not quite identical) options.\n>\n> Just to make sure we're on the same page, the options I see are:\n>\n> - (A): Neither `--keep-redundant-commits` nor `--empty=keep` imply\n>   `--allow-empty`, `--keep-redundant-commits` is soft-deprecated\n> - (B): Both `--keep-redundant-commits` and `--empty=keep` imply\n>   `--allow-empty`, `--keep-redundant-commits` is soft-deprecated\n> - (C): Both `--keep-redundant-commits` and `--empty=keep` imply\n>   `--allow-empty`, `--keep-redundant-commits` is *not* soft-deprecated\n>   as it is more descriptive as noted by Junio here[1]\n> - (D): `--keep-redundant-commits` continues to imply `--allow-empty` but\n>   `--empty=keep` does not. `--keep-redundant-commits` is *not*\n>   soft-deprecated as it behaves differently.\n>\n> (A) represents this v2 of the patch.\n>\n> I'm coming around to (B) based on Junio's workflow concerns, but to be\n> honest I am fine with any of these options. Junio, I *think* you're\n> saying you'd prefer (B) or (C)? Phillip, it sounds like you are okay\n> with (D) based on your response -- how do you feel about (B)?\n>\n> [1]: https://lore.kernel.org/git/xmqq8r4gnd3c.fsf@gitster.g/\n\nThank you both for your review and insight!\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"489482","messageId":"1004c565-ee6c-4aa4-8226-47d0ef7c8631@gmail.com","threadId":"60764","inReplyTo":"17b74c2ffa1884ed.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-02-27T10:39:33Z","receivedAt":"2024-02-27T10:39:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 26/02/2024 03:32, Brian Lyles wrote:\n> Hi Phillip and Junio\n> On Fri, Feb 23, 2024 at 12:08 AM Brian Lyles <brianmlyles@gmail.com> wrote:\n>>\n>> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>>> I agree that if we were starting from scratch there would be no reason\n>>> to tie --apply-empty and --keep-redundant-commits together but I'm not\n>>> sure it is worth the disruption of changing it now. We're about to add\n>>> empty=keep which won't imply --allow-empty for anyone who wants that\n>>> behavior and I still tend to think the practical effect of implying\n>>> --allow-empty with --keep-redundant-commits is largely beneficial as I'm\n>>> skeptical that users want to keep commits that become empty but not the\n>>> ones that started empty.\n>>\n>> I think that's fair. I am okay dropping this potentially disruptive\n>> change.\n>>\n>> It sounds like you are on board with `--empty=keep` not having the same\n>> implication?\n\nYes indeed\n\n>> That said...\n>>\n>> On Thu, Feb 22, 2024 at 12:41 PM Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>>> I do not quite see a good reason to do the opposite, dropping\n>>> commits that started out as empty but keeping the ones that have\n>>> become empty.  Such a behaviour has additional downside that after\n>>> such a cherry-pick, when you cherry-pick the resulting history onto\n>>> yet another base, your precious \"were not empty but have become so\n>>> during the initial cherry-pick\" commits will appear as commits that\n>>> were empty from the start.  So I do not see much reason to allow the\n>>> decoupling, even with the new \"empty=keep\" thing that does not imply\n>>> \"allow-empty\".\n>>\n>> Junio -- can you clarify this part?\n>>\n>>> So I do not see much reason to allow the decoupling, even with the new\n>>> \"empty=keep\" thing that does not imply \"allow-empty\"\n>>\n>> I'm not 100% sure if you are saying that you want `--empty=keep` to\n>> *also* imply `--allow-empty`, or that you simply want\n>> `--keep-redundant-commits` to continue implying `--allow-empty`\n>> *despite* the new `--empty=keep` no implying the same.\n\nFWIW I read it as the latter, but I can't claim to know what was in \nJunio's mind when he wrote it.\n\n>> On the one hand, I agree with Phillip's sentiment of \"if we were\n>> starting from scratch there would be no reason to tie --apply-empty and\n>> --keep-redundant-commits together\" (though your points perhaps provide\n>> such a reason). On the other, if both `--keep-redundant-commits` and\n>> `--empty=keep` behave the same way, it makes sense to soft-deprecate\n>> `--keep-redundant-commits` as I have currently done later in this\n>> series. If they do not behave the same way, that deprecation makes less\n>> sense and we have two very similar (but not quite identical) options.\n>>\n>> Just to make sure we're on the same page, the options I see are:\n>>\n>> - (A): Neither `--keep-redundant-commits` nor `--empty=keep` imply\n>>    `--allow-empty`, `--keep-redundant-commits` is soft-deprecated\n>> - (B): Both `--keep-redundant-commits` and `--empty=keep` imply\n>>    `--allow-empty`, `--keep-redundant-commits` is soft-deprecated\n>> - (C): Both `--keep-redundant-commits` and `--empty=keep` imply\n>>    `--allow-empty`, `--keep-redundant-commits` is *not* soft-deprecated\n>>    as it is more descriptive as noted by Junio here[1]\n>> - (D): `--keep-redundant-commits` continues to imply `--allow-empty` but\n>>    `--empty=keep` does not. `--keep-redundant-commits` is *not*\n>>    soft-deprecated as it behaves differently.\n>>\n>> (A) represents this v2 of the patch.\n>>\n>> I'm coming around to (B) based on Junio's workflow concerns, but to be\n>> honest I am fine with any of these options. Junio, I *think* you're\n>> saying you'd prefer (B) or (C)? Phillip, it sounds like you are okay\n>> with (D) based on your response -- how do you feel about (B)?\n\nYes, I'd prefer (D) as I think it gets confusing if some values of \n--empty imply --allow-empty and others don't. I could live with (B) though.\n\nBest Wishes\n\nPhillip\n"},{"id":"489551","messageId":"xmqqttltu7zs.fsf@gitster.g","threadId":"60764","inReplyTo":"1004c565-ee6c-4aa4-8226-47d0ef7c8631@gmail.com","subject":"Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-02-27T17:33:59Z","receivedAt":"2024-02-27T17:34:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n>>>> I do not quite see a good reason to do the opposite, dropping\n>>>> commits that started out as empty but keeping the ones that have\n>>>> become empty.  Such a behaviour has additional downside that after\n>>>> such a cherry-pick, when you cherry-pick the resulting history onto\n>>>> yet another base, your precious \"were not empty but have become so\n>>>> during the initial cherry-pick\" commits will appear as commits that\n>>>> were empty from the start.  So I do not see much reason to allow the\n>>>> decoupling, even with the new \"empty=keep\" thing that does not imply\n>>>> \"allow-empty\".\n>>>\n>>> Junio -- can you clarify this part?\n>>>\n>>>> So I do not see much reason to allow the decoupling, even with the new\n>>>> \"empty=keep\" thing that does not imply \"allow-empty\"\n>>>\n>>> I'm not 100% sure if you are saying that you want `--empty=keep` to\n>>> *also* imply `--allow-empty`, or that you simply want\n>>> `--keep-redundant-commits` to continue implying `--allow-empty`\n>>> *despite* the new `--empty=keep` no implying the same.\n>\n> FWIW I read it as the latter, but I can't claim to know what was in\n> Junio's mind when he wrote it.\n\nGiven that \"drop what was empty originally while keeping what became\nempty\" would \"lose\" what it wanted to keep (i.e. the one that has\nbecome empty\") when used to cherry-pick the result of doing such a\ncherry-pick, I do not think allowing such combination makes as much\nsense as the opposite \"keep what was empty originally while dropping\nwhat became empty\", which does not have such a property.\n\nAnd it does not matter if that is expressed via the combination of\nexisting --allow-empty and --keep-redundant-commits options, or the\nnewly proposed --empty=keep option.  If we start allowing the \"drop\nwhat was originally empty and keep what has become empty\"\ncombination if we make empty=keep not to imply --allow-empty, I do\nnot think it is a good idea.\n\nThat was what was on my mind when I wrote it.  It may be that I was\nnot following the discussion correctly, and making \"empty=keep\" not\nto imply \"allow-empty\" does *not* allow a request to \"drop what was\noriginally empty, keep what has become empty\".  If that is the case,\nthen I have no objection to making \"empty=keep\" not to imply\n\"allow-empty\".\n\nThanks.\n"},{"id":"490330","messageId":"20240310184602.539656-1-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:41:59Z","receivedAt":"2024-03-10T18:46:54Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The ultimate goal of this series is to allow git-cherry-pick(1) to\nautomatically drop redundant commits. The mechanism chosen is an\n`--empty` option that provides the same flexibility as the `--empty`\noptions for git-rebase(1) and git-am(1).\n\nSome secondary goals are to improve the consistency in the values and\ndocumentation for this option across the three commands.\n\nSee \"Does extending `--empty` to git-cherry-pick make sense?\" [1] for\nsome context for why this option is desired in git-cherry-pick(1).\n\n[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com\n\nAlong the way, I (with some help from Elijah and Phillip) found a few\nother things in the docs and related sequencer code to clean up.\n\nThe primary difference from v2 of this patch is that I no longer make\nany attempt to change the behavior of `--keep-redundant-commits`\nimplying `--allow-empty`, and the new `--empty=keep` will likewise also\nimply `--allow-empty`. See \"Re: [PATCH v2 8/8] cherry-pick: add\n`--empty` for more robust redundant commit handling\" [2] and the\nprevious messages in that thread for more context. Patch 6/8 from v2 is\ndropped entirely, with some adjustments to the ultimate patch in this\nseries as well.\n\n[2]: https://lore.kernel.org/git/xmqqttltu7zs.fsf@gitster.g/\n\nBrian Lyles (7):\n  docs: address inaccurate `--empty` default with `--exec`\n  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n  rebase: update `--empty=ask` to `--empty=stop`\n  sequencer: treat error reading HEAD as unborn branch\n  sequencer: do not require `allow_empty` for redundant commit options\n  cherry-pick: enforce `--keep-redundant-commits` incompatibility\n  cherry-pick: add `--empty` for more robust redundant commit handling\n\n Documentation/git-am.txt          | 20 ++++++----\n Documentation/git-cherry-pick.txt | 30 +++++++++++----\n Documentation/git-rebase.txt      | 26 ++++++++-----\n builtin/rebase.c                  | 16 +++++---\n builtin/revert.c                  | 38 +++++++++++++++++-\n sequencer.c                       | 64 ++++++++++++++++---------------\n t/t3424-rebase-empty.sh           | 55 ++++++++++++++++++++++++--\n t/t3501-revert-cherry-pick.sh     | 14 +++++--\n t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++\n 10 files changed, 279 insertions(+), 67 deletions(-)\n\n-- \n2.43.0\n\n"},{"id":"490331","messageId":"20240310184602.539656-2-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 1/7] docs: address inaccurate `--empty` default with `--exec`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:00Z","receivedAt":"2024-03-10T18:46:56Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The documentation for git-rebase(1) indicates that using the `--exec`\noption will use `--empty=drop`. This is inaccurate: when `--interactive`\nis not explicitly provided, `--exec` results in `--empty=keep`\nbehaviors.\n\nCorrectly indicate the behavior of `--exec` using `--empty=keep` when\n`--interactive` is not specified.\n\nReported-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt | 10 +++++-----\n t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 06206521fc..3334e85356 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes).  With drop (the default), commits that\n \tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask (implied by `--interactive`), the rebase will halt when\n-\tan empty commit is applied allowing you to choose whether to\n-\tdrop it, edit files more, or just commit the empty changes.\n-\tOther options, like `--exec`, will use the default of drop unless\n-\t`-i`/`--interactive` is explicitly specified.\n+\tWith ask, the rebase will halt when an empty commit is applied\n+\tallowing you to choose whether to drop it, edit files more, or just\n+\tcommit the empty changes.\n+\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n+\tOtherwise, when the `--exec` option is used, the default becomes keep.\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 5e1045a0af..73ff35ced2 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '\n \ttest_path_is_missing .git/MERGE_MSG\n '\n \n+test_expect_success 'rebase --exec --empty=drop' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=drop upstream &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=keep upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec uses default of --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=ask' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.43.0\n\n"},{"id":"490332","messageId":"20240310184602.539656-3-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:01Z","receivedAt":"2024-03-10T18:46:57Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Both of these pages document very similar `--empty` options, but with\ndifferent styles. The exact behavior of these `--empty` options differs\nsomewhat, but consistent styling in the docs is still beneficial. This\ncommit aims to make them more consistent.\n\nBreak the possible values for `--empty` into separate sections for\nreadability. Alphabetical order is chosen for consistency.\n\nIn a future commit, we'll be documenting a new `--empty` option for\ngit-cherry-pick(1), making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-am.txt     | 20 +++++++++++++-------\n Documentation/git-rebase.txt | 25 ++++++++++++++++---------\n 2 files changed, 29 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-am.txt b/Documentation/git-am.txt\nindex e080458d6c..f852e0ba79 100644\n--- a/Documentation/git-am.txt\n+++ b/Documentation/git-am.txt\n@@ -66,13 +66,19 @@ OPTIONS\n --quoted-cr=<action>::\n \tThis flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).\n \n---empty=(stop|drop|keep)::\n-\tBy default, or when the option is set to 'stop', the command\n-\terrors out on an input e-mail message lacking a patch\n-\tand stops in the middle of the current am session. When this\n-\toption is set to 'drop', skip such an e-mail message instead.\n-\tWhen this option is set to 'keep', create an empty commit,\n-\trecording the contents of the e-mail message as its log.\n+--empty=(drop|keep|stop)::\n+\tHow to handle an e-mail message lacking a patch:\n++\n+--\n+`drop`;;\n+\tThe e-mail message will be skipped.\n+`keep`;;\n+\tAn empty commit will be created, with the contents of the e-mail\n+\tmessage as its log.\n+`stop`;;\n+\tThe command will fail, stopping in the middle of the current `am`\n+\tsession. This is the default behavior.\n+--\n \n -m::\n --message-id::\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 3334e85356..0b0d0ccb80 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(drop|keep|ask)::\n+--empty=(ask|drop|keep)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n-\tupstream changes).  With drop (the default), commits that\n-\tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask, the rebase will halt when an empty commit is applied\n-\tallowing you to choose whether to drop it, edit files more, or just\n-\tcommit the empty changes.\n-\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n-\tOtherwise, when the `--exec` option is used, the default becomes keep.\n+\tupstream changes):\n++\n+--\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified.\n+`drop`;;\n+\tThe commit will be dropped. This is the default behavior.\n+`keep`;;\n+\tThe commit will be kept. This option is implied when `--exec` is\n+\tspecified unless `-i`/`--interactive` is also specified.\n+--\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\n@@ -704,7 +711,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(drop|keep|ask)` option for changing the behavior\n+also has an `--empty=(ask|drop|keep)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\n-- \n2.43.0\n\n"},{"id":"490333","messageId":"20240310184602.539656-4-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 3/7] rebase: update `--empty=ask` to `--empty=stop`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:02Z","receivedAt":"2024-03-10T18:46:59Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09), `stop` was used\ninstead of `ask`. `stop` is a more accurate term for describing what\nreally happens, and consistency is good.\n\nUpdate git-rebase(1) to also use `stop`, while keeping `ask` as a\ndeprecated synonym. Update the tests to primarily use `stop`, but also\nensure that `ask` is still allowed.\n\nIn a future commit, we'll be adding a new `--empty` option for\ngit-cherry-pick(1) as well, making the consistency even more relevant.\n\nReported-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt | 15 ++++++++-------\n builtin/rebase.c             | 16 ++++++++++------\n t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----\n 3 files changed, 34 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 0b0d0ccb80..67dd0a533e 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(ask|drop|keep)::\n+--empty=(drop|keep|stop)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes):\n +\n --\n-`ask`;;\n-\tThe rebase will halt when the commit is applied, allowing you to\n-\tchoose whether to drop it, edit files more, or just commit the empty\n-\tchanges. This option is implied when `-i`/`--interactive` is\n-\tspecified.\n `drop`;;\n \tThe commit will be dropped. This is the default behavior.\n `keep`;;\n \tThe commit will be kept. This option is implied when `--exec` is\n \tspecified unless `-i`/`--interactive` is also specified.\n+`stop`;;\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified. `ask` is a deprecated synonym of `stop`.\n --\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n@@ -711,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(ask|drop|keep)` option for changing the behavior\n+also has an `--empty=(drop|keep|stop)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 5b086f651a..a4916781ce 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -58,7 +58,7 @@ enum empty_type {\n \tEMPTY_UNSPECIFIED = -1,\n \tEMPTY_DROP,\n \tEMPTY_KEEP,\n-\tEMPTY_ASK\n+\tEMPTY_STOP\n };\n \n enum action {\n@@ -951,10 +951,14 @@ static enum empty_type parse_empty_value(const char *value)\n \t\treturn EMPTY_DROP;\n \telse if (!strcasecmp(value, \"keep\"))\n \t\treturn EMPTY_KEEP;\n-\telse if (!strcasecmp(value, \"ask\"))\n-\t\treturn EMPTY_ASK;\n+\telse if (!strcasecmp(value, \"stop\"))\n+\t\treturn EMPTY_STOP;\n+\telse if (!strcasecmp(value, \"ask\")) {\n+\t\twarning(_(\"--empty=ask is deprecated; use '--empty=stop' instead.\"));\n+\t\treturn EMPTY_STOP;\n+\t}\n \n-\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"ask\\\".\"), value);\n+\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"stop\\\".\"), value);\n }\n \n static int parse_opt_keep_empty(const struct option *opt, const char *arg,\n@@ -1133,7 +1137,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"instead of ignoring them\"),\n \t\t\t      1, PARSE_OPT_HIDDEN),\n \t\tOPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),\n-\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|ask)\",\n+\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|stop)\",\n \t\t\t       N_(\"how to handle commits that become empty\"),\n \t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\tOPT_CALLBACK_F('k', \"keep-empty\", &options, NULL,\n@@ -1550,7 +1554,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (options.empty == EMPTY_UNSPECIFIED) {\n \t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n-\t\t\toptions.empty = EMPTY_ASK;\n+\t\t\toptions.empty = EMPTY_STOP;\n \t\telse if (options.exec.nr > 0)\n \t\t\toptions.empty = EMPTY_KEEP;\n \t\telse\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 73ff35ced2..1ee6b00fd5 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'rebase --merge --empty=stop' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --merge --empty=stop upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'rebase --merge --empty=ask' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --merge --empty=ask upstream &&\n@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive --empty=ask' '\n+test_expect_success 'rebase --interactive --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --interactive --empty=ask upstream &&\n+\ttest_must_fail git rebase --interactive --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive uses default of --empty=ask' '\n+test_expect_success 'rebase --interactive uses default of --empty=stop' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --interactive upstream &&\n \n@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --exec --empty=ask' '\n+test_expect_success 'rebase --exec --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n-- \n2.43.0\n\n"},{"id":"490334","messageId":"20240310184602.539656-5-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:03Z","receivedAt":"2024-03-10T18:47:00Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When using git-cherry-pick(1) with `--allow-empty` while on an unborn\nbranch, an error is thrown. This is inconsistent with the same\ncherry-pick when `--allow-empty` is not specified.\n\nTreat a failure reading HEAD as an unborn branch in\n`is_index_unchanged`. This is consistent with other sequencer logic such\nas `do_pick_commit`. When on an unborn branch, use the `empty_tree` as\nthe tree to compare against.\n\nAdd a new test to cover this scenario. While modelled off of the\nexisting 'cherry-pick on unborn branch' test, some improvements can be\nmade:\n\n- Use `git switch --orphan unborn` instead of `git checkout --orphan\n  unborn` to avoid the need for a separate `rm -rf *` call\n- Avoid using `--quiet` in the `git diff` call to make debugging easier\n  in the event of a failure\n\nMake these improvements to the existing test as well as the new test.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nDifferences from v2:\n\n- Minor code and test cleanup per [1] and the other replies in that\n  thread\n\n[1]: https://lore.kernel.org/git/62247a1c-0249-4ce1-8626-fe97b89c23dc@gmail.com/\n\n sequencer.c                   | 35 +++++++++++++++++++++--------------\n t/t3501-revert-cherry-pick.sh | 14 +++++++++++---\n 2 files changed, 32 insertions(+), 17 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f49a871ac0..a62ce244c1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -770,29 +770,36 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)\n static int is_index_unchanged(struct repository *r)\n {\n \tstruct object_id head_oid, *cache_tree_oid;\n+\tconst struct object_id *head_tree_oid;\n \tstruct commit *head_commit;\n \tstruct index_state *istate = r->index;\n \n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n-\t\treturn error(_(\"could not resolve HEAD commit\"));\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n+\t\t/*\n+\t\t * Treat an error reading HEAD as an unborn branch.\n+\t\t */\n+\t\thead_tree_oid = the_hash_algo->empty_tree;\n+\t} else {\n+\t\thead_commit = lookup_commit(r, &head_oid);\n \n-\thead_commit = lookup_commit(r, &head_oid);\n+\t\t/*\n+\t\t * If head_commit is NULL, check_commit, called from\n+\t\t * lookup_commit, would have indicated that head_commit is not\n+\t\t * a commit object already.  repo_parse_commit() will return failure\n+\t\t * without further complaints in such a case.  Otherwise, if\n+\t\t * the commit is invalid, repo_parse_commit() will complain.  So\n+\t\t * there is nothing for us to say here.  Just return failure.\n+\t\t */\n+\t\tif (repo_parse_commit(r, head_commit))\n+\t\t\treturn -1;\n \n-\t/*\n-\t * If head_commit is NULL, check_commit, called from\n-\t * lookup_commit, would have indicated that head_commit is not\n-\t * a commit object already.  repo_parse_commit() will return failure\n-\t * without further complaints in such a case.  Otherwise, if\n-\t * the commit is invalid, repo_parse_commit() will complain.  So\n-\t * there is nothing for us to say here.  Just return failure.\n-\t */\n-\tif (repo_parse_commit(r, head_commit))\n-\t\treturn -1;\n+\t\thead_tree_oid = get_commit_tree_oid(head_commit);\n+\t}\n \n \tif (!(cache_tree_oid = get_cache_tree_oid(istate)))\n \t\treturn -1;\n \n-\treturn oideq(cache_tree_oid, get_commit_tree_oid(head_commit));\n+\treturn oideq(cache_tree_oid, head_tree_oid);\n }\n \n static int write_author_script(const char *message)\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex aeab689a98..8a1d154ca6 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '\n '\n \n test_expect_success 'cherry-pick on unborn branch' '\n-\tgit checkout --orphan unborn &&\n+\tgit switch --orphan unborn &&\n \tgit rm --cached -r . &&\n-\trm -rf * &&\n \tgit cherry-pick initial &&\n-\tgit diff --quiet initial &&\n+\tgit diff initial &&\n+\ttest_cmp_rev ! initial HEAD\n+'\n+\n+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n+\tgit checkout --detach &&\n+\tgit branch -D unborn &&\n+\tgit switch --orphan unborn &&\n+\tgit cherry-pick initial --allow-empty &&\n+\tgit diff initial &&\n \ttest_cmp_rev ! initial HEAD\n '\n \n-- \n2.43.0\n\n"},{"id":"490335","messageId":"20240310184602.539656-6-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 5/7] sequencer: do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:04Z","receivedAt":"2024-03-10T18:47:02Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"A consumer of the sequencer that wishes to take advantage of either the\n`keep_redundant_commits` or `drop_redundant_commits` feature must also\nspecify `allow_empty`. However, these refer to two distinct types of\nempty commits:\n\n- `allow_empty` refers specifically to commits which start empty\n- `keep_redundant_commits` refers specifically to commits that do not\n  start empty, but become empty due to the content already existing in\n  the target history\n\nConceptually, there is no reason that the behavior for handling one of\nthese should be entangled with the other. It is particularly unintuitive\nto require `allow_empty` in order for `drop_redundant_commits` to have\nan effect: in order to prevent redundant commits automatically,\ninitially-empty commits would need to be kept automatically as well.\n\nInstead, rewrite the `allow_empty()` logic to remove the over-arching\nrequirement that `allow_empty` be specified in order to reach any of the\nkeep/drop behaviors. Only if the commit was originally empty will\n`allow_empty` have an effect.\n\nNote that no behavioral changes should result from this commit -- it\nmerely sets the stage for future commits. In one such future commit, an\n`--empty` option will be added to git-cherry-pick(1), meaning that\n`drop_redundant_commits` will be used by that command.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n sequencer.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a62ce244c1..8dce175f2e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1726,34 +1726,25 @@ static int allow_empty(struct repository *r,\n \tint index_unchanged, originally_empty;\n \n \t/*\n-\t * Four cases:\n+\t * For a commit that is initially empty, allow_empty determines if it\n+\t * should be kept or not\n \t *\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, and\n-\t *     just drop the ones that become empty\n-\t *\n-\t * (3) we allow ones that were initially empty, but\n-\t *     halt for the ones that become empty;\n-\t *\n-\t * (4) we allow both.\n+\t * For a commit that becomes empty, keep_redundant_commits and\n+\t * drop_redundant_commits determine whether the commit should be kept or\n+\t * dropped. If neither is specified, halt.\n \t */\n-\tif (!opts->allow_empty)\n-\t\treturn 0; /* let \"git commit\" barf as necessary */\n-\n \tindex_unchanged = is_index_unchanged(r);\n \tif (index_unchanged < 0)\n \t\treturn index_unchanged;\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n \n-\tif (opts->keep_redundant_commits)\n-\t\treturn 1;\n-\n \toriginally_empty = is_original_commit_empty(commit);\n \tif (originally_empty < 0)\n \t\treturn originally_empty;\n \tif (originally_empty)\n+\t\treturn opts->allow_empty;\n+\telse if (opts->keep_redundant_commits)\n \t\treturn 1;\n \telse if (opts->drop_redundant_commits)\n \t\treturn 2;\n-- \n2.43.0\n\n"},{"id":"490336","messageId":"20240310184602.539656-7-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:05Z","receivedAt":"2024-03-10T18:47:04Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When `--keep-redundant-commits` was added in  b27cfb0d8d\n(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was\nnot marked as incompatible with the various operations needed to\ncontinue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and\n`--quit`).\n\nEnforce this incompatibility via `verify_opt_compatible` like we do for\nthe other various options.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nDifferences from v2:\n\n- New tests are added to t3505-cherry-pick-empty.sh instead of a new\n  test file\n\n builtin/revert.c             |  1 +\n t/t3505-cherry-pick-empty.sh | 14 ++++++++++++++\n 2 files changed, 15 insertions(+)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 89821bab95..a1936ef70e 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -167,6 +167,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--ff\", opts->allow_ff,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n+\t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n \t\t\t\tNULL);\n \t}\n\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex eba3c38d5a..61f91aaa0a 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -99,4 +99,18 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n \ttest_cmp expect actual\n '\n\n+test_expect_success '--keep-redundant-commits is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n test_done\n-- \n2.43.0\n\n"},{"id":"490337","messageId":"20240310184602.539656-8-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-10T18:42:06Z","receivedAt":"2024-03-10T18:47:05Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a\ncommit being made redundant if the content from the picked commit is\nalready present in the target history. However, git-cherry-pick(1) does\nnot have the same options available that git-rebase(1) and git-am(1) have.\n\nThere are three things that can be done with these redundant commits:\ndrop them, keep them, or have the cherry-pick stop and wait for the user\nto take an action. git-rebase(1) has the `--empty` option added in commit\ne98c4269c8 (rebase (interactive-backend): fix handling of commits that\nbecome empty, 2020-02-15), which handles all three of these scenarios.\nSimilarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09).\n\ngit-cherry-pick(1), on the other hand, only supports two of the three\npossiblities: Keep the redundant commits via `--keep-redundant-commits`,\nor have the cherry-pick fail by not specifying that option. There is no\nway to automatically drop redundant commits.\n\nIn order to bring git-cherry-pick(1) more in-line with git-rebase(1) and\ngit-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It\nhas the same three options (keep, drop, and stop), and largely behaves\nthe same. The notable difference is that for git-cherry-pick(1), the\ndefault will be `stop`, which maintains the current behavior when the\noption is not specified.\n\nLike the existing `--keep-redundant-commits`, `--empty=keep` will imply\n`--allow-empty`.\n\nThe `--keep-redundant-commits` option will be documented as a deprecated\nsynonym of `--empty=keep`, and will be supported for backwards\ncompatibility for the time being.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nDifferences from v2:\n\n- `--empty=keep` will now imply `--allow-empty`, consistent with\n  `--keep-redundant-commits`. See [1] for more information.\n- Tests for persistence of the new behaviors after `--continue`, etc.\n  are more focused on user-visible behaviors rather than implementation\n  details.\n- The new empty_action enum uses -1 for unspecified instead of 0.\n\n[1]: https://lore.kernel.org/git/xmqqttltu7zs.fsf@gitster.g/\n\n Documentation/git-cherry-pick.txt | 30 +++++++++++++++++++------\n builtin/revert.c                  | 37 ++++++++++++++++++++++++++++++-\n sequencer.c                       |  6 +++++\n t/t3505-cherry-pick-empty.sh      | 37 ++++++++++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++++++++++++\n 5 files changed, 133 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex fdcad3d200..e6a61503c7 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -131,20 +131,36 @@ effect to your index in a row.\n \teven without this option.  Note also, that use of this option only\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n-\tprevious commit are dropped.  To force the inclusion of those commits\n-\tuse `--keep-redundant-commits`.\n+\tprevious commit will cause the cherry-pick to fail.  To force the\n+\tinclusion of those commits, use `--empty=keep`.\n \n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n \tThis option overrides that behavior, allowing commits with empty\n \tmessages to be cherry picked.\n \n+--empty=(drop|keep|stop)::\n+\tHow to handle commits being cherry-picked that are redundant with\n+\tchanges already in the current history.\n++\n+--\n+`drop`;;\n+\tThe commit will be dropped.\n+`keep`;;\n+\tThe commit will be kept. Implies `--allow-empty`.\n+`stop`;;\n+\tThe cherry-pick will stop when the commit is applied, allowing\n+\tyou to examine the commit. This is the default behavior.\n+--\n++\n+Note that this option specifies how to handle a commit that was not initially\n+empty, but rather became empty due to a previous commit. Commits that were\n+initially empty will cause the cherry-pick to fail. To force the inclusion of\n+those commits, use `--allow-empty`.\n++\n+\n --keep-redundant-commits::\n-\tIf a commit being cherry picked duplicates a commit already in the\n-\tcurrent history, it will become empty.  By default these\n-\tredundant commits cause `cherry-pick` to stop so the user can\n-\texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object.  Implies `--allow-empty`.\n+\tDeprecated synonym for `--empty=keep`.\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 a1936ef70e..53935d2c68 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n+enum empty_action {\n+\tEMPTY_COMMIT_UNSPECIFIED = -1,\n+\tSTOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */\n+\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n+\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n+};\n+\n+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n+{\n+\tint *opt_value = opt->value;\n+\n+\tBUG_ON_OPT_NEG(unset);\n+\n+\tif (!strcmp(arg, \"stop\"))\n+\t\t*opt_value = STOP_ON_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"drop\"))\n+\t\t*opt_value = DROP_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"keep\"))\n+\t\t*opt_value = KEEP_EMPTY_COMMIT;\n+\telse\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n+\n+\treturn 0;\n+}\n+\n static int option_parse_m(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n \tconst char *cleanup_arg = NULL;\n+\tenum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\tOPT_BOOL(0, \"ff\", &opts->allow_ff, N_(\"allow fast-forward\")),\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, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"deprecated: use --empty=keep instead\")),\n+\t\t\tOPT_CALLBACK_F(0, \"empty\", &empty_opt, \"(stop|drop|keep)\",\n+\t\t\t\t       N_(\"how to handle commits that become empty\"),\n+\t\t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\n@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n \n+\tif (opts->action == REPLAY_PICK) {\n+\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n+\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n+\t}\n+\n \t/* implies allow_empty */\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n@@ -168,6 +202,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n \t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n+\t\t\t\t\"--empty\", empty_opt != EMPTY_COMMIT_UNSPECIFIED,\n \t\t\t\tNULL);\n \t}\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 8dce175f2e..a3c73ecb9f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2924,6 +2924,9 @@ static int populate_opts_cb(const char *key, const char *value,\n \telse if (!strcmp(key, \"options.allow-empty-message\"))\n \t\topts->allow_empty_message =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n+\telse if (!strcmp(key, \"options.drop-redundant-commits\"))\n+\t\topts->drop_redundant_commits =\n+\t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n \t\topts->keep_redundant_commits =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n@@ -3468,6 +3471,9 @@ static int save_opts(struct replay_opts *opts)\n \tif (opts->allow_empty_message)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.allow-empty-message\", \"true\");\n+\tif (opts->drop_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file,\n+\t\t\t\t\"options.drop-redundant-commits\", \"true\");\n \tif (opts->keep_redundant_commits)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.keep-redundant-commits\", \"true\");\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex 61f91aaa0a..9748443530 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n \tgit commit -m \"add file2 on the side\"\n '\n \n-test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n \tgit reset --hard &&\n \tgit checkout fork^0 &&\n \ttest_must_fail git cherry-pick main\n@@ -113,4 +113,39 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '\n \tgit cherry-pick --abort\n '\n \n+test_expect_success '--empty is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=stop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\ttest_must_fail git cherry-pick --empty=stop main 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=drop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=drop main &&\n+\ttest_commit_message HEAD -m \"add file2 on the side\"\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=keep' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=keep main &&\n+\ttest_commit_message HEAD -m \"add file2 on main\"\n+'\n+\n test_done\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 72020a51c4..7eb52b12ed 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cherry-pick persists --empty=stop correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&\n+\ttest_must_fail git cherry-pick --skip 2>msg &&\n+\ttest_grep \"The previous cherry-pick is now empty\" msg &&\n+\trm msg &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=drop correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&\n+\tgit cherry-pick --skip &&\n+\ttest_cmp_rev yetanotherpick HEAD\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=keep correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&\n+\tgit cherry-pick --skip &&\n+\ttest_cmp_rev yetanotherpick HEAD^\n+'\n+\n test_expect_success 'revert persists opts correctly' '\n \tpristine_detach initial &&\n \t# to make sure that the session to revert a sequence\n-- \n2.43.0\n\n"},{"id":"490349","messageId":"xmqq4jddwrzr.fsf@gitster.g","threadId":"60764","inReplyTo":"20240310184602.539656-5-brianmlyles@gmail.com","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-11T00:07:36Z","receivedAt":"2024-03-11T00:07:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brian Lyles <brianmlyles@gmail.com> writes:\n\n> When using git-cherry-pick(1) with `--allow-empty` while on an unborn\n> branch, an error is thrown. This is inconsistent with the same\n> cherry-pick when `--allow-empty` is not specified.\n\nWhen cherry-picking on top of an unborn branch without the option,\nhow does the code figure out that we are on an unborn branch?  We\nmust be doing the detection, as we'd need to at least know the fact\nthat we are on an unborn in order to make the resulting commit a\nroot commit and also in order to apply the cherry-picked changes\nagainst an empty tree.\n\n> Treat a failure reading HEAD as an unborn branch in\n> `is_index_unchanged`. This is consistent with other sequencer logic such\n> as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as\n> the tree to compare against.\n\nIt is not a good code hygiene to assume that a failure to read HEAD\nalways means we are on an unborn branch, even if that is the most\nlikely cause of the failure.  We may instead want to positively\ndetermine that we are on an unborn state, by seeing if the HEAD is a\nsymbolic ref that points at a ref in refs/heads/* hierarchy, and\nthat ref does not exist.\n\nBut if the existing sequencer code is littered with such loose logic\neverywhere, perhaps imitating the looseness for now with a NEEDSWORK\ncomment to later clean them all up would be the least we could do\nright now.  If we want to be more ambitious, a few clean-up patches\nthat refactors existing code paths that detect that we are on an\nunborn branch into a call to a single helper function, and then\ntightens its implementation, before doing this step would be even\nbetter (but I offhand do not know how bad the current code is, so I\nwould understand it if it may turn out to be a lot more work than\nyou would want to invest in).\n"},{"id":"490365","messageId":"xmqqh6hcu2tg.fsf@gitster.g","threadId":"60764","inReplyTo":"xmqq4jddwrzr.fsf@gitster.g","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-11T16:54:19Z","receivedAt":"2024-03-11T16:54:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> It is not a good code hygiene to assume that a failure to read HEAD\n> always means we are on an unborn branch, even if that is the most\n> likely cause of the failure.  We may instead want to positively\n> determine that we are on an unborn state, by seeing if the HEAD is a\n> symbolic ref that points at a ref in refs/heads/* hierarchy, and\n> that ref does not exist.\n\nI suspect that you are almost there.\n\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n+\t\t/*\n+\t\t * Treat an error reading HEAD as an unborn branch.\n+\t\t */\n\nAfter we see this error, if we make a call to resolve_ref_unsafe()\nwith RESOLVE_REF_NO_RECURSE, the call should return the branch that\nwe are on but is not yet born, and &oid will get the null_oid.  I am\nnot sure if there is a way to combine the two calls into one, but\nbecause the failure case (i.e. doing anything on an unborn branch)\nis a rare case that happens only once before actually giving birth\nto a new branch, it probably is not worth spending extra brain\ncycles on it and just use a simple and stupid \"when resolving fails,\nsee if we are in a rare salvageable case with extra code\" approach.\n\n"},{"id":"490439","messageId":"17bbe214bab8dc40.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"xmqqh6hcu2tg.fsf@gitster.g","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-12T02:04:22Z","receivedAt":"2024-03-12T02:04:24Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Junio,\n\nOn Mon, Mar 11, 2024 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n>> It is not a good code hygiene to assume that a failure to read HEAD\n>> always means we are on an unborn branch, even if that is the most\n>> likely cause of the failure.  We may instead want to positively\n>> determine that we are on an unborn state, by seeing if the HEAD is a\n>> symbolic ref that points at a ref in refs/heads/* hierarchy, and\n>> that ref does not exist.\n> \n> I suspect that you are almost there.\n> \n> +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n> +\t\t/*\n> +\t\t * Treat an error reading HEAD as an unborn branch.\n> +\t\t */\n> \n> After we see this error, if we make a call to resolve_ref_unsafe()\n> with RESOLVE_REF_NO_RECURSE, the call should return the branch that\n> we are on but is not yet born, and &oid will get the null_oid.  I am\n> not sure if there is a way to combine the two calls into one, but\n> because the failure case (i.e. doing anything on an unborn branch)\n> is a rare case that happens only once before actually giving birth\n> to a new branch, it probably is not worth spending extra brain\n> cycles on it and just use a simple and stupid \"when resolving fails,\n> see if we are in a rare salvageable case with extra code\" approach.\n\nIf I'm understanding you correctly, it sounds like you're hinting at\nsomething like this:\n\n\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n\t\t/*\n\t\t * Check to see if this is an unborn branch\n\t\t */\n\t\tif (resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_NO_RECURSE, &head_oid, NULL)\n\t\t\t&& is_null_oid(&head_oid)) {\n\t\t\thead_tree_oid = the_hash_algo->empty_tree;\n\t\t} else {\n\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n\t\t}\n\t} else {\n\t\t...\n\t}\n\nThis does in fact seem to have the same effect as the original patch in\nthis case. If this is in line with what you are looking for, I can\ninclude this in v4. The commit message would be adjusted slightly to be:\n\n\tsequencer: handle unborn branch with `--allow-empty`\n\n\tWhen using git-cherry-pick(1) with `--allow-empty` while on an\n\tunborn branch, an error is thrown. This is inconsistent with the\n\tsame cherry-pick when `--allow-empty` is not specified.\n\n\tDetect unborn branches in `is_index_unchanged`. When on an unborn\n\tbranch, use the `empty_tree` as the tree to compare against.\n\n\t...\n\nDo these changes look good?\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"490520","messageId":"xmqqil1rjdf7.fsf@gitster.g","threadId":"60764","inReplyTo":"17bbe214bab8dc40.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-12T22:25:16Z","receivedAt":"2024-03-12T22:25:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Brian Lyles\" <brianmlyles@gmail.com> writes:\n\n> If I'm understanding you correctly, it sounds like you're hinting at\n> something like this:\n\nYou may also want to add _READING (I do not know offhand).  Also\nyou'd want to make sure resolve_ref_unsafe() returned a plausible\nlooking refname (i.e. passes starts_with(\"refs/heads/\").\n\nBut other than that, yeah, doing these as \"extra checks\" only after\nwe see the primary resolve_ref(\"HEAD\") fails was what I had in mind.\n\nThanks.\n"},{"id":"490541","messageId":"87155d50-40b8-4394-99f3-5194d7da785a@gmail.com","threadId":"60764","inReplyTo":"20240310184602.539656-5-brianmlyles@gmail.com","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-13T15:10:02Z","receivedAt":"2024-03-13T15:10:07Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/03/2024 18:42, Brian Lyles wrote:\n> - Avoid using `--quiet` in the `git diff` call to make debugging easier\n>    in the event of a failure\n\nSorry, I forgot when I was reviewing v2 that we need to replace --quiet \nwith --exit-code otherwise the diff will never fail. Apart from that I \ndon't have anything to add to Junio's comments on this patch.\n\nBest Wishes\n\nPhillip\n"},{"id":"490550","messageId":"d7c926ce-8a2d-4828-a3b0-3c4a9bcfe92a@gmail.com","threadId":"60764","inReplyTo":"20240310184602.539656-8-brianmlyles@gmail.com","subject":"Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-13T16:10:35Z","receivedAt":"2024-03-13T16:10:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/03/2024 18:42, Brian Lyles wrote:\n> As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a\n> commit being made redundant if the content from the picked commit is\n> already present in the target history. However, git-cherry-pick(1) does\n> not have the same options available that git-rebase(1) and git-am(1) have.\n> \n> There are three things that can be done with these redundant commits:\n> drop them, keep them, or have the cherry-pick stop and wait for the user\n> to take an action. git-rebase(1) has the `--empty` option added in commit\n> e98c4269c8 (rebase (interactive-backend): fix handling of commits that\n> become empty, 2020-02-15), which handles all three of these scenarios.\n> Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support\n> --empty=<option> to handle empty patches, 2021-12-09).\n> \n> git-cherry-pick(1), on the other hand, only supports two of the three\n> possiblities: Keep the redundant commits via `--keep-redundant-commits`,\n> or have the cherry-pick fail by not specifying that option. There is no\n> way to automatically drop redundant commits.\n> \n> In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and\n> git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It\n> has the same three options (keep, drop, and stop), and largely behaves\n> the same. The notable difference is that for git-cherry-pick(1), the\n> default will be `stop`, which maintains the current behavior when the\n> option is not specified.\n> \n> Like the existing `--keep-redundant-commits`, `--empty=keep` will imply\n> `--allow-empty`.\n\nI think this is reasonable. git rebase defaults to keeping commits that \nstart empty so now that \"git cherry-pick --empty=keep\" implies \n\"--allow-empty\" it will behave the same as \"git rebase --empty=keep\" as \nwell as matching the behavior of \"git cherry-pick --keep-redundant-commits\".\n\n> The `--keep-redundant-commits` option will be documented as a deprecated\n> synonym of `--empty=keep`, and will be supported for backwards\n> compatibility for the time being.\n> \n> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n> ---\n> \n> Differences from v2:\n> \n> - `--empty=keep` will now imply `--allow-empty`, consistent with\n>    `--keep-redundant-commits`. See [1] for more information.\n> - Tests for persistence of the new behaviors after `--continue`, etc.\n>    are more focused on user-visible behaviors rather than implementation\n>    details.\n> - The new empty_action enum uses -1 for unspecified instead of 0.\n\nThanks for reworking the tests. I've left a small comment below but this \nis looking good.\n\n> +--empty=(drop|keep|stop)::\n> +\tHow to handle commits being cherry-picked that are redundant with\n> +\tchanges already in the current history.\n> ++\n> +--\n> +`drop`;;\n> +\tThe commit will be dropped.\n> +`keep`;;\n> +\tThe commit will be kept. Implies `--allow-empty`.\n> +`stop`;;\n> +\tThe cherry-pick will stop when the commit is applied, allowing\n> +\tyou to examine the commit. This is the default behavior.\n> +--\n> ++\n> +Note that this option specifies how to handle a commit that was not initially\n> +empty, but rather became empty due to a previous commit. Commits that were\n> +initially empty will cause the cherry-pick to fail. To force the inclusion of\n> +those commits, use `--allow-empty`.\n\nI found this last paragraph is slightly confusing now --empty=keep \nimplies --allow-empty. Maybe we could change the middle sentence to say \nsomething like\n\n     With the exception of `--empty=keep` commits that were initially\n     empty will cause the cherry-pick to fail.\n\n> +\tif (opts->action == REPLAY_PICK) {\n> +\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n> +\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n> +\t}\n> +\n>   \t/* implies allow_empty */\n>   \tif (opts->keep_redundant_commits)\n>   \t\topts->allow_empty = 1;\n\n--empty=keep sets opts->keep_redundant_commits above so this makes it \nimply --allow-empty - good.\n\nBest Wishes\n\nPhillip\n"},{"id":"490551","messageId":"d2a57122-0957-437e-8b97-a79e72db5e4f@gmail.com","threadId":"60764","inReplyTo":"20240310184602.539656-1-brianmlyles@gmail.com","subject":"Re: [PATCH v3 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-13T16:12:21Z","receivedAt":"2024-03-13T16:12:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 10/03/2024 18:41, Brian Lyles wrote:\n> The ultimate goal of this series is to allow git-cherry-pick(1) to\n> automatically drop redundant commits. The mechanism chosen is an\n> `--empty` option that provides the same flexibility as the `--empty`\n> options for git-rebase(1) and git-am(1).\n> \n> Some secondary goals are to improve the consistency in the values and\n> documentation for this option across the three commands.\n> \n> See \"Does extending `--empty` to git-cherry-pick make sense?\" [1] for\n> some context for why this option is desired in git-cherry-pick(1).\n> \n> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com\n> \n> Along the way, I (with some help from Elijah and Phillip) found a few\n> other things in the docs and related sequencer code to clean up.\n> \n> The primary difference from v2 of this patch is that I no longer make\n> any attempt to change the behavior of `--keep-redundant-commits`\n> implying `--allow-empty`, and the new `--empty=keep` will likewise also\n> imply `--allow-empty`. See \"Re: [PATCH v2 8/8] cherry-pick: add\n> `--empty` for more robust redundant commit handling\" [2] and the\n> previous messages in that thread for more context. Patch 6/8 from v2 is\n> dropped entirely, with some adjustments to the ultimate patch in this\n> series as well.\n\nThis is looking good, I've left a couple of comments but there is \nnothing major.\n\nThanks for working on it\n\nPhillip\n\n> [2]: https://lore.kernel.org/git/xmqqttltu7zs.fsf@gitster.g/\n> \n> Brian Lyles (7):\n>    docs: address inaccurate `--empty` default with `--exec`\n>    docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n>    rebase: update `--empty=ask` to `--empty=stop`\n>    sequencer: treat error reading HEAD as unborn branch\n>    sequencer: do not require `allow_empty` for redundant commit options\n>    cherry-pick: enforce `--keep-redundant-commits` incompatibility\n>    cherry-pick: add `--empty` for more robust redundant commit handling\n> \n>   Documentation/git-am.txt          | 20 ++++++----\n>   Documentation/git-cherry-pick.txt | 30 +++++++++++----\n>   Documentation/git-rebase.txt      | 26 ++++++++-----\n>   builtin/rebase.c                  | 16 +++++---\n>   builtin/revert.c                  | 38 +++++++++++++++++-\n>   sequencer.c                       | 64 ++++++++++++++++---------------\n>   t/t3424-rebase-empty.sh           | 55 ++++++++++++++++++++++++--\n>   t/t3501-revert-cherry-pick.sh     | 14 +++++--\n>   t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++++-\n>   t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++\n>   10 files changed, 279 insertions(+), 67 deletions(-)\n> \n"},{"id":"490554","messageId":"xmqq1q8edpb4.fsf@gitster.g","threadId":"60764","inReplyTo":"d7c926ce-8a2d-4828-a3b0-3c4a9bcfe92a@gmail.com","subject":"Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-13T17:17:19Z","receivedAt":"2024-03-13T17:17:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n>> +Note that this option specifies how to handle a commit that was not initially\n>> +empty, but rather became empty due to a previous commit. Commits that were\n>> +initially empty will cause the cherry-pick to fail. To force the inclusion of\n>> +those commits, use `--allow-empty`.\n>\n> I found this last paragraph is slightly confusing now --empty=keep\n> implies --allow-empty. Maybe we could change the middle sentence to\n> say something like\n>\n>     With the exception of `--empty=keep` commits that were initially\n>     empty will cause the cherry-pick to fail.\n\nThat is certainly easier to read and much less confusing.\n\nThanks, both.\n"},{"id":"490755","messageId":"17bd1fb6986bb4d8.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"xmqqil1rjdf7.fsf@gitster.g","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-16T03:05:02Z","receivedAt":"2024-03-16T03:05:04Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"\nOn Tue, Mar 12, 2024 at 5:25 PM Junio C Hamano <gitster@pobox.com> wrote:\n\n> \"Brian Lyles\" <brianmlyles@gmail.com> writes:\n> \n>> If I'm understanding you correctly, it sounds like you're hinting at\n>> something like this:\n> \n> You may also want to add _READING (I do not know offhand).  Also\n> you'd want to make sure resolve_ref_unsafe() returned a plausible\n> looking refname (i.e. passes starts_with(\"refs/heads/\").\n> \n> But other than that, yeah, doing these as \"extra checks\" only after\n> we see the primary resolve_ref(\"HEAD\") fails was what I had in mind.\n> \n> Thanks.\n\nMakes sense to me. From reading the documentation for\nRESOLVE_REF_READING, I think we do want that as well, and the\nstarts_with(\"refs/heads/\") check works as expected too. I'll incorporate\nthose into v4.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"490756","messageId":"17bd1fdc7357bd91.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"87155d50-40b8-4394-99f3-5194d7da785a@gmail.com","subject":"Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-16T03:07:45Z","receivedAt":"2024-03-16T03:07:46Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Wed, Mar 13, 2024 at 10:10 AM <phillip.wood123@gmail.com> wrote:\n\n> Hi Brian\n> \n> On 10/03/2024 18:42, Brian Lyles wrote:\n>> - Avoid using `--quiet` in the `git diff` call to make debugging easier\n>>    in the event of a failure\n> \n> Sorry, I forgot when I was reviewing v2 that we need to replace --quiet \n> with --exit-code otherwise the diff will never fail. Apart from that I \n> don't have anything to add to Junio's comments on this patch.\n\nAh, of course -- thank you for catching that. I'll include that in v4.\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"490760","messageId":"CAHPHrSfiMbU55K2=8+hJZy1cMSRbYM77pCK8BdcAPHLvapHO_A@mail.gmail.com","threadId":"60764","inReplyTo":"xmqq1q8edpb4.fsf@gitster.g","subject":"Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-16T05:20:01Z","receivedAt":"2024-03-16T05:20:39Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip and Junio\n\nApologies in advance if this is a duplicate message -- it appears that my reply\nnever showed up on the public archive at https://lore.kernel.org/git/ for some\nreason, and I'm unsure if those CC'd received it either. As such, I am resending\nit.\n\nOn Wed, Mar 13, 2024 at 12:17 PM Junio C Hamano <gitster@pobox.com> wrote:\n\n> phillip.wood123@gmail.com writes:\n>\n>>> +Note that this option specifies how to handle a commit that was not initially\n>>> +empty, but rather became empty due to a previous commit. Commits that were\n>>> +initially empty will cause the cherry-pick to fail. To force the inclusion of\n>>> +those commits, use `--allow-empty`.\n>>\n>> I found this last paragraph is slightly confusing now --empty=keep\n>> implies --allow-empty. Maybe we could change the middle sentence to\n>> say something like\n>>\n>>     With the exception of `--empty=keep` commits that were initially\n>>     empty will cause the cherry-pick to fail.\n>\n> That is certainly easier to read and much less confusing.\n\nI agree that this paragraph is slightly confusing. I tried this\nsuggestion on but found it to not sit quite right, I think because the\ntwo exceptions (--empty=keep and --allow-empty) were not part of the\nsame sentence, so it felt a little disjointed. How would you feel about\nthe following instead, which aims to be more clear and specific about\nthe behavior?\n\n        Note that `--empty=drop` and `--empty=stop` only specify how to\n        handle a commit that was not initially empty, but rather became\n        empty due to a previous commit. Commits that were initially empty\n        will still cause the cherry-pick to fail unless one of\n        `--empty=keep` or `--allow-empty` are specified.\n\nThank you both again for your time reviewing this!\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"491030","messageId":"507f8792-5f79-4ab3-b328-abfbc5dbffc5@gmail.com","threadId":"60764","inReplyTo":"CAHPHrSfiMbU55K2=8+hJZy1cMSRbYM77pCK8BdcAPHLvapHO_A@mail.gmail.com","subject":"Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-20T19:35:12Z","receivedAt":"2024-03-20T19:35:14Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 16/03/2024 05:20, Brian Lyles wrote:\n> On Wed, Mar 13, 2024 at 12:17 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> phillip.wood123@gmail.com writes:\n>>>> +Note that this option specifies how to handle a commit that was not initially\n>>>> +empty, but rather became empty due to a previous commit. Commits that were\n>>>> +initially empty will cause the cherry-pick to fail. To force the inclusion of\n>>>> +those commits, use `--allow-empty`.\n>>>\n>>> I found this last paragraph is slightly confusing now --empty=keep\n>>> implies --allow-empty. Maybe we could change the middle sentence to\n>>> say something like\n>>>\n>>>      With the exception of `--empty=keep` commits that were initially\n>>>      empty will cause the cherry-pick to fail.\n>>\n>> That is certainly easier to read and much less confusing.\n> \n> I agree that this paragraph is slightly confusing. I tried this\n> suggestion on but found it to not sit quite right, I think because the\n> two exceptions (--empty=keep and --allow-empty) were not part of the\n> same sentence, so it felt a little disjointed. How would you feel about\n> the following instead, which aims to be more clear and specific about\n> the behavior?\n> \n>          Note that `--empty=drop` and `--empty=stop` only specify how to\n>          handle a commit that was not initially empty, but rather became\n>          empty due to a previous commit. Commits that were initially empty\n>          will still cause the cherry-pick to fail unless one of\n>          `--empty=keep` or `--allow-empty` are specified.\n\nThat looks fine to me\n\nBest Wishes\n\nPhillip\n"},{"id":"491073","messageId":"20240320233724.214369-1-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:36:55Z","receivedAt":"2024-03-20T23:39:13Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The ultimate goal of this series is to allow git-cherry-pick(1) to\nautomatically drop redundant commits. The mechanism chosen is an\n`--empty` option that provides the same flexibility as the `--empty`\noptions for git-rebase(1) and git-am(1).\n\nSome secondary goals are to improve the consistency in the values and\ndocumentation for this option across the three commands.\n\nSee \"Does extending `--empty` to git-cherry-pick make sense?\" [1] for\nsome context for why this option is desired in git-cherry-pick(1).\n\n[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com\n\nAlong the way, I (with some help from Elijah and Phillip) found a few\nother things in the docs and related sequencer code to clean up.\n\nThis re-roll contains only minor changes from v3.\n\nBrian Lyles (7):\n  docs: address inaccurate `--empty` default with `--exec`\n  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n  rebase: update `--empty=ask` to `--empty=stop`\n  sequencer: handle unborn branch with `--allow-empty`\n  sequencer: do not require `allow_empty` for redundant commit options\n  cherry-pick: enforce `--keep-redundant-commits` incompatibility\n  cherry-pick: add `--empty` for more robust redundant commit handling\n\n Documentation/git-am.txt          | 20 +++++----\n Documentation/git-cherry-pick.txt | 30 ++++++++++----\n Documentation/git-rebase.txt      | 26 ++++++++----\n builtin/rebase.c                  | 16 +++++---\n builtin/revert.c                  | 38 ++++++++++++++++-\n sequencer.c                       | 68 +++++++++++++++++--------------\n t/t3424-rebase-empty.sh           | 55 +++++++++++++++++++++++--\n t/t3501-revert-cherry-pick.sh     | 14 +++++--\n t/t3505-cherry-pick-empty.sh      | 51 ++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   | 32 +++++++++++++++\n 10 files changed, 283 insertions(+), 67 deletions(-)\n\n-- \n2.43.2\n\n"},{"id":"491074","messageId":"20240320233724.214369-2-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 1/7] docs: address inaccurate `--empty` default with `--exec`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:36:56Z","receivedAt":"2024-03-20T23:39:14Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The documentation for git-rebase(1) indicates that using the `--exec`\noption will use `--empty=drop`. This is inaccurate: when `--interactive`\nis not explicitly provided, `--exec` results in `--empty=keep`\nbehaviors.\n\nCorrectly indicate the behavior of `--exec` using `--empty=keep` when\n`--interactive` is not specified.\n\nReported-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt | 10 +++++-----\n t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 06206521fc..3334e85356 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes).  With drop (the default), commits that\n \tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask (implied by `--interactive`), the rebase will halt when\n-\tan empty commit is applied allowing you to choose whether to\n-\tdrop it, edit files more, or just commit the empty changes.\n-\tOther options, like `--exec`, will use the default of drop unless\n-\t`-i`/`--interactive` is explicitly specified.\n+\tWith ask, the rebase will halt when an empty commit is applied\n+\tallowing you to choose whether to drop it, edit files more, or just\n+\tcommit the empty changes.\n+\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n+\tOtherwise, when the `--exec` option is used, the default becomes keep.\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 5e1045a0af..73ff35ced2 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '\n \ttest_path_is_missing .git/MERGE_MSG\n '\n \n+test_expect_success 'rebase --exec --empty=drop' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=drop upstream &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=keep upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec uses default of --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=ask' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.43.2\n\n"},{"id":"491075","messageId":"20240320233724.214369-3-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:36:57Z","receivedAt":"2024-03-20T23:39:16Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Both of these pages document very similar `--empty` options, but with\ndifferent styles. The exact behavior of these `--empty` options differs\nsomewhat, but consistent styling in the docs is still beneficial. This\ncommit aims to make them more consistent.\n\nBreak the possible values for `--empty` into separate sections for\nreadability. Alphabetical order is chosen for consistency.\n\nIn a future commit, we'll be documenting a new `--empty` option for\ngit-cherry-pick(1), making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-am.txt     | 20 +++++++++++++-------\n Documentation/git-rebase.txt | 25 ++++++++++++++++---------\n 2 files changed, 29 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-am.txt b/Documentation/git-am.txt\nindex e080458d6c..f852e0ba79 100644\n--- a/Documentation/git-am.txt\n+++ b/Documentation/git-am.txt\n@@ -66,13 +66,19 @@ OPTIONS\n --quoted-cr=<action>::\n \tThis flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).\n \n---empty=(stop|drop|keep)::\n-\tBy default, or when the option is set to 'stop', the command\n-\terrors out on an input e-mail message lacking a patch\n-\tand stops in the middle of the current am session. When this\n-\toption is set to 'drop', skip such an e-mail message instead.\n-\tWhen this option is set to 'keep', create an empty commit,\n-\trecording the contents of the e-mail message as its log.\n+--empty=(drop|keep|stop)::\n+\tHow to handle an e-mail message lacking a patch:\n++\n+--\n+`drop`;;\n+\tThe e-mail message will be skipped.\n+`keep`;;\n+\tAn empty commit will be created, with the contents of the e-mail\n+\tmessage as its log.\n+`stop`;;\n+\tThe command will fail, stopping in the middle of the current `am`\n+\tsession. This is the default behavior.\n+--\n \n -m::\n --message-id::\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 3334e85356..0b0d0ccb80 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(drop|keep|ask)::\n+--empty=(ask|drop|keep)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n-\tupstream changes).  With drop (the default), commits that\n-\tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask, the rebase will halt when an empty commit is applied\n-\tallowing you to choose whether to drop it, edit files more, or just\n-\tcommit the empty changes.\n-\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n-\tOtherwise, when the `--exec` option is used, the default becomes keep.\n+\tupstream changes):\n++\n+--\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified.\n+`drop`;;\n+\tThe commit will be dropped. This is the default behavior.\n+`keep`;;\n+\tThe commit will be kept. This option is implied when `--exec` is\n+\tspecified unless `-i`/`--interactive` is also specified.\n+--\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\n@@ -704,7 +711,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(drop|keep|ask)` option for changing the behavior\n+also has an `--empty=(ask|drop|keep)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\n-- \n2.43.2\n\n"},{"id":"491076","messageId":"20240320233724.214369-4-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 3/7] rebase: update `--empty=ask` to `--empty=stop`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:36:58Z","receivedAt":"2024-03-20T23:39:17Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09), `stop` was used\ninstead of `ask`. `stop` is a more accurate term for describing what\nreally happens, and consistency is good.\n\nUpdate git-rebase(1) to also use `stop`, while keeping `ask` as a\ndeprecated synonym. Update the tests to primarily use `stop`, but also\nensure that `ask` is still allowed.\n\nIn a future commit, we'll be adding a new `--empty` option for\ngit-cherry-pick(1) as well, making the consistency even more relevant.\n\nReported-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt | 15 ++++++++-------\n builtin/rebase.c             | 16 ++++++++++------\n t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----\n 3 files changed, 34 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 0b0d0ccb80..67dd0a533e 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(ask|drop|keep)::\n+--empty=(drop|keep|stop)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes):\n +\n --\n-`ask`;;\n-\tThe rebase will halt when the commit is applied, allowing you to\n-\tchoose whether to drop it, edit files more, or just commit the empty\n-\tchanges. This option is implied when `-i`/`--interactive` is\n-\tspecified.\n `drop`;;\n \tThe commit will be dropped. This is the default behavior.\n `keep`;;\n \tThe commit will be kept. This option is implied when `--exec` is\n \tspecified unless `-i`/`--interactive` is also specified.\n+`stop`;;\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified. `ask` is a deprecated synonym of `stop`.\n --\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n@@ -711,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(ask|drop|keep)` option for changing the behavior\n+also has an `--empty=(drop|keep|stop)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 5b086f651a..a4916781ce 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -58,7 +58,7 @@ enum empty_type {\n \tEMPTY_UNSPECIFIED = -1,\n \tEMPTY_DROP,\n \tEMPTY_KEEP,\n-\tEMPTY_ASK\n+\tEMPTY_STOP\n };\n \n enum action {\n@@ -951,10 +951,14 @@ static enum empty_type parse_empty_value(const char *value)\n \t\treturn EMPTY_DROP;\n \telse if (!strcasecmp(value, \"keep\"))\n \t\treturn EMPTY_KEEP;\n-\telse if (!strcasecmp(value, \"ask\"))\n-\t\treturn EMPTY_ASK;\n+\telse if (!strcasecmp(value, \"stop\"))\n+\t\treturn EMPTY_STOP;\n+\telse if (!strcasecmp(value, \"ask\")) {\n+\t\twarning(_(\"--empty=ask is deprecated; use '--empty=stop' instead.\"));\n+\t\treturn EMPTY_STOP;\n+\t}\n \n-\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"ask\\\".\"), value);\n+\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"stop\\\".\"), value);\n }\n \n static int parse_opt_keep_empty(const struct option *opt, const char *arg,\n@@ -1133,7 +1137,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"instead of ignoring them\"),\n \t\t\t      1, PARSE_OPT_HIDDEN),\n \t\tOPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),\n-\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|ask)\",\n+\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|stop)\",\n \t\t\t       N_(\"how to handle commits that become empty\"),\n \t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\tOPT_CALLBACK_F('k', \"keep-empty\", &options, NULL,\n@@ -1550,7 +1554,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (options.empty == EMPTY_UNSPECIFIED) {\n \t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n-\t\t\toptions.empty = EMPTY_ASK;\n+\t\t\toptions.empty = EMPTY_STOP;\n \t\telse if (options.exec.nr > 0)\n \t\t\toptions.empty = EMPTY_KEEP;\n \t\telse\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 73ff35ced2..1ee6b00fd5 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'rebase --merge --empty=stop' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --merge --empty=stop upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'rebase --merge --empty=ask' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --merge --empty=ask upstream &&\n@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive --empty=ask' '\n+test_expect_success 'rebase --interactive --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --interactive --empty=ask upstream &&\n+\ttest_must_fail git rebase --interactive --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive uses default of --empty=ask' '\n+test_expect_success 'rebase --interactive uses default of --empty=stop' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --interactive upstream &&\n \n@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --exec --empty=ask' '\n+test_expect_success 'rebase --exec --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n-- \n2.43.2\n\n"},{"id":"491077","messageId":"20240320233724.214369-5-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:36:59Z","receivedAt":"2024-03-20T23:39:18Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When using git-cherry-pick(1) with `--allow-empty` while on an unborn\nbranch, an error is thrown. This is inconsistent with the same\ncherry-pick when `--allow-empty` is not specified.\n\nDetect unborn branches in `is_index_unchanged`. When on an unborn\nbranch, use the `empty_tree` as the tree to compare against.\n\nAdd a new test to cover this scenario. While modelled off of the\nexisting 'cherry-pick on unborn branch' test, some improvements can be\nmade:\n\n- Use `git switch --orphan unborn` instead of `git checkout --orphan\n  unborn` to avoid the need for a separate `rm -rf *` call\n- Avoid using `--quiet` in the `git diff` call to make debugging easier\n  in the event of a failure. Use simply `--exit-code` instead.\n\nMake these improvements to the existing test as well as the new test.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nChanges from v3:\n\n- More robustly validate that we are on an unborn branch, rather than\n  assuming that an error while reading the HEAD implies an unborn branch\n- Replace `--quiet` with `--exit-code` in the tests rather than just\n  removing `--quiet`, to ensure that the test still fails appropriately\n  if any differences are found.\n\n sequencer.c                   | 39 ++++++++++++++++++++++-------------\n t/t3501-revert-cherry-pick.sh | 14 ++++++++++---\n 2 files changed, 36 insertions(+), 17 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f49a871ac0..f31d71ebad 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -770,29 +770,40 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)\n static int is_index_unchanged(struct repository *r)\n {\n \tstruct object_id head_oid, *cache_tree_oid;\n+\tconst struct object_id *head_tree_oid;\n \tstruct commit *head_commit;\n \tstruct index_state *istate = r->index;\n+\tconst char *head_name;\n \n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n-\t\treturn error(_(\"could not resolve HEAD commit\"));\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n+\t\t/*\n+\t\t * Check to see if this is an unborn branch\n+\t\t */\n+\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n+\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n+\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n+\t\thead_tree_oid = the_hash_algo->empty_tree;\n+\t} else {\n+\t\thead_commit = lookup_commit(r, &head_oid);\n \n-\thead_commit = lookup_commit(r, &head_oid);\n+\t\t/*\n+\t\t * If head_commit is NULL, check_commit, called from\n+\t\t * lookup_commit, would have indicated that head_commit is not\n+\t\t * a commit object already.  repo_parse_commit() will return failure\n+\t\t * without further complaints in such a case.  Otherwise, if\n+\t\t * the commit is invalid, repo_parse_commit() will complain.  So\n+\t\t * there is nothing for us to say here.  Just return failure.\n+\t\t */\n+\t\tif (repo_parse_commit(r, head_commit))\n+\t\t\treturn -1;\n \n-\t/*\n-\t * If head_commit is NULL, check_commit, called from\n-\t * lookup_commit, would have indicated that head_commit is not\n-\t * a commit object already.  repo_parse_commit() will return failure\n-\t * without further complaints in such a case.  Otherwise, if\n-\t * the commit is invalid, repo_parse_commit() will complain.  So\n-\t * there is nothing for us to say here.  Just return failure.\n-\t */\n-\tif (repo_parse_commit(r, head_commit))\n-\t\treturn -1;\n+\t\thead_tree_oid = get_commit_tree_oid(head_commit);\n+\t}\n \n \tif (!(cache_tree_oid = get_cache_tree_oid(istate)))\n \t\treturn -1;\n \n-\treturn oideq(cache_tree_oid, get_commit_tree_oid(head_commit));\n+\treturn oideq(cache_tree_oid, head_tree_oid);\n }\n \n static int write_author_script(const char *message)\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex aeab689a98..af73227512 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '\n '\n \n test_expect_success 'cherry-pick on unborn branch' '\n-\tgit checkout --orphan unborn &&\n+\tgit switch --orphan unborn &&\n \tgit rm --cached -r . &&\n-\trm -rf * &&\n \tgit cherry-pick initial &&\n-\tgit diff --quiet initial &&\n+\tgit diff --exit-code initial &&\n+\ttest_cmp_rev ! initial HEAD\n+'\n+\n+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n+\tgit checkout --detach &&\n+\tgit branch -D unborn &&\n+\tgit switch --orphan unborn &&\n+\tgit cherry-pick initial --allow-empty &&\n+\tgit diff --exit-code initial &&\n \ttest_cmp_rev ! initial HEAD\n '\n \n-- \n2.43.2\n\n"},{"id":"491078","messageId":"20240320233724.214369-6-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 5/7] sequencer: do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:37:00Z","receivedAt":"2024-03-20T23:39:19Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"A consumer of the sequencer that wishes to take advantage of either the\n`keep_redundant_commits` or `drop_redundant_commits` feature must also\nspecify `allow_empty`. However, these refer to two distinct types of\nempty commits:\n\n- `allow_empty` refers specifically to commits which start empty\n- `keep_redundant_commits` refers specifically to commits that do not\n  start empty, but become empty due to the content already existing in\n  the target history\n\nConceptually, there is no reason that the behavior for handling one of\nthese should be entangled with the other. It is particularly unintuitive\nto require `allow_empty` in order for `drop_redundant_commits` to have\nan effect: in order to prevent redundant commits automatically,\ninitially-empty commits would need to be kept automatically as well.\n\nInstead, rewrite the `allow_empty()` logic to remove the over-arching\nrequirement that `allow_empty` be specified in order to reach any of the\nkeep/drop behaviors. Only if the commit was originally empty will\n`allow_empty` have an effect.\n\nNote that no behavioral changes should result from this commit -- it\nmerely sets the stage for future commits. In one such future commit, an\n`--empty` option will be added to git-cherry-pick(1), meaning that\n`drop_redundant_commits` will be used by that command.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n sequencer.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f31d71ebad..b8d8f15e65 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1730,34 +1730,25 @@ static int allow_empty(struct repository *r,\n \tint index_unchanged, originally_empty;\n \n \t/*\n-\t * Four cases:\n+\t * For a commit that is initially empty, allow_empty determines if it\n+\t * should be kept or not\n \t *\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, and\n-\t *     just drop the ones that become empty\n-\t *\n-\t * (3) we allow ones that were initially empty, but\n-\t *     halt for the ones that become empty;\n-\t *\n-\t * (4) we allow both.\n+\t * For a commit that becomes empty, keep_redundant_commits and\n+\t * drop_redundant_commits determine whether the commit should be kept or\n+\t * dropped. If neither is specified, halt.\n \t */\n-\tif (!opts->allow_empty)\n-\t\treturn 0; /* let \"git commit\" barf as necessary */\n-\n \tindex_unchanged = is_index_unchanged(r);\n \tif (index_unchanged < 0)\n \t\treturn index_unchanged;\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n \n-\tif (opts->keep_redundant_commits)\n-\t\treturn 1;\n-\n \toriginally_empty = is_original_commit_empty(commit);\n \tif (originally_empty < 0)\n \t\treturn originally_empty;\n \tif (originally_empty)\n+\t\treturn opts->allow_empty;\n+\telse if (opts->keep_redundant_commits)\n \t\treturn 1;\n \telse if (opts->drop_redundant_commits)\n \t\treturn 2;\n-- \n2.43.2\n\n"},{"id":"491079","messageId":"20240320233724.214369-7-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:37:01Z","receivedAt":"2024-03-20T23:39:21Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When `--keep-redundant-commits` was added in  b27cfb0d8d\n(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was\nnot marked as incompatible with the various operations needed to\ncontinue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and\n`--quit`).\n\nEnforce this incompatibility via `verify_opt_compatible` like we do for\nthe other various options.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n builtin/revert.c             |  1 +\n t/t3505-cherry-pick-empty.sh | 14 ++++++++++++++\n 2 files changed, 15 insertions(+)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 89821bab95..a1936ef70e 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -167,6 +167,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--ff\", opts->allow_ff,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n+\t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n \t\t\t\tNULL);\n \t}\n \ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex eba3c38d5a..61f91aaa0a 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -99,4 +99,18 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--keep-redundant-commits is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n test_done\n-- \n2.43.2\n\n"},{"id":"491080","messageId":"20240320233724.214369-8-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v4 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-20T23:37:02Z","receivedAt":"2024-03-20T23:39:22Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a\ncommit being made redundant if the content from the picked commit is\nalready present in the target history. However, git-cherry-pick(1) does\nnot have the same options available that git-rebase(1) and git-am(1) have.\n\nThere are three things that can be done with these redundant commits:\ndrop them, keep them, or have the cherry-pick stop and wait for the user\nto take an action. git-rebase(1) has the `--empty` option added in commit\ne98c4269c8 (rebase (interactive-backend): fix handling of commits that\nbecome empty, 2020-02-15), which handles all three of these scenarios.\nSimilarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09).\n\ngit-cherry-pick(1), on the other hand, only supports two of the three\npossiblities: Keep the redundant commits via `--keep-redundant-commits`,\nor have the cherry-pick fail by not specifying that option. There is no\nway to automatically drop redundant commits.\n\nIn order to bring git-cherry-pick(1) more in-line with git-rebase(1) and\ngit-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It\nhas the same three options (keep, drop, and stop), and largely behaves\nthe same. The notable difference is that for git-cherry-pick(1), the\ndefault will be `stop`, which maintains the current behavior when the\noption is not specified.\n\nLike the existing `--keep-redundant-commits`, `--empty=keep` will imply\n`--allow-empty`.\n\nThe `--keep-redundant-commits` option will be documented as a deprecated\nsynonym of `--empty=keep`, and will be supported for backwards\ncompatibility for the time being.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nChanges from v3:\n\n- Re-worded the note about initially-empty commits in the documentation\n  for the new `--empty` option.\n\n Documentation/git-cherry-pick.txt | 30 +++++++++++++++++++------\n builtin/revert.c                  | 37 ++++++++++++++++++++++++++++++-\n sequencer.c                       |  6 +++++\n t/t3505-cherry-pick-empty.sh      | 37 ++++++++++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++++++++++++\n 5 files changed, 133 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex fdcad3d200..81ace900fc 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -131,20 +131,36 @@ effect to your index in a row.\n \teven without this option.  Note also, that use of this option only\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n-\tprevious commit are dropped.  To force the inclusion of those commits\n-\tuse `--keep-redundant-commits`.\n+\tprevious commit will cause the cherry-pick to fail.  To force the\n+\tinclusion of those commits, use `--empty=keep`.\n \n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n \tThis option overrides that behavior, allowing commits with empty\n \tmessages to be cherry picked.\n \n+--empty=(drop|keep|stop)::\n+\tHow to handle commits being cherry-picked that are redundant with\n+\tchanges already in the current history.\n++\n+--\n+`drop`;;\n+\tThe commit will be dropped.\n+`keep`;;\n+\tThe commit will be kept. Implies `--allow-empty`.\n+`stop`;;\n+\tThe cherry-pick will stop when the commit is applied, allowing\n+\tyou to examine the commit. This is the default behavior.\n+--\n++\n+Note that `--empty=drop` and `--empty=stop` only specify how to handle a\n+commit that was not initially empty, but rather became empty due to a previous\n+commit. Commits that were initially empty will still cause the cherry-pick to\n+fail unless one of `--empty=keep` or `--allow-empty` are specified.\n++\n+\n --keep-redundant-commits::\n-\tIf a commit being cherry picked duplicates a commit already in the\n-\tcurrent history, it will become empty.  By default these\n-\tredundant commits cause `cherry-pick` to stop so the user can\n-\texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object.  Implies `--allow-empty`.\n+\tDeprecated synonym for `--empty=keep`.\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 a1936ef70e..53935d2c68 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n+enum empty_action {\n+\tEMPTY_COMMIT_UNSPECIFIED = -1,\n+\tSTOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */\n+\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n+\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n+};\n+\n+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n+{\n+\tint *opt_value = opt->value;\n+\n+\tBUG_ON_OPT_NEG(unset);\n+\n+\tif (!strcmp(arg, \"stop\"))\n+\t\t*opt_value = STOP_ON_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"drop\"))\n+\t\t*opt_value = DROP_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"keep\"))\n+\t\t*opt_value = KEEP_EMPTY_COMMIT;\n+\telse\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n+\n+\treturn 0;\n+}\n+\n static int option_parse_m(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n \tconst char *cleanup_arg = NULL;\n+\tenum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\tOPT_BOOL(0, \"ff\", &opts->allow_ff, N_(\"allow fast-forward\")),\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, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"deprecated: use --empty=keep instead\")),\n+\t\t\tOPT_CALLBACK_F(0, \"empty\", &empty_opt, \"(stop|drop|keep)\",\n+\t\t\t\t       N_(\"how to handle commits that become empty\"),\n+\t\t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\n@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n \n+\tif (opts->action == REPLAY_PICK) {\n+\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n+\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n+\t}\n+\n \t/* implies allow_empty */\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n@@ -168,6 +202,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n \t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n+\t\t\t\t\"--empty\", empty_opt != EMPTY_COMMIT_UNSPECIFIED,\n \t\t\t\tNULL);\n \t}\n \ndiff --git a/sequencer.c b/sequencer.c\nindex b8d8f15e65..42bac5ebd7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2928,6 +2928,9 @@ static int populate_opts_cb(const char *key, const char *value,\n \telse if (!strcmp(key, \"options.allow-empty-message\"))\n \t\topts->allow_empty_message =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n+\telse if (!strcmp(key, \"options.drop-redundant-commits\"))\n+\t\topts->drop_redundant_commits =\n+\t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n \t\topts->keep_redundant_commits =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n@@ -3472,6 +3475,9 @@ static int save_opts(struct replay_opts *opts)\n \tif (opts->allow_empty_message)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.allow-empty-message\", \"true\");\n+\tif (opts->drop_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file,\n+\t\t\t\t\"options.drop-redundant-commits\", \"true\");\n \tif (opts->keep_redundant_commits)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.keep-redundant-commits\", \"true\");\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex 61f91aaa0a..9748443530 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n \tgit commit -m \"add file2 on the side\"\n '\n \n-test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n \tgit reset --hard &&\n \tgit checkout fork^0 &&\n \ttest_must_fail git cherry-pick main\n@@ -113,4 +113,39 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '\n \tgit cherry-pick --abort\n '\n \n+test_expect_success '--empty is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=stop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\ttest_must_fail git cherry-pick --empty=stop main 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=drop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=drop main &&\n+\ttest_commit_message HEAD -m \"add file2 on the side\"\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=keep' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=keep main &&\n+\ttest_commit_message HEAD -m \"add file2 on main\"\n+'\n+\n test_done\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 72020a51c4..7eb52b12ed 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cherry-pick persists --empty=stop correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&\n+\ttest_must_fail git cherry-pick --skip 2>msg &&\n+\ttest_grep \"The previous cherry-pick is now empty\" msg &&\n+\trm msg &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=drop correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&\n+\tgit cherry-pick --skip &&\n+\ttest_cmp_rev yetanotherpick HEAD\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=keep correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&\n+\tgit cherry-pick --skip &&\n+\ttest_cmp_rev yetanotherpick HEAD^\n+'\n+\n test_expect_success 'revert persists opts correctly' '\n \tpristine_detach initial &&\n \t# to make sure that the session to revert a sequence\n-- \n2.43.2\n\n"},{"id":"491114","messageId":"ghttkzykru.fsf@gouders.net","threadId":"60764","inReplyTo":"20240320233724.214369-5-brianmlyles@gmail.com","subject":"Re: [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`","fromName":"Dirk Gouders","fromEmail":"dirk@gouders.net","sentAt":"2024-03-21T09:52:21Z","receivedAt":"2024-03-21T09:52:41Z","isPatch":true,"sender":{"key":"dirk@gouders.net","avatar":"https://avatars.githubusercontent.com/u/81326422?v=4"},"body":"Brian Lyles <brianmlyles@gmail.com> writes:\n\n> +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n> +\t\t/*\n> +\t\t * Check to see if this is an unborn branch\n> +\t\t */\n> +\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n> +\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n> +\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n> +\t\thead_tree_oid = the_hash_algo->empty_tree;\n> +\t} else {\n> +\t\thead_commit = lookup_commit(r, &head_oid);\n>  \n> -\thead_commit = lookup_commit(r, &head_oid);\n> +\t\t/*\n> +\t\t * If head_commit is NULL, check_commit, called from\n> +\t\t * lookup_commit, would have indicated that head_commit is not\n> +\t\t * a commit object already.  repo_parse_commit() will return failure\n> +\t\t * without further complaints in such a case.  Otherwise, if\n> +\t\t * the commit is invalid, repo_parse_commit() will complain.  So\n> +\t\t * there is nothing for us to say here.  Just return failure.\n> +\t\t */\n> +\t\tif (repo_parse_commit(r, head_commit))\n> +\t\t\treturn -1;\n\nNot that I am qualified to do a review of your changes but I am in a\nsituation where I am trying to understand Git code in general and\n(perhaps normal for this situation) wondering about varying styles of\ncommenting code -- could be that I am just too new to the code base and\ndo not yet understand the obvious things that don't need comments.\n\nIn the above example, there is a short but outstanding comment that\nannounces a check (and if I understood correctly by [1] it is a kind of\ntrick that could deserve some more information) and it does _not_\ncomment on the result.  Of course, I have an idea where the correct\nplace for a comment /* This is an unborn branch -- handle it as if... */\ncould be, but I'm not sure.\n\nSo, my intention is by no means to trigger another spin of this series\n-- it is just a view from someone trying to understand not just this\ncode ;-)\n\nDirk\n\n[1] https://lore.kernel.org/git/xmqqh6hcu2tg.fsf@gitster.g/\n"},{"id":"491156","messageId":"xmqqh6gzblmc.fsf@gitster.g","threadId":"60764","inReplyTo":"ghttkzykru.fsf@gouders.net","subject":"Re: [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-21T16:22:35Z","receivedAt":"2024-03-21T16:22:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dirk Gouders <dirk@gouders.net> writes:\n\n> Brian Lyles <brianmlyles@gmail.com> writes:\n>\n>> +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n>> +\t\t/*\n>> +\t\t * Check to see if this is an unborn branch\n>> +\t\t */\n> In the above example, there is a short but outstanding comment that\n> announces a check (and if I understood correctly by [1] it is a kind of\n> trick that could deserve some more information) and it does _not_\n> comment on the result.  Of course, I have an idea where the correct\n> place for a comment /* This is an unborn branch -- handle it as if... */\n> could be, but I'm not sure.\n\nYou mean \"Check to see if this is an unborn branch, and if so, use\nan empty tree to compare against, instead of the tree of the HEAD\nthat does not yet exist\"?\n\nI think that is possible, but the use of the_hash_algo->empty_tree\nindicates that clearly enough.  But we need to stop somewhere and\nwhat we see above may be a reasonable place to do so.\n\nIf anything, we may want to say why we want to continue as if we had\nan empty tree (as opposed to fail and return with an error()), or\nthe tree to compare with is computed here for what purpose.  But the\nname of the function may tell what this whole computation and\ncomparison is for, so it probably is not needed, either.\n\n\n"},{"id":"491169","messageId":"ghh6gzz7ux.fsf@gouders.net","threadId":"60764","inReplyTo":"xmqqh6gzblmc.fsf@gitster.g","subject":"Re: [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`","fromName":"Dirk Gouders","fromEmail":"dirk@gouders.net","sentAt":"2024-03-21T19:45:58Z","receivedAt":"2024-03-21T19:46:13Z","isPatch":true,"sender":{"key":"dirk@gouders.net","avatar":"https://avatars.githubusercontent.com/u/81326422?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Dirk Gouders <dirk@gouders.net> writes:\n>\n>> Brian Lyles <brianmlyles@gmail.com> writes:\n>>\n>>> +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n>>> +\t\t/*\n>>> +\t\t * Check to see if this is an unborn branch\n>>> +\t\t */\n>> In the above example, there is a short but outstanding comment that\n>> announces a check (and if I understood correctly by [1] it is a kind of\n>> trick that could deserve some more information) and it does _not_\n>> comment on the result.  Of course, I have an idea where the correct\n>> place for a comment /* This is an unborn branch -- handle it as if... */\n>> could be, but I'm not sure.\n>\n> You mean \"Check to see if this is an unborn branch, and if so, use\n> an empty tree to compare against, instead of the tree of the HEAD\n> that does not yet exist\"?\n>\n> I think that is possible, but the use of the_hash_algo->empty_tree\n> indicates that clearly enough.  But we need to stop somewhere and\n> what we see above may be a reasonable place to do so.\n>\n> If anything, we may want to say why we want to continue as if we had\n> an empty tree (as opposed to fail and return with an error()), or\n> the tree to compare with is computed here for what purpose.  But the\n> name of the function may tell what this whole computation and\n> comparison is for, so it probably is not needed, either.\n\nThank you for the reply.\n\nI guess, the hidden question in my comment was: \"Do experienced\nGit developers understand the code as something obvious?\".\nAnd I read your answer as a \"Yes, no problem.\".\n\nNow, I can put this subject aside and later, after more reading, check\nif my understanding improved sufficiently to now understand that code\nwithout additional comments, as you already do.\n\nDirk\n"},{"id":"491457","messageId":"79c4d09f-fc1b-40d1-9655-3a9e166918cb@gmail.com","threadId":"60764","inReplyTo":"20240320233724.214369-1-brianmlyles@gmail.com","subject":"Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-25T14:38:22Z","receivedAt":"2024-03-25T14:38:24Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 20/03/2024 23:36, Brian Lyles wrote:\n> This re-roll contains only minor changes from v3.\n\nHere is the range diff with a couple of comments\n\n1:  5a503a7026 = 1:  5aa3f7cec2 docs: address inaccurate `--empty` default with `--exec`\n2:  36278e0d40 = 2:  abb7d7233a docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n3:  a2f603da50 = 3:  ef8224d286 rebase: update `--empty=ask` to `--empty=stop`\n4:  0701f9f9fc ! 4:  21ed2dfb65 sequencer: treat error reading HEAD as unborn branch\n     @@ Metadata\n      Author: Brian Lyles <brianmlyles@gmail.com>\n      \n       ## Commit message ##\n     -    sequencer: treat error reading HEAD as unborn branch\n     +    sequencer: handle unborn branch with `--allow-empty`\n      \n          When using git-cherry-pick(1) with `--allow-empty` while on an unborn\n          branch, an error is thrown. This is inconsistent with the same\n          cherry-pick when `--allow-empty` is not specified.\n      \n     -    Treat a failure reading HEAD as an unborn branch in\n     -    `is_index_unchanged`. This is consistent with other sequencer logic such\n     -    as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as\n     -    the tree to compare against.\n     +    Detect unborn branches in `is_index_unchanged`. When on an unborn\n     +    branch, use the `empty_tree` as the tree to compare against.\n      \n          Add a new test to cover this scenario. While modelled off of the\n          existing 'cherry-pick on unborn branch' test, some improvements can be\n     @@ Commit message\n          - Use `git switch --orphan unborn` instead of `git checkout --orphan\n            unborn` to avoid the need for a separate `rm -rf *` call\n          - Avoid using `--quiet` in the `git diff` call to make debugging easier\n     -      in the event of a failure\n     +      in the event of a failure. Use simply `--exit-code` instead.\n      \n          Make these improvements to the existing test as well as the new test.\n      \n          Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n     +    Helped-by: Junio C Hamano <gitster@pobox.com>\n          Signed-off-by: Brian Lyles <brianmlyles@gmail.com>\n      \n       ## sequencer.c ##\n     @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist\n      +\tconst struct object_id *head_tree_oid;\n       \tstruct commit *head_commit;\n       \tstruct index_state *istate = r->index;\n     -\n     +-\n      -\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n      -\t\treturn error(_(\"could not resolve HEAD commit\"));\n      -\n     @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist\n      -\t */\n      -\tif (repo_parse_commit(r, head_commit))\n      -\t\treturn -1;\n     ++\tconst char *head_name;\n     ++\n      +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n      +\t\t/*\n     -+\t\t * Treat an error reading HEAD as an unborn branch.\n     ++\t\t * Check to see if this is an unborn branch\n      +\t\t */\n\nYou are only editing an existing comment here and it is not worth a\nre-roll on its own, but for one line comments we prefer\n\n\t/* Check to see if this is an unborn branch */\n\n     ++\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n     ++\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n\nWhile we don't mind the occasional line that is a little over 80\ncharacters these really are rather long.\n\n     ++\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n      +\t\thead_tree_oid = the_hash_algo->empty_tree;\n      +\t} else {\n      +\t\thead_commit = lookup_commit(r, &head_oid);\n     @@ t/t3501-revert-cherry-pick.sh: test_expect_success 'revert forbidden on dirty wo\n      -\trm -rf * &&\n       \tgit cherry-pick initial &&\n      -\tgit diff --quiet initial &&\n     -+\tgit diff initial &&\n     ++\tgit diff --exit-code initial &&\n      +\ttest_cmp_rev ! initial HEAD\n      +'\n      +\n     @@ t/t3501-revert-cherry-pick.sh: test_expect_success 'revert forbidden on dirty wo\n      +\tgit branch -D unborn &&\n      +\tgit switch --orphan unborn &&\n      +\tgit cherry-pick initial --allow-empty &&\n     -+\tgit diff initial &&\n     ++\tgit diff --exit-code initial &&\n       \ttest_cmp_rev ! initial HEAD\n       '\n\nThanks for updating these tests\n       \n5:  3634da0f70 = 5:  5c16a01056 sequencer: do not require `allow_empty` for redundant commit options\n6:  e98b414697 = 6:  3ee057e485 cherry-pick: enforce `--keep-redundant-commits` incompatibility\n7:  cc4030d8ec ! 7:  0cd4620ef7 cherry-pick: add `--empty` for more robust redundant commit handling\n     @@ Documentation/git-cherry-pick.txt: effect to your index in a row.\n      +\tyou to examine the commit. This is the default behavior.\n      +--\n      ++\n     -+Note that this option specifies how to handle a commit that was not initially\n     -+empty, but rather became empty due to a previous commit. Commits that were\n     -+initially empty will cause the cherry-pick to fail. To force the inclusion of\n     -+those commits, use `--allow-empty`.\n     ++Note that `--empty=drop` and `--empty=stop` only specify how to handle a\n     ++commit that was not initially empty, but rather became empty due to a previous\n     ++commit. Commits that were initially empty will still cause the cherry-pick to\n     ++fail unless one of `--empty=keep` or `--allow-empty` are specified.\n      ++\n      +\n       --keep-redundant-commits::\n\nThe new wording here looks good\n\nApart from the minor style issues this all looks good to me, thanks\nfor working on it, it will be a useful addition to be able to drop\ncherry-picks that become empty.\n\nBest Wishes\n\nPhillip\n"},{"id":"491459","messageId":"17c00de527e3a0c4.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","threadId":"60764","inReplyTo":"79c4d09f-fc1b-40d1-9655-3a9e166918cb@gmail.com","subject":"Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T16:12:16Z","receivedAt":"2024-03-25T16:12:17Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Hi Phillip\n\nOn Mon, Mar 25, 2024 at 9:38 AM <phillip.wood123@gmail.com> wrote:\n\n>      @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist\n>       -\t */\n>       -\tif (repo_parse_commit(r, head_commit))\n>       -\t\treturn -1;\n>      ++\tconst char *head_name;\n>      ++\n>       +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n>       +\t\t/*\n>      -+\t\t * Treat an error reading HEAD as an unborn branch.\n>      ++\t\t * Check to see if this is an unborn branch\n>       +\t\t */\n> \n> You are only editing an existing comment here and it is not worth a\n> re-roll on its own, but for one line comments we prefer\n> \n> \t/* Check to see if this is an unborn branch */\n\nExisting in v3, but new with this series. Since it sounds like a re-roll\nis desired anyway to address the line length issue below, I can go ahead\nand fix this in v5 as well.\n\n>      ++\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n>      ++\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n> \n> While we don't mind the occasional line that is a little over 80\n> characters these really are rather long.\n> \n\nYou're right, these got a little long. I wasn't able to identify a\ndefinitive wrapping style for these cases, so I'll include my proposed\nupdate here just to avoid another re-roll. Does the following diff from\nv4 to a proposed v5 work for you?\n\n@@ -776,11 +776,13 @@ static int is_index_unchanged(struct repository *r)\n \tconst char *head_name;\n \n \tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n-\t\t/*\n-\t\t * Check to see if this is an unborn branch\n-\t\t */\n-\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n-\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n+\t\t/* Check to see if this is an unborn branch */\n+\t\thead_name = resolve_ref_unsafe(\"HEAD\",\n+\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\t\t\t&head_oid, NULL);\n+\t\tif (!head_name\n+\t\t\t|| !starts_with(head_name, \"refs/heads/\")\n+\t\t\t|| !is_null_oid(&head_oid))\n \t\t\treturn error(_(\"could not resolve HEAD commit\"));\n \t\thead_tree_oid = the_hash_algo->empty_tree;\n \t} else {\n\n> Apart from the minor style issues this all looks good to me, thanks\n> for working on it, it will be a useful addition to be able to drop\n> cherry-picks that become empty.\n\nThanks, I really appreciate your help with this series!\n\n-- \nThank you,\nBrian Lyles\n"},{"id":"491494","messageId":"2ab7445f-08ee-4608-96ad-8171f9ce1b73@gmail.com","threadId":"60764","inReplyTo":"17c00de527e3a0c4.70b1dd9aae081c6e.203dcd72f6563036@zivdesk","subject":"Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-25T19:36:33Z","receivedAt":"2024-03-25T19:36:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 25/03/2024 16:12, Brian Lyles wrote:\n>>       ++\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n>>       ++\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n>>\n>> While we don't mind the occasional line that is a little over 80\n>> characters these really are rather long.\n>>\n> \n> You're right, these got a little long. I wasn't able to identify a\n> definitive wrapping style for these cases, so I'll include my proposed\n> update here just to avoid another re-roll. Does the following diff from\n> v4 to a proposed v5 work for you?\n> \n> @@ -776,11 +776,13 @@ static int is_index_unchanged(struct repository *r)\n>   \tconst char *head_name;\n>   \n>   \tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n> -\t\t/*\n> -\t\t * Check to see if this is an unborn branch\n> -\t\t */\n> -\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n> -\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n> +\t\t/* Check to see if this is an unborn branch */\n> +\t\thead_name = resolve_ref_unsafe(\"HEAD\",\n> +\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n> +\t\t\t&head_oid, NULL);\n> +\t\tif (!head_name\n> +\t\t\t|| !starts_with(head_name, \"refs/heads/\")\n> +\t\t\t|| !is_null_oid(&head_oid))\n>   \t\t\treturn error(_(\"could not resolve HEAD commit\"));\n\nNormally we'd write this as\n\n\tif (!head_name ||\n\t    starts_with(head_name, \"refs/heads/\") ||\n\t    !is_null_oid(&head_oid))\n\t\treturn error(...)\n\nbreaking lines after an operator and indenting to the open bracket after \nthe if. The rest looks good. Junio was talking about merging this to \nnext in the latest \"what's cooking\" email so I'd double check he hasn't \ndone that yet before re-rolling.\n\n>   \t\thead_tree_oid = the_hash_algo->empty_tree;\n>   \t} else {\n> \n>> Apart from the minor style issues this all looks good to me, thanks\n>> for working on it, it will be a useful addition to be able to drop\n>> cherry-picks that become empty.\n> \n> Thanks, I really appreciate your help with this series!\n\nThank you for working on it, I've enjoyed reading your patches\n\nBest Wishes\n\nPhillip\n"},{"id":"491515","messageId":"xmqqa5mmhvx5.fsf@gitster.g","threadId":"60764","inReplyTo":"2ab7445f-08ee-4608-96ad-8171f9ce1b73@gmail.com","subject":"Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-25T20:57:10Z","receivedAt":"2024-03-25T20:57:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n>> +\t\t/* Check to see if this is an unborn branch */\n>> +\t\thead_name = resolve_ref_unsafe(\"HEAD\",\n>> +\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n>> +\t\t\t&head_oid, NULL);\n>> +\t\tif (!head_name\n>> +\t\t\t|| !starts_with(head_name, \"refs/heads/\")\n>> +\t\t\t|| !is_null_oid(&head_oid))\n>>   \t\t\treturn error(_(\"could not resolve HEAD commit\"));\n>\n> Normally we'd write this as\n>\n> \tif (!head_name ||\n> \t    starts_with(head_name, \"refs/heads/\") ||\n> \t    !is_null_oid(&head_oid))\n> \t\treturn error(...)\n>\n> breaking lines after an operator and indenting to the open bracket\n> after the if.\n\n> The rest looks good. Junio was talking about merging\n> this to next in the latest \"what's cooking\" email so I'd double check\n> he hasn't done that yet before re-rolling.\n\nI do not want to rush things---if we find that this is worth\ntouching up (and obviously we do---otherwise we would not be having\nthis conversation), then let's do have a quick v5 with minimal\nrange-diff.\n\n> Thank you for working on it, I've enjoyed reading your patches\n\nLikewise.  Thanks, both.\n\n"},{"id":"491544","messageId":"20240325232451.963946-1-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:47Z","receivedAt":"2024-03-25T23:25:54Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The ultimate goal of this series is to allow git-cherry-pick(1) to\nautomatically drop redundant commits. The mechanism chosen is an\n`--empty` option that provides the same flexibility as the `--empty`\noptions for git-rebase(1) and git-am(1).\n\nSome secondary goals are to improve the consistency in the values and\ndocumentation for this option across the three commands.\n\nSee \"Does extending `--empty` to git-cherry-pick make sense?\" [1] for\nsome context for why this option is desired in git-cherry-pick(1).\n\n[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com\n\nAlong the way, I (with some help from Elijah and Phillip) found a few\nother things in the docs and related sequencer code to clean up.\n\nThis is the final planned re-roll of this series, addressing two minor\nstyle concerns with commit 4/7 as noted in this [2] thread. All other\ncommits are left unchanged.\n\n[2]: https://lore.kernel.org/git/xmqqa5mmhvx5.fsf@gitster.g\n\nRange-diff from v4:\n\n1:  f6b8a655cd = 1:  f6b8a655cd docs: address inaccurate `--empty` default with `--exec`\n2:  401de76c0b = 2:  401de76c0b docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n3:  031b3bb7bb = 3:  031b3bb7bb rebase: update `--empty=ask` to `--empty=stop`\n4:  fd53c39482 ! 4:  d3bfe41819 sequencer: handle unborn branch with `--allow-empty`\n    @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist\n      \tstruct commit *head_commit;\n      \tstruct index_state *istate = r->index;\n     +\tconst char *head_name;\n    -\n    --\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n    --\t\treturn error(_(\"could not resolve HEAD commit\"));\n    ++\n     +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n    -+\t\t/*\n    -+\t\t * Check to see if this is an unborn branch\n    -+\t\t */\n    -+\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n    -+\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n    ++\t\t/* Check to see if this is an unborn branch */\n    ++\t\thead_name = resolve_ref_unsafe(\"HEAD\",\n    ++\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n    ++\t\t\t&head_oid, NULL);\n    ++\t\tif (!head_name ||\n    ++\t\t\t!starts_with(head_name, \"refs/heads/\") ||\n    ++\t\t\t!is_null_oid(&head_oid))\n     +\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n     +\t\thead_tree_oid = the_hash_algo->empty_tree;\n     +\t} else {\n     +\t\thead_commit = lookup_commit(r, &head_oid);\n\n    +-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n    +-\t\treturn error(_(\"could not resolve HEAD commit\"));\n    +-\n     -\thead_commit = lookup_commit(r, &head_oid);\n     +\t\t/*\n     +\t\t * If head_commit is NULL, check_commit, called from\n5:  90dca45c12 = 5:  5e690bca6e sequencer: do not require `allow_empty` for redundant commit options\n6:  ab3b6afc97 = 6:  ed03908e9e cherry-pick: enforce `--keep-redundant-commits` incompatibility\n7:  0e2577ea56 = 7:  d3cf068c45 cherry-pick: add `--empty` for more robust redundant commit handling\n\n\nBrian Lyles (7):\n  docs: address inaccurate `--empty` default with `--exec`\n  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n  rebase: update `--empty=ask` to `--empty=stop`\n  sequencer: handle unborn branch with `--allow-empty`\n  sequencer: do not require `allow_empty` for redundant commit options\n  cherry-pick: enforce `--keep-redundant-commits` incompatibility\n  cherry-pick: add `--empty` for more robust redundant commit handling\n\n Documentation/git-am.txt          | 20 ++++++---\n Documentation/git-cherry-pick.txt | 30 ++++++++++---\n Documentation/git-rebase.txt      | 26 +++++++----\n builtin/rebase.c                  | 16 ++++---\n builtin/revert.c                  | 38 +++++++++++++++-\n sequencer.c                       | 72 ++++++++++++++++++-------------\n t/t3424-rebase-empty.sh           | 55 +++++++++++++++++++++--\n t/t3501-revert-cherry-pick.sh     | 14 ++++--\n t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++\n 10 files changed, 286 insertions(+), 68 deletions(-)\n\n-- \n2.43.2\n\n"},{"id":"491545","messageId":"20240325232451.963946-2-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 1/7] docs: address inaccurate `--empty` default with `--exec`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:48Z","receivedAt":"2024-03-25T23:25:55Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"The documentation for git-rebase(1) indicates that using the `--exec`\noption will use `--empty=drop`. This is inaccurate: when `--interactive`\nis not explicitly provided, `--exec` results in `--empty=keep`\nbehaviors.\n\nCorrectly indicate the behavior of `--exec` using `--empty=keep` when\n`--interactive` is not specified.\n\nReported-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt | 10 +++++-----\n t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 43 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 06206521fc..3334e85356 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes).  With drop (the default), commits that\n \tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask (implied by `--interactive`), the rebase will halt when\n-\tan empty commit is applied allowing you to choose whether to\n-\tdrop it, edit files more, or just commit the empty changes.\n-\tOther options, like `--exec`, will use the default of drop unless\n-\t`-i`/`--interactive` is explicitly specified.\n+\tWith ask, the rebase will halt when an empty commit is applied\n+\tallowing you to choose whether to drop it, edit files more, or just\n+\tcommit the empty changes.\n+\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n+\tOtherwise, when the `--exec` option is used, the default becomes keep.\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 5e1045a0af..73ff35ced2 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '\n \ttest_path_is_missing .git/MERGE_MSG\n '\n \n+test_expect_success 'rebase --exec --empty=drop' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=drop upstream &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" --empty=keep upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec uses default of --empty=keep' '\n+\tgit checkout -B testing localmods &&\n+\tgit rebase --exec \"true\" upstream &&\n+\n+\ttest_write_lines D C2 C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rebase --exec --empty=ask' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.43.2\n\n"},{"id":"491546","messageId":"20240325232451.963946-3-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:49Z","receivedAt":"2024-03-25T23:25:56Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"Both of these pages document very similar `--empty` options, but with\ndifferent styles. The exact behavior of these `--empty` options differs\nsomewhat, but consistent styling in the docs is still beneficial. This\ncommit aims to make them more consistent.\n\nBreak the possible values for `--empty` into separate sections for\nreadability. Alphabetical order is chosen for consistency.\n\nIn a future commit, we'll be documenting a new `--empty` option for\ngit-cherry-pick(1), making the consistency even more relevant.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-am.txt     | 20 +++++++++++++-------\n Documentation/git-rebase.txt | 25 ++++++++++++++++---------\n 2 files changed, 29 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-am.txt b/Documentation/git-am.txt\nindex e080458d6c..f852e0ba79 100644\n--- a/Documentation/git-am.txt\n+++ b/Documentation/git-am.txt\n@@ -66,13 +66,19 @@ OPTIONS\n --quoted-cr=<action>::\n \tThis flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).\n \n---empty=(stop|drop|keep)::\n-\tBy default, or when the option is set to 'stop', the command\n-\terrors out on an input e-mail message lacking a patch\n-\tand stops in the middle of the current am session. When this\n-\toption is set to 'drop', skip such an e-mail message instead.\n-\tWhen this option is set to 'keep', create an empty commit,\n-\trecording the contents of the e-mail message as its log.\n+--empty=(drop|keep|stop)::\n+\tHow to handle an e-mail message lacking a patch:\n++\n+--\n+`drop`;;\n+\tThe e-mail message will be skipped.\n+`keep`;;\n+\tAn empty commit will be created, with the contents of the e-mail\n+\tmessage as its log.\n+`stop`;;\n+\tThe command will fail, stopping in the middle of the current `am`\n+\tsession. This is the default behavior.\n+--\n \n -m::\n --message-id::\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 3334e85356..0b0d0ccb80 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(drop|keep|ask)::\n+--empty=(ask|drop|keep)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n-\tupstream changes).  With drop (the default), commits that\n-\tbecome empty are dropped.  With keep, such commits are kept.\n-\tWith ask, the rebase will halt when an empty commit is applied\n-\tallowing you to choose whether to drop it, edit files more, or just\n-\tcommit the empty changes.\n-\tWhen the `-i`/`--interactive` option is used, the default becomes ask.\n-\tOtherwise, when the `--exec` option is used, the default becomes keep.\n+\tupstream changes):\n++\n+--\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified.\n+`drop`;;\n+\tThe commit will be dropped. This is the default behavior.\n+`keep`;;\n+\tThe commit will be kept. This option is implied when `--exec` is\n+\tspecified unless `-i`/`--interactive` is also specified.\n+--\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n is specified), and commits which are clean cherry-picks (as determined\n@@ -704,7 +711,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(drop|keep|ask)` option for changing the behavior\n+also has an `--empty=(ask|drop|keep)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\n-- \n2.43.2\n\n"},{"id":"491547","messageId":"20240325232451.963946-4-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 3/7] rebase: update `--empty=ask` to `--empty=stop`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:50Z","receivedAt":"2024-03-25T23:25:58Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09), `stop` was used\ninstead of `ask`. `stop` is a more accurate term for describing what\nreally happens, and consistency is good.\n\nUpdate git-rebase(1) to also use `stop`, while keeping `ask` as a\ndeprecated synonym. Update the tests to primarily use `stop`, but also\nensure that `ask` is still allowed.\n\nIn a future commit, we'll be adding a new `--empty` option for\ngit-cherry-pick(1) as well, making the consistency even more relevant.\n\nReported-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-rebase.txt | 15 ++++++++-------\n builtin/rebase.c             | 16 ++++++++++------\n t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----\n 3 files changed, 34 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 0b0d0ccb80..67dd0a533e 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.\n +\n See also INCOMPATIBLE OPTIONS below.\n \n---empty=(ask|drop|keep)::\n+--empty=(drop|keep|stop)::\n \tHow to handle commits that are not empty to start and are not\n \tclean cherry-picks of any upstream commit, but which become\n \tempty after rebasing (because they contain a subset of already\n \tupstream changes):\n +\n --\n-`ask`;;\n-\tThe rebase will halt when the commit is applied, allowing you to\n-\tchoose whether to drop it, edit files more, or just commit the empty\n-\tchanges. This option is implied when `-i`/`--interactive` is\n-\tspecified.\n `drop`;;\n \tThe commit will be dropped. This is the default behavior.\n `keep`;;\n \tThe commit will be kept. This option is implied when `--exec` is\n \tspecified unless `-i`/`--interactive` is also specified.\n+`stop`;;\n+`ask`;;\n+\tThe rebase will halt when the commit is applied, allowing you to\n+\tchoose whether to drop it, edit files more, or just commit the empty\n+\tchanges. This option is implied when `-i`/`--interactive` is\n+\tspecified. `ask` is a deprecated synonym of `stop`.\n --\n +\n Note that commits which start empty are kept (unless `--no-keep-empty`\n@@ -711,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).\n Similar to the apply backend, by default the merge backend drops\n commits that become empty unless `-i`/`--interactive` is specified (in\n which case it stops and asks the user what to do).  The merge backend\n-also has an `--empty=(ask|drop|keep)` option for changing the behavior\n+also has an `--empty=(drop|keep|stop)` option for changing the behavior\n of handling commits that become empty.\n \n Directory rename detection\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 5b086f651a..a4916781ce 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -58,7 +58,7 @@ enum empty_type {\n \tEMPTY_UNSPECIFIED = -1,\n \tEMPTY_DROP,\n \tEMPTY_KEEP,\n-\tEMPTY_ASK\n+\tEMPTY_STOP\n };\n \n enum action {\n@@ -951,10 +951,14 @@ static enum empty_type parse_empty_value(const char *value)\n \t\treturn EMPTY_DROP;\n \telse if (!strcasecmp(value, \"keep\"))\n \t\treturn EMPTY_KEEP;\n-\telse if (!strcasecmp(value, \"ask\"))\n-\t\treturn EMPTY_ASK;\n+\telse if (!strcasecmp(value, \"stop\"))\n+\t\treturn EMPTY_STOP;\n+\telse if (!strcasecmp(value, \"ask\")) {\n+\t\twarning(_(\"--empty=ask is deprecated; use '--empty=stop' instead.\"));\n+\t\treturn EMPTY_STOP;\n+\t}\n \n-\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"ask\\\".\"), value);\n+\tdie(_(\"unrecognized empty type '%s'; valid values are \\\"drop\\\", \\\"keep\\\", and \\\"stop\\\".\"), value);\n }\n \n static int parse_opt_keep_empty(const struct option *opt, const char *arg,\n@@ -1133,7 +1137,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"instead of ignoring them\"),\n \t\t\t      1, PARSE_OPT_HIDDEN),\n \t\tOPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),\n-\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|ask)\",\n+\t\tOPT_CALLBACK_F(0, \"empty\", &options, \"(drop|keep|stop)\",\n \t\t\t       N_(\"how to handle commits that become empty\"),\n \t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\tOPT_CALLBACK_F('k', \"keep-empty\", &options, NULL,\n@@ -1550,7 +1554,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tif (options.empty == EMPTY_UNSPECIFIED) {\n \t\tif (options.flags & REBASE_INTERACTIVE_EXPLICIT)\n-\t\t\toptions.empty = EMPTY_ASK;\n+\t\t\toptions.empty = EMPTY_STOP;\n \t\telse if (options.exec.nr > 0)\n \t\t\toptions.empty = EMPTY_KEEP;\n \t\telse\ndiff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh\nindex 73ff35ced2..1ee6b00fd5 100755\n--- a/t/t3424-rebase-empty.sh\n+++ b/t/t3424-rebase-empty.sh\n@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'rebase --merge --empty=stop' '\n+\tgit checkout -B testing localmods &&\n+\ttest_must_fail git rebase --merge --empty=stop upstream &&\n+\n+\tgit rebase --skip &&\n+\n+\ttest_write_lines D C B A >expect &&\n+\tgit log --format=%s >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'rebase --merge --empty=ask' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --merge --empty=ask upstream &&\n@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive --empty=ask' '\n+test_expect_success 'rebase --interactive --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --interactive --empty=ask upstream &&\n+\ttest_must_fail git rebase --interactive --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --interactive uses default of --empty=ask' '\n+test_expect_success 'rebase --interactive uses default of --empty=stop' '\n \tgit checkout -B testing localmods &&\n \ttest_must_fail git rebase --interactive upstream &&\n \n@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'rebase --exec --empty=ask' '\n+test_expect_success 'rebase --exec --empty=stop' '\n \tgit checkout -B testing localmods &&\n-\ttest_must_fail git rebase --exec \"true\" --empty=ask upstream &&\n+\ttest_must_fail git rebase --exec \"true\" --empty=stop upstream &&\n \n \tgit rebase --skip &&\n \n-- \n2.43.2\n\n"},{"id":"491548","messageId":"20240325232451.963946-5-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 4/7] sequencer: handle unborn branch with `--allow-empty`","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:51Z","receivedAt":"2024-03-25T23:25:59Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When using git-cherry-pick(1) with `--allow-empty` while on an unborn\nbranch, an error is thrown. This is inconsistent with the same\ncherry-pick when `--allow-empty` is not specified.\n\nDetect unborn branches in `is_index_unchanged`. When on an unborn\nbranch, use the `empty_tree` as the tree to compare against.\n\nAdd a new test to cover this scenario. While modelled off of the\nexisting 'cherry-pick on unborn branch' test, some improvements can be\nmade:\n\n- Use `git switch --orphan unborn` instead of `git checkout --orphan\n  unborn` to avoid the need for a separate `rm -rf *` call\n- Avoid using `--quiet` in the `git diff` call to make debugging easier\n  in the event of a failure. Use simply `--exit-code` instead.\n\nMake these improvements to the existing test as well as the new test.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n\nChanges from v4:\n\n- Use single-line block comment style since the comment is a single line\n  of text.\n- Wrap two longer lines of code.\n\n sequencer.c                   | 43 +++++++++++++++++++++++------------\n t/t3501-revert-cherry-pick.sh | 14 +++++++++---\n 2 files changed, 39 insertions(+), 18 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f49a871ac0..e3f0a52f72 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -770,29 +770,42 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)\n static int is_index_unchanged(struct repository *r)\n {\n \tstruct object_id head_oid, *cache_tree_oid;\n+\tconst struct object_id *head_tree_oid;\n \tstruct commit *head_commit;\n \tstruct index_state *istate = r->index;\n+\tconst char *head_name;\n+\n+\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n+\t\t/* Check to see if this is an unborn branch */\n+\t\thead_name = resolve_ref_unsafe(\"HEAD\",\n+\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n+\t\t\t&head_oid, NULL);\n+\t\tif (!head_name ||\n+\t\t\t!starts_with(head_name, \"refs/heads/\") ||\n+\t\t\t!is_null_oid(&head_oid))\n+\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n+\t\thead_tree_oid = the_hash_algo->empty_tree;\n+\t} else {\n+\t\thead_commit = lookup_commit(r, &head_oid);\n \n-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n-\t\treturn error(_(\"could not resolve HEAD commit\"));\n-\n-\thead_commit = lookup_commit(r, &head_oid);\n+\t\t/*\n+\t\t * If head_commit is NULL, check_commit, called from\n+\t\t * lookup_commit, would have indicated that head_commit is not\n+\t\t * a commit object already.  repo_parse_commit() will return failure\n+\t\t * without further complaints in such a case.  Otherwise, if\n+\t\t * the commit is invalid, repo_parse_commit() will complain.  So\n+\t\t * there is nothing for us to say here.  Just return failure.\n+\t\t */\n+\t\tif (repo_parse_commit(r, head_commit))\n+\t\t\treturn -1;\n \n-\t/*\n-\t * If head_commit is NULL, check_commit, called from\n-\t * lookup_commit, would have indicated that head_commit is not\n-\t * a commit object already.  repo_parse_commit() will return failure\n-\t * without further complaints in such a case.  Otherwise, if\n-\t * the commit is invalid, repo_parse_commit() will complain.  So\n-\t * there is nothing for us to say here.  Just return failure.\n-\t */\n-\tif (repo_parse_commit(r, head_commit))\n-\t\treturn -1;\n+\t\thead_tree_oid = get_commit_tree_oid(head_commit);\n+\t}\n \n \tif (!(cache_tree_oid = get_cache_tree_oid(istate)))\n \t\treturn -1;\n \n-\treturn oideq(cache_tree_oid, get_commit_tree_oid(head_commit));\n+\treturn oideq(cache_tree_oid, head_tree_oid);\n }\n \n static int write_author_script(const char *message)\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex aeab689a98..af73227512 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '\n '\n \n test_expect_success 'cherry-pick on unborn branch' '\n-\tgit checkout --orphan unborn &&\n+\tgit switch --orphan unborn &&\n \tgit rm --cached -r . &&\n-\trm -rf * &&\n \tgit cherry-pick initial &&\n-\tgit diff --quiet initial &&\n+\tgit diff --exit-code initial &&\n+\ttest_cmp_rev ! initial HEAD\n+'\n+\n+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '\n+\tgit checkout --detach &&\n+\tgit branch -D unborn &&\n+\tgit switch --orphan unborn &&\n+\tgit cherry-pick initial --allow-empty &&\n+\tgit diff --exit-code initial &&\n \ttest_cmp_rev ! initial HEAD\n '\n \n-- \n2.43.2\n\n"},{"id":"491549","messageId":"20240325232451.963946-6-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 5/7] sequencer: do not require `allow_empty` for redundant commit options","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:52Z","receivedAt":"2024-03-25T23:26:00Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"A consumer of the sequencer that wishes to take advantage of either the\n`keep_redundant_commits` or `drop_redundant_commits` feature must also\nspecify `allow_empty`. However, these refer to two distinct types of\nempty commits:\n\n- `allow_empty` refers specifically to commits which start empty\n- `keep_redundant_commits` refers specifically to commits that do not\n  start empty, but become empty due to the content already existing in\n  the target history\n\nConceptually, there is no reason that the behavior for handling one of\nthese should be entangled with the other. It is particularly unintuitive\nto require `allow_empty` in order for `drop_redundant_commits` to have\nan effect: in order to prevent redundant commits automatically,\ninitially-empty commits would need to be kept automatically as well.\n\nInstead, rewrite the `allow_empty()` logic to remove the over-arching\nrequirement that `allow_empty` be specified in order to reach any of the\nkeep/drop behaviors. Only if the commit was originally empty will\n`allow_empty` have an effect.\n\nNote that no behavioral changes should result from this commit -- it\nmerely sets the stage for future commits. In one such future commit, an\n`--empty` option will be added to git-cherry-pick(1), meaning that\n`drop_redundant_commits` will be used by that command.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n sequencer.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e3f0a52f72..04ee94bd81 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1732,34 +1732,25 @@ static int allow_empty(struct repository *r,\n \tint index_unchanged, originally_empty;\n \n \t/*\n-\t * Four cases:\n+\t * For a commit that is initially empty, allow_empty determines if it\n+\t * should be kept or not\n \t *\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, and\n-\t *     just drop the ones that become empty\n-\t *\n-\t * (3) we allow ones that were initially empty, but\n-\t *     halt for the ones that become empty;\n-\t *\n-\t * (4) we allow both.\n+\t * For a commit that becomes empty, keep_redundant_commits and\n+\t * drop_redundant_commits determine whether the commit should be kept or\n+\t * dropped. If neither is specified, halt.\n \t */\n-\tif (!opts->allow_empty)\n-\t\treturn 0; /* let \"git commit\" barf as necessary */\n-\n \tindex_unchanged = is_index_unchanged(r);\n \tif (index_unchanged < 0)\n \t\treturn index_unchanged;\n \tif (!index_unchanged)\n \t\treturn 0; /* we do not have to say --allow-empty */\n \n-\tif (opts->keep_redundant_commits)\n-\t\treturn 1;\n-\n \toriginally_empty = is_original_commit_empty(commit);\n \tif (originally_empty < 0)\n \t\treturn originally_empty;\n \tif (originally_empty)\n+\t\treturn opts->allow_empty;\n+\telse if (opts->keep_redundant_commits)\n \t\treturn 1;\n \telse if (opts->drop_redundant_commits)\n \t\treturn 2;\n-- \n2.43.2\n\n"},{"id":"491550","messageId":"20240325232451.963946-7-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:53Z","receivedAt":"2024-03-25T23:26:01Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"When `--keep-redundant-commits` was added in  b27cfb0d8d\n(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was\nnot marked as incompatible with the various operations needed to\ncontinue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and\n`--quit`).\n\nEnforce this incompatibility via `verify_opt_compatible` like we do for\nthe other various options.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n builtin/revert.c             |  1 +\n t/t3505-cherry-pick-empty.sh | 14 ++++++++++++++\n 2 files changed, 15 insertions(+)\n\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex 89821bab95..a1936ef70e 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -167,6 +167,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--ff\", opts->allow_ff,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n+\t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n \t\t\t\tNULL);\n \t}\n \ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex eba3c38d5a..61f91aaa0a 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -99,4 +99,18 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--keep-redundant-commits is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n test_done\n-- \n2.43.2\n\n"},{"id":"491551","messageId":"20240325232451.963946-8-brianmlyles@gmail.com","threadId":"60764","inReplyTo":"20240119060721.3734775-2-brianmlyles@gmail.com","subject":"[PATCH v5 7/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Brian Lyles","fromEmail":"brianmlyles@gmail.com","sentAt":"2024-03-25T23:16:54Z","receivedAt":"2024-03-25T23:26:03Z","isPatch":true,"sender":{"key":"brianmlyles@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1123282?v=4"},"body":"As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a\ncommit being made redundant if the content from the picked commit is\nalready present in the target history. However, git-cherry-pick(1) does\nnot have the same options available that git-rebase(1) and git-am(1) have.\n\nThere are three things that can be done with these redundant commits:\ndrop them, keep them, or have the cherry-pick stop and wait for the user\nto take an action. git-rebase(1) has the `--empty` option added in commit\ne98c4269c8 (rebase (interactive-backend): fix handling of commits that\nbecome empty, 2020-02-15), which handles all three of these scenarios.\nSimilarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support\n--empty=<option> to handle empty patches, 2021-12-09).\n\ngit-cherry-pick(1), on the other hand, only supports two of the three\npossiblities: Keep the redundant commits via `--keep-redundant-commits`,\nor have the cherry-pick fail by not specifying that option. There is no\nway to automatically drop redundant commits.\n\nIn order to bring git-cherry-pick(1) more in-line with git-rebase(1) and\ngit-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It\nhas the same three options (keep, drop, and stop), and largely behaves\nthe same. The notable difference is that for git-cherry-pick(1), the\ndefault will be `stop`, which maintains the current behavior when the\noption is not specified.\n\nLike the existing `--keep-redundant-commits`, `--empty=keep` will imply\n`--allow-empty`.\n\nThe `--keep-redundant-commits` option will be documented as a deprecated\nsynonym of `--empty=keep`, and will be supported for backwards\ncompatibility for the time being.\n\nSigned-off-by: Brian Lyles <brianmlyles@gmail.com>\n---\n Documentation/git-cherry-pick.txt | 30 +++++++++++++++++++------\n builtin/revert.c                  | 37 ++++++++++++++++++++++++++++++-\n sequencer.c                       |  6 +++++\n t/t3505-cherry-pick-empty.sh      | 37 ++++++++++++++++++++++++++++++-\n t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++++++++++++\n 5 files changed, 133 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex fdcad3d200..81ace900fc 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -131,20 +131,36 @@ effect to your index in a row.\n \teven without this option.  Note also, that use of this option only\n \tkeeps commits that were initially empty (i.e. the commit recorded the\n \tsame tree as its parent).  Commits which are made empty due to a\n-\tprevious commit are dropped.  To force the inclusion of those commits\n-\tuse `--keep-redundant-commits`.\n+\tprevious commit will cause the cherry-pick to fail.  To force the\n+\tinclusion of those commits, use `--empty=keep`.\n \n --allow-empty-message::\n \tBy default, cherry-picking a commit with an empty message will fail.\n \tThis option overrides that behavior, allowing commits with empty\n \tmessages to be cherry picked.\n \n+--empty=(drop|keep|stop)::\n+\tHow to handle commits being cherry-picked that are redundant with\n+\tchanges already in the current history.\n++\n+--\n+`drop`;;\n+\tThe commit will be dropped.\n+`keep`;;\n+\tThe commit will be kept. Implies `--allow-empty`.\n+`stop`;;\n+\tThe cherry-pick will stop when the commit is applied, allowing\n+\tyou to examine the commit. This is the default behavior.\n+--\n++\n+Note that `--empty=drop` and `--empty=stop` only specify how to handle a\n+commit that was not initially empty, but rather became empty due to a previous\n+commit. Commits that were initially empty will still cause the cherry-pick to\n+fail unless one of `--empty=keep` or `--allow-empty` are specified.\n++\n+\n --keep-redundant-commits::\n-\tIf a commit being cherry picked duplicates a commit already in the\n-\tcurrent history, it will become empty.  By default these\n-\tredundant commits cause `cherry-pick` to stop so the user can\n-\texamine the commit. This option overrides that behavior and\n-\tcreates an empty commit object.  Implies `--allow-empty`.\n+\tDeprecated synonym for `--empty=keep`.\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 a1936ef70e..53935d2c68 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)\n \treturn opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;\n }\n \n+enum empty_action {\n+\tEMPTY_COMMIT_UNSPECIFIED = -1,\n+\tSTOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */\n+\tDROP_EMPTY_COMMIT,         /* skip with a notice message */\n+\tKEEP_EMPTY_COMMIT,         /* keep recording as empty commits */\n+};\n+\n+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)\n+{\n+\tint *opt_value = opt->value;\n+\n+\tBUG_ON_OPT_NEG(unset);\n+\n+\tif (!strcmp(arg, \"stop\"))\n+\t\t*opt_value = STOP_ON_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"drop\"))\n+\t\t*opt_value = DROP_EMPTY_COMMIT;\n+\telse if (!strcmp(arg, \"keep\"))\n+\t\t*opt_value = KEEP_EMPTY_COMMIT;\n+\telse\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n+\n+\treturn 0;\n+}\n+\n static int option_parse_m(const struct option *opt,\n \t\t\t  const char *arg, int unset)\n {\n@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n \tconst char *cleanup_arg = NULL;\n+\tenum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\tOPT_BOOL(0, \"ff\", &opts->allow_ff, N_(\"allow fast-forward\")),\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, \"keep-redundant-commits\", &opts->keep_redundant_commits, N_(\"deprecated: use --empty=keep instead\")),\n+\t\t\tOPT_CALLBACK_F(0, \"empty\", &empty_opt, \"(stop|drop|keep)\",\n+\t\t\t\t       N_(\"how to handle commits that become empty\"),\n+\t\t\t\t       PARSE_OPT_NONEG, parse_opt_empty),\n \t\t\tOPT_END(),\n \t\t};\n \t\toptions = parse_options_concat(options, cp_extra);\n@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \tprepare_repo_settings(the_repository);\n \tthe_repository->settings.command_requires_full_index = 0;\n \n+\tif (opts->action == REPLAY_PICK) {\n+\t\topts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);\n+\t\topts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);\n+\t}\n+\n \t/* implies allow_empty */\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n@@ -168,6 +202,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,\n \t\t\t\t\"--rerere-autoupdate\", opts->allow_rerere_auto == RERERE_AUTOUPDATE,\n \t\t\t\t\"--no-rerere-autoupdate\", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,\n \t\t\t\t\"--keep-redundant-commits\", opts->keep_redundant_commits,\n+\t\t\t\t\"--empty\", empty_opt != EMPTY_COMMIT_UNSPECIFIED,\n \t\t\t\tNULL);\n \t}\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 04ee94bd81..72ca483459 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2930,6 +2930,9 @@ static int populate_opts_cb(const char *key, const char *value,\n \telse if (!strcmp(key, \"options.allow-empty-message\"))\n \t\topts->allow_empty_message =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n+\telse if (!strcmp(key, \"options.drop-redundant-commits\"))\n+\t\topts->drop_redundant_commits =\n+\t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n \telse if (!strcmp(key, \"options.keep-redundant-commits\"))\n \t\topts->keep_redundant_commits =\n \t\t\tgit_config_bool_or_int(key, value, ctx->kvi, &error_flag);\n@@ -3474,6 +3477,9 @@ static int save_opts(struct replay_opts *opts)\n \tif (opts->allow_empty_message)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.allow-empty-message\", \"true\");\n+\tif (opts->drop_redundant_commits)\n+\t\tres |= git_config_set_in_file_gently(opts_file,\n+\t\t\t\t\"options.drop-redundant-commits\", \"true\");\n \tif (opts->keep_redundant_commits)\n \t\tres |= git_config_set_in_file_gently(opts_file,\n \t\t\t\t\"options.keep-redundant-commits\", \"true\");\ndiff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh\nindex 61f91aaa0a..9748443530 100755\n--- a/t/t3505-cherry-pick-empty.sh\n+++ b/t/t3505-cherry-pick-empty.sh\n@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '\n \tgit commit -m \"add file2 on the side\"\n '\n \n-test_expect_success 'cherry-pick a no-op without --keep-redundant' '\n+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '\n \tgit reset --hard &&\n \tgit checkout fork^0 &&\n \ttest_must_fail git cherry-pick main\n@@ -113,4 +113,39 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '\n \tgit cherry-pick --abort\n '\n \n+test_expect_success '--empty is incompatible with operations' '\n+\ttest_must_fail git cherry-pick HEAD 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --continue 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --continue\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --skip 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --skip\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --abort 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --abort\" output &&\n+\ttest_must_fail git cherry-pick --empty=stop --quit 2>output &&\n+\ttest_grep \"fatal: cherry-pick: --empty cannot be used with --quit\" output &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=stop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\ttest_must_fail git cherry-pick --empty=stop main 2>output &&\n+\ttest_grep \"The previous cherry-pick is now empty\" output\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=drop' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=drop main &&\n+\ttest_commit_message HEAD -m \"add file2 on the side\"\n+'\n+\n+test_expect_success 'cherry-pick a no-op with --empty=keep' '\n+\tgit reset --hard &&\n+\tgit checkout fork^0 &&\n+\tgit cherry-pick --empty=keep main &&\n+\ttest_commit_message HEAD -m \"add file2 on main\"\n+'\n+\n test_done\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 72020a51c4..7eb52b12ed 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'cherry-pick persists --empty=stop correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&\n+\ttest_must_fail git cherry-pick --skip 2>msg &&\n+\ttest_grep \"The previous cherry-pick is now empty\" msg &&\n+\trm msg &&\n+\tgit cherry-pick --abort\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=drop correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&\n+\tgit cherry-pick --skip &&\n+\ttest_cmp_rev yetanotherpick HEAD\n+'\n+\n+test_expect_success 'cherry-pick persists --empty=keep correctly' '\n+\tpristine_detach yetanotherpick &&\n+\t# Picking `anotherpick` forces a conflict so that we stop. That\n+\t# commit is then skipped, after which we pick `yetanotherpick`\n+\t# while already on `yetanotherpick` to cause an empty commit\n+\ttest_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&\n+\tgit cherry-pick --skip &&\n+\ttest_cmp_rev yetanotherpick HEAD^\n+'\n+\n test_expect_success 'revert persists opts correctly' '\n \tpristine_detach initial &&\n \t# to make sure that the session to revert a sequence\n-- \n2.43.2\n\n"},{"id":"491582","messageId":"a397f3dd-e4e1-4275-b17d-1daca9e166fe@gmail.com","threadId":"60764","inReplyTo":"20240325232451.963946-1-brianmlyles@gmail.com","subject":"Re: [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-26T14:45:45Z","receivedAt":"2024-03-26T14:45:47Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Brian\n\nOn 25/03/2024 23:16, Brian Lyles wrote:\n> This is the final planned re-roll of this series, addressing two minor\n> style concerns with commit 4/7 as noted in this [2] thread. All other\n> commits are left unchanged.\n> \n> [2]: https://lore.kernel.org/git/xmqqa5mmhvx5.fsf@gitster.g\n> \n> Range-diff from v4:\n> \n> 1:  f6b8a655cd = 1:  f6b8a655cd docs: address inaccurate `--empty` default with `--exec`\n> 2:  401de76c0b = 2:  401de76c0b docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n> 3:  031b3bb7bb = 3:  031b3bb7bb rebase: update `--empty=ask` to `--empty=stop`\n> 4:  fd53c39482 ! 4:  d3bfe41819 sequencer: handle unborn branch with `--allow-empty`\n>      @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist\n>        \tstruct commit *head_commit;\n>        \tstruct index_state *istate = r->index;\n>       +\tconst char *head_name;\n>      -\n>      --\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n>      --\t\treturn error(_(\"could not resolve HEAD commit\"));\n>      ++\n>       +\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL)) {\n>      -+\t\t/*\n>      -+\t\t * Check to see if this is an unborn branch\n>      -+\t\t */\n>      -+\t\thead_name = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);\n>      -+\t\tif (!head_name || !starts_with(head_name, \"refs/heads/\") || !is_null_oid(&head_oid))\n>      ++\t\t/* Check to see if this is an unborn branch */\n>      ++\t\thead_name = resolve_ref_unsafe(\"HEAD\",\n>      ++\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n>      ++\t\t\t&head_oid, NULL);\n>      ++\t\tif (!head_name ||\n>      ++\t\t\t!starts_with(head_name, \"refs/heads/\") ||\n>      ++\t\t\t!is_null_oid(&head_oid))\n>       +\t\t\treturn error(_(\"could not resolve HEAD commit\"));\n>       +\t\thead_tree_oid = the_hash_algo->empty_tree;\n>       +\t} else {\n>       +\t\thead_commit = lookup_commit(r, &head_oid);\n\nThis version is definitely more readable, thanks\n\n>      +-\tif (!resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING, &head_oid, NULL))\n>      +-\t\treturn error(_(\"could not resolve HEAD commit\"));\n>      +-\n>       -\thead_commit = lookup_commit(r, &head_oid);\n>       +\t\t/*\n>       +\t\t * If head_commit is NULL, check_commit, called from\n\nThis looks strange, but if I do a range-diff locally from v4 to v5 I \nonly see the line wrapping changes above so I don't think it is anything \nto worry about.\n\nThanks for working on this\n\nPhillip\n\n> 5:  90dca45c12 = 5:  5e690bca6e sequencer: do not require `allow_empty` for redundant commit options\n> 6:  ab3b6afc97 = 6:  ed03908e9e cherry-pick: enforce `--keep-redundant-commits` incompatibility\n> 7:  0e2577ea56 = 7:  d3cf068c45 cherry-pick: add `--empty` for more robust redundant commit handling\n> \n> \n> Brian Lyles (7):\n>    docs: address inaccurate `--empty` default with `--exec`\n>    docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)\n>    rebase: update `--empty=ask` to `--empty=stop`\n>    sequencer: handle unborn branch with `--allow-empty`\n>    sequencer: do not require `allow_empty` for redundant commit options\n>    cherry-pick: enforce `--keep-redundant-commits` incompatibility\n>    cherry-pick: add `--empty` for more robust redundant commit handling\n> \n>   Documentation/git-am.txt          | 20 ++++++---\n>   Documentation/git-cherry-pick.txt | 30 ++++++++++---\n>   Documentation/git-rebase.txt      | 26 +++++++----\n>   builtin/rebase.c                  | 16 ++++---\n>   builtin/revert.c                  | 38 +++++++++++++++-\n>   sequencer.c                       | 72 ++++++++++++++++++-------------\n>   t/t3424-rebase-empty.sh           | 55 +++++++++++++++++++++--\n>   t/t3501-revert-cherry-pick.sh     | 14 ++++--\n>   t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++-\n>   t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++\n>   10 files changed, 286 insertions(+), 68 deletions(-)\n> \n"},{"id":"491588","messageId":"xmqqplvgalud.fsf@gitster.g","threadId":"60764","inReplyTo":"a397f3dd-e4e1-4275-b17d-1daca9e166fe@gmail.com","subject":"Re: [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-03-26T18:28:58Z","receivedAt":"2024-03-26T18:29:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n> Hi Brian\n> ...\n>>       +\t\thead_commit = lookup_commit(r, &head_oid);\n>\n> This version is definitely more readable, thanks\n>\n>...\n>\n> This looks strange, but if I do a range-diff locally from v4 to v5 I\n> only see the line wrapping changes above so I don't think it is\n> anything to worry about.\n>\n> Thanks for working on this\n\nSo, I take it as an Ack from the person who acted as the main\nreviewer for this series?  If so, I'll mark the topic for 'next'.\n\nTanks, both of you.  The resulting series does look very good.\n"},{"id":"491705","messageId":"b952aee0-68f8-4df0-be40-109b28f4383f@gmail.com","threadId":"60764","inReplyTo":"xmqqplvgalud.fsf@gitster.g","subject":"Re: [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-03-27T16:37:30Z","receivedAt":"2024-03-27T16:37:33Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 26/03/2024 18:28, Junio C Hamano wrote:\n> phillip.wood123@gmail.com writes:\n> \n>> Hi Brian\n>> ...\n>>>        +\t\thead_commit = lookup_commit(r, &head_oid);\n>>\n>> This version is definitely more readable, thanks\n>>\n>> ...\n>>\n>> This looks strange, but if I do a range-diff locally from v4 to v5 I\n>> only see the line wrapping changes above so I don't think it is\n>> anything to worry about.\n>>\n>> Thanks for working on this\n> \n> So, I take it as an Ack from the person who acted as the main\n> reviewer for this series? \n\nYes I'm happy\n\n> If so, I'll mark the topic for 'next'.\n\nExcellent\n\n> Tanks, both of you.  The resulting series does look very good.\n\nAll credit to Brian - it has been a well structured and well written \nseries since the first iteration.\n\nBest Wishes\n\nPhillip\n"}]}