All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
@ 2024-01-19  5:59 brianmlyles
  2024-01-19  5:59 ` [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am` brianmlyles
                   ` (38 more replies)
  0 siblings, 39 replies; 118+ messages in thread
From: brianmlyles @ 2024-01-19  5:59 UTC (permalink / raw)
  To: git; +Cc: me, newren, Brian Lyles

From: Brian Lyles <brianmlyles@gmail.com>

Previously, a consumer of the sequencer that wishes to take advantage of
either the `keep_redundant_commits` or `drop_redundant_commits` feature
must also specify `allow_empty`.

The only consumer of `drop_redundant_commits` is `git-rebase`, which
already allows empty commits by default and simply always enables
`allow_empty`. `keep_redundant_commits` was also consumed by
`git-cherry-pick`, which had to specify `allow-empty` when
`keep_redundant_commits` was specified in order for the sequencer's
`allow_empty()` to actually respect `keep_redundant_commits`.

The latter is an interesting case: As noted in the docs, this means that
`--keep-redundant-commits` implies `--allow-empty`, despite the two
having distinct, non-overlapping meanings:

- `allow_empty` refers specifically to commits which start empty, as
  indicated by the documentation for `--allow-empty` within
  `git-cherry-pick`:

  "Note also, that use of this option only keeps commits that were
  initially empty (i.e. the commit recorded the same tree as its
  parent). Commits which are made empty due to a previous commit are
  dropped. To force the inclusion of those commits use
  --keep-redundant-commits."

- `keep_redundant_commits` refers specifically to commits that do not
  start empty, but become empty due to the content already existing in
  the target history. This is indicated by the documentation for
  `--keep-redundant-commits` within `git-cherry-pick`:

  "If a commit being cherry picked duplicates a commit already in the
  current history, it will become empty. By default these redundant
  commits cause cherry-pick to stop so the user can examine the commit.
  This option overrides that behavior and creates an empty commit
  object. Implies --allow-empty."

This implication of `--allow-empty` therefore seems incorrect: One
should be able to keep a commit that becomes empty without also being
forced to pick commits that start as empty. However, today, the
following series of commands would result in both the commit that became
empty and the commit that started empty being picked despite only
`--keep-redundant-commits` being specified:

    git init
    echo "a" >test
    git add test
    git commit -m "Initial commit"
    echo "b" >test
    git commit -am "a -> b"
    git commit --allow-empty -m "empty"
    git cherry-pick --keep-redundant-commits HEAD^ HEAD

The same cherry-pick with `--allow-empty` would fail on the redundant
commit, and with neither option would fail on the empty commit.

In a future commit, an `--empty` option will be added to
`git-cherry-pick`, meaning that `drop_redundant_commits` will be
available in that command. For that to be possible with the current
implementation of the sequencer's `allow_empty()`, `git-cherry-pick`
would need to specify `allow_empty` with `drop_redundant_commits` as
well, which is an even less intuitive implication of `--allow-empty`: in
order to prevent redundant commits automatically, initially-empty
commits would need to be kept automatically.

Instead, this commit rewrites the `allow_empty()` logic to remove the
over-arching requirement that `allow_empty` be specified in order to
reach any of the keep/drop behaviors. Only if the commit was originally
empty will `allow_empty` have an effect.

For some amount of backwards compatibility with the existing code and
tests, I have opted to preserve the behavior of returning 0 when:

- `allow_empty` is specified, and
- either `is_index_unchanged` or `is_original_commit_empty` indicates an
  error

This is primarily out of caution -- I am not positive what downstream
impacts this might have.

Note that this commit is a breaking change: `--keep-redundant-commits`
will no longer imply `--allow-empty`. It would be possible to maintain
the current behavior of `--keep-redundant-commits` implying
`--allow-empty` if it were needed to avoid a breaking change, but I
believe that decoupling them entirely is the correct behavior.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Disclaimer: This is my first contribution to the git project, and thus
my first attempt at submitting a patch via `git-send-email`. It is also
the first time I've touched worked in C in over a decade, and I really
didn't work with it much before that either. I welcome any and all
feedback on what I may have gotten wrong regarding the patch submission
process, the code changes, or my commit messages.

This is the first in a series of commits that aims to introduce an
`--empty` option to `git-cherry-pick` that provides the same flexibility
as the `--empty` options for `git-rebase` and `git-am`, as well as
improve the consistency in the values and documentation for this option
across the three commands.

The main thing that may be controversial with this particular commit is
that I am proposing a breaking change. As described in the above
message, I do not think that it makes sense to tie `--allow-empty` and
`--keep-redundant-commits` together since they appear to be intended to
work with different types of empty commits. That being said, if it is
deemed unacceptable to make this breaking change, we can consider an
alternative approach where we maintain the behavior of
`--keep-redundant-commits` implying `--allow-empty`, while preventing
the need for the future `--empty=drop` to have that same implication.

 Documentation/git-cherry-pick.txt | 10 +++++++---
 builtin/revert.c                  |  4 ----
 sequencer.c                       | 18 ++++++++++--------
 t/t3505-cherry-pick-empty.sh      |  5 +++++
 4 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d200..806295a730 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,8 +131,8 @@ effect to your index in a row.
 	even without this option.  Note also, that use of this option only
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
-	previous commit are dropped.  To force the inclusion of those commits
-	use `--keep-redundant-commits`.
+	previous commit will cause the cherry-pick to fail.  To force the
+	inclusion of those commits use `--keep-redundant-commits`.
 
 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
@@ -144,7 +144,11 @@ effect to your index in a row.
 	current history, it will become empty.  By default these
 	redundant commits cause `cherry-pick` to stop so the user can
 	examine the commit. This option overrides that behavior and
-	creates an empty commit object.  Implies `--allow-empty`.
+	creates an empty commit object. Note that use of this option only
+	results in an empty commit when the commit was not initially empty,
+	but rather became empty due to a previous commit. Commits that were
+	initially empty will cause the cherry-pick to fail. To force the
+	inclusion of those commits use `--allow-empty`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index e6f9a1ad26..b2cfde7a87 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -136,10 +136,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
-	/* implies allow_empty */
-	if (opts->keep_redundant_commits)
-		opts->allow_empty = 1;
-
 	if (cleanup_arg) {
 		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
 		opts->explicit_cleanup = 1;
diff --git a/sequencer.c b/sequencer.c
index d584cac8ed..582bde8d46 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1739,22 +1739,24 @@ static int allow_empty(struct repository *r,
 	 *
 	 * (4) we allow both.
 	 */
-	if (!opts->allow_empty)
-		return 0; /* let "git commit" barf as necessary */
-
 	index_unchanged = is_index_unchanged(r);
-	if (index_unchanged < 0)
+	if (index_unchanged < 0) {
+		if (!opts->allow_empty)
+			return 0;
 		return index_unchanged;
+	}
 	if (!index_unchanged)
 		return 0; /* we do not have to say --allow-empty */
 
-	if (opts->keep_redundant_commits)
-		return 1;
-
 	originally_empty = is_original_commit_empty(commit);
-	if (originally_empty < 0)
+	if (originally_empty < 0) {
+		if (!opts->allow_empty)
+			return 0;
 		return originally_empty;
+	}
 	if (originally_empty)
+		return opts->allow_empty;
+	else if (opts->keep_redundant_commits)
 		return 1;
 	else if (opts->drop_redundant_commits)
 		return 2;
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38d5a..6adfd25351 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -59,6 +59,11 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
 	test_must_fail git cherry-pick empty-change-branch
 '
 
+test_expect_success 'cherry pick an empty non-ff commit with --keep-redundant-commits' '
+	git checkout main &&
+	test_must_fail git cherry-pick --keep-redundant-commits empty-change-branch
+'
+
 test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
 	git checkout main &&
 	git cherry-pick --allow-empty empty-change-branch
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
@ 2024-01-19  5:59 ` brianmlyles
  2024-01-23 14:24   ` Phillip Wood
  2024-01-19  5:59 ` [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop` brianmlyles
                   ` (37 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: brianmlyles @ 2024-01-19  5:59 UTC (permalink / raw)
  To: git; +Cc: me, newren, Brian Lyles

From: Brian Lyles <brianmlyles@gmail.com>

Both of these pages document very similar `--empty` options, but with
different styles. This commit aims to make them more consistent.

In a future commit, we'll be documenting a new `--empty` option for
`git-cherry-pick`, making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-am.txt     | 18 ++++++++++++------
 Documentation/git-rebase.txt | 17 ++++++++++++-----
 2 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e080458d6c..77df5e606a 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -67,12 +67,18 @@ OPTIONS
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
 --empty=(stop|drop|keep)::
-	By default, or when the option is set to 'stop', the command
-	errors out on an input e-mail message lacking a patch
-	and stops in the middle of the current am session. When this
-	option is set to 'drop', skip such an e-mail message instead.
-	When this option is set to 'keep', create an empty commit,
-	recording the contents of the e-mail message as its log.
+	How to handle an e-mail message lacking a patch:
++
+--
+`stop`;;
+	The command will fail, stopping in the middle of the current `am`
+	session. This is the default behavior.
+`drop`;;
+	The e-mail message will be skipped.
+`keep`;;
+	An empty commit will be created, with the contents of the e-mail
+	message as its log.
+--
 
 -m::
 --message-id::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index b4526ca246..3ee85f6d86 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -293,13 +293,20 @@ See also INCOMPATIBLE OPTIONS below.
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
-	upstream changes).  With drop (the default), commits that
-	become empty are dropped.  With keep, such commits are kept.
-	With ask (implied by `--interactive`), the rebase will halt when
-	an empty commit is applied allowing you to choose whether to
-	drop it, edit files more, or just commit the empty changes.
+	upstream changes):
++
+--
+`drop`;;
+	The empty commit will be dropped. This is the default behavior.
+`keep`;;
+	The empty commit will be kept.
+`ask`;;
+	The rebase will halt when the empty commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `--interactive` is specified.
 	Other options, like `--exec`, will use the default of drop unless
 	`-i`/`--interactive` is explicitly specified.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
  2024-01-19  5:59 ` [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am` brianmlyles
@ 2024-01-19  5:59 ` brianmlyles
  2024-01-23 14:24   ` Phillip Wood
  2024-01-19  5:59 ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling brianmlyles
                   ` (36 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: brianmlyles @ 2024-01-19  5:59 UTC (permalink / raw)
  To: git; +Cc: me, newren, Brian Lyles

From: Brian Lyles <brianmlyles@gmail.com>

When `git-am` got its own `--empty` option in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09), `stop` was used
instead of `ask`. `stop` is a more accurate term for describing what
really happens, and consistency is good. This commit updates
`git-rebase` to also use `stop`, while keeping `ask` as a deprecated
synonym.

In a future commit, we'll be adding a new `--empty` option for
`git-cherry-pick` as well, making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt |  8 +++++---
 builtin/rebase.c             | 12 ++++++------
 t/t3424-rebase-empty.sh      | 17 ++++++++++++++---
 3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 3ee85f6d86..fe74d0c367 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,7 +289,7 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(drop|keep|ask)::
+--empty=(drop|keep|stop)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
@@ -300,12 +300,14 @@ See also INCOMPATIBLE OPTIONS below.
 	The empty commit will be dropped. This is the default behavior.
 `keep`;;
 	The empty commit will be kept.
-`ask`;;
+`stop`;;
 	The rebase will halt when the empty commit is applied, allowing you to
 	choose whether to drop it, edit files more, or just commit the empty
 	changes. This option is implied when `--interactive` is specified.
 	Other options, like `--exec`, will use the default of drop unless
 	`-i`/`--interactive` is explicitly specified.
+`ask`;;
+	A deprecated synonym of `stop`.
 --
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
@@ -702,7 +704,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(drop|keep|ask)` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 043c65dccd..1fb9d8263d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -62,7 +62,7 @@ enum empty_type {
 	EMPTY_UNSPECIFIED = -1,
 	EMPTY_DROP,
 	EMPTY_KEEP,
-	EMPTY_ASK
+	EMPTY_STOP
 };
 
 enum action {
@@ -963,10 +963,10 @@ static enum empty_type parse_empty_value(const char *value)
 		return EMPTY_DROP;
 	else if (!strcasecmp(value, "keep"))
 		return EMPTY_KEEP;
-	else if (!strcasecmp(value, "ask"))
-		return EMPTY_ASK;
+	else if (!strcasecmp(value, "stop") || !strcasecmp(value, "ask"))
+		return EMPTY_STOP;
 
-	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
 }
 
 static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1145,7 +1145,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				 "instead of ignoring them"),
 			      1, PARSE_OPT_HIDDEN),
 		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
+		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
 			       N_("how to handle commits that become empty"),
 			       PARSE_OPT_NONEG, parse_opt_empty),
 		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1562,7 +1562,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (options.empty == EMPTY_UNSPECIFIED) {
 		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-			options.empty = EMPTY_ASK;
+			options.empty = EMPTY_STOP;
 		else if (options.exec.nr > 0)
 			options.empty = EMPTY_KEEP;
 		else
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a0af..e3ddec88a2 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --merge --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --interactive --empty=ask upstream &&
+	test_must_fail git rebase --interactive --empty=stop upstream &&
 
 	git rebase --skip &&
 
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --interactive upstream &&
 
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
  2024-01-19  5:59 ` [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am` brianmlyles
  2024-01-19  5:59 ` [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop` brianmlyles
@ 2024-01-19  5:59 ` brianmlyles
  2024-01-20 20:24   ` Kristoffer Haugsbakk
  2024-01-23 14:25   ` Phillip Wood
  2024-01-20 21:38 ` [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options Kristoffer Haugsbakk
                   ` (35 subsequent siblings)
  38 siblings, 2 replies; 118+ messages in thread
From: brianmlyles @ 2024-01-19  5:59 UTC (permalink / raw)
  To: git; +Cc: me, newren, Brian Lyles

From: Brian Lyles <brianmlyles@gmail.com>

As with `git-rebase` and `git-am`, `git-cherry-pick` can result in a
commit being made redundant if the content from the picked commit is
already present in the target history. However, `git-cherry-pick` does
not have the same options available that `git-rebase` and `git-am` have.

There are three things that can be done with these redundant commits:
drop them, keep them, or have the cherry-pick stop and wait for the user
to take an action. `git-rebase` has the `--empty` option added in commit
e98c4269c8 (rebase (interactive-backend): fix handling of commits that
become empty, 2020-02-15), which handles all three of these scenarios.
Similarly, `git-am` got its own `--empty` in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09).

`git-cherry-pick`, on the other hand, only supports two of the three
possiblities: Keep the redundant commits via `--keep-redundant-commits`,
or have the cherry-pick fail by not specifying that option. There is no
way to automatically drop redundant commits.

In order to bring `git-cherry-pick` more in-line with `git-rebase` and
`git-am`, this commit adds an `--empty` option to `git-cherry-pick`. It
has the same three options (keep, drop, and stop), and largely behaves
the same. The notable difference is that for `git-cherry-pick`, the
default will be `stop`, which maintains the current behavior when the
option is not specified.

The `--keep-redundant-commits` option will be documented as a deprecated
synonym of `--empty=keep`, and will be supported for backwards
compatibility for the time being.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-cherry-pick.txt | 28 ++++++++++++++++++-------
 builtin/revert.c                  | 35 ++++++++++++++++++++++++++++++-
 sequencer.c                       |  6 ++++++
 t/t3505-cherry-pick-empty.sh      | 26 ++++++++++++++++++++++-
 4 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 806295a730..8c20a10d4b 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -132,23 +132,37 @@ effect to your index in a row.
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
 	previous commit will cause the cherry-pick to fail.  To force the
-	inclusion of those commits use `--keep-redundant-commits`.
+	inclusion of those commits use `--empty=keep`.
 
 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
 	This option overrides that behavior, allowing commits with empty
 	messages to be cherry picked.
 
---keep-redundant-commits::
-	If a commit being cherry picked duplicates a commit already in the
-	current history, it will become empty.  By default these
-	redundant commits cause `cherry-pick` to stop so the user can
-	examine the commit. This option overrides that behavior and
-	creates an empty commit object. Note that use of this option only
+--empty=(stop|drop|keep)::
+	How to handle commits being cherry-picked that are redundant with
+	changes already in the current history.
++
+-- 
+`stop`;;
+	The cherry-pick will stop when the empty commit is applied, allowing
+	you to examine the commit. This is the default behavior.
+`drop`;;
+	The empty commit will be dropped.
+`keep`;;
+	The empty commit will be kept. Note that use of this option only
 	results in an empty commit when the commit was not initially empty,
 	but rather became empty due to a previous commit. Commits that were
 	initially empty will cause the cherry-pick to fail. To force the
 	inclusion of those commits use `--allow-empty`.
+--
++
+Note that commits which start empty will cause the cherry-pick to fail (unless
+`--allow-empty` is specified).
++
+
+--keep-redundant-commits::
+	Deprecated synonym for `--empty=keep`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index b2cfde7a87..1491c45e26 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -45,6 +45,30 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
 	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
+enum empty_action {
+	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of a cherry-pick */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "stop"))
+		*opt_value = STOP_ON_EMPTY_COMMIT;
+	else if (!strcmp(arg, "drop"))
+		*opt_value = DROP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "keep"))
+		*opt_value = KEEP_EMPTY_COMMIT;
+	else
+		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+	return 0;
+}
+
 static int option_parse_m(const struct option *opt,
 			  const char *arg, int unset)
 {
@@ -87,6 +111,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	const char *cleanup_arg = NULL;
+	enum empty_action empty_opt;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -116,7 +141,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+				       N_("how to handle commits that become empty"),
+				       PARSE_OPT_NONEG, parse_opt_empty),
 			OPT_END(),
 		};
 		options = parse_options_concat(options, cp_extra);
@@ -136,6 +164,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
+	if (opts->action == REPLAY_PICK) {
+		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+	}
+
 	if (cleanup_arg) {
 		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
 		opts->explicit_cleanup = 1;
diff --git a/sequencer.c b/sequencer.c
index 582bde8d46..c49c27c795 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2934,6 +2934,9 @@ static int populate_opts_cb(const char *key, const char *value,
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+	else if (!strcmp(key, "options.drop-redundant-commits"))
+		opts->drop_redundant_commits =
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3478,6 +3481,9 @@ static int save_opts(struct replay_opts *opts)
 	if (opts->allow_empty_message)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.allow-empty-message", "true");
+	if (opts->drop_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.drop-redundant-commits", "true");
 	if (opts->keep_redundant_commits)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.keep-redundant-commits", "true");
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 6adfd25351..ae0cf7886a 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -89,7 +89,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
 	git commit -m "add file2 on the side"
 '
 
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
 	git reset --hard &&
 	git checkout fork^0 &&
 	test_must_fail git cherry-pick main
@@ -104,4 +104,28 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick a no-op with --empty=ask' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick --empty=ask main
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=drop main &&
+	git show -s --format=%s >actual &&
+	echo "add file2 on the side" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=keep main &&
+	git show -s --format=%s >actual &&
+	echo "add file2 on main" >expect &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling brianmlyles
@ 2024-01-20 20:24   ` Kristoffer Haugsbakk
  2024-01-21 18:28     ` Brian Lyles
  2024-01-23 14:25   ` Phillip Wood
  1 sibling, 1 reply; 118+ messages in thread
From: Kristoffer Haugsbakk @ 2024-01-20 20:24 UTC (permalink / raw)
  To: brianmlyles; +Cc: Taylor Blau, Elijah Newren, git

Hi

On Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
> ---keep-redundant-commits::
> -	If a commit being cherry picked duplicates a commit already in the
> -	current history, it will become empty.  By default these
> -	redundant commits cause `cherry-pick` to stop so the user can
> -	examine the commit. This option overrides that behavior and
> -	creates an empty commit object. Note that use of this option only
> +--empty=(stop|drop|keep)::
> +	How to handle commits being cherry-picked that are redundant with
> +	changes already in the current history.
> ++
> +-- 

Trailing whitespace on this line.

-- 
Kristoffer Haugsbakk

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (2 preceding siblings ...)
  2024-01-19  5:59 ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling brianmlyles
@ 2024-01-20 21:38 ` Kristoffer Haugsbakk
  2024-01-21 18:19   ` Brian Lyles
  2024-01-23 14:23 ` Phillip Wood
                   ` (34 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Kristoffer Haugsbakk @ 2024-01-20 21:38 UTC (permalink / raw)
  To: brianmlyles; +Cc: Taylor Blau, Elijah Newren, git

Initial discussion (proposal): https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/

On Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
>
> Previously, a consumer of the sequencer that wishes to take advantage of
> either the `keep_redundant_commits` or `drop_redundant_commits` feature
> must also specify `allow_empty`.

Previously to this change? It is preferred to describe what the code
currently does without this change in the present tense.[1] The change
itself uses the imperative mood.[2]

† 1: SubmittingPatches, “The problem statement that describes the status
    quo …”
† 2: SubmittingPatches, “Describe your changes in imperative mood […] as
    if you are giving orders to the codebase to change its behavior.”

> The only consumer of `drop_redundant_commits` is `git-rebase`, which
> already allows empty commits by default and simply always enables
> `allow_empty`. `keep_redundant_commits` was also consumed by
> `git-cherry-pick`, which had to specify `allow-empty` when
> `keep_redundant_commits` was specified in order for the sequencer's
> `allow_empty()` to actually respect `keep_redundant_commits`.
>
> The latter is an interesting case: As noted in the docs, this means that
> `--keep-redundant-commits` implies `--allow-empty`, despite the two
> having distinct, non-overlapping meanings:

Huh. I’m used to the git-rebase(1) behavior and I definitely would have
just assumed that git-cherry-pick(1) behaves the same. :) Nice catch.

> This implication of `--allow-empty` therefore seems incorrect: One
> should be able to keep a commit that becomes empty without also being
> forced to pick commits that start as empty. However, today, the
> following series of commands would result in both the commit that became
> empty and the commit that started empty being picked despite only
> `--keep-redundant-commits` being specified:

Nice description of the current problem. All of it.

> In a future commit, an `--empty` option will be added to
> `git-cherry-pick`, meaning that `drop_redundant_commits` will be
> available in that command. For that to be possible with the current
> implementation of the sequencer's `allow_empty()`, `git-cherry-pick`
> would need to specify `allow_empty` with `drop_redundant_commits` as
> well, which is an even less intuitive implication of `--allow-empty`: in
> order to prevent redundant commits automatically, initially-empty
> commits would need to be kept automatically.
>
> Instead, this commit rewrites the `allow_empty()` logic to remove the
> over-arching requirement that `allow_empty` be specified in order to
> reach any of the keep/drop behaviors. Only if the commit was originally
> empty will `allow_empty` have an effect.

In general, phrases like “this commit <verb>” or “this patch <verb>” can
be rewritten to the “commanding” style (see [2]).[3] But here you’re
starting a new paragraph after having talked about a future commit, so
using the commanding style might be stylistically difficult to pull off
without breaking the flow of the text.

And “this [commit][patch] <verb>” seems to be used with some regularity in
any case.

🔗 3: https://lore.kernel.org/git/xmqqedeqienh.fsf@gitster.g/

> Disclaimer: This is my first contribution to the git project, and thus
> my first attempt at submitting a patch via `git-send-email`. It is also
> the first time I've touched worked in C in over a decade, and I really
> didn't work with it much before that either. I welcome any and all
> feedback on what I may have gotten wrong regarding the patch submission
> process, the code changes, or my commit messages.

This part (after the commit message) looks like the cover letter for the
series (the four patches). `SubmittingPatches` recommends submitting that
in a dedicated email message (for series that have more than one
patch). Maybe this cover letter style is just an alternative that is
equally accepted. But most series use a separate cover letter message for
what it’s worth.

Cheers

-- 
Kristoffer Haugsbakk

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-20 21:38 ` [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options Kristoffer Haugsbakk
@ 2024-01-21 18:19   ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-01-21 18:19 UTC (permalink / raw)
  To: Kristoffer Haugsbakk; +Cc: Taylor Blau, Elijah Newren, git

On Sat, Jan 20, 2024 at 3:38 PM Kristoffer Haugsbakk
<code@khaugsbakk.name> wrote:

Hi Kristoffer,
Thank you for taking some time to review my patch.

> Initial discussion (proposal): https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/

I ought to have included this in a cover letter -- thanks for linking it
here, and I will include this with v2.

> On Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:
> > From: Brian Lyles <brianmlyles@gmail.com>
> >
> > Previously, a consumer of the sequencer that wishes to take advantage of
> > either the `keep_redundant_commits` or `drop_redundant_commits` feature
> > must also specify `allow_empty`.
>
> Previously to this change? It is preferred to describe what the code
> currently does without this change in the present tense.[1] The change
> itself uses the imperative mood.[2]
>
> † 1: SubmittingPatches, “The problem statement that describes the status
>     quo …”
> † 2: SubmittingPatches, “Describe your changes in imperative mood […] as
>     if you are giving orders to the codebase to change its behavior.”

I appreciate the stylistic feedback here. I will tweak this for v2 of
the patch.

> > In a future commit, an `--empty` option will be added to
> > `git-cherry-pick`, meaning that `drop_redundant_commits` will be
> > available in that command. For that to be possible with the current
> > implementation of the sequencer's `allow_empty()`, `git-cherry-pick`
> > would need to specify `allow_empty` with `drop_redundant_commits` as
> > well, which is an even less intuitive implication of `--allow-empty`: in
> > order to prevent redundant commits automatically, initially-empty
> > commits would need to be kept automatically.
> >
> > Instead, this commit rewrites the `allow_empty()` logic to remove the
> > over-arching requirement that `allow_empty` be specified in order to
> > reach any of the keep/drop behaviors. Only if the commit was originally
> > empty will `allow_empty` have an effect.
>
> In general, phrases like “this commit <verb>” or “this patch <verb>” can
> be rewritten to the “commanding” style (see [2]).[3] But here you’re
> starting a new paragraph after having talked about a future commit, so
> using the commanding style might be stylistically difficult to pull off
> without breaking the flow of the text.
>
> And “this [commit][patch] <verb>” seems to be used with some regularity in
> any case.
>
> 🔗 3: https://lore.kernel.org/git/xmqqedeqienh.fsf@gitster.g/

I think I should be able to adjust this to a more commanding style
without breaking the flow. I'll give that a shot in v2 as well.

> > Disclaimer: This is my first contribution to the git project, and thus
> > my first attempt at submitting a patch via `git-send-email`. It is also
> > the first time I've touched worked in C in over a decade, and I really
> > didn't work with it much before that either. I welcome any and all
> > feedback on what I may have gotten wrong regarding the patch submission
> > process, the code changes, or my commit messages.
>
> This part (after the commit message) looks like the cover letter for the
> series (the four patches). `SubmittingPatches` recommends submitting that
> in a dedicated email message (for series that have more than one
> patch). Maybe this cover letter style is just an alternative that is
> equally accepted. But most series use a separate cover letter message for
> what it’s worth.

That makes sense -- when I send v2, I can pull this part out into a
separate cover letter email.

I will hold off on sending out v2 for a few days to give others a chance
to weigh in on this series.

Thanks again,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-20 20:24   ` Kristoffer Haugsbakk
@ 2024-01-21 18:28     ` Brian Lyles
  2024-01-21 22:05       ` Kristoffer Haugsbakk
                         ` (3 more replies)
  0 siblings, 4 replies; 118+ messages in thread
From: Brian Lyles @ 2024-01-21 18:28 UTC (permalink / raw)
  To: Kristoffer Haugsbakk; +Cc: Taylor Blau, Elijah Newren, git

On Sat, Jan 20, 2024 at 2:24 PM Kristoffer Haugsbakk
<code@khaugsbakk.name> wrote:

> On Fri, Jan 19, 2024, at 06:59, brianmlyles@gmail.com wrote:
> > From: Brian Lyles <brianmlyles@gmail.com>
> > ---keep-redundant-commits::
> > -     If a commit being cherry picked duplicates a commit already in the
> > -     current history, it will become empty.  By default these
> > -     redundant commits cause `cherry-pick` to stop so the user can
> > -     examine the commit. This option overrides that behavior and
> > -     creates an empty commit object. Note that use of this option only
> > +--empty=(stop|drop|keep)::
> > +     How to handle commits being cherry-picked that are redundant with
> > +     changes already in the current history.
> > ++
> > +--
>
> Trailing whitespace on this line.

Thank you -- This will be corrected with v2.

Is the sample pre-commit hook the ideal way to prevent this in the
future? Or is there some config I could set globally to enforce this
across repositories? I was having a little trouble finding a good way to
accomplish this globally.

Thanks,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-21 18:28     ` Brian Lyles
@ 2024-01-21 22:05       ` Kristoffer Haugsbakk
  2024-01-21 22:41       ` Junio C Hamano
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 118+ messages in thread
From: Kristoffer Haugsbakk @ 2024-01-21 22:05 UTC (permalink / raw)
  To: brianmlyles; +Cc: Taylor Blau, Elijah Newren, git

On Sun, Jan 21, 2024, at 19:28, Brian Lyles wrote:
> Is the sample pre-commit hook the ideal way to prevent this in the
> future? Or is there some config I could set globally to enforce this
> across repositories? I was having a little trouble finding a good way to
> accomplish this globally.

I don’t know of any global config. So a pre-commit hook is probably the
safest bet. Personally I set all my editors to remove trailing space and
they very seldom mess it up. :)

-- 
Kristoffer Haugsbakk

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-21 18:28     ` Brian Lyles
  2024-01-21 22:05       ` Kristoffer Haugsbakk
@ 2024-01-21 22:41       ` Junio C Hamano
  2024-01-22 10:40       ` Phillip Wood
  2024-01-22 20:55       ` Kristoffer Haugsbakk
  3 siblings, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-01-21 22:41 UTC (permalink / raw)
  To: Brian Lyles; +Cc: Kristoffer Haugsbakk, Taylor Blau, Elijah Newren, git

Brian Lyles <brianmlyles@gmail.com> writes:

>> Trailing whitespace on this line.
>
> Thank you -- This will be corrected with v2.
>
> Is the sample pre-commit hook the ideal way to prevent this in the
> future?

The example we ship in templates/hooks--pre-commit.sample in our
source has such a check at the end of it.


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-21 18:28     ` Brian Lyles
  2024-01-21 22:05       ` Kristoffer Haugsbakk
  2024-01-21 22:41       ` Junio C Hamano
@ 2024-01-22 10:40       ` Phillip Wood
  2024-01-22 20:55       ` Kristoffer Haugsbakk
  3 siblings, 0 replies; 118+ messages in thread
From: Phillip Wood @ 2024-01-22 10:40 UTC (permalink / raw)
  To: Brian Lyles, Kristoffer Haugsbakk; +Cc: Taylor Blau, Elijah Newren, git

Hi Brian

On 21/01/2024 18:28, Brian Lyles wrote:
> Is the sample pre-commit hook the ideal way to prevent this in the
> future? Or is there some config I could set globally to enforce this
> across repositories? I was having a little trouble finding a good way to
> accomplish this globally.

If you want to run the same hooks in all your repositories then you can 
run 'git config --global core.hooksPath <my-hooks-path>' and git will 
look for hooks in 'my-hooks-path' instead of '.git/hooks'. It makes it 
tricky to run different linters in different projects though.

I'll try and take a proper look at these patches in the next couple of days.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-21 18:28     ` Brian Lyles
                         ` (2 preceding siblings ...)
  2024-01-22 10:40       ` Phillip Wood
@ 2024-01-22 20:55       ` Kristoffer Haugsbakk
  2024-01-23  5:23         ` Brian Lyles
  3 siblings, 1 reply; 118+ messages in thread
From: Kristoffer Haugsbakk @ 2024-01-22 20:55 UTC (permalink / raw)
  To: brianmlyles; +Cc: Taylor Blau, Elijah Newren, git

On Sun, Jan 21, 2024, at 19:28, Brian Lyles wrote:
> Thank you -- This will be corrected with v2.
>
> Is the sample pre-commit hook the ideal way to prevent this in the
> future? Or is there some config I could set globally to enforce this
> across repositories? I was having a little trouble finding a good way to
> accomplish this globally.
>
> Thanks,
> Brian Lyles

Oh, and this thread reminded me https://lore.kernel.org/git/xmqqle8hrtcs.fsf@gitster.g/T/#t

that editorconfig[1] has this option:

```
trim_trailing_whitespace = true
```

So I guess that should be enough for all editors that respect this
config (although I haven’t tested it).

🔗 1: https://editorconfig.org/

-- 
Kristoffer Haugsbakk

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-22 20:55       ` Kristoffer Haugsbakk
@ 2024-01-23  5:23         ` Brian Lyles
  2024-01-23  7:11           ` Kristoffer Haugsbakk
  2024-01-23 17:32           ` Junio C Hamano
  0 siblings, 2 replies; 118+ messages in thread
From: Brian Lyles @ 2024-01-23  5:23 UTC (permalink / raw)
  To: Kristoffer Haugsbakk; +Cc: Taylor Blau, Elijah Newren, git

On Sun, Jan 21, 2024 at 4:05 PM Kristoffer Haugsbakk
<code@khaugsbakk.name> wrote:

> I don’t know of any global config. So a pre-commit hook is probably the
> safest bet. Personally I set all my editors to remove trailing space and
> they very seldom mess it up. :)

Fair point -- Apparently this was a gap in my nvim config.
Easily added.

On Mon, Jan 22, 2024 at 2:55 PM Kristoffer Haugsbakk
<code@khaugsbakk.name> wrote:

> Oh, and this thread reminded me https://lore.kernel.org/git/xmqqle8hrtcs.fsf@gitster.g/T/#t
>
> that editorconfig[1] has this option:
>
> ```
> trim_trailing_whitespace = true
> ```
>
> So I guess that should be enough for all editors that respect this
> config (although I haven’t tested it).
>
> 🔗 1: https://editorconfig.org/

Is there a good reason that this should not just be added to the
`.editorconfig` in this repository? Would a patch for this be welcome?

I do see that there are ~130 files with trailing whitespace in maint
today, though I suspect that most of those are not intentional.

Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-23  5:23         ` Brian Lyles
@ 2024-01-23  7:11           ` Kristoffer Haugsbakk
  2024-01-23 17:32           ` Junio C Hamano
  1 sibling, 0 replies; 118+ messages in thread
From: Kristoffer Haugsbakk @ 2024-01-23  7:11 UTC (permalink / raw)
  To: brianmlyles; +Cc: Taylor Blau, Elijah Newren, git

On Tue, Jan 23, 2024, at 06:23, Brian Lyles wrote:
>> Oh, and this thread reminded me https://lore.kernel.org/git/xmqqle8hrtcs.fsf@gitster.g/T/#t
>>
>> that editorconfig[1] has this option:
>>
>> ```
>> trim_trailing_whitespace = true
>> ```
>> […]
>
> Is there a good reason that this should not just be added to the
> `.editorconfig` in this repository? Would a patch for this be welcome?

I was thinking the same thing when I saw that other thread. It doesn’t
hurt to try.

I was also curious about some subtleties like: does it dictate enforcing
this for all the lines of a touched files or only the modified ones?
Because the latter is much more useful.

-- 
Kristoffer Haugsbakk

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (3 preceding siblings ...)
  2024-01-20 21:38 ` [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options Kristoffer Haugsbakk
@ 2024-01-23 14:23 ` Phillip Wood
  2024-01-23 18:18   ` Junio C Hamano
  2024-01-24 11:01   ` Phillip Wood
  2024-01-24 11:01 ` Phillip Wood
                   ` (33 subsequent siblings)
  38 siblings, 2 replies; 118+ messages in thread
From: Phillip Wood @ 2024-01-23 14:23 UTC (permalink / raw)
  To: brianmlyles, git; +Cc: me, newren

Hi Brian

Let me start by saying that overall I'm impressed with the quality of 
this submission. I've left quite a few comments but for a first patch 
series it is very good.

On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
> 
> Previously, a consumer of the sequencer that wishes to take advantage of
> either the `keep_redundant_commits` or `drop_redundant_commits` feature
> must also specify `allow_empty`.
> 
> The only consumer of `drop_redundant_commits` is `git-rebase`, which
> already allows empty commits by default and simply always enables
> `allow_empty`. `keep_redundant_commits` was also consumed by
> `git-cherry-pick`, which had to specify `allow-empty` when
> `keep_redundant_commits` was specified in order for the sequencer's
> `allow_empty()` to actually respect `keep_redundant_commits`.

I think it might be more persuasive to start the commit message by 
explaining what user visible change you're trying to make and why rather 
than concentrating on the implementation details.

> The latter is an interesting case: As noted in the docs, this means that
> `--keep-redundant-commits` implies `--allow-empty`, despite the two
> having distinct, non-overlapping meanings:
> 
> - `allow_empty` refers specifically to commits which start empty, as
>    indicated by the documentation for `--allow-empty` within
>    `git-cherry-pick`:
> 
>    "Note also, that use of this option only keeps commits that were
>    initially empty (i.e. the commit recorded the same tree as its
>    parent). Commits which are made empty due to a previous commit are
>    dropped. To force the inclusion of those commits use
>    --keep-redundant-commits."
> 
> - `keep_redundant_commits` refers specifically to commits that do not
>    start empty, but become empty due to the content already existing in
>    the target history. This is indicated by the documentation for
>    `--keep-redundant-commits` within `git-cherry-pick`:
> 
>    "If a commit being cherry picked duplicates a commit already in the
>    current history, it will become empty. By default these redundant
>    commits cause cherry-pick to stop so the user can examine the commit.
>    This option overrides that behavior and creates an empty commit
>    object. Implies --allow-empty."
> 
> This implication of `--allow-empty` therefore seems incorrect: One
> should be able to keep a commit that becomes empty without also being
> forced to pick commits that start as empty.

Do you have a practical example of where you want to keep the commits 
that become empty but not the ones that start empty? I agree there is a 
distinction but I think the common case is that the user wants to keep 
both types of empty commit or none. I'm not against giving the user the 
option to keep one or the other if it is useful but I'm wary of changing 
the default.

> However, today, the
> following series of commands would result in both the commit that became
> empty and the commit that started empty being picked despite only
> `--keep-redundant-commits` being specified:
> 
>      git init
>      echo "a" >test
>      git add test
>      git commit -m "Initial commit"
>      echo "b" >test
>      git commit -am "a -> b"
>      git commit --allow-empty -m "empty"
>      git cherry-pick --keep-redundant-commits HEAD^ HEAD
> 
> The same cherry-pick with `--allow-empty` would fail on the redundant
> commit, and with neither option would fail on the empty commit.
> 
> In a future commit, an `--empty` option will be added to
> `git-cherry-pick`, meaning that `drop_redundant_commits` will be
> available in that command. For that to be possible with the current
> implementation of the sequencer's `allow_empty()`, `git-cherry-pick`
> would need to specify `allow_empty` with `drop_redundant_commits` as
> well, which is an even less intuitive implication of `--allow-empty`: in
> order to prevent redundant commits automatically, initially-empty
> commits would need to be kept automatically.
>
> Instead, this commit rewrites the `allow_empty()` logic to remove the
> over-arching requirement that `allow_empty` be specified in order to
> reach any of the keep/drop behaviors. Only if the commit was originally
> empty will `allow_empty` have an effect.

rebase always sets "opts->allow_empty = 1" in 
builtin/rebase.c:get_replay_opts() and if the user passes 
--no-keep-empty drops commits that start empty from the list of commits 
to be picked. This is slightly confusing but is more efficient as we 
don't do waste time trying to pick a commit we're going to drop. Can we 
do something similar for "git cherry-pick"? When cherry-picking a 
sequence of commits I think it should just work because the code is 
shared with rebase, for a single commit we'd need to add a test to see 
if it is empty in single_pick() before calling pick_commits().

> For some amount of backwards compatibility with the existing code and
> tests, I have opted to preserve the behavior of returning 0 when:
> 
> - `allow_empty` is specified, and
> - either `is_index_unchanged` or `is_original_commit_empty` indicates an
>    error

I'm not sure that is a good idea as it is hiding an error that we didn't 
hit before because we returned early.

> This is primarily out of caution -- I am not positive what downstream
> impacts this might have.
> 
> Note that this commit is a breaking change: `--keep-redundant-commits`
> will no longer imply `--allow-empty`. It would be possible to maintain
> the current behavior of `--keep-redundant-commits` implying
> `--allow-empty` if it were needed to avoid a breaking change, but I
> believe that decoupling them entirely is the correct behavior.

Thank you for being clear about the change in behavior, as I said above 
I'm wary of changing the default unless there is a compelling reason but 
I'm happy to support

     git cherry-pick --keep-redundant-commits --no-allow-empty

if it is needed.

> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> ---
> 
> Disclaimer: This is my first contribution to the git project, and thus
> my first attempt at submitting a patch via `git-send-email`. It is also
> the first time I've touched worked in C in over a decade, and I really
> didn't work with it much before that either. I welcome any and all
> feedback on what I may have gotten wrong regarding the patch submission
> process, the code changes, or my commit messages.

As others have mentioned I think it would be useful to have a 
cover-letter where we can discuss the aim of the patch series 
independently of the individual patches.

> This is the first in a series of commits that aims to introduce an
> `--empty` option to `git-cherry-pick` that provides the same flexibility
> as the `--empty` options for `git-rebase` and `git-am`, as well as
> improve the consistency in the values and documentation for this option
> across the three commands.

I think that is a good aim

> The main thing that may be controversial with this particular commit is
> that I am proposing a breaking change. As described in the above
> message, I do not think that it makes sense to tie `--allow-empty` and
> `--keep-redundant-commits` together since they appear to be intended to
> work with different types of empty commits. That being said, if it is
> deemed unacceptable to make this breaking change, we can consider an
> alternative approach where we maintain the behavior of
> `--keep-redundant-commits` implying `--allow-empty`, while preventing
> the need for the future `--empty=drop` to have that same implication.

As I said above I think it would be worth looking at what "git rebase" 
does to see if we can do the same thing for "git cherry-pick".

 > [...]> +test_expect_success 'cherry pick an empty non-ff commit with 
--keep-redundant-commits' '
> +	git checkout main &&
> +	test_must_fail git cherry-pick --keep-redundant-commits empty-change-branch

When using test_must_fail it is a good idea to check the error message 
to make sure that it's failing for the reason we expect (see patch 4).

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`
  2024-01-19  5:59 ` [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am` brianmlyles
@ 2024-01-23 14:24   ` Phillip Wood
  2024-01-27 21:22     ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-01-23 14:24 UTC (permalink / raw)
  To: brianmlyles, git; +Cc: me, newren

Hi Brian

On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
> 
> Both of these pages document very similar `--empty` options, but with
> different styles. This commit aims to make them more consistent.

I think that's reasonable though the options they are worded as doing 
different things. For "am" it talks about the patch being empty - i.e. a 
patch of an empty commit whereas for "rebase" the option applies to 
non-empty commits that become empty. What does "am" do if you try to 
apply a patch whose changes are already present?

If you're aiming for consistency then it would be worth listing the 
possible values in the same order for each command.


> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index b4526ca246..3ee85f6d86 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -293,13 +293,20 @@ See also INCOMPATIBLE OPTIONS below.
>   	How to handle commits that are not empty to start and are not
>   	clean cherry-picks of any upstream commit, but which become
>   	empty after rebasing (because they contain a subset of already
> -	upstream changes).  With drop (the default), commits that
> -	become empty are dropped.  With keep, such commits are kept.
> -	With ask (implied by `--interactive`), the rebase will halt when
> -	an empty commit is applied allowing you to choose whether to
> -	drop it, edit files more, or just commit the empty changes.
> +	upstream changes):
> ++
> +--
> +`drop`;;
> +	The empty commit will be dropped. This is the default behavior.

I think it would be clearer to say "The commit" - I found "The empty 
commit" confusing as the commit that is being picked isn't empty.

> +`keep`;;
> +	The empty commit will be kept.
> +`ask`;;
> +	The rebase will halt when the empty commit is applied, allowing you to
> +	choose whether to drop it, edit files more, or just commit the empty
> +	changes. This option is implied when `--interactive` is specified.
>   	Other options, like `--exec`, will use the default of drop unless
>   	`-i`/`--interactive` is explicitly specified.

Thanks for adding a bit more detail about the default, however it looks 
to me like we keep commits that become empty when --exec is specified

	if (options.empty == EMPTY_UNSPECIFIED) {
		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
			options.empty = EMPTY_STOP;
		else if (options.exec.nr > 0)
			options.empty = EMPTY_KEEP;
		else
			options.empty = EMPTY_DROP;
	}

Off the top of my head I'm not sure why or if that is a good idea.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`
  2024-01-19  5:59 ` [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop` brianmlyles
@ 2024-01-23 14:24   ` Phillip Wood
  2024-01-27 21:49     ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-01-23 14:24 UTC (permalink / raw)
  To: brianmlyles, git; +Cc: me, newren

Hi Brian

On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
> 
> When `git-am` got its own `--empty` option in 7c096b8d61 (am: support
> --empty=<option> to handle empty patches, 2021-12-09), `stop` was used
> instead of `ask`. `stop` is a more accurate term for describing what
> really happens,

I can see your reasoning but I think of stopping as git's way of asking 
what to do so I'm not sure if "stop" is better than "ask". I don't know 
how we ended up with two different terms - the prior art is "ask" so 
maybe we should change "am --empty" instead. Lets see what others think.

It would be helpful to mention the tests in the commit message - we end 
up with a mixture of "--empty=ask" and "--empty=stop" I assume that is 
by design

> and consistency is good. This commit updates
> `git-rebase` to also use `stop`, while keeping `ask` as a deprecated
> synonym.

If we're deprecating "ask" do we want to print a warning recommending 
"stop" instead?

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling brianmlyles
  2024-01-20 20:24   ` Kristoffer Haugsbakk
@ 2024-01-23 14:25   ` Phillip Wood
  2024-01-23 18:01     ` Junio C Hamano
  2024-01-27 23:56     ` Brian Lyles
  1 sibling, 2 replies; 118+ messages in thread
From: Phillip Wood @ 2024-01-23 14:25 UTC (permalink / raw)
  To: brianmlyles, git; +Cc: me, newren

Hi Brian


On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
> 
> As with `git-rebase` and `git-am`, `git-cherry-pick` can result in a
> commit being made redundant if the content from the picked commit is
> already present in the target history. However, `git-cherry-pick` does
> not have the same options available that `git-rebase` and `git-am` have.
> 
> There are three things that can be done with these redundant commits:
> drop them, keep them, or have the cherry-pick stop and wait for the user
> to take an action. `git-rebase` has the `--empty` option added in commit
> e98c4269c8 (rebase (interactive-backend): fix handling of commits that
> become empty, 2020-02-15), which handles all three of these scenarios.
> Similarly, `git-am` got its own `--empty` in 7c096b8d61 (am: support
> --empty=<option> to handle empty patches, 2021-12-09).
> 
> `git-cherry-pick`, on the other hand, only supports two of the three
> possiblities: Keep the redundant commits via `--keep-redundant-commits`,
> or have the cherry-pick fail by not specifying that option. There is no
> way to automatically drop redundant commits.
> 
> In order to bring `git-cherry-pick` more in-line with `git-rebase` and
> `git-am`, this commit adds an `--empty` option to `git-cherry-pick`. It
> has the same three options (keep, drop, and stop), and largely behaves
> the same. The notable difference is that for `git-cherry-pick`, the
> default will be `stop`, which maintains the current behavior when the
> option is not specified.

Thanks for the well explained commit message

> The `--keep-redundant-commits` option will be documented as a deprecated
> synonym of `--empty=keep`, and will be supported for backwards
> compatibility for the time being.

I'm not sure if we need to deprecate it as in "it will be removed in the 
future" or just reduce it prominence in favor of --empty

> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> ---
>   Documentation/git-cherry-pick.txt | 28 ++++++++++++++++++-------
>   builtin/revert.c                  | 35 ++++++++++++++++++++++++++++++-
>   sequencer.c                       |  6 ++++++
>   t/t3505-cherry-pick-empty.sh      | 26 ++++++++++++++++++++++-
>   4 files changed, 86 insertions(+), 9 deletions(-)
> 
> diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
> index 806295a730..8c20a10d4b 100644
> --- a/Documentation/git-cherry-pick.txt
> +++ b/Documentation/git-cherry-pick.txt
> @@ -132,23 +132,37 @@ effect to your index in a row.
>   	keeps commits that were initially empty (i.e. the commit recorded the
>   	same tree as its parent).  Commits which are made empty due to a
>   	previous commit will cause the cherry-pick to fail.  To force the
> -	inclusion of those commits use `--keep-redundant-commits`.
> +	inclusion of those commits use `--empty=keep`.
>   
>   --allow-empty-message::
>   	By default, cherry-picking a commit with an empty message will fail.
>   	This option overrides that behavior, allowing commits with empty
>   	messages to be cherry picked.
>   
> ---keep-redundant-commits::
> -	If a commit being cherry picked duplicates a commit already in the
> -	current history, it will become empty.  By default these
> -	redundant commits cause `cherry-pick` to stop so the user can
> -	examine the commit. This option overrides that behavior and
> -	creates an empty commit object. Note that use of this option only
> +--empty=(stop|drop|keep)::
> +	How to handle commits being cherry-picked that are redundant with
> +	changes already in the current history.
> ++
> +--
> +`stop`;;

I'm still on the fence about "stop" vs "ask". I see in your tests you've 
accidentally used "ask" which makes me wonder if that is the more 
familiar term for users who probably use "git rebase" more often than 
"git am".

> +	The cherry-pick will stop when the empty commit is applied, allowing
> +	you to examine the commit. This is the default behavior.
> +`drop`;;
> +	The empty commit will be dropped.
> +`keep`;;
> +	The empty commit will be kept. Note that use of this option only
>   	results in an empty commit when the commit was not initially empty,
>   	but rather became empty due to a previous commit. Commits that were
>   	initially empty will cause the cherry-pick to fail. To force the
>   	inclusion of those commits use `--allow-empty`.
> +--
> ++
> +Note that commits which start empty will cause the cherry-pick to fail (unless
> +`--allow-empty` is specified).
> ++
> +
> +--keep-redundant-commits::
> +	Deprecated synonym for `--empty=keep`.
>   
>   --strategy=<strategy>::
>   	Use the given merge strategy.  Should only be used once.
> diff --git a/builtin/revert.c b/builtin/revert.c
> index b2cfde7a87..1491c45e26 100644
> --- a/builtin/revert.c
> +++ b/builtin/revert.c
> @@ -45,6 +45,30 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
>   	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
>   }
>   
> +enum empty_action {
> +	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of a cherry-pick */
> +	DROP_EMPTY_COMMIT,         /* skip with a notice message */
> +	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
> +};
> +
> +static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
> +{
> +	int *opt_value = opt->value;
> +
> +	BUG_ON_OPT_NEG(unset);
> +
> +	if (!strcmp(arg, "stop"))
> +		*opt_value = STOP_ON_EMPTY_COMMIT;
> +	else if (!strcmp(arg, "drop"))
> +		*opt_value = DROP_EMPTY_COMMIT;
> +	else if (!strcmp(arg, "keep"))
> +		*opt_value = KEEP_EMPTY_COMMIT;
> +	else
> +		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
> +
> +	return 0;
> +}
> +
>   static int option_parse_m(const struct option *opt,
>   			  const char *arg, int unset)
>   {
> @@ -87,6 +111,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
>   	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
>   	const char *me = action_name(opts);
>   	const char *cleanup_arg = NULL;
> +	enum empty_action empty_opt;
>   	int cmd = 0;
>   	struct option base_options[] = {
>   		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
> @@ -116,7 +141,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
>   			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
>   			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
>   			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
> -			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
> +			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
> +			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
> +				       N_("how to handle commits that become empty"),
> +				       PARSE_OPT_NONEG, parse_opt_empty),
>   			OPT_END(),
>   		};
>   		options = parse_options_concat(options, cp_extra);
> @@ -136,6 +164,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
>   	prepare_repo_settings(the_repository);
>   	the_repository->settings.command_requires_full_index = 0;
>   
> +	if (opts->action == REPLAY_PICK) {
> +		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
> +		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
> +	}

The code changes look good but I think we want to update 
verify_opt_compatible() to check for "--empty" being combined with 
"--continue" etc. as well.

>   	if (cleanup_arg) {
>   		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
>   		opts->explicit_cleanup = 1;
> diff --git a/sequencer.c b/sequencer.c
> index 582bde8d46..c49c27c795 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -2934,6 +2934,9 @@ static int populate_opts_cb(const char *key, const char *value,
>   	else if (!strcmp(key, "options.allow-empty-message"))
>   		opts->allow_empty_message =
>   			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
> +	else if (!strcmp(key, "options.drop-redundant-commits"))
> +		opts->drop_redundant_commits =
> +			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
>   	else if (!strcmp(key, "options.keep-redundant-commits"))
>   		opts->keep_redundant_commits =
>   			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
> @@ -3478,6 +3481,9 @@ static int save_opts(struct replay_opts *opts)
>   	if (opts->allow_empty_message)
>   		res |= git_config_set_in_file_gently(opts_file,
>   				"options.allow-empty-message", "true");
> +	if (opts->drop_redundant_commits)
> +		res |= git_config_set_in_file_gently(opts_file,
> +				"options.drop-redundant-commits", "true");

It is good that we're saving the option - it would be good to add a test 
to check that we remember --empty after stopping for a conflict resolution.

>   	if (opts->keep_redundant_commits)
>   		res |= git_config_set_in_file_gently(opts_file,
>   				"options.keep-redundant-commits", "true");
> diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
> index 6adfd25351..ae0cf7886a 100755
> --- a/t/t3505-cherry-pick-empty.sh
> +++ b/t/t3505-cherry-pick-empty.sh
> @@ -89,7 +89,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
>   	git commit -m "add file2 on the side"
>   '
>   
> -test_expect_success 'cherry-pick a no-op without --keep-redundant' '
> +test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
>   	git reset --hard &&
>   	git checkout fork^0 &&
>   	test_must_fail git cherry-pick main
> @@ -104,4 +104,28 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
>   	test_cmp expect actual
>   '
>   
> +test_expect_success 'cherry-pick a no-op with --empty=ask' '
> +	git reset --hard &&
> +	git checkout fork^0 &&
> +	test_must_fail git cherry-pick --empty=ask main

This is an example of why it is a good idea to check the error message 
when using "test_must_fail" as here the test will fail due to a bad 
value passed to "--empty" rather than for the reason we want the test to 
check. It would be good to add a separate test to check that we reject 
invalid "--empty" values.

> +'
> +
> +test_expect_success 'cherry-pick a no-op with --empty=drop' '
> +	git reset --hard &&
> +	git checkout fork^0 &&
> +	git cherry-pick --empty=drop main &&
> +	git show -s --format=%s >actual &&
> +	echo "add file2 on the side" >expect &&
> +	test_cmp expect actual

I think you could simplify this by using test_commit_message

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-23  5:23         ` Brian Lyles
  2024-01-23  7:11           ` Kristoffer Haugsbakk
@ 2024-01-23 17:32           ` Junio C Hamano
  2024-01-23 18:41             ` Subject: [PATCH] CoC: whitespace fix Junio C Hamano
  2024-01-23 18:49             ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling Junio C Hamano
  1 sibling, 2 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-01-23 17:32 UTC (permalink / raw)
  To: Brian Lyles; +Cc: Kristoffer Haugsbakk, Taylor Blau, Elijah Newren, git

Brian Lyles <brianmlyles@gmail.com> writes:

> I do see that there are ~130 files with trailing whitespace in maint
> today, though I suspect that most of those are not intentional.

I got curious and took a look at files that has hits with "lines
that end with SP or HT":

    $ git grep -l -e '[  ]$'

There are some that can be cleaned up, but many of them are
intentional.

For example, CODE_OF_CONDUCT.md has these two (shown with $$$)
I think can be removed without breaking markdown:

    Community Impact Guidelines were inspired by $$$
    [Mozilla's code of conduct enforcement ladder][Mozilla CoC].

    For answers to common questions about this code of conduct, see the FAQ at
    [https://www.contributor-covenant.org/faq][FAQ]. Translations are available $$$
    at [https://www.contributor-covenant.org/translations][translations].

The one in Documentation/user-manual.txt is on borderline.  They
appear in a sample output from a command the user is typing (again,
$$$ shows where the SP at the end of line is):

    diff --git a/init-db.c b/init-db.c
    index 65898fa..b002dc6 100644
    --- a/init-db.c
    +++ b/init-db.c
    @@ -7,7 +7,7 @@
     $$$
     int main(int argc, char **argv)
    ...

As its purpose is to show human readers what they see in their
terminal should _look_ like, we _could_ do without the single space
on these otherwise empty lines, which denotes an empty line that
hasn't changed in the diff output.  But it would no longer match
byte-for-byte with what we are trying to illustrate.

There are many hits from the above grep in t/t[0-9][0-9][0-9][0-9]/
directories; these are canonical/expected output used in tests and
the spaces at the end of these lines are expected.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-23 14:25   ` Phillip Wood
@ 2024-01-23 18:01     ` Junio C Hamano
  2024-01-28  0:07       ` Brian Lyles
  2024-01-27 23:56     ` Brian Lyles
  1 sibling, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-01-23 18:01 UTC (permalink / raw)
  To: Phillip Wood; +Cc: brianmlyles, git, me, newren

Phillip Wood <phillip.wood123@gmail.com> writes:

> Thanks for the well explained commit message

;-)

>> The `--keep-redundant-commits` option will be documented as a deprecated
>> synonym of `--empty=keep`, and will be supported for backwards
>> compatibility for the time being.
>
> I'm not sure if we need to deprecate it as in "it will be removed in
> the future" or just reduce it prominence in favor of --empty

True, especially since --empty=keep is much less descriptive and the
part after "note that ..." below will take a long time before
sticking in readers' brain.

>> +--empty=(stop|drop|keep)::
>> +	How to handle commits being cherry-picked that are redundant with
>> +	changes already in the current history.

It might make it easier to understand if we moved the description in
'keep' that says something about --allow-empty here, as it should
apply to other two choices if I understand correctly.  Let me try:

    This option specifies how a commit that is not originally empty
    but becomes a no-op when cherry-picked due to earlier changes
    already applied or already in the current history.  Regardless
    of this this option, `cherry-pick` will fail on a commit that is
    empty in the original history---see `--allow-empty` to pass them
    intact.

or something.  Then the description of `keep` can become just as
short as other two, e.g. a single sentence "The commit that becomes
empty will be kept".

>> ...
>> +	The cherry-pick will stop when the empty commit is applied, allowing
>> +	you to examine the commit. This is the default behavior.
>> +`drop`;;
>> +	The empty commit will be dropped.
>> +`keep`;;
>> +	The empty commit will be kept. Note that use of this option only
>>   	results in an empty commit when the commit was not initially empty,
>>   	but rather became empty due to a previous commit. Commits that were
>>   	initially empty will cause the cherry-pick to fail. To force the
>>   	inclusion of those commits use `--allow-empty`.
>> +--
>> ++
>> +Note that commits which start empty will cause the cherry-pick to fail (unless
>> +`--allow-empty` is specified).

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-23 14:23 ` Phillip Wood
@ 2024-01-23 18:18   ` Junio C Hamano
  2024-01-24 11:01   ` Phillip Wood
  1 sibling, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-01-23 18:18 UTC (permalink / raw)
  To: Phillip Wood; +Cc: brianmlyles, git, me, newren

Phillip Wood <phillip.wood123@gmail.com> writes:

>> This implication of `--allow-empty` therefore seems incorrect: One
>> should be able to keep a commit that becomes empty without also being
>> forced to pick commits that start as empty.
>
> Do you have a practical example of where you want to keep the commits
> that become empty but not the ones that start empty? I agree there is
> a distinction but I think the common case is that the user wants to
> keep both types of empty commit or none. I'm not against giving the
> user the option to keep one or the other if it is useful but I'm wary
> of changing the default.

This may not a new issue introduced by this series, but one thing I
would be worried about the usability of the keep-redundant is that I
know it takes more than one tries of cherry-picking of the same
series, at least to me, to get a series right.  The initial attempt
may make some commit empty and thanks to --keep-redundant they will
be kept, but I'll inevitably find more things I need to tweak and
cherry-pick the resulting series, possibly on a different base.  And
to this second round of cherry-pick, these "were not, but now have
become empty" commits appear empty from the start.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Subject: [PATCH] CoC: whitespace fix
  2024-01-23 17:32           ` Junio C Hamano
@ 2024-01-23 18:41             ` Junio C Hamano
  2024-01-24  3:06               ` Elijah Newren
  2024-01-23 18:49             ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling Junio C Hamano
  1 sibling, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-01-23 18:41 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Taylor Blau, Elijah Newren, Brian Lyles

> For example, CODE_OF_CONDUCT.md has these two (shown with $$$)
> I think can be removed without breaking markdown:
>
>     Community Impact Guidelines were inspired by $$$
>     [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
>
>     For answers to common questions about this code of conduct, see the FAQ at
>     [https://www.contributor-covenant.org/faq][FAQ]. Translations are available $$$
>     at [https://www.contributor-covenant.org/translations][translations].


Before I forget...

------ >8 ----------- >8 ----------- >8 ----------- >8 ------
Subject: [PATCH] CoC: whitespace fix

Fix two lines with trailing whitespaces.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 CODE_OF_CONDUCT.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 0215b1fd4c..e58917c50a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -130,11 +130,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
 version 2.0, available at
 [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
 
-Community Impact Guidelines were inspired by 
+Community Impact Guidelines were inspired by
 [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
 
 For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][FAQ]. Translations are available 
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
 at [https://www.contributor-covenant.org/translations][translations].
 
 [homepage]: https://www.contributor-covenant.org
-- 
2.43.0-386-ge02ecfcc53


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-23 17:32           ` Junio C Hamano
  2024-01-23 18:41             ` Subject: [PATCH] CoC: whitespace fix Junio C Hamano
@ 2024-01-23 18:49             ` Junio C Hamano
  1 sibling, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-01-23 18:49 UTC (permalink / raw)
  To: git; +Cc: Kristoffer Haugsbakk, Taylor Blau, Elijah Newren, Brian Lyles

Junio C Hamano <gitster@pobox.com> writes:

> I got curious and took a look at files that has hits with "lines
> that end with SP or HT":
>
>     $ git grep -l -e '[  ]$'

Another more interest way to check is to do this:

    $ git diff --check $(git hash-object --stdin -t tree </dev/null)

which will not just catch trailing whitespaces but other whitespace
errors.  Good thing is that the projects can even customize the rules
used for various paths using the attributes mechanism.

Here is a sample patch based on what the above command line found.

------ >8 ----------- >8 ----------- >8 ----------- >8 ------
Subject: [PATCH] docs: a few whitespace fixes

Some documentation files have 8-space indented lines where other
indented lines in the same list use a single HT to indent.  As
Documentation/.gitattributes says *.txt files use the default
whitespace rules, let's fix some of them as a practice.

Note that git-add documentation has other instances of space
indented lines, but they are samples of manually aligned output
of the "git add -i" command and it would be better to keep them
as-is.  Which in turn may mean we may want to loosen the whitespace
rules for some parts of the documentation files, but we currently do
not have such a feature.  The attribute based whitespace rules apply
to the whole file.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/diff-format.txt | 8 ++++----
 Documentation/git-add.txt     | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git c/Documentation/diff-format.txt w/Documentation/diff-format.txt
index a3ae8747a2..bcaf9ca608 100644
--- c/Documentation/diff-format.txt
+++ w/Documentation/diff-format.txt
@@ -8,16 +8,16 @@ These commands all compare two sets of things; what is
 compared differs:
 
 git-diff-index <tree-ish>::
-        compares the <tree-ish> and the files on the filesystem.
+	compares the <tree-ish> and the files on the filesystem.
 
 git-diff-index --cached <tree-ish>::
-        compares the <tree-ish> and the index.
+	compares the <tree-ish> and the index.
 
 git-diff-tree [-r] <tree-ish-1> <tree-ish-2> [<pattern>...]::
-        compares the trees named by the two arguments.
+	compares the trees named by the two arguments.
 
 git-diff-files [<pattern>...]::
-        compares the index and the files on the filesystem.
+	compares the index and the files on the filesystem.
 
 The "git-diff-tree" command begins its output by printing the hash of
 what is being compared. After that, all the commands print one output
diff --git c/Documentation/git-add.txt w/Documentation/git-add.txt
index ed44c1cb31..e1a5c27acd 100644
--- c/Documentation/git-add.txt
+++ w/Documentation/git-add.txt
@@ -73,7 +73,7 @@ in linkgit:gitglossary[7].
 
 -v::
 --verbose::
-        Be verbose.
+	Be verbose.
 
 -f::
 --force::


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: Subject: [PATCH] CoC: whitespace fix
  2024-01-23 18:41             ` Subject: [PATCH] CoC: whitespace fix Junio C Hamano
@ 2024-01-24  3:06               ` Elijah Newren
  0 siblings, 0 replies; 118+ messages in thread
From: Elijah Newren @ 2024-01-24  3:06 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Kristoffer Haugsbakk, Taylor Blau, Brian Lyles

On Tue, Jan 23, 2024 at 10:41 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> > For example, CODE_OF_CONDUCT.md has these two (shown with $$$)
> > I think can be removed without breaking markdown:
> >
> >     Community Impact Guidelines were inspired by $$$
> >     [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
> >
> >     For answers to common questions about this code of conduct, see the FAQ at
> >     [https://www.contributor-covenant.org/faq][FAQ]. Translations are available $$$
> >     at [https://www.contributor-covenant.org/translations][translations].
>
>
> Before I forget...
>
> ------ >8 ----------- >8 ----------- >8 ----------- >8 ------
> Subject: [PATCH] CoC: whitespace fix
>
> Fix two lines with trailing whitespaces.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  CODE_OF_CONDUCT.md | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
> index 0215b1fd4c..e58917c50a 100644
> --- a/CODE_OF_CONDUCT.md
> +++ b/CODE_OF_CONDUCT.md
> @@ -130,11 +130,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
>  version 2.0, available at
>  [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
>
> -Community Impact Guidelines were inspired by
> +Community Impact Guidelines were inspired by
>  [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
>
>  For answers to common questions about this code of conduct, see the FAQ at
> -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
> +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
>  at [https://www.contributor-covenant.org/translations][translations].
>
>  [homepage]: https://www.contributor-covenant.org
> --

I'm always happy to see trailing whitespace removed.  :-)   LGTM.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (4 preceding siblings ...)
  2024-01-23 14:23 ` Phillip Wood
@ 2024-01-24 11:01 ` Phillip Wood
  2024-01-27 23:30   ` Brian Lyles
  2024-02-10  7:43 ` [PATCH v2 0/8] cherry-pick: add `--empty` Brian Lyles
                   ` (32 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-01-24 11:01 UTC (permalink / raw)
  To: brianmlyles, git; +Cc: me, newren

Hi Brian

Here are some code comments now I've realized why we need to change it

On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> From: Brian Lyles <brianmlyles@gmail.com>
 >   Documentation/git-cherry-pick.txt | 10 +++++++---
 >   builtin/revert.c                  |  4 ----
 >   sequencer.c                       | 18 ++++++++++--------
 >   t/t3505-cherry-pick-empty.sh      |  5 +++++
 >   4 files changed, 22 insertions(+), 15 deletions(-)
 >
 > diff --git a/Documentation/git-cherry-pick.txt 
b/Documentation/git-cherry-pick.txt
 > index fdcad3d200..806295a730 100644
 > --- a/Documentation/git-cherry-pick.txt
 > +++ b/Documentation/git-cherry-pick.txt
 > @@ -131,8 +131,8 @@ effect to your index in a row.
 >       even without this option.  Note also, that use of this option only
 >       keeps commits that were initially empty (i.e. the commit 
recorded the
 >       same tree as its parent).  Commits which are made empty due to a
 > -    previous commit are dropped.  To force the inclusion of those 
commits
 > -    use `--keep-redundant-commits`.
 > +    previous commit will cause the cherry-pick to fail.  To force the
 > +    inclusion of those commits use `--keep-redundant-commits`.
 >
 >   --allow-empty-message::
 >       By default, cherry-picking a commit with an empty message will 
fail.
 > @@ -144,7 +144,11 @@ effect to your index in a row.
 >       current history, it will become empty.  By default these
 >       redundant commits cause `cherry-pick` to stop so the user can
 >       examine the commit. This option overrides that behavior and
 > -    creates an empty commit object.  Implies `--allow-empty`.
 > +    creates an empty commit object. Note that use of this option only
 > +    results in an empty commit when the commit was not initially empty,
 > +    but rather became empty due to a previous commit. Commits that were
 > +    initially empty will cause the cherry-pick to fail. To force the
 > +    inclusion of those commits use `--allow-empty`.
 >
 >   --strategy=<strategy>::
 >       Use the given merge strategy.  Should only be used once.
 > diff --git a/builtin/revert.c b/builtin/revert.c
 > index e6f9a1ad26..b2cfde7a87 100644
 > --- a/builtin/revert.c
 > +++ b/builtin/revert.c
 > @@ -136,10 +136,6 @@ static int run_sequencer(int argc, const char 
**argv, const char *prefix,
 >       prepare_repo_settings(the_repository);
 >       the_repository->settings.command_requires_full_index = 0;
 >
 > -    /* implies allow_empty */
 > -    if (opts->keep_redundant_commits)
 > -        opts->allow_empty = 1;

I'm wary of this, especially after Juino's comments in 
https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/

The documentation changes look if we do decide to change the default.

 >       if (cleanup_arg) {
 >           opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
 >           opts->explicit_cleanup = 1;
 > diff --git a/sequencer.c b/sequencer.c
 > index d584cac8ed..582bde8d46 100644
 > --- a/sequencer.c
 > +++ b/sequencer.c
 > @@ -1739,22 +1739,24 @@ static int allow_empty(struct repository *r,
 >        *
 >        * (4) we allow both.
 >        */

The comment above should be updated if we change the behavior of this 
function.

 > -    if (!opts->allow_empty)
 > -        return 0; /* let "git commit" barf as necessary */
 > -

Here we stop returning early if allow_empty is not set - this allows us 
to apply allow_empty only to commits that start empty below.

 >       index_unchanged = is_index_unchanged(r);
 > -    if (index_unchanged < 0)
 > +    if (index_unchanged < 0) {
 > +        if (!opts->allow_empty)
 > +            return 0;
 >           return index_unchanged;
 > +    }

I don't think we want to hide the error here or below from 
originally_empty()

 >       if (!index_unchanged)
 >           return 0; /* we do not have to say --allow-empty */
 >
 > -    if (opts->keep_redundant_commits)
 > -        return 1;
 > -

We move this check so that we do not unconditionally keep commits that 
are initially empty when opts->keep_redundant_commits is set.

 >       originally_empty = is_original_commit_empty(commit);
 > -    if (originally_empty < 0)
 > +    if (originally_empty < 0) {
 > +        if (!opts->allow_empty)
 > +            return 0;
 >           return originally_empty;
 > +    }
 >       if (originally_empty)
 > +        return opts->allow_empty;

Now opts->allow_empty only applies to commits that were originally empty

 > +    else if (opts->keep_redundant_commits)
 >           return 1;

This ensures we keep commits that become empty when 
opts->redundant_commits is set.

The changes to allow_empty() all looks good apart from hiding errors 
from index_unchanged() and originally_empty()

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-23 14:23 ` Phillip Wood
  2024-01-23 18:18   ` Junio C Hamano
@ 2024-01-24 11:01   ` Phillip Wood
  1 sibling, 0 replies; 118+ messages in thread
From: Phillip Wood @ 2024-01-24 11:01 UTC (permalink / raw)
  To: brianmlyles, git; +Cc: me, newren

Hi Brian

On 23/01/2024 14:23, Phillip Wood wrote:

> rebase always sets "opts->allow_empty = 1" in 
> builtin/rebase.c:get_replay_opts() and if the user passes 
> --no-keep-empty drops commits that start empty from the list of commits 
> to be picked. This is slightly confusing but is more efficient as we 
> don't do waste time trying to pick a commit we're going to drop. Can we 
> do something similar for "git cherry-pick"? When cherry-picking a 
> sequence of commits I think it should just work because the code is 
> shared with rebase, for a single commit we'd need to add a test to see 
> if it is empty in single_pick() before calling pick_commits().

Having thought about this again I don't think we can reuse the same 
approach as rebase because cherry-pick and rebase have different 
behaviors. "git rebase --no-keep-empty" drops empty commits whereas "git 
cherry-pick" wants to error out if it sees an empty commit. So I think 
your approach of reworking allow_empty() in the sequencer is the right 
approach.

Sorry for the confusion

Phillip


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`
  2024-01-23 14:24   ` Phillip Wood
@ 2024-01-27 21:22     ` Brian Lyles
  2024-02-01 14:02       ` Phillip Wood
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-01-27 21:22 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren

Hi Phillip

On Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> > From: Brian Lyles <brianmlyles@gmail.com>
> >
> > Both of these pages document very similar `--empty` options, but with
> > different styles. This commit aims to make them more consistent.
>
> I think that's reasonable though the options they are worded as doing
> different things. For "am" it talks about the patch being empty - i.e. a
> patch of an empty commit whereas for "rebase" the option applies to
> non-empty commits that become empty. What does "am" do if you try to
> apply a patch whose changes are already present?

Hm -- as you mention, this does appear to have a different meaning for
git-am(1) than it does for git-rebase(1). Regardless of the `--empty`
value passed to git-am(1), a non-empty patch that is already present
appears to error and stop.

That is an unfortunate difference. I think that my updated version of
the git-am(1) docs is still easier to read, and preserves the original
meaning. So I'm inclined to say that it's still an improvement worth
making, and perhaps my commit message should just clarify that.
Thoughts?

> If you're aiming for consistency then it would be worth listing the
> possible values in the same order for each command.

That makes sense. I had initially maintained the existing order in which
these were documented, keeping the default option first. I think that
the updated layout makes the order less relevant by making it easier to
read and identify the default anyway.

I could see alphabetical being better, though with the changes later in
this series we'd end up with the deprecated `ask` being first or
out-or-order at the end. What are your thoughts on the ideal order for
these?

> > diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> > index b4526ca246..3ee85f6d86 100644
> > --- a/Documentation/git-rebase.txt
> > +++ b/Documentation/git-rebase.txt
> > @@ -293,13 +293,20 @@ See also INCOMPATIBLE OPTIONS below.
> >       How to handle commits that are not empty to start and are not
> >       clean cherry-picks of any upstream commit, but which become
> >       empty after rebasing (because they contain a subset of already
> > -     upstream changes).  With drop (the default), commits that
> > -     become empty are dropped.  With keep, such commits are kept.
> > -     With ask (implied by `--interactive`), the rebase will halt when
> > -     an empty commit is applied allowing you to choose whether to
> > -     drop it, edit files more, or just commit the empty changes.
> > +     upstream changes):
> > ++
> > +--
> > +`drop`;;
> > +     The empty commit will be dropped. This is the default behavior.
>
> I think it would be clearer to say "The commit" - I found "The empty
> commit" confusing as the commit that is being picked isn't empty.

I could see that -- I'll adjust this for v2 of the patch.

> > +`keep`;;
> > +     The empty commit will be kept.
> > +`ask`;;
> > +     The rebase will halt when the empty commit is applied, allowing you to
> > +     choose whether to drop it, edit files more, or just commit the empty
> > +     changes. This option is implied when `--interactive` is specified.
> >       Other options, like `--exec`, will use the default of drop unless
> >       `-i`/`--interactive` is explicitly specified.
>
> Thanks for adding a bit more detail about the default, however it looks
> to me like we keep commits that become empty when --exec is specified
>
>         if (options.empty == EMPTY_UNSPECIFIED) {
>                 if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
>                         options.empty = EMPTY_STOP;
>                 else if (options.exec.nr > 0)
>                         options.empty = EMPTY_KEEP;
>                 else
>                         options.empty = EMPTY_DROP;
>         }
>
> Off the top of my head I'm not sure why or if that is a good idea.

The two lines indicating this behavior are actually pre-existing -- I
did not change them in this patch and thus didn't even think to fact
check them.

Upon testing this, I've confirmed that you are correct about the actual
behavior. I will address this in a separate commit in v2.

Thanks,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`
  2024-01-23 14:24   ` Phillip Wood
@ 2024-01-27 21:49     ` Brian Lyles
  2024-02-01 14:02       ` Phillip Wood
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-01-27 21:49 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren

Hi Phillip

On Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> > From: Brian Lyles <brianmlyles@gmail.com>
> >
> > When `git-am` got its own `--empty` option in 7c096b8d61 (am: support
> > --empty=<option> to handle empty patches, 2021-12-09), `stop` was used
> > instead of `ask`. `stop` is a more accurate term for describing what
> > really happens,
>
> I can see your reasoning but I think of stopping as git's way of asking
> what to do so I'm not sure if "stop" is better than "ask". I don't know
> how we ended up with two different terms - the prior art is "ask" so
> maybe we should change "am --empty" instead. Lets see what others think.

The suggestion to use 'stop' instead of 'ask' for rebase was initially
Elijah's[1], which I agreed with. I am certainly open to others'
opinions here though, and am content with whatever is decided. I am
mostly aiming for consistency between git-rebase(1), git-am(1), and
ultimately git-cherry-pick(1).

[1]: https://lore.kernel.org/git/CABPp-BGJfvBhO_zEX8nLoa8WNsjmwvtZ2qOjmYm9iPoZg4SwPw@mail.gmail.com/

> It would be helpful to mention the tests in the commit message - we end
> up with a mixture of "--empty=ask" and "--empty=stop" I assume that is
> by design

You are correct -- the intent being to ensure that `--ask` continues
working for as long as it is supported. I'll add this to the message in
v2.

> > and consistency is good. This commit updates
> > `git-rebase` to also use `stop`, while keeping `ask` as a deprecated
> > synonym.
>
> If we're deprecating "ask" do we want to print a warning recommending
> "stop" instead?

That makes sense -- I will include a warning for this in v2.

Thanks,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-24 11:01 ` Phillip Wood
@ 2024-01-27 23:30   ` Brian Lyles
  2024-01-28 16:36     ` Brian Lyles
  2024-02-01 10:57     ` Phillip Wood
  0 siblings, 2 replies; 118+ messages in thread
From: Brian Lyles @ 2024-01-27 23:30 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren, gitster

[+cc Junio]

Hi Phillip

On Tue, Jan 23, 2024 at 8:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Brian
>
> Let me start by saying that overall I'm impressed with the quality of
> this submission. I've left quite a few comments but for a first patch
> series it is very good.

Thank you for the kind words!

> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> > From: Brian Lyles <brianmlyles@gmail.com>
> >
> > Previously, a consumer of the sequencer that wishes to take advantage of
> > either the `keep_redundant_commits` or `drop_redundant_commits` feature
> > must also specify `allow_empty`.
> >
> > The only consumer of `drop_redundant_commits` is `git-rebase`, which
> > already allows empty commits by default and simply always enables
> > `allow_empty`. `keep_redundant_commits` was also consumed by
> > `git-cherry-pick`, which had to specify `allow-empty` when
> > `keep_redundant_commits` was specified in order for the sequencer's
> > `allow_empty()` to actually respect `keep_redundant_commits`.
>
> I think it might be more persuasive to start the commit message by
> explaining what user visible change you're trying to make and why rather
> than concentrating on the implementation details.

I struggled a bit with this initially because the motivation behind the
change in this particular commit was driven by a technical issue in my
mind. The side-effect with git-cherry-pick(s) `--allow-empty` and
`--keep-redundant-commits` was mildly problematic, but less concerning
that the future problem that we'd have once git-cherry-pick(1) got the
more robust `--empty` option in a later commit in this series.

I think my problem came down to this commit trying to solve two problems
at once -- the underlying technical concern _and_ the git-cherry-pick(1)
behavior.

In v2, I intend to break this commit into two:

- Update `allow_empty()` to not require `allow_empty`, but without
  actually changing any consumers (and thus without making any
  functional change)
- Update git-cherry-pick(1) such that `--keep-redundant-commits` no
  longer implies `--allow-empty`.

This allows me to better justify the technical change technically and
the functional change functionally, while also making it easier to drop
the functional change if we decide that a breaking change is not
warranted to address this.

> Do you have a practical example of where you want to keep the commits
> that become empty but not the ones that start empty? I agree there is a
> distinction but I think the common case is that the user wants to keep
> both types of empty commit or none. I'm not against giving the user the
> option to keep one or the other if it is useful but I'm wary of changing
> the default.

That practical example is documented in the initial discussion[1], which
I should have ought to have linked in a cover letter for this series
(and will do so in v2). I'll avoid copying the details here, but we'd
very much like to be able to programmatically drop the commits that
become empty when doing the automated cherry-pick described there.

[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/

> rebase always sets "opts->allow_empty = 1" in
> builtin/rebase.c:get_replay_opts() and if the user passes
> --no-keep-empty drops commits that start empty from the list of commits
> to be picked. This is slightly confusing but is more efficient as we
> don't do waste time trying to pick a commit we're going to drop. Can we
> do something similar for "git cherry-pick"? When cherry-picking a
> sequence of commits I think it should just work because the code is
> shared with rebase, for a single commit we'd need to add a test to see
> if it is empty in single_pick() before calling pick_commits().

Just noting here for future readers here that you sent a followup email
indicating this was not viable:

> On Wed, Jan 24, 2024 at 5:01 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Having thought about this again I don't think we can reuse the same
> approach as rebase because cherry-pick and rebase have different
> behaviors. "git rebase --no-keep-empty" drops empty commits whereas "git
> cherry-pick" wants to error out if it sees an empty commit. So I think
> your approach of reworking allow_empty() in the sequencer is the right
> approach.
>
> Sorry for the confusion

No worries. If you have any suggestions for how I might better explain
these changes in the commit message, please let me know.

> > For some amount of backwards compatibility with the existing code and
> > tests, I have opted to preserve the behavior of returning 0 when:
> >
> > - `allow_empty` is specified, and
> > - either `is_index_unchanged` or `is_original_commit_empty` indicates an
> >    error
>
> I'm not sure that is a good idea as it is hiding an error that we didn't
> hit before because we returned early.

I think you're right -- Previously the error could not have been hit,
but now it can. An error is still an error, and we should handle it
regardless of how `allow_empty` was set. I'll address this in v2 by
simply returning the error.

> > Note that this commit is a breaking change: `--keep-redundant-commits`
> > will no longer imply `--allow-empty`. It would be possible to maintain
> > the current behavior of `--keep-redundant-commits` implying
> > `--allow-empty` if it were needed to avoid a breaking change, but I
> > believe that decoupling them entirely is the correct behavior.
>
> Thank you for being clear about the change in behavior, as I said above
> I'm wary of changing the default unless there is a compelling reason but
> I'm happy to support
>
>      git cherry-pick --keep-redundant-commits --no-allow-empty
>
> if it is needed.

I totally understand being wary here.

I've certainly convinced myself that having the future `--empty=drop`
behavior introduced later in this patch should not imply
`--allow-empty`.

I also _think_ that the existing behavior of `--keep-redundant-commits`
is probably technically not ideal or correct, but could be convinced
that changing it now is not worthwhile. I will defer to group consensus
here.

> > Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> > ---
> >
> > Disclaimer: This is my first contribution to the git project, and thus
> > my first attempt at submitting a patch via `git-send-email`. It is also
> > the first time I've touched worked in C in over a decade, and I really
> > didn't work with it much before that either. I welcome any and all
> > feedback on what I may have gotten wrong regarding the patch submission
> > process, the code changes, or my commit messages.
>
> As others have mentioned I think it would be useful to have a
> cover-letter where we can discuss the aim of the patch series
> independently of the individual patches.

Absolutely, will do in v2.

>  > [...]> +test_expect_success 'cherry pick an empty non-ff commit with
> --keep-redundant-commits' '
> > +     git checkout main &&
> > +     test_must_fail git cherry-pick --keep-redundant-commits empty-change-branch
>
> When using test_must_fail it is a good idea to check the error message
> to make sure that it's failing for the reason we expect (see patch 4).

Thanks for the tip, I'll add this in v2.

On Wed, Jan 24, 2024 at 5:01 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> Here are some code comments now I've realized why we need to change it
>
> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
> > From: Brian Lyles <brianmlyles@gmail.com>
>  >   Documentation/git-cherry-pick.txt | 10 +++++++---
>  >   builtin/revert.c                  |  4 ----
>  >   sequencer.c                       | 18 ++++++++++--------
>  >   t/t3505-cherry-pick-empty.sh      |  5 +++++
>  >   4 files changed, 22 insertions(+), 15 deletions(-)
>  >
>  > diff --git a/Documentation/git-cherry-pick.txt
> b/Documentation/git-cherry-pick.txt
>  > index fdcad3d200..806295a730 100644
>  > --- a/Documentation/git-cherry-pick.txt
>  > +++ b/Documentation/git-cherry-pick.txt
>  > @@ -131,8 +131,8 @@ effect to your index in a row.
>  >       even without this option.  Note also, that use of this option only
>  >       keeps commits that were initially empty (i.e. the commit
> recorded the
>  >       same tree as its parent).  Commits which are made empty due to a
>  > -    previous commit are dropped.  To force the inclusion of those
> commits
>  > -    use `--keep-redundant-commits`.
>  > +    previous commit will cause the cherry-pick to fail.  To force the
>  > +    inclusion of those commits use `--keep-redundant-commits`.
>  >
>  >   --allow-empty-message::
>  >       By default, cherry-picking a commit with an empty message will
> fail.
>  > @@ -144,7 +144,11 @@ effect to your index in a row.
>  >       current history, it will become empty.  By default these
>  >       redundant commits cause `cherry-pick` to stop so the user can
>  >       examine the commit. This option overrides that behavior and
>  > -    creates an empty commit object.  Implies `--allow-empty`.
>  > +    creates an empty commit object. Note that use of this option only
>  > +    results in an empty commit when the commit was not initially empty,
>  > +    but rather became empty due to a previous commit. Commits that were
>  > +    initially empty will cause the cherry-pick to fail. To force the
>  > +    inclusion of those commits use `--allow-empty`.
>  >
>  >   --strategy=<strategy>::
>  >       Use the given merge strategy.  Should only be used once.
>  > diff --git a/builtin/revert.c b/builtin/revert.c
>  > index e6f9a1ad26..b2cfde7a87 100644
>  > --- a/builtin/revert.c
>  > +++ b/builtin/revert.c
>  > @@ -136,10 +136,6 @@ static int run_sequencer(int argc, const char
> **argv, const char *prefix,
>  >       prepare_repo_settings(the_repository);
>  >       the_repository->settings.command_requires_full_index = 0;
>  >
>  > -    /* implies allow_empty */
>  > -    if (opts->keep_redundant_commits)
>  > -        opts->allow_empty = 1;
>
> I'm wary of this, especially after Juino's comments in
> https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/

As noted above, I've split this commit into two, and am open to
discussing dropping the functional change to `--keep-redundant-commits`

>  >       if (cleanup_arg) {
>  >           opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
>  >           opts->explicit_cleanup = 1;
>  > diff --git a/sequencer.c b/sequencer.c
>  > index d584cac8ed..582bde8d46 100644
>  > --- a/sequencer.c
>  > +++ b/sequencer.c
>  > @@ -1739,22 +1739,24 @@ static int allow_empty(struct repository *r,
>  >        *
>  >        * (4) we allow both.
>  >        */
>
> The comment above should be updated if we change the behavior of this
> function.

Of course, good catch.

> I don't think we want to hide the error here or below from
> originally_empty()
>
>  >       if (!index_unchanged)
>  >           return 0; /* we do not have to say --allow-empty */
>  >
>  > -    if (opts->keep_redundant_commits)
>  > -        return 1;
>  > -

Agreed, will address in v2 as mentioned above.

Thanks,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-23 14:25   ` Phillip Wood
  2024-01-23 18:01     ` Junio C Hamano
@ 2024-01-27 23:56     ` Brian Lyles
  1 sibling, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-01-27 23:56 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren, gitster

[+cc Junio]

On Tue, Jan 23, 2024 at 8:25 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Brian
>
>
> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
>
> > The `--keep-redundant-commits` option will be documented as a deprecated
> > synonym of `--empty=keep`, and will be supported for backwards
> > compatibility for the time being.
>
> I'm not sure if we need to deprecate it as in "it will be removed in the
> future" or just reduce it prominence in favor of --empty

This is also related to Junio's comment:

> On Tue, Jan 23, 2024 at 12:01 PM Junio C Hamano <gitster@pobox.com>
wrote:
>
> True, especially since --empty=keep is much less descriptive and the
> part after "note that ..." below will take a long time before
> sticking in readers' brain.

My primary motivation here was simply for consistency with `--empty` for
both git-rebase(1) and git-am(1). In theory, I am not opposed to
updating this patch to instead simply add a `--drop-redundant-commits`
option if we feel that provides better usability. However, I think that
the consistency would be better.

I will happily defer to the group consensus here, with the options I see
being:

1. No deprecation: just make `--keep-redundant-commits` a synonym of
  `--empty=keep`
2. Soft deprecation: Give a warning when `--keep-redundant-commits` is
  used
3. Add `--drop-redundant-commits` instead of `--empty`

My preference would be 2, followed by 1 and then 3.

> I'm still on the fence about "stop" vs "ask". I see in your tests you've
> accidentally used "ask" which makes me wonder if that is the more
> familiar term for users who probably use "git rebase" more often than
> "git am".

Oh, thank you for catching that. The cause here was actually because I
had initially written these tests prior to adding the "ask -> stop"
change rather than familiarity. I simply failed to update the tests
after moving things around.

> The code changes look good but I think we want to update
> verify_opt_compatible() to check for "--empty" being combined with
> "--continue" etc. as well.

It looks like `--keep-redundant-commits` was not being included in these
checks previously. I suspect that to be an oversight though.

I can add this for v2.

>
> >       if (cleanup_arg) {
> >               opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
> >               opts->explicit_cleanup = 1;
> > diff --git a/sequencer.c b/sequencer.c
> > index 582bde8d46..c49c27c795 100644
> > --- a/sequencer.c
> > +++ b/sequencer.c
> > @@ -2934,6 +2934,9 @@ static int populate_opts_cb(const char *key, const char *value,
> >       else if (!strcmp(key, "options.allow-empty-message"))
> >               opts->allow_empty_message =
> >                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
> > +     else if (!strcmp(key, "options.drop-redundant-commits"))
> > +             opts->drop_redundant_commits =
> > +                     git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
> >       else if (!strcmp(key, "options.keep-redundant-commits"))
> >               opts->keep_redundant_commits =
> >                       git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
> > @@ -3478,6 +3481,9 @@ static int save_opts(struct replay_opts *opts)
> >       if (opts->allow_empty_message)
> >               res |= git_config_set_in_file_gently(opts_file,
> >                               "options.allow-empty-message", "true");
> > +     if (opts->drop_redundant_commits)
> > +             res |= git_config_set_in_file_gently(opts_file,
> > +                             "options.drop-redundant-commits", "true");
>
> It is good that we're saving the option - it would be good to add a test
> to check that we remember --empty after stopping for a conflict resolution.

I can add a test for this in v2

> >       if (opts->keep_redundant_commits)
> >               res |= git_config_set_in_file_gently(opts_file,
> >                               "options.keep-redundant-commits", "true");
> > diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
> > index 6adfd25351..ae0cf7886a 100755
> > --- a/t/t3505-cherry-pick-empty.sh
> > +++ b/t/t3505-cherry-pick-empty.sh
> > @@ -89,7 +89,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
> >       git commit -m "add file2 on the side"
> >   '
> >
> > -test_expect_success 'cherry-pick a no-op without --keep-redundant' '
> > +test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
> >       git reset --hard &&
> >       git checkout fork^0 &&
> >       test_must_fail git cherry-pick main
> > @@ -104,4 +104,28 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
> >       test_cmp expect actual
> >   '
> >
> > +test_expect_success 'cherry-pick a no-op with --empty=ask' '
> > +     git reset --hard &&
> > +     git checkout fork^0 &&
> > +     test_must_fail git cherry-pick --empty=ask main
>
> This is an example of why it is a good idea to check the error message
> when using "test_must_fail" as here the test will fail due to a bad
> value passed to "--empty" rather than for the reason we want the test to
> check. It would be good to add a separate test to check that we reject
> invalid "--empty" values.

An excellent catch, thank you. Will be addressed in v2

> > +'
> > +
> > +test_expect_success 'cherry-pick a no-op with --empty=drop' '
> > +     git reset --hard &&
> > +     git checkout fork^0 &&
> > +     git cherry-pick --empty=drop main &&
> > +     git show -s --format=%s >actual &&
> > +     echo "add file2 on the side" >expect &&
> > +     test_cmp expect actual
>
> I think you could simplify this by using test_commit_message

Thanks for pointing that function out -- you're absolutely right. Will
be addressed in v2.


Thanks for the review,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling
  2024-01-23 18:01     ` Junio C Hamano
@ 2024-01-28  0:07       ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-01-28  0:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Phillip Wood, git, me, newren

Hi Junio

On Tue, Jan 23, 2024 at 12:01 PM Junio C Hamano <gitster@pobox.com> wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
> > Thanks for the well explained commit message
>
> ;-)
>
> >> The `--keep-redundant-commits` option will be documented as a deprecated
> >> synonym of `--empty=keep`, and will be supported for backwards
> >> compatibility for the time being.
> >
> > I'm not sure if we need to deprecate it as in "it will be removed in
> > the future" or just reduce it prominence in favor of --empty
>
> True, especially since --empty=keep is much less descriptive and the
> part after "note that ..." below will take a long time before
> sticking in readers' brain.

I responded to this in my reply to Phillip, and CC'd you there.

> >> +--empty=(stop|drop|keep)::
> >> +    How to handle commits being cherry-picked that are redundant with
> >> +    changes already in the current history.
>
> It might make it easier to understand if we moved the description in
> 'keep' that says something about --allow-empty here, as it should
> apply to other two choices if I understand correctly.  Let me try:
>
>     This option specifies how a commit that is not originally empty
>     but becomes a no-op when cherry-picked due to earlier changes
>     already applied or already in the current history.  Regardless
>     of this this option, `cherry-pick` will fail on a commit that is
>     empty in the original history---see `--allow-empty` to pass them
>     intact.
>
> or something.  Then the description of `keep` can become just as
> short as other two, e.g. a single sentence "The commit that becomes
> empty will be kept".

Thank you for this suggestion. You are correct that the difference
between `--empty` and `allow-empty` is relevant regardless of the option
selected by the user.

In fact, this entire tidbit is somewhat duplicative with the note I
already added after the options:

> Note that commits which start empty will cause the cherry-pick to fail
> (unless `--allow-empty` is specified).

I'll clean this up in v2. Here's what I am thinking currently:

> --empty=(stop|drop|keep)::
>     How to handle commits being cherry-picked that are redundant with
>     changes already in the current history.
> +
> --
> `stop`;;
>     The cherry-pick will stop when the commit is applied, allowing
>     you to examine the commit. This is the default behavior.
> `drop`;;
>     The commit will be dropped.
> `keep`;;
>     The commit will be kept.
> --
> +
> Note that this option species how to handle a commit that was not initially
> empty, but rather became empty due to a previous commit. Commits that were
> initially empty will cause the cherry-pick to fail. To force the inclusion of
> those commits, use `--allow-empty`.
> +

Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-27 23:30   ` Brian Lyles
@ 2024-01-28 16:36     ` Brian Lyles
  2024-01-29 10:55       ` Phillip Wood
  2024-02-01 10:57     ` Phillip Wood
  1 sibling, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-01-28 16:36 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren, gitster

[+cc Junio]

On Sat, Jan 27, 2024 at 5:30 PM Brian Lyles <brianmlyles@gmail.com> wrote:

> > > For some amount of backwards compatibility with the existing code and
> > > tests, I have opted to preserve the behavior of returning 0 when:
> > >
> > > - `allow_empty` is specified, and
> > > - either `is_index_unchanged` or `is_original_commit_empty` indicates an
> > >    error
> >
> > I'm not sure that is a good idea as it is hiding an error that we didn't
> > hit before because we returned early.
>
> I think you're right -- Previously the error could not have been hit,
> but now it can. An error is still an error, and we should handle it
> regardless of how `allow_empty` was set. I'll address this in v2 by
> simply returning the error.

As I dig into this more, I'm noticing that this may have unintended side
effects that I'm unsure of. After making this change, I noticed a couple
of failures in the cherry-pick test suite. The others may be a knock-on
of this initial failure:

    expecting success of 3501.8 'cherry-pick on unborn branch':
            git checkout --orphan unborn &&
            git rm --cached -r . &&
            rm -rf * &&
            git cherry-pick initial &&
            git diff --quiet initial &&
            test_cmp_rev ! initial HEAD

    A       extra_file
    Switched to a new branch 'unborn'
    rm 'extra_file'
    rm 'spoo'
    error: could not resolve HEAD commit
    fatal: cherry-pick failed
    not ok 8 - cherry-pick on unborn branch
    #
    #               git checkout --orphan unborn &&
    #               git rm --cached -r . &&
    #               rm -rf * &&
    #               git cherry-pick initial &&
    #               git diff --quiet initial &&
    #               test_cmp_rev ! initial HEAD
    #

It looks like this is caused specifically by not hiding the error from
`index_unchanged`

    index_unchanged = is_index_unchanged(r);
    if (index_unchanged < 0) {
        return index_unchanged;
    }

At this point, my inexperience with the git codebase and these more edge
case scenarios starts to show. I'm unsure how to best approach this. It
seems that exposing these errors when `allow_empty` is not set causes
the entire cherry-pick to fail in situations where it would not
previously. Here is what that same test looks like prior to any of my
changes from this series:

    expecting success of 3501.8 'cherry-pick on unborn branch':
            git checkout --orphan unborn &&
            git rm --cached -r . &&
            rm -rf * &&
            git cherry-pick initial &&
            git diff --quiet initial &&
            test_cmp_rev ! initial HEAD

    A       extra_file
    Switched to a new branch 'unborn'
    rm 'extra_file'
    rm 'spoo'
    [unborn 38e6d75] initial
     Author: A U Thor <author@example.com>
     Date: Thu Apr 7 15:13:13 2005 -0700
     1 file changed, 15 insertions(+)
     create mode 100644 oops
    ok 8 - cherry-pick on unborn branch

Conceptually I definitely agree that it seems odd to hide these errors
just because `allow_empty` was not set, but I fear this to be a breaking
change for which I don't have a good understanding of the impact.

Any guidance here would be appreciated.

Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-28 16:36     ` Brian Lyles
@ 2024-01-29 10:55       ` Phillip Wood
  2024-02-10  5:50         ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-01-29 10:55 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, me, newren, gitster

Hi Brian

On 28/01/2024 16:36, Brian Lyles wrote:
> [+cc Junio]
> 
> On Sat, Jan 27, 2024 at 5:30 PM Brian Lyles <brianmlyles@gmail.com> wrote:
> 
>>>> For some amount of backwards compatibility with the existing code and
>>>> tests, I have opted to preserve the behavior of returning 0 when:
>>>>
>>>> - `allow_empty` is specified, and
>>>> - either `is_index_unchanged` or `is_original_commit_empty` indicates an
>>>>     error
>>>
>>> I'm not sure that is a good idea as it is hiding an error that we didn't
>>> hit before because we returned early.
>>
>> I think you're right -- Previously the error could not have been hit,
>> but now it can. An error is still an error, and we should handle it
>> regardless of how `allow_empty` was set. I'll address this in v2 by
>> simply returning the error.
> 
> As I dig into this more, I'm noticing that this may have unintended side
> effects that I'm unsure of. After making this change, I noticed a couple
> of failures in the cherry-pick test suite. The others may be a knock-on
> of this initial failure:
> 
>      expecting success of 3501.8 'cherry-pick on unborn branch':
>              git checkout --orphan unborn &&
>              git rm --cached -r . &&
>              rm -rf * &&
>              git cherry-pick initial &&
>              git diff --quiet initial &&
>              test_cmp_rev ! initial HEAD
> 
>      A       extra_file
>      Switched to a new branch 'unborn'
>      rm 'extra_file'
>      rm 'spoo'
>      error: could not resolve HEAD commit
>      fatal: cherry-pick failed
>      not ok 8 - cherry-pick on unborn branch
>      #
>      #               git checkout --orphan unborn &&
>      #               git rm --cached -r . &&
>      #               rm -rf * &&
>      #               git cherry-pick initial &&
>      #               git diff --quiet initial &&
>      #               test_cmp_rev ! initial HEAD
>      #
> 
> It looks like this is caused specifically by not hiding the error from
> `index_unchanged`

Oh dear, that's a pain. I haven't checked but suspect we already hit 
this when running

     git cherry-pick --allow-empty

on an orphan checkout. In do_pick_commit() we treat an error reading 
HEAD as an unborn branch so I think we could do the same here. If the 
branch is unborn then we can use the_hash_algo->empty_tree as the tree 
to compare to.

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-27 23:30   ` Brian Lyles
  2024-01-28 16:36     ` Brian Lyles
@ 2024-02-01 10:57     ` Phillip Wood
  2024-02-10  4:34       ` Brian Lyles
  1 sibling, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-02-01 10:57 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, me, newren, gitster

Hi Brian

On 27/01/2024 23:30, Brian Lyles wrote:
> On Tue, Jan 23, 2024 at 8:23 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
>>> From: Brian Lyles <brianmlyles@gmail.com>
>>>
>>> Previously, a consumer of the sequencer that wishes to take advantage of
>>> either the `keep_redundant_commits` or `drop_redundant_commits` feature
>>> must also specify `allow_empty`.
>>>
>>> The only consumer of `drop_redundant_commits` is `git-rebase`, which
>>> already allows empty commits by default and simply always enables
>>> `allow_empty`. `keep_redundant_commits` was also consumed by
>>> `git-cherry-pick`, which had to specify `allow-empty` when
>>> `keep_redundant_commits` was specified in order for the sequencer's
>>> `allow_empty()` to actually respect `keep_redundant_commits`.
>>
>> I think it might be more persuasive to start the commit message by
>> explaining what user visible change you're trying to make and why rather
>> than concentrating on the implementation details.
> 
> I struggled a bit with this initially because the motivation behind the
> change in this particular commit was driven by a technical issue in my
> mind. The side-effect with git-cherry-pick(s) `--allow-empty` and
> `--keep-redundant-commits` was mildly problematic, but less concerning
> that the future problem that we'd have once git-cherry-pick(1) got the
> more robust `--empty` option in a later commit in this series.
> 
> I think my problem came down to this commit trying to solve two problems
> at once -- the underlying technical concern _and_ the git-cherry-pick(1)
> behavior.
> 
> In v2, I intend to break this commit into two:
> 
> - Update `allow_empty()` to not require `allow_empty`, but without
>    actually changing any consumers (and thus without making any
>    functional change)
> - Update git-cherry-pick(1) such that `--keep-redundant-commits` no
>    longer implies `--allow-empty`.
> 
> This allows me to better justify the technical change technically and
> the functional change functionally, while also making it easier to drop
> the functional change if we decide that a breaking change is not
> warranted to address this.

That sounds like a good strategy. I'm wondering if we should not change 
the behavior of `--keep-redundant-commits` to avoid breaking existing 
users but have `--empty=keep|drop` not imply `--allow-empty`. What ever 
we do we'll annoy someone. It is confusing to have subtly different 
behaviors for `--keep-redundant-commits` and `--empty=keep` but it 
avoids breaking existing users. If we change `--keep-redundant-commits` 
we potentially upset existing users but we don't confuse others with the 
subtle difference between the two.

>> Do you have a practical example of where you want to keep the commits
>> that become empty but not the ones that start empty? I agree there is a
>> distinction but I think the common case is that the user wants to keep
>> both types of empty commit or none. I'm not against giving the user the
>> option to keep one or the other if it is useful but I'm wary of changing
>> the default.
> 
> That practical example is documented in the initial discussion[1], which
> I should have ought to have linked in a cover letter for this series
> (and will do so in v2). I'll avoid copying the details here, but we'd
> very much like to be able to programmatically drop the commits that
> become empty when doing the automated cherry-pick described there.
> 
> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/

Maybe I've missed something but that seems to be an argument for 
implementing `--empty=drop` which is completely reasonable but doesn't 
explain why someone using `--keep-redundant-commits` would want to keep 
the commits that become empty while dropping the commits that start empty.

> [...]
>> Thank you for being clear about the change in behavior, as I said above
>> I'm wary of changing the default unless there is a compelling reason but
>> I'm happy to support
>>
>>       git cherry-pick --keep-redundant-commits --no-allow-empty
>>
>> if it is needed.
> 
> I totally understand being wary here.
> 
> I've certainly convinced myself that having the future `--empty=drop`
> behavior introduced later in this patch should not imply
> `--allow-empty`.

I agree with that

> I also _think_ that the existing behavior of `--keep-redundant-commits`
> is probably technically not ideal or correct, but could be convinced
> that changing it now is not worthwhile. I will defer to group consensus
> here.

There is definitely an argument that the existing behavior conflates the 
two flavors of empty commit. I think one can also argue that the 
conflation is beneficial as most of the time users don't care about the 
distinction when using `--keep-redundant-commits` and so it is not worth 
changing the behavior of the existing option.

I sounds like you've got a good plan for v2, I look forward to reading it.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am`
  2024-01-27 21:22     ` Brian Lyles
@ 2024-02-01 14:02       ` Phillip Wood
  0 siblings, 0 replies; 118+ messages in thread
From: Phillip Wood @ 2024-02-01 14:02 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, me, newren

Hi Brian

On 27/01/2024 21:22, Brian Lyles wrote:
> On Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
>>> From: Brian Lyles <brianmlyles@gmail.com>
>>>
>>> Both of these pages document very similar `--empty` options, but with
>>> different styles. This commit aims to make them more consistent.
>>
>> I think that's reasonable though the options they are worded as doing
>> different things. For "am" it talks about the patch being empty - i.e. a
>> patch of an empty commit whereas for "rebase" the option applies to
>> non-empty commits that become empty. What does "am" do if you try to
>> apply a patch whose changes are already present?
> 
> Hm -- as you mention, this does appear to have a different meaning for
> git-am(1) than it does for git-rebase(1). Regardless of the `--empty`
> value passed to git-am(1), a non-empty patch that is already present
> appears to error and stop.
> 
> That is an unfortunate difference. I think that my updated version of
> the git-am(1) docs is still easier to read, and preserves the original
> meaning. So I'm inclined to say that it's still an improvement worth
> making, and perhaps my commit message should just clarify that.
> Thoughts?

Yes I agree the change is worthwhile but I think it would benefit from 
an updated commit message.

>> If you're aiming for consistency then it would be worth listing the
>> possible values in the same order for each command.
> 
> That makes sense. I had initially maintained the existing order in which
> these were documented, keeping the default option first. I think that
> the updated layout makes the order less relevant by making it easier to
> read and identify the default anyway.
> 
> I could see alphabetical being better, though with the changes later in
> this series we'd end up with the deprecated `ask` being first or
> out-or-order at the end. What are your thoughts on the ideal order for
> these?

Alphabetical sounds reasonable, we could sort on the non deprecated 
names with stop and ask grouped together

drop;;
     ...
keep;;
     ...
stop;;
ask;;
     ...
     `ask` is a deprecated synonym of `stop`

>>> +`keep`;;
>>> +     The empty commit will be kept.
>>> +`ask`;;
>>> +     The rebase will halt when the empty commit is applied, allowing you to
>>> +     choose whether to drop it, edit files more, or just commit the empty
>>> +     changes. This option is implied when `--interactive` is specified.
>>>        Other options, like `--exec`, will use the default of drop unless
>>>        `-i`/`--interactive` is explicitly specified.
>>
>> Thanks for adding a bit more detail about the default, however it looks
>> to me like we keep commits that become empty when --exec is specified
>>
>>          if (options.empty == EMPTY_UNSPECIFIED) {
>>                  if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
>>                          options.empty = EMPTY_STOP;
>>                  else if (options.exec.nr > 0)
>>                          options.empty = EMPTY_KEEP;
>>                  else
>>                          options.empty = EMPTY_DROP;
>>          }
>>
>> Off the top of my head I'm not sure why or if that is a good idea.
> 
> The two lines indicating this behavior are actually pre-existing -- I
> did not change them in this patch and thus didn't even think to fact
> check them.
> 
> Upon testing this, I've confirmed that you are correct about the actual
> behavior. I will address this in a separate commit in v2.

Thanks, I'd missed that those were context lines in the diff.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop`
  2024-01-27 21:49     ` Brian Lyles
@ 2024-02-01 14:02       ` Phillip Wood
  0 siblings, 0 replies; 118+ messages in thread
From: Phillip Wood @ 2024-02-01 14:02 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, me, newren

Hi Brian

On 27/01/2024 21:49, Brian Lyles wrote:
> On Tue, Jan 23, 2024 at 8:24 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 19/01/2024 05:59, brianmlyles@gmail.com wrote:
>>> From: Brian Lyles <brianmlyles@gmail.com>
>>>
>>> When `git-am` got its own `--empty` option in 7c096b8d61 (am: support
>>> --empty=<option> to handle empty patches, 2021-12-09), `stop` was used
>>> instead of `ask`. `stop` is a more accurate term for describing what
>>> really happens,
>>
>> I can see your reasoning but I think of stopping as git's way of asking
>> what to do so I'm not sure if "stop" is better than "ask". I don't know
>> how we ended up with two different terms - the prior art is "ask" so
>> maybe we should change "am --empty" instead. Lets see what others think.
> 
> The suggestion to use 'stop' instead of 'ask' for rebase was initially
> Elijah's[1], which I agreed with. I am certainly open to others'
> opinions here though, and am content with whatever is decided. I am
> mostly aiming for consistency between git-rebase(1), git-am(1), and
> ultimately git-cherry-pick(1).
> 
> [1]: https://lore.kernel.org/git/CABPp-BGJfvBhO_zEX8nLoa8WNsjmwvtZ2qOjmYm9iPoZg4SwPw@mail.gmail.com/

Thanks for the link, that is useful context

>> It would be helpful to mention the tests in the commit message - we end
>> up with a mixture of "--empty=ask" and "--empty=stop" I assume that is
>> by design
> 
> You are correct -- the intent being to ensure that `--ask` continues
> working for as long as it is supported. I'll add this to the message in
> v2.

That makes sense,

Thanks

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-02-01 10:57     ` Phillip Wood
@ 2024-02-10  4:34       ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  4:34 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren, gitster

Hi Phillip

On Thu, Feb 1, 2024 at 4:57 AM Phillip Wood <phillip.wood123@gmail.com>
wrote:

> That sounds like a good strategy. I'm wondering if we should not change 
> the behavior of `--keep-redundant-commits` to avoid breaking existing 
> users but have `--empty=keep|drop` not imply `--allow-empty`. What ever 
> we do we'll annoy someone. It is confusing to have subtly different 
> behaviors for `--keep-redundant-commits` and `--empty=keep` but it 
> avoids breaking existing users. If we change `--keep-redundant-commits` 
> we potentially upset existing users but we don't confuse others with the 
> subtle difference between the two.

I am starting to come around to this approach for this particular series
just to avoid anything potentially controversial holding it up.

>>> Do you have a practical example of where you want to keep the commits
>>> that become empty but not the ones that start empty? I agree there is a
>>> distinction but I think the common case is that the user wants to keep
>>> both types of empty commit or none. I'm not against giving the user the
>>> option to keep one or the other if it is useful but I'm wary of changing
>>> the default.
>> 
>> That practical example is documented in the initial discussion[1], which
>> I should have ought to have linked in a cover letter for this series
>> (and will do so in v2). I'll avoid copying the details here, but we'd
>> very much like to be able to programmatically drop the commits that
>> become empty when doing the automated cherry-pick described there.
>> 
>> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com/
> 
> Maybe I've missed something but that seems to be an argument for 
> implementing `--empty=drop` which is completely reasonable but doesn't 
> explain why someone using `--keep-redundant-commits` would want to keep 
> the commits that become empty while dropping the commits that start empty.

Nope, you didn't miss something -- I just didn't read properly.

I don't have a concrete example here, but the behavior *feels* quite odd
to me. But I suppose that's not a good enough reason to make a breaking
change.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options
  2024-01-29 10:55       ` Phillip Wood
@ 2024-02-10  5:50         ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  5:50 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, me, newren, gitster

On Mon, Jan 29, 2024 at 4:55 AM Phillip Wood <phillip.wood123@gmail.com>
wrote:

>>>> I'm not sure that is a good idea as it is hiding an error that we didn't
>>>> hit before because we returned early.
>>>
>>> I think you're right -- Previously the error could not have been hit,
>>> but now it can. An error is still an error, and we should handle it
>>> regardless of how `allow_empty` was set. I'll address this in v2 by
>>> simply returning the error.
>> 
>> As I dig into this more, I'm noticing that this may have unintended side
>> effects that I'm unsure of. After making this change, I noticed a couple
>> of failures in the cherry-pick test suite. The others may be a knock-on
>> of this initial failure:
>> 
>>      expecting success of 3501.8 'cherry-pick on unborn branch':
>>              git checkout --orphan unborn &&
>>              git rm --cached -r . &&
>>              rm -rf * &&
>>              git cherry-pick initial &&
>>              git diff --quiet initial &&
>>              test_cmp_rev ! initial HEAD
>> 
>>      A       extra_file
>>      Switched to a new branch 'unborn'
>>      rm 'extra_file'
>>      rm 'spoo'
>>      error: could not resolve HEAD commit
>>      fatal: cherry-pick failed
>>      not ok 8 - cherry-pick on unborn branch
>>      #
>>      #               git checkout --orphan unborn &&
>>      #               git rm --cached -r . &&
>>      #               rm -rf * &&
>>      #               git cherry-pick initial &&
>>      #               git diff --quiet initial &&
>>      #               test_cmp_rev ! initial HEAD
>>      #
>> 
>> It looks like this is caused specifically by not hiding the error from
>> `index_unchanged`
> 
> Oh dear, that's a pain. I haven't checked but suspect we already hit 
> this when running
> 
>      git cherry-pick --allow-empty
> 
> on an orphan checkout. In do_pick_commit() we treat an error reading 
> HEAD as an unborn branch so I think we could do the same here. If the 
> branch is unborn then we can use the_hash_algo->empty_tree as the tree 
> to compare to.

You're correct that `git cherry-pick --allow-empty` on an unborn branch
hits the same error today, and that using `empty_tree` seems to resolve
it. Thank you for this tip, I'll include a fix and corresponding test as
a separate commit in v2.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v2 0/8] cherry-pick: add `--empty`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (5 preceding siblings ...)
  2024-01-24 11:01 ` Phillip Wood
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-22 16:39   ` phillip.wood123
  2024-02-10  7:43 ` [PATCH v2 1/8] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
                   ` (31 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

The ultimate goal of this series is to allow git-cherry-pick(1) to
automatically drop redundant commits. The mechanism chosen is an
`--empty` option that provides the same flexibility as the `--empty`
options for git-rebase(1) and git-am(1).

Some secondary goals are to improve the consistency in the values and
documentation for this option across the three commands.

See "Does extending `--empty` to git-cherry-pick make sense?" [1] for
some context for why this option is desired in git-cherry-pick(1).

[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com

Along the way, I (with some help from Elijah and Phillip) found a few
other things in the docs and related sequencer code to clean up.

Brian Lyles (8):
  docs: address inaccurate `--empty` default with `--exec`
  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  rebase: update `--empty=ask` to `--empty=drop`
  sequencer: treat error reading HEAD as unborn branch
  sequencer: do not require `allow_empty` for redundant commit options
  cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`
  cherry-pick: enforce `--keep-redundant-commits` incompatibility
  cherry-pick: add `--empty` for more robust redundant commit handling

 Documentation/git-am.txt                    | 20 ++++---
 Documentation/git-cherry-pick.txt           | 30 +++++++---
 Documentation/git-rebase.txt                | 26 ++++++---
 builtin/rebase.c                            | 16 +++--
 builtin/revert.c                            | 40 +++++++++++--
 sequencer.c                                 | 65 +++++++++++----------
 t/t3424-rebase-empty.sh                     | 55 ++++++++++++++++-
 t/t3501-revert-cherry-pick.sh               | 11 ++++
 t/t3505-cherry-pick-empty.sh                | 29 ++++++++-
 t/t3510-cherry-pick-sequence.sh             | 40 +++++++++++++
 t/t3515-cherry-pick-incompatible-options.sh | 48 +++++++++++++++
 11 files changed, 312 insertions(+), 68 deletions(-)
 create mode 100755 t/t3515-cherry-pick-incompatible-options.sh

-- 
2.43.0


^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v2 1/8] docs: address inaccurate `--empty` default with `--exec`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (6 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 0/8] cherry-pick: add `--empty` Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-10  7:43 ` [PATCH v2 2/8] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
                   ` (30 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

The documentation for git-rebase(1) indicates that using the `--exec`
option will use `--empty=drop`. This is inaccurate: when `--interactive`
is not explicitly provided, `--exec` results in `--empty=keep`
behaviors.

Correctly indicate the behavior of `--exec` using `--empty=keep` when
`--interactive` is not specified.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
Reported-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

This commit was not present in v1. It is a fix to some existing
documentation that Phillip noticed was incorrect.


 Documentation/git-rebase.txt | 10 +++++-----
 t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 150734fb39..9d7397b696 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.
 	empty after rebasing (because they contain a subset of already
 	upstream changes).  With drop (the default), commits that
 	become empty are dropped.  With keep, such commits are kept.
-	With ask (implied by `--interactive`), the rebase will halt when
-	an empty commit is applied allowing you to choose whether to
-	drop it, edit files more, or just commit the empty changes.
-	Other options, like `--exec`, will use the default of drop unless
-	`-i`/`--interactive` is explicitly specified.
+	With ask, the rebase will halt when an empty commit is applied
+	allowing you to choose whether to drop it, edit files more, or just
+	commit the empty changes.
+	When the `-i`/`--interactive` option is used, the default becomes ask.
+	Otherwise, when the `--exec` option is used, the default becomes keep.
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a0af..73ff35ced2 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
 	test_path_is_missing .git/MERGE_MSG
 '

+test_expect_success 'rebase --exec --empty=drop' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=drop upstream &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=keep upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=ask' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 2/8] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (7 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 1/8] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-10  7:43 ` [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop` Brian Lyles
                   ` (29 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

Both of these pages document very similar `--empty` options, but with
different styles. The exact behavior of these `--empty` options differs
somewhat, but consistent styling in the docs is still beneficial. This
commit aims to make them more consistent.

Break the possible values for `--empty` into separate sections for
readability. Alphabetical order is chosen for consistency.

In a future commit, we'll be documenting a new `--empty` option for
git-cherry-pick(1), making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Changes since v1:
- Options are now listed in alphabetical order per Phillip's
  recommendation.


 Documentation/git-am.txt     | 20 +++++++++++++-------
 Documentation/git-rebase.txt | 25 ++++++++++++++++---------
 2 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e080458d6c..f852e0ba79 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -66,13 +66,19 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).

---empty=(stop|drop|keep)::
-	By default, or when the option is set to 'stop', the command
-	errors out on an input e-mail message lacking a patch
-	and stops in the middle of the current am session. When this
-	option is set to 'drop', skip such an e-mail message instead.
-	When this option is set to 'keep', create an empty commit,
-	recording the contents of the e-mail message as its log.
+--empty=(drop|keep|stop)::
+	How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+	The e-mail message will be skipped.
+`keep`;;
+	An empty commit will be created, with the contents of the e-mail
+	message as its log.
+`stop`;;
+	The command will fail, stopping in the middle of the current `am`
+	session. This is the default behavior.
+--

 -m::
 --message-id::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9d7397b696..68cdebd2aa 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.

---empty=(drop|keep|ask)::
+--empty=(ask|drop|keep)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
-	upstream changes).  With drop (the default), commits that
-	become empty are dropped.  With keep, such commits are kept.
-	With ask, the rebase will halt when an empty commit is applied
-	allowing you to choose whether to drop it, edit files more, or just
-	commit the empty changes.
-	When the `-i`/`--interactive` option is used, the default becomes ask.
-	Otherwise, when the `--exec` option is used, the default becomes keep.
+	upstream changes):
++
+--
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified.
+`drop`;;
+	The commit will be dropped. This is the default behavior.
+`keep`;;
+	The commit will be kept. This option is implied when `--exec` is
+	specified unless `-i`/`--interactive` is also specified.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
@@ -698,7 +705,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(drop|keep|ask)` option for changing the behavior
+also has an `--empty=(ask|drop|keep)` option for changing the behavior
 of handling commits that become empty.

 Directory rename detection
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (8 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 2/8] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-11  4:54   ` Brian Lyles
  2024-02-22 16:34   ` phillip.wood123
  2024-02-10  7:43 ` [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch Brian Lyles
                   ` (28 subsequent siblings)
  38 siblings, 2 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09), `stop` was used
instead of `ask`. `stop` is a more accurate term for describing what
really happens, and consistency is good.

Update git-rebase(1) to also use `stop`, while keeping `ask` as a
deprecated synonym. Update the tests to primarily use `stop`, but also
ensure that `ask` is still allowed.

In a future commit, we'll be adding a new `--empty` option for
git-cherry-pick(1) as well, making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
Reported-by: Elijah Newren <newren@gmail.com>
---
 Documentation/git-rebase.txt | 15 ++++++++-------
 builtin/rebase.c             | 16 ++++++++++------
 t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 68cdebd2aa..6f64084a95 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(ask|drop|keep)::
+--empty=(drop|keep|stop)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
 	upstream changes):
 +
 --
-`ask`;;
-	The rebase will halt when the commit is applied, allowing you to
-	choose whether to drop it, edit files more, or just commit the empty
-	changes. This option is implied when `-i`/`--interactive` is
-	specified.
 `drop`;;
 	The commit will be dropped. This is the default behavior.
 `keep`;;
 	The commit will be kept. This option is implied when `--exec` is
 	specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified. `ask` is a deprecated synonym of `stop`.
 --
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
@@ -705,7 +706,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(ask|drop|keep)` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 4084a6abb8..3b9bb2fa06 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -58,7 +58,7 @@ enum empty_type {
 	EMPTY_UNSPECIFIED = -1,
 	EMPTY_DROP,
 	EMPTY_KEEP,
-	EMPTY_ASK
+	EMPTY_STOP
 };
 
 enum action {
@@ -954,10 +954,14 @@ static enum empty_type parse_empty_value(const char *value)
 		return EMPTY_DROP;
 	else if (!strcasecmp(value, "keep"))
 		return EMPTY_KEEP;
-	else if (!strcasecmp(value, "ask"))
-		return EMPTY_ASK;
+	else if (!strcasecmp(value, "stop"))
+		return EMPTY_STOP;
+	else if (!strcasecmp(value, "ask")) {
+		warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+		return EMPTY_STOP;
+	}
 
-	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
 }
 
 static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1136,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				 "instead of ignoring them"),
 			      1, PARSE_OPT_HIDDEN),
 		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
+		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
 			       N_("how to handle commits that become empty"),
 			       PARSE_OPT_NONEG, parse_opt_empty),
 		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1553,7 +1557,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (options.empty == EMPTY_UNSPECIFIED) {
 		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-			options.empty = EMPTY_ASK;
+			options.empty = EMPTY_STOP;
 		else if (options.exec.nr > 0)
 			options.empty = EMPTY_KEEP;
 		else
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 73ff35ced2..1ee6b00fd5 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --merge --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --interactive --empty=ask upstream &&
+	test_must_fail git rebase --interactive --empty=stop upstream &&
 
 	git rebase --skip &&
 
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --interactive upstream &&
 
@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --exec --empty=ask' '
+test_expect_success 'rebase --exec --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+	test_must_fail git rebase --exec "true" --empty=stop upstream &&
 
 	git rebase --skip &&
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (9 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop` Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-22 16:34   ` phillip.wood123
  2024-02-10  7:43 ` [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
                   ` (27 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

When using git-cherry-pick(1) with `--allow-empty` while on an unborn
branch, an error is thrown. This is inconsistent with the same
cherry-pick when `--allow-empty` is not specified.

Treat a failure reading HEAD as an unborn branch in
`is_index_unchanged`. This is consistent with other sequencer logic such
as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as
the tree to compare against.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

This is another new commit that was not present in v1.

See this comment[1] from Phillip for context.

[1]: https://lore.kernel.org/git/b5213705-4cd6-40ef-8c5f-32b214534b8b@gmail.com/


 sequencer.c                   | 36 ++++++++++++++++++++---------------
 t/t3501-revert-cherry-pick.sh | 11 +++++++++++
 2 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 3cc88d8a80..b1b19512de 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -769,30 +769,36 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
 
 static int is_index_unchanged(struct repository *r)
 {
-	struct object_id head_oid, *cache_tree_oid;
+	struct object_id head_oid, *cache_tree_oid, head_tree_oid;
 	struct commit *head_commit;
 	struct index_state *istate = r->index;
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-		return error(_("could not resolve HEAD commit"));
+	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+		/*
+		 * Treat an error reading HEAD as an unborn branch.
+		 */
+		head_tree_oid = *the_hash_algo->empty_tree;
+	} else {
+		head_commit = lookup_commit(r, &head_oid);
 
-	head_commit = lookup_commit(r, &head_oid);
+		/*
+		 * If head_commit is NULL, check_commit, called from
+		 * lookup_commit, would have indicated that head_commit is not
+		 * a commit object already.  repo_parse_commit() will return failure
+		 * without further complaints in such a case.  Otherwise, if
+		 * the commit is invalid, repo_parse_commit() will complain.  So
+		 * there is nothing for us to say here.  Just return failure.
+		 */
+		if (repo_parse_commit(r, head_commit))
+			return -1;
 
-	/*
-	 * If head_commit is NULL, check_commit, called from
-	 * lookup_commit, would have indicated that head_commit is not
-	 * a commit object already.  repo_parse_commit() will return failure
-	 * without further complaints in such a case.  Otherwise, if
-	 * the commit is invalid, repo_parse_commit() will complain.  So
-	 * there is nothing for us to say here.  Just return failure.
-	 */
-	if (repo_parse_commit(r, head_commit))
-		return -1;
+		head_tree_oid = *get_commit_tree_oid(head_commit);
+	}
 
 	if (!(cache_tree_oid = get_cache_tree_oid(istate)))
 		return -1;
 
-	return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+	return oideq(cache_tree_oid, &head_tree_oid);
 }
 
 static int write_author_script(const char *message)
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index aeab689a98..390e0ed186 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -112,6 +112,17 @@ test_expect_success 'cherry-pick on unborn branch' '
 	test_cmp_rev ! initial HEAD
 '
 
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+	git checkout main &&
+	git branch -D unborn &&
+	git checkout --orphan unborn &&
+	git rm --cached -r . &&
+	rm -rf * &&
+	git cherry-pick initial --allow-empty &&
+	git diff --quiet initial &&
+	test_cmp_rev ! initial HEAD
+'
+
 test_expect_success 'cherry-pick "-" to pick from previous branch' '
 	git checkout unborn &&
 	test_commit to-pick actual content &&
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (10 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-22 16:35   ` phillip.wood123
  2024-02-10  7:43 ` [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits` Brian Lyles
                   ` (26 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

A consumer of the sequencer that wishes to take advantage of either the
`keep_redundant_commits` or `drop_redundant_commits` feature must also
specify `allow_empty`. However, these refer to two distinct types of
empty commits:

- `allow_empty` refers specifically to commits which start empty
- `keep_redundant_commits` refers specifically to commits that do not
  start empty, but become empty due to the content already existing in
  the target history

Conceptually, there is no reason that the behavior for handling one of
these should be entangled with the other. It is particularly unintuitive
to require `allow_empty` in order for `drop_redundant_commits` to have
an effect: in order to prevent redundant commits automatically,
initially-empty commits would need to be kept automatically as well.

Instead, rewrite the `allow_empty()` logic to remove the over-arching
requirement that `allow_empty` be specified in order to reach any of the
keep/drop behaviors. Only if the commit was originally empty will
`allow_empty` have an effect.

Note that no behavioral changes should result from this commit -- it
merely sets the stage for future commits. In one such future commit, an
`--empty` option will be added to git-cherry-pick(1), meaning that
`drop_redundant_commits` will be used by that command.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

This is the first half of the first commit[1] in v1, which has now been
split up. While the next commit may be considered somewhat
controversial, this part of the change should not be.

[1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/

 sequencer.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index b1b19512de..3f41863dae 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1725,34 +1725,25 @@ static int allow_empty(struct repository *r,
 	int index_unchanged, originally_empty;

 	/*
-	 * Four cases:
+	 * For a commit that is initially empty, allow_empty determines if it
+	 * should be kept or not
 	 *
-	 * (1) we do not allow empty at all and error out.
-	 *
-	 * (2) we allow ones that were initially empty, and
-	 *     just drop the ones that become empty
-	 *
-	 * (3) we allow ones that were initially empty, but
-	 *     halt for the ones that become empty;
-	 *
-	 * (4) we allow both.
+	 * For a commit that becomes empty, keep_redundant_commits and
+	 * drop_redundant_commits determine whether the commit should be kept or
+	 * dropped. If neither is specified, halt.
 	 */
-	if (!opts->allow_empty)
-		return 0; /* let "git commit" barf as necessary */
-
 	index_unchanged = is_index_unchanged(r);
 	if (index_unchanged < 0)
 		return index_unchanged;
 	if (!index_unchanged)
 		return 0; /* we do not have to say --allow-empty */

-	if (opts->keep_redundant_commits)
-		return 1;
-
 	originally_empty = is_original_commit_empty(commit);
 	if (originally_empty < 0)
 		return originally_empty;
 	if (originally_empty)
+		return opts->allow_empty;
+	else if (opts->keep_redundant_commits)
 		return 1;
 	else if (opts->drop_redundant_commits)
 		return 2;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (11 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-22 16:35   ` Phillip Wood
  2024-02-10  7:43 ` [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
                   ` (25 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

As noted in the git-cherry-pick(1) docs, `--keep-redundant-commits`
implies `--allow-empty`, despite the two having distinct,
non-overlapping meanings:

- `allow_empty` refers specifically to commits which start empty, as
  indicated by the documentation for `--allow-empty` within
  git-cherry-pick(1):

  "Note also, that use of this option only keeps commits that were
  initially empty (i.e. the commit recorded the same tree as its
  parent). Commits which are made empty due to a previous commit are
  dropped. To force the inclusion of those commits use
  --keep-redundant-commits."

- `keep_redundant_commits` refers specifically to commits that do not
  start empty, but become empty due to the content already existing in
  the target history. This is indicated by the documentation for
  `--keep-redundant-commits` within git-cherry-pick(1):

  "If a commit being cherry picked duplicates a commit already in the
  current history, it will become empty. By default these redundant
  commits cause cherry-pick to stop so the user can examine the commit.
  This option overrides that behavior and creates an empty commit
  object. Implies --allow-empty."

This implication of `--allow-empty` therefore seems incorrect: One
should be able to keep a commit that becomes empty without also being
forced to pick commits that start as empty. However, the following
series of commands results in both the commit that became empty and the
commit that started empty being picked, despite only
`--keep-redundant-commits` being specified:

    git init
    echo "a" >test
    git add test
    git commit -m "Initial commit"
    echo "b" >test
    git commit -am "a -> b"
    git commit --allow-empty -m "empty"
    git cherry-pick --keep-redundant-commits HEAD^ HEAD

The same cherry-pick with `--allow-empty` would fail on the redundant
commit, and with neither option would fail on the empty commit.

Do not imply `--allow-empty` when using `--keep-redundant-commits` with
git-cherry-pick(1).

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

This is the second half of the first commit[1] in v1, which has now been
split up.

This commit proposes a breaking change, albeit one that seems correct
and relatively minor to me. If this change is deemed too controversial,
I am prepared to drop it from the series. See Junio's[2] and
Phillip's[3] comments on v1 for additional context.

[1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/
[2]: https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/
[3]: https://lore.kernel.org/git/8ff4650c-f84f-41bd-a46c-3b845ff29b70@gmail.com/

 Documentation/git-cherry-pick.txt | 10 +++++++---
 builtin/revert.c                  |  4 ----
 t/t3505-cherry-pick-empty.sh      |  6 ++++++
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d200..c88bb88822 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,8 +131,8 @@ effect to your index in a row.
 	even without this option.  Note also, that use of this option only
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
-	previous commit are dropped.  To force the inclusion of those commits
-	use `--keep-redundant-commits`.
+	previous commit will cause the cherry-pick to fail.  To force the
+	inclusion of those commits, use `--keep-redundant-commits`.

 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
@@ -144,7 +144,11 @@ effect to your index in a row.
 	current history, it will become empty.  By default these
 	redundant commits cause `cherry-pick` to stop so the user can
 	examine the commit. This option overrides that behavior and
-	creates an empty commit object.  Implies `--allow-empty`.
+	creates an empty commit object. Note that use of this option only
+	results in an empty commit when the commit was not initially empty,
+	but rather became empty due to a previous commit. Commits that were
+	initially empty will cause the cherry-pick to fail. To force the
+	inclusion of those commits use `--allow-empty`.

 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index 89821bab95..d83977e36e 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -134,10 +134,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;

-	/* implies allow_empty */
-	if (opts->keep_redundant_commits)
-		opts->allow_empty = 1;
-
 	if (cleanup_arg) {
 		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
 		opts->explicit_cleanup = 1;
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38d5a..2709cfc677 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -59,6 +59,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
 	test_must_fail git cherry-pick empty-change-branch
 '

+test_expect_success 'cherry pick an empty non-ff commit with --keep-redundant-commits' '
+	git checkout main &&
+	test_must_fail git cherry-pick --keep-redundant-commits empty-change-branch 2>output &&
+	test_grep "The previous cherry-pick is now empty" output
+'
+
 test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
 	git checkout main &&
 	git cherry-pick --allow-empty empty-change-branch
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (12 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits` Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-22 16:35   ` Phillip Wood
  2024-02-10  7:43 ` [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
                   ` (24 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When `--keep-redundant-commits` was added in  b27cfb0d8d
(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was
not marked as incompatible with the various operations needed to
continue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and
`--quit`).

Enforce this incompatibility via `verify_opt_compatible` like we do for
the other various options.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

This commit was not present in v1 either. It addresses an existing issue
that I noticed after Phillip pointed out the same deficiency for my new
`--empty` option introduced in the ultimate commit in this series.

[1]: https://lore.kernel.org/git/CAHPHrSf+joHe6ikErHLgWrk-_qjSROS-dXCHagxWGDAF=2deDg@mail.gmail.com/

 builtin/revert.c                            |  1 +
 t/t3515-cherry-pick-incompatible-options.sh | 34 +++++++++++++++++++++
 2 files changed, 35 insertions(+)
 create mode 100755 t/t3515-cherry-pick-incompatible-options.sh

diff --git a/builtin/revert.c b/builtin/revert.c
index d83977e36e..48c426f277 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -163,6 +163,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--ff", opts->allow_ff,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+				"--keep-redundant-commits", opts->keep_redundant_commits,
 				NULL);
 	}
 
diff --git a/t/t3515-cherry-pick-incompatible-options.sh b/t/t3515-cherry-pick-incompatible-options.sh
new file mode 100755
index 0000000000..6100ab64fd
--- /dev/null
+++ b/t/t3515-cherry-pick-incompatible-options.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='test if cherry-pick detects and aborts on incompatible options'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	echo first > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "first" &&
+
+	echo second > file1 &&
+	git add file1 &&
+	test_tick &&
+	git commit -m "second"
+'
+
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
+test_done
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (13 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
@ 2024-02-10  7:43 ` Brian Lyles
  2024-02-11 20:50   ` Jean-Noël AVILA
  2024-02-22 16:36   ` phillip.wood123
  2024-03-10 18:41 ` [PATCH v3 0/7] " Brian Lyles
                   ` (23 subsequent siblings)
  38 siblings, 2 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-10  7:43 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a
commit being made redundant if the content from the picked commit is
already present in the target history. However, git-cherry-pick(1) does
not have the same options available that git-rebase(1) and git-am(1) have.

There are three things that can be done with these redundant commits:
drop them, keep them, or have the cherry-pick stop and wait for the user
to take an action. git-rebase(1) has the `--empty` option added in commit
e98c4269c8 (rebase (interactive-backend): fix handling of commits that
become empty, 2020-02-15), which handles all three of these scenarios.
Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09).

git-cherry-pick(1), on the other hand, only supports two of the three
possiblities: Keep the redundant commits via `--keep-redundant-commits`,
or have the cherry-pick fail by not specifying that option. There is no
way to automatically drop redundant commits.

In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and
git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It
has the same three options (keep, drop, and stop), and largely behaves
the same. The notable difference is that for git-cherry-pick(1), the
default will be `stop`, which maintains the current behavior when the
option is not specified.

The `--keep-redundant-commits` option will be documented as a deprecated
synonym of `--empty=keep`, and will be supported for backwards
compatibility for the time being.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-cherry-pick.txt           | 32 +++++++++++------
 builtin/revert.c                            | 37 ++++++++++++++++++-
 sequencer.c                                 |  6 ++++
 t/t3505-cherry-pick-empty.sh                | 23 +++++++++++-
 t/t3510-cherry-pick-sequence.sh             | 40 +++++++++++++++++++++
 t/t3515-cherry-pick-incompatible-options.sh | 14 ++++++++
 6 files changed, 140 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index c88bb88822..a444d960b1 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -132,23 +132,35 @@ effect to your index in a row.
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
 	previous commit will cause the cherry-pick to fail.  To force the
-	inclusion of those commits, use `--keep-redundant-commits`.
+	inclusion of those commits, use `--empty=keep`.

 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
 	This option overrides that behavior, allowing commits with empty
 	messages to be cherry picked.

+--empty=(drop|keep|stop)::
+	How to handle commits being cherry-picked that are redundant with
+	changes already in the current history.
++
+--
+`drop`;;
+	The commit will be dropped.
+`keep`;;
+	The commit will be kept.
+`stop`;;
+	The cherry-pick will stop when the commit is applied, allowing
+	you to examine the commit. This is the default behavior.
+--
++
+Note that this option species how to handle a commit that was not initially
+empty, but rather became empty due to a previous commit. Commits that were
+initially empty will cause the cherry-pick to fail. To force the inclusion of
+those commits, use `--allow-empty`.
++
+
 --keep-redundant-commits::
-	If a commit being cherry picked duplicates a commit already in the
-	current history, it will become empty.  By default these
-	redundant commits cause `cherry-pick` to stop so the user can
-	examine the commit. This option overrides that behavior and
-	creates an empty commit object. Note that use of this option only
-	results in an empty commit when the commit was not initially empty,
-	but rather became empty due to a previous commit. Commits that were
-	initially empty will cause the cherry-pick to fail. To force the
-	inclusion of those commits use `--allow-empty`.
+	Deprecated synonym for `--empty=keep`.

 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index 48c426f277..27efb6284b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
 	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }

+enum empty_action {
+	EMPTY_COMMIT_UNSPECIFIED = 0,
+	STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "stop"))
+		*opt_value = STOP_ON_EMPTY_COMMIT;
+	else if (!strcmp(arg, "drop"))
+		*opt_value = DROP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "keep"))
+		*opt_value = KEEP_EMPTY_COMMIT;
+	else
+		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+	return 0;
+}
+
 static int option_parse_m(const struct option *opt,
 			  const char *arg, int unset)
 {
@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	const char *cleanup_arg = NULL;
+	enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+				       N_("how to handle commits that become empty"),
+				       PARSE_OPT_NONEG, parse_opt_empty),
 			OPT_END(),
 		};
 		options = parse_options_concat(options, cp_extra);
@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;

+	if (opts->action == REPLAY_PICK) {
+		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+	}
+
 	if (cleanup_arg) {
 		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
 		opts->explicit_cleanup = 1;
@@ -164,6 +198,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
 				"--keep-redundant-commits", opts->keep_redundant_commits,
+				"--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
 				NULL);
 	}

diff --git a/sequencer.c b/sequencer.c
index 3f41863dae..509a5244d2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2921,6 +2921,9 @@ static int populate_opts_cb(const char *key, const char *value,
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+	else if (!strcmp(key, "options.drop-redundant-commits"))
+		opts->drop_redundant_commits =
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3465,6 +3468,9 @@ static int save_opts(struct replay_opts *opts)
 	if (opts->allow_empty_message)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.allow-empty-message", "true");
+	if (opts->drop_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.drop-redundant-commits", "true");
 	if (opts->keep_redundant_commits)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.keep-redundant-commits", "true");
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 2709cfc677..669416c158 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -90,7 +90,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
 	git commit -m "add file2 on the side"
 '

-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
 	git reset --hard &&
 	git checkout fork^0 &&
 	test_must_fail git cherry-pick main
@@ -105,4 +105,25 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
 	test_cmp expect actual
 '

+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick --empty=stop main 2>output &&
+	test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=drop main &&
+	test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=keep main &&
+	test_commit_message HEAD -m "add file2 on main"
+'
+
 test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 72020a51c4..5f6c45dfe3 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,46 @@ test_expect_success 'cherry-pick persists opts correctly' '
 	test_cmp expect actual
 '

+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+	pristine_detach initial &&
+	# to make sure that the session to cherry-pick a sequence
+	# gets interrupted, use a high-enough number that is larger
+	# than the number of parents of any commit we have created
+	mainline=4 &&
+	test_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&
+	test_path_is_file .git/sequencer/opts &&
+	test_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&
+	test_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+	pristine_detach initial &&
+	# to make sure that the session to cherry-pick a sequence
+	# gets interrupted, use a high-enough number that is larger
+	# than the number of parents of any commit we have created
+	mainline=4 &&
+	test_expect_code 128 git cherry-pick -s -m $mainline --empty=drop initial..anotherpick &&
+	test_path_is_file .git/sequencer/opts &&
+	test_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&
+	echo "true" >expect &&
+	git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+	pristine_detach initial &&
+	# to make sure that the session to cherry-pick a sequence
+	# gets interrupted, use a high-enough number that is larger
+	# than the number of parents of any commit we have created
+	mainline=4 &&
+	test_expect_code 128 git cherry-pick -s -m $mainline --empty=keep initial..anotherpick &&
+	test_path_is_file .git/sequencer/opts &&
+	echo "true" >expect &&
+	git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits >actual &&
+	test_cmp expect actual &&
+	test_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits
+'
+
 test_expect_success 'revert persists opts correctly' '
 	pristine_detach initial &&
 	# to make sure that the session to revert a sequence
diff --git a/t/t3515-cherry-pick-incompatible-options.sh b/t/t3515-cherry-pick-incompatible-options.sh
index 6100ab64fd..b2780fdbf3 100755
--- a/t/t3515-cherry-pick-incompatible-options.sh
+++ b/t/t3515-cherry-pick-incompatible-options.sh
@@ -31,4 +31,18 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '
 	git cherry-pick --abort
 '

+test_expect_success '--empty is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
 test_done
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`
  2024-02-10  7:43 ` [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop` Brian Lyles
@ 2024-02-11  4:54   ` Brian Lyles
  2024-02-14 11:05     ` Phillip Wood
  2024-02-22 16:34   ` phillip.wood123
  1 sibling, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-11  4:54 UTC (permalink / raw)
  To: git; +Cc: newren, me, phillip.wood123, gitster

I just noticed that I incorrectly specified `--empty=drop` in the
subject of this commit. It should read "rebase: update `--empty=ask` to
`--empty=stop`". This will need to be corrected in a v3 reroll.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-10  7:43 ` [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
@ 2024-02-11 20:50   ` Jean-Noël AVILA
  2024-02-12  1:35     ` Brian Lyles
  2024-02-22 16:36   ` phillip.wood123
  1 sibling, 1 reply; 118+ messages in thread
From: Jean-Noël AVILA @ 2024-02-11 20:50 UTC (permalink / raw)
  To: git, Brian Lyles

Hello,

There's a typo.

On Saturday, 10 February 2024 08:43:56 CET Brian Lyles wrote:
> +--
> ++
> +Note that this option species how to handle a commit that was not initially

this option *specifies*

> +empty, but rather became empty due to a previous commit. Commits that were
> +initially empty will cause the cherry-pick to fail. To force the inclusion of
> +those commits, use `--allow-empty`.
> ++
> +
>  --keep-redundant-commits::
> -	If a commit being cherry picked duplicates a commit already in the
> -	current history, it will become empty.  By default these
> -	redundant commits cause `cherry-pick` to stop so the user can
> -	examine the commit. This option overrides that behavior and
> -	creates an empty commit object. Note that use of this option only
> -	results in an empty commit when the commit was not initially empty,
> -	but rather became empty due to a previous commit. Commits that were
> -	initially empty will cause the cherry-pick to fail. To force the
> -	inclusion of those commits use `--allow-empty`.
> +	Deprecated synonym for `--empty=keep`.
> 
>  --strategy=<strategy>::
>  	Use the given merge strategy.  Should only be used once.

Otherwise, documentation looks good to me.

JN



^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-11 20:50   ` Jean-Noël AVILA
@ 2024-02-12  1:35     ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-12  1:35 UTC (permalink / raw)
  To: Jean-Noël AVILA; +Cc: git

On Sun, Feb 11, 2024 at 2:50 PM Jean-Noël AVILA <jn.avila@free.fr>
wrote:

> Hello,
> 
> There's a typo.
> 
> On Saturday, 10 February 2024 08:43:56 CET Brian Lyles wrote:
>> +--
>> ++
>> +Note that this option species how to handle a commit that was not initially
> 
> this option *specifies*

Thank you for catching this, I'll correct it in v3.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`
  2024-02-11  4:54   ` Brian Lyles
@ 2024-02-14 11:05     ` Phillip Wood
  0 siblings, 0 replies; 118+ messages in thread
From: Phillip Wood @ 2024-02-14 11:05 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 11/02/2024 04:54, Brian Lyles wrote:
> I just noticed that I incorrectly specified `--empty=drop` in the
> subject of this commit. It should read "rebase: update `--empty=ask` to
> `--empty=stop`". This will need to be corrected in a v3 reroll.

Thanks for flagging that, I'm afraid it will probably be next week 
before I take a proper look at these

Best Wishes

Phillip



^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`
  2024-02-10  7:43 ` [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop` Brian Lyles
  2024-02-11  4:54   ` Brian Lyles
@ 2024-02-22 16:34   ` phillip.wood123
  2024-02-22 18:27     ` Junio C Hamano
  1 sibling, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-02-22 16:34 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support
> --empty=<option> to handle empty patches, 2021-12-09), `stop` was used
> instead of `ask`. `stop` is a more accurate term for describing what
> really happens, and consistency is good.
> 
> Update git-rebase(1) to also use `stop`, while keeping `ask` as a
> deprecated synonym. Update the tests to primarily use `stop`, but also
> ensure that `ask` is still allowed.
> 
> In a future commit, we'll be adding a new `--empty` option for
> git-cherry-pick(1) as well, making the consistency even more relevant.

I'm sightly nervous of deprecating "ask" as the warnings have the 
potential to annoy users but it would be good to use consistent 
terminology so it may well be worth it. This patch the previous ones 
look good apart from one minor issue ...

> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> Reported-by: Elijah Newren <newren@gmail.com>

I think we normally put Reported-by: and Helped-by: etc above the patch 
authors Signed-off-by: trailer.

Best Wishes

Phillip

> ---
>   Documentation/git-rebase.txt | 15 ++++++++-------
>   builtin/rebase.c             | 16 ++++++++++------
>   t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----
>   3 files changed, 34 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index 68cdebd2aa..6f64084a95 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
>   +
>   See also INCOMPATIBLE OPTIONS below.
>   
> ---empty=(ask|drop|keep)::
> +--empty=(drop|keep|stop)::
>   	How to handle commits that are not empty to start and are not
>   	clean cherry-picks of any upstream commit, but which become
>   	empty after rebasing (because they contain a subset of already
>   	upstream changes):
>   +
>   --
> -`ask`;;
> -	The rebase will halt when the commit is applied, allowing you to
> -	choose whether to drop it, edit files more, or just commit the empty
> -	changes. This option is implied when `-i`/`--interactive` is
> -	specified.
>   `drop`;;
>   	The commit will be dropped. This is the default behavior.
>   `keep`;;
>   	The commit will be kept. This option is implied when `--exec` is
>   	specified unless `-i`/`--interactive` is also specified.
> +`stop`;;
> +`ask`;;
> +	The rebase will halt when the commit is applied, allowing you to
> +	choose whether to drop it, edit files more, or just commit the empty
> +	changes. This option is implied when `-i`/`--interactive` is
> +	specified. `ask` is a deprecated synonym of `stop`.
>   --
>   +
>   Note that commits which start empty are kept (unless `--no-keep-empty`
> @@ -705,7 +706,7 @@ be dropped automatically with `--no-keep-empty`).
>   Similar to the apply backend, by default the merge backend drops
>   commits that become empty unless `-i`/`--interactive` is specified (in
>   which case it stops and asks the user what to do).  The merge backend
> -also has an `--empty=(ask|drop|keep)` option for changing the behavior
> +also has an `--empty=(drop|keep|stop)` option for changing the behavior
>   of handling commits that become empty.
>   
>   Directory rename detection
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 4084a6abb8..3b9bb2fa06 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -58,7 +58,7 @@ enum empty_type {
>   	EMPTY_UNSPECIFIED = -1,
>   	EMPTY_DROP,
>   	EMPTY_KEEP,
> -	EMPTY_ASK
> +	EMPTY_STOP
>   };
>   
>   enum action {
> @@ -954,10 +954,14 @@ static enum empty_type parse_empty_value(const char *value)
>   		return EMPTY_DROP;
>   	else if (!strcasecmp(value, "keep"))
>   		return EMPTY_KEEP;
> -	else if (!strcasecmp(value, "ask"))
> -		return EMPTY_ASK;
> +	else if (!strcasecmp(value, "stop"))
> +		return EMPTY_STOP;
> +	else if (!strcasecmp(value, "ask")) {
> +		warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
> +		return EMPTY_STOP;
> +	}
>   
> -	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
> +	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
>   }
>   
>   static int parse_opt_keep_empty(const struct option *opt, const char *arg,
> @@ -1136,7 +1140,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   				 "instead of ignoring them"),
>   			      1, PARSE_OPT_HIDDEN),
>   		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
> -		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
> +		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
>   			       N_("how to handle commits that become empty"),
>   			       PARSE_OPT_NONEG, parse_opt_empty),
>   		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
> @@ -1553,7 +1557,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   
>   	if (options.empty == EMPTY_UNSPECIFIED) {
>   		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
> -			options.empty = EMPTY_ASK;
> +			options.empty = EMPTY_STOP;
>   		else if (options.exec.nr > 0)
>   			options.empty = EMPTY_KEEP;
>   		else
> diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
> index 73ff35ced2..1ee6b00fd5 100755
> --- a/t/t3424-rebase-empty.sh
> +++ b/t/t3424-rebase-empty.sh
> @@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
>   	test_cmp expect actual
>   '
>   
> +test_expect_success 'rebase --merge --empty=stop' '
> +	git checkout -B testing localmods &&
> +	test_must_fail git rebase --merge --empty=stop upstream &&
> +
> +	git rebase --skip &&
> +
> +	test_write_lines D C B A >expect &&
> +	git log --format=%s >actual &&
> +	test_cmp expect actual
> +'
> +
>   test_expect_success 'rebase --merge --empty=ask' '
>   	git checkout -B testing localmods &&
>   	test_must_fail git rebase --merge --empty=ask upstream &&
> @@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
>   	test_cmp expect actual
>   '
>   
> -test_expect_success 'rebase --interactive --empty=ask' '
> +test_expect_success 'rebase --interactive --empty=stop' '
>   	git checkout -B testing localmods &&
> -	test_must_fail git rebase --interactive --empty=ask upstream &&
> +	test_must_fail git rebase --interactive --empty=stop upstream &&
>   
>   	git rebase --skip &&
>   
> @@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
>   	test_cmp expect actual
>   '
>   
> -test_expect_success 'rebase --interactive uses default of --empty=ask' '
> +test_expect_success 'rebase --interactive uses default of --empty=stop' '
>   	git checkout -B testing localmods &&
>   	test_must_fail git rebase --interactive upstream &&
>   
> @@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '
>   	test_cmp expect actual
>   '
>   
> -test_expect_success 'rebase --exec --empty=ask' '
> +test_expect_success 'rebase --exec --empty=stop' '
>   	git checkout -B testing localmods &&
> -	test_must_fail git rebase --exec "true" --empty=ask upstream &&
> +	test_must_fail git rebase --exec "true" --empty=stop upstream &&
>   
>   	git rebase --skip &&
>   

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch
  2024-02-10  7:43 ` [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch Brian Lyles
@ 2024-02-22 16:34   ` phillip.wood123
  2024-02-23  5:28     ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-02-22 16:34 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster, Phillip Wood

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> When using git-cherry-pick(1) with `--allow-empty` while on an unborn
> branch, an error is thrown. This is inconsistent with the same
> cherry-pick when `--allow-empty` is not specified.
> 
> Treat a failure reading HEAD as an unborn branch in
> `is_index_unchanged`. This is consistent with other sequencer logic such
> as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as
> the tree to compare against.
> 
> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
> ---
> 
> This is another new commit that was not present in v1.
> 
> See this comment[1] from Phillip for context.
> 
> [1]: https://lore.kernel.org/git/b5213705-4cd6-40ef-8c5f-32b214534b8b@gmail.com/

Thanks for fixing this and adding a test, I've left a few small comments 
below.

>   static int is_index_unchanged(struct repository *r)
>   {
> -	struct object_id head_oid, *cache_tree_oid;
> +	struct object_id head_oid, *cache_tree_oid, head_tree_oid;

I think we can make `head_tree_oid` a pointer like cache_tree_oid and 
avoid de-referencing `the_hash_algo->empty_tree` and the return value of 
`get_commit_tree_oid()`. I think the only reason to copy it would be if 
the underlying object had a shorter lifetime than `head_tree_oid` but I 
don't think that's the case.

> +test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
> +	git checkout main &&

I'm a bit confused by this - are we already on the branch "unborn" and 
so need to move away from it to delete it?

> +	git branch -D unborn &&
> +	git checkout --orphan unborn &&
> +	git rm --cached -r . &&
> +	rm -rf * &&

"git switch --orphan" leaves us with an empty index and working copy 
without having to remove the files ourselves.

> +	git cherry-pick initial --allow-empty &&
> +	git diff --quiet initial &&

I'd drop "--quiet" here as it makes debugging easier if we can see the 
diff if the test fails.

> +	test_cmp_rev ! initial HEAD
> +'

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options
  2024-02-10  7:43 ` [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
@ 2024-02-22 16:35   ` phillip.wood123
  0 siblings, 0 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-02-22 16:35 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> A consumer of the sequencer that wishes to take advantage of either the
> `keep_redundant_commits` or `drop_redundant_commits` feature must also
> specify `allow_empty`. However, these refer to two distinct types of
> empty commits:
> 
> - `allow_empty` refers specifically to commits which start empty
> - `keep_redundant_commits` refers specifically to commits that do not
>    start empty, but become empty due to the content already existing in
>    the target history
> 
> Conceptually, there is no reason that the behavior for handling one of
> these should be entangled with the other. It is particularly unintuitive
> to require `allow_empty` in order for `drop_redundant_commits` to have
> an effect: in order to prevent redundant commits automatically,
> initially-empty commits would need to be kept automatically as well.
> 
> Instead, rewrite the `allow_empty()` logic to remove the over-arching
> requirement that `allow_empty` be specified in order to reach any of the
> keep/drop behaviors. Only if the commit was originally empty will
> `allow_empty` have an effect.
> 
> Note that no behavioral changes should result from this commit -- it
> merely sets the stage for future commits.

Thanks for clarifying that. I think splitting this change out is a good 
idea. The patch looks good.

Best Wishes

Phillip

> In one such future commit, an
> `--empty` option will be added to git-cherry-pick(1), meaning that
> `drop_redundant_commits` will be used by that command.
> 
> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> ---
> 
> This is the first half of the first commit[1] in v1, which has now been
> split up. While the next commit may be considered somewhat
> controversial, this part of the change should not be.
> 
> [1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/
> 
>   sequencer.c | 23 +++++++----------------
>   1 file changed, 7 insertions(+), 16 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index b1b19512de..3f41863dae 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -1725,34 +1725,25 @@ static int allow_empty(struct repository *r,
>   	int index_unchanged, originally_empty;
> 
>   	/*
> -	 * Four cases:
> +	 * For a commit that is initially empty, allow_empty determines if it
> +	 * should be kept or not
>   	 *
> -	 * (1) we do not allow empty at all and error out.
> -	 *
> -	 * (2) we allow ones that were initially empty, and
> -	 *     just drop the ones that become empty
> -	 *
> -	 * (3) we allow ones that were initially empty, but
> -	 *     halt for the ones that become empty;
> -	 *
> -	 * (4) we allow both.
> +	 * For a commit that becomes empty, keep_redundant_commits and
> +	 * drop_redundant_commits determine whether the commit should be kept or
> +	 * dropped. If neither is specified, halt.
>   	 */
> -	if (!opts->allow_empty)
> -		return 0; /* let "git commit" barf as necessary */
> -
>   	index_unchanged = is_index_unchanged(r);
>   	if (index_unchanged < 0)
>   		return index_unchanged;
>   	if (!index_unchanged)
>   		return 0; /* we do not have to say --allow-empty */
> 
> -	if (opts->keep_redundant_commits)
> -		return 1;
> -
>   	originally_empty = is_original_commit_empty(commit);
>   	if (originally_empty < 0)
>   		return originally_empty;
>   	if (originally_empty)
> +		return opts->allow_empty;
> +	else if (opts->keep_redundant_commits)
>   		return 1;
>   	else if (opts->drop_redundant_commits)
>   		return 2;

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`
  2024-02-10  7:43 ` [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits` Brian Lyles
@ 2024-02-22 16:35   ` Phillip Wood
  2024-02-22 18:41     ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-02-22 16:35 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> As noted in the git-cherry-pick(1) docs, `--keep-redundant-commits`
> implies `--allow-empty`, despite the two having distinct,
> non-overlapping meanings:
> 
> - `allow_empty` refers specifically to commits which start empty, as
>    indicated by the documentation for `--allow-empty` within
>    git-cherry-pick(1):
> 
>    "Note also, that use of this option only keeps commits that were
>    initially empty (i.e. the commit recorded the same tree as its
>    parent). Commits which are made empty due to a previous commit are
>    dropped. To force the inclusion of those commits use
>    --keep-redundant-commits."
> 
> - `keep_redundant_commits` refers specifically to commits that do not
>    start empty, but become empty due to the content already existing in
>    the target history. This is indicated by the documentation for
>    `--keep-redundant-commits` within git-cherry-pick(1):
> 
>    "If a commit being cherry picked duplicates a commit already in the
>    current history, it will become empty. By default these redundant
>    commits cause cherry-pick to stop so the user can examine the commit.
>    This option overrides that behavior and creates an empty commit
>    object. Implies --allow-empty."
> 
> This implication of `--allow-empty` therefore seems incorrect: One
> should be able to keep a commit that becomes empty without also being
> forced to pick commits that start as empty. However, the following
> series of commands results in both the commit that became empty and the
> commit that started empty being picked, despite only
> `--keep-redundant-commits` being specified:
> 
>      git init
>      echo "a" >test
>      git add test
>      git commit -m "Initial commit"
>      echo "b" >test
>      git commit -am "a -> b"
>      git commit --allow-empty -m "empty"
>      git cherry-pick --keep-redundant-commits HEAD^ HEAD
> 
> The same cherry-pick with `--allow-empty` would fail on the redundant
> commit, and with neither option would fail on the empty commit.
> 
> Do not imply `--allow-empty` when using `--keep-redundant-commits` with
> git-cherry-pick(1).
>
> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> ---
> 
> This is the second half of the first commit[1] in v1, which has now been
> split up.
> 
> This commit proposes a breaking change, albeit one that seems correct
> and relatively minor to me. If this change is deemed too controversial,
> I am prepared to drop it from the series. See Junio's[2] and
> Phillip's[3] comments on v1 for additional context.

I agree that if we were starting from scratch there would be no reason 
to tie --apply-empty and --keep-redundant-commits together but I'm not 
sure it is worth the disruption of changing it now. We're about to add 
empty=keep which won't imply --allow-empty for anyone who wants that 
behavior and I still tend to think the practical effect of implying 
--allow-empty with --keep-redundant-commits is largely beneficial as I'm 
skeptical that users want to keep commits that become empty but not the 
ones that started empty.

Best Wishes

Phillip

> [1]: https://lore.kernel.org/git/20240119060721.3734775-2-brianmlyles@gmail.com/
> [2]: https://lore.kernel.org/git/xmqqy1cfnca7.fsf@gitster.g/
> [3]: https://lore.kernel.org/git/8ff4650c-f84f-41bd-a46c-3b845ff29b70@gmail.com/
> 
>   Documentation/git-cherry-pick.txt | 10 +++++++---
>   builtin/revert.c                  |  4 ----
>   t/t3505-cherry-pick-empty.sh      |  6 ++++++
>   3 files changed, 13 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
> index fdcad3d200..c88bb88822 100644
> --- a/Documentation/git-cherry-pick.txt
> +++ b/Documentation/git-cherry-pick.txt
> @@ -131,8 +131,8 @@ effect to your index in a row.
>   	even without this option.  Note also, that use of this option only
>   	keeps commits that were initially empty (i.e. the commit recorded the
>   	same tree as its parent).  Commits which are made empty due to a
> -	previous commit are dropped.  To force the inclusion of those commits
> -	use `--keep-redundant-commits`.
> +	previous commit will cause the cherry-pick to fail.  To force the
> +	inclusion of those commits, use `--keep-redundant-commits`.
> 
>   --allow-empty-message::
>   	By default, cherry-picking a commit with an empty message will fail.
> @@ -144,7 +144,11 @@ effect to your index in a row.
>   	current history, it will become empty.  By default these
>   	redundant commits cause `cherry-pick` to stop so the user can
>   	examine the commit. This option overrides that behavior and
> -	creates an empty commit object.  Implies `--allow-empty`.
> +	creates an empty commit object. Note that use of this option only
> +	results in an empty commit when the commit was not initially empty,
> +	but rather became empty due to a previous commit. Commits that were
> +	initially empty will cause the cherry-pick to fail. To force the
> +	inclusion of those commits use `--allow-empty`.
> 
>   --strategy=<strategy>::
>   	Use the given merge strategy.  Should only be used once.
> diff --git a/builtin/revert.c b/builtin/revert.c
> index 89821bab95..d83977e36e 100644
> --- a/builtin/revert.c
> +++ b/builtin/revert.c
> @@ -134,10 +134,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
>   	prepare_repo_settings(the_repository);
>   	the_repository->settings.command_requires_full_index = 0;
> 
> -	/* implies allow_empty */
> -	if (opts->keep_redundant_commits)
> -		opts->allow_empty = 1;
> -
>   	if (cleanup_arg) {
>   		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
>   		opts->explicit_cleanup = 1;
> diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
> index eba3c38d5a..2709cfc677 100755
> --- a/t/t3505-cherry-pick-empty.sh
> +++ b/t/t3505-cherry-pick-empty.sh
> @@ -59,6 +59,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
>   	test_must_fail git cherry-pick empty-change-branch
>   '
> 
> +test_expect_success 'cherry pick an empty non-ff commit with --keep-redundant-commits' '
> +	git checkout main &&
> +	test_must_fail git cherry-pick --keep-redundant-commits empty-change-branch 2>output &&
> +	test_grep "The previous cherry-pick is now empty" output
> +'
> +
>   test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
>   	git checkout main &&
>   	git cherry-pick --allow-empty empty-change-branch


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-02-10  7:43 ` [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
@ 2024-02-22 16:35   ` Phillip Wood
  2024-02-23  6:23     ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Phillip Wood @ 2024-02-22 16:35 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> When `--keep-redundant-commits` was added in  b27cfb0d8d
> (git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was
> not marked as incompatible with the various operations needed to
> continue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and
> `--quit`).
> 
> Enforce this incompatibility via `verify_opt_compatible` like we do for
> the other various options.
> 
> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> ---
> 
> This commit was not present in v1 either. It addresses an existing issue
> that I noticed after Phillip pointed out the same deficiency for my new
> `--empty` option introduced in the ultimate commit in this series.

Well spotted, do we really need a new test file just for this though? I 
wonder if the new test would be better off living in 
t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh

Best Wishes

Phillip

> [1]: https://lore.kernel.org/git/CAHPHrSf+joHe6ikErHLgWrk-_qjSROS-dXCHagxWGDAF=2deDg@mail.gmail.com/
> 
>   builtin/revert.c                            |  1 +
>   t/t3515-cherry-pick-incompatible-options.sh | 34 +++++++++++++++++++++
>   2 files changed, 35 insertions(+)
>   create mode 100755 t/t3515-cherry-pick-incompatible-options.sh
> 
> diff --git a/builtin/revert.c b/builtin/revert.c
> index d83977e36e..48c426f277 100644
> --- a/builtin/revert.c
> +++ b/builtin/revert.c
> @@ -163,6 +163,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
>   				"--ff", opts->allow_ff,
>   				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
>   				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
> +				"--keep-redundant-commits", opts->keep_redundant_commits,
>   				NULL);
>   	}
>   
> diff --git a/t/t3515-cherry-pick-incompatible-options.sh b/t/t3515-cherry-pick-incompatible-options.sh
> new file mode 100755
> index 0000000000..6100ab64fd
> --- /dev/null
> +++ b/t/t3515-cherry-pick-incompatible-options.sh
> @@ -0,0 +1,34 @@
> +#!/bin/sh
> +
> +test_description='test if cherry-pick detects and aborts on incompatible options'
> +
> +. ./test-lib.sh
> +
> +test_expect_success setup '
> +
> +	echo first > file1 &&
> +	git add file1 &&
> +	test_tick &&
> +	git commit -m "first" &&
> +
> +	echo second > file1 &&
> +	git add file1 &&
> +	test_tick &&
> +	git commit -m "second"
> +'
> +
> +test_expect_success '--keep-redundant-commits is incompatible with operations' '
> +	test_must_fail git cherry-pick HEAD 2>output &&
> +	test_grep "The previous cherry-pick is now empty" output &&
> +	test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
> +	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
> +	test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
> +	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
> +	test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
> +	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
> +	test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
> +	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
> +	git cherry-pick --abort
> +'
> +
> +test_done


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-10  7:43 ` [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
  2024-02-11 20:50   ` Jean-Noël AVILA
@ 2024-02-22 16:36   ` phillip.wood123
  2024-02-23  6:58     ` Brian Lyles
  1 sibling, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-02-22 16:36 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a
> commit being made redundant if the content from the picked commit is
> already present in the target history. However, git-cherry-pick(1) does
> not have the same options available that git-rebase(1) and git-am(1) have.
> 
> There are three things that can be done with these redundant commits:
> drop them, keep them, or have the cherry-pick stop and wait for the user
> to take an action. git-rebase(1) has the `--empty` option added in commit
> e98c4269c8 (rebase (interactive-backend): fix handling of commits that
> become empty, 2020-02-15), which handles all three of these scenarios.
> Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support
> --empty=<option> to handle empty patches, 2021-12-09).
> 
> git-cherry-pick(1), on the other hand, only supports two of the three
> possiblities: Keep the redundant commits via `--keep-redundant-commits`,
> or have the cherry-pick fail by not specifying that option. There is no
> way to automatically drop redundant commits.
> 
> In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and
> git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It
> has the same three options (keep, drop, and stop), and largely behaves
> the same. The notable difference is that for git-cherry-pick(1), the
> default will be `stop`, which maintains the current behavior when the
> option is not specified.
> 
> The `--keep-redundant-commits` option will be documented as a deprecated
> synonym of `--empty=keep`, and will be supported for backwards
> compatibility for the time being.

I'm leaning towards leaving `--keep-redundant-commits` alone. That 
introduces an inconsistency between `--keep-redundant-commits` and 
`--empty=keep` as the latter does not imply `--allow-empty` but it does 
avoid breaking existing users. We could document 
`--keep-redundant-commits` as predating `--empty` and behaving like 
`--empty=keep --allow-empty`. The documentation and implementation of 
the new option look good modulo the typo that has already been pointed 
out and a couple of small comments below.

> +enum empty_action {
> +	EMPTY_COMMIT_UNSPECIFIED = 0,

We tend to use -1 for unspecified options

> +	STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
> +	DROP_EMPTY_COMMIT,         /* skip with a notice message */
> +	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
> +};


> +test_expect_success 'cherry-pick persists --empty=stop correctly' '
> +	pristine_detach initial &&
> +	# to make sure that the session to cherry-pick a sequence
> +	# gets interrupted, use a high-enough number that is larger
> +	# than the number of parents of any commit we have created
> +	mainline=4 &&
> +	test_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&
> +	test_path_is_file .git/sequencer/opts &&
> +	test_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&
> +	test_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits
> +'

Thanks for adding these tests to check that the --empty option persists. 
Usually for tests like this we prefer to check the user visible behavior 
rather than the implementation details (I suspect we have some older 
tests that do the latter). To check the behavior we usually arrange for 
a merge conflict but using -m is a creative alternative, then we'd run 
"git cherry-pick --continue" and check that the commits that become 
empty have been preserved or dropped or that the cherry-pick stops.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 0/8] cherry-pick: add `--empty`
  2024-02-10  7:43 ` [PATCH v2 0/8] cherry-pick: add `--empty` Brian Lyles
@ 2024-02-22 16:39   ` phillip.wood123
  0 siblings, 0 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-02-22 16:39 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/02/2024 07:43, Brian Lyles wrote:
> The ultimate goal of this series is to allow git-cherry-pick(1) to
> automatically drop redundant commits. The mechanism chosen is an
> `--empty` option that provides the same flexibility as the `--empty`
> options for git-rebase(1) and git-am(1).
> 
> Some secondary goals are to improve the consistency in the values and
> documentation for this option across the three commands.
> 
> See "Does extending `--empty` to git-cherry-pick make sense?" [1] for
> some context for why this option is desired in git-cherry-pick(1).
> 
> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com
> 
> Along the way, I (with some help from Elijah and Phillip) found a few
> other things in the docs and related sequencer code to clean up.

Thanks for the revised patches - they are looking good and were a 
pleasant read. I've left a few small comments, my main concern is the 
change to `--keep-redundant-commits` in patch 6 which I'm not sure is 
really worth the disruption.

Best Wishes

Phillip

> Brian Lyles (8):
>    docs: address inaccurate `--empty` default with `--exec`
>    docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
>    rebase: update `--empty=ask` to `--empty=drop`
>    sequencer: treat error reading HEAD as unborn branch
>    sequencer: do not require `allow_empty` for redundant commit options
>    cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`
>    cherry-pick: enforce `--keep-redundant-commits` incompatibility
>    cherry-pick: add `--empty` for more robust redundant commit handling
> 
>   Documentation/git-am.txt                    | 20 ++++---
>   Documentation/git-cherry-pick.txt           | 30 +++++++---
>   Documentation/git-rebase.txt                | 26 ++++++---
>   builtin/rebase.c                            | 16 +++--
>   builtin/revert.c                            | 40 +++++++++++--
>   sequencer.c                                 | 65 +++++++++++----------
>   t/t3424-rebase-empty.sh                     | 55 ++++++++++++++++-
>   t/t3501-revert-cherry-pick.sh               | 11 ++++
>   t/t3505-cherry-pick-empty.sh                | 29 ++++++++-
>   t/t3510-cherry-pick-sequence.sh             | 40 +++++++++++++
>   t/t3515-cherry-pick-incompatible-options.sh | 48 +++++++++++++++
>   11 files changed, 312 insertions(+), 68 deletions(-)
>   create mode 100755 t/t3515-cherry-pick-incompatible-options.sh
> 

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop`
  2024-02-22 16:34   ` phillip.wood123
@ 2024-02-22 18:27     ` Junio C Hamano
  0 siblings, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-02-22 18:27 UTC (permalink / raw)
  To: phillip.wood123; +Cc: Brian Lyles, git, newren, me

phillip.wood123@gmail.com writes:

>> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
>> Reported-by: Elijah Newren <newren@gmail.com>
>
> I think we normally put Reported-by: and Helped-by: etc above the
> patch authors Signed-off-by: trailer.

True.

Teaching how to fish, the rule is to try emulating chronological
order of events.  Elijah reported and then the patch was written,
and the "seal on the envelope" is what the author's sign-off
attached at the end of the chain of events.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits`
  2024-02-22 16:35   ` Phillip Wood
@ 2024-02-22 18:41     ` Junio C Hamano
  0 siblings, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-02-22 18:41 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Brian Lyles, git, newren, me

Phillip Wood <phillip.wood123@gmail.com> writes:

> ... I still tend to think the practical effect of implying
> --allow-empty with --keep-redundant-commits is largely beneficial as
> I'm skeptical that users want to keep commits that become empty but
> not the ones that started empty.

I share that feeling exactly.

There are good reasons to keep a commit that starts as empty (as
much as creating an empty commit in the first place), so if
anything, a more common workflow element would be to drop the ones
that have become unnecessary (e.g. because the upstream already
added a change that is equivalent to what is being picked) while
keeping the ones that are empty from the start (e.g. in some
workflows an empty commit can be used as a container of
metainfo---you can imagine that in an N-commit chain leading to the
tip of a topic branch forked from the master branch, the topmost
commit is an empty one with the cover letter being prepared, so that
the resulting topic branch can be either (1) made into a patch
series with an advanced version of "git format-patch" that knows how
to use such an empty top commit in the cover letter message, or (2)
merged to the mainline via a pull request, with an advanced version
of "git merge" that notices the empty commit at the tip, and makes a
merge with the commit topic~1 while using the empty top commit to
write the message for the merge commit.

I do not quite see a good reason to do the opposite, dropping
commits that started out as empty but keeping the ones that have
become empty.  Such a behaviour has additional downside that after
such a cherry-pick, when you cherry-pick the resulting history onto
yet another base, your precious "were not empty but have become so
during the initial cherry-pick" commits will appear as commits that
were empty from the start.  So I do not see much reason to allow the
decoupling, even with the new "empty=keep" thing that does not imply
"allow-empty".

Thanks.


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch
  2024-02-22 16:34   ` phillip.wood123
@ 2024-02-23  5:28     ` Brian Lyles
  2024-02-25 16:57       ` phillip.wood123
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-23  5:28 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster

On Thu, Feb 22, 2024 at 10:34 AM <phillip.wood123@gmail.com> wrote:

>>   static int is_index_unchanged(struct repository *r)
>>   {
>> -	struct object_id head_oid, *cache_tree_oid;
>> +	struct object_id head_oid, *cache_tree_oid, head_tree_oid;
> 
> I think we can make `head_tree_oid` a pointer like cache_tree_oid and 
> avoid de-referencing `the_hash_algo->empty_tree` and the return value of 
> `get_commit_tree_oid()`. I think the only reason to copy it would be if 
> the underlying object had a shorter lifetime than `head_tree_oid` but I 
> don't think that's the case.

Makes sense -- I'll update this in v3.

>> +test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
>> +	git checkout main &&
> 
> I'm a bit confused by this - are we already on the branch "unborn" and 
> so need to move away from it to delete it?

Yes, the previous test leaves us on that branch. In v3, I will update
this to instead just use `git checkout --detach`, as that does seem a
little less confusing than switching to some other branch that is only
relevant because it's not `unborn`. If there is a cleaner way to do
this, I'd be happy to switch to it.

>> +	git branch -D unborn &&
>> +	git checkout --orphan unborn &&
>> +	git rm --cached -r . &&
>> +	rm -rf * &&
> 
> "git switch --orphan" leaves us with an empty index and working copy 
> without having to remove the files ourselves.

Thanks for pointing this out. Using git-switch(1) here instead of
git-checkout(1) allows us to drop the `rm -rf *` call form both the
existing 'cherry-pick on unborn branch' test as well as my new test. It
appears that the `git rm --cached -r .` call is still necessary in the
existing test.

>> +	git cherry-pick initial --allow-empty &&
>> +	git diff --quiet initial &&
>
> I'd drop "--quiet" here as it makes debugging easier if we can see the 
> diff if the test fails.

This makes sense. In v3, I will update this new test as well as the
existing test to not use `--quiet`.

Combining the above suggestions, here's the version of the existing and
new tests that I intend to use in v3. Let me know if this isn't what you
had in mind!

    test_expect_success 'cherry-pick on unborn branch' '
    	git switch --orphan unborn &&
    	git rm --cached -r . &&
    	git cherry-pick initial &&
    	git diff initial &&
    	test_cmp_rev ! initial HEAD
    '
    
    test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
    	git checkout --detach &&
    	git branch -D unborn &&
    	git switch --orphan unborn &&
    	git cherry-pick initial --allow-empty &&
    	git diff initial &&
    	test_cmp_rev ! initial HEAD
    '

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-02-22 16:35   ` Phillip Wood
@ 2024-02-23  6:23     ` Brian Lyles
  2024-02-23 17:41       ` Junio C Hamano
  2024-02-25 16:58       ` phillip.wood123
  0 siblings, 2 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-23  6:23 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster

On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:

> Well spotted, do we really need a new test file just for this though? I 
> wonder if the new test would be better off living in 
> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh

I was modelling this off of 't3422-rebase-incompatible-options.sh'.
Additionally, I do feel like these tests are only tangentially related
to the tests that actually exercise the features themselves. Notably,
the setup requirements are drastically different (simpler) since the
test should fail long before any setup actually matters. For that
reason, I think a separate file where other future tests for
incompatible options can also live does make sense.

Is there any particular downside to the new file that I am unaware of?

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-22 16:36   ` phillip.wood123
@ 2024-02-23  6:58     ` Brian Lyles
  2024-02-25 16:57       ` phillip.wood123
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-23  6:58 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster


On Thu, Feb 22, 2024 at 10:36 AM <phillip.wood123@gmail.com> wrote:

> I'm leaning towards leaving `--keep-redundant-commits` alone. That 
> introduces an inconsistency between `--keep-redundant-commits` and 
> `--empty=keep` as the latter does not imply `--allow-empty` but it does 
> avoid breaking existing users. We could document 
> `--keep-redundant-commits` as predating `--empty` and behaving like 
> `--empty=keep --allow-empty`. The documentation and implementation of 
> the new option look good modulo the typo that has already been pointed 
> out and a couple of small comments below.

I think I'm on board with leaving `--keep-redundant-commits` alone. I'm
on the fence about having `--empty=keep` imply `--allow-empty` after
seeing Junio's concerns. I laid out the options that I see in a reply to
patch 6/8[1] and would appreciate input there. I'll adjust the details
of this commit in v3 based on what we decide there.

[1]: https://lore.kernel.org/git/17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk/
> 
>> +enum empty_action {
>> +	EMPTY_COMMIT_UNSPECIFIED = 0,
> 
> We tend to use -1 for unspecified options

Thanks, I'll update this in v3.

>> +test_expect_success 'cherry-pick persists --empty=stop correctly' '
>> +	pristine_detach initial &&
>> +	# to make sure that the session to cherry-pick a sequence
>> +	# gets interrupted, use a high-enough number that is larger
>> +	# than the number of parents of any commit we have created
>> +	mainline=4 &&
>> +	test_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&
>> +	test_path_is_file .git/sequencer/opts &&
>> +	test_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&
>> +	test_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits
>> +'
> 
> Thanks for adding these tests to check that the --empty option persists. 
> Usually for tests like this we prefer to check the user visible behavior 
> rather than the implementation details (I suspect we have some older 
> tests that do the latter). To check the behavior we usually arrange for 
> a merge conflict but using -m is a creative alternative, then we'd run 
> "git cherry-pick --continue" and check that the commits that become 
> empty have been preserved or dropped or that the cherry-pick stops.

Indeed, I was modelling these new tests after other existing tests in
this file. While I agree with you in theory, I am hesitant to make these
new tests drastically different from the existing tests that are testing
the same mechanisms (and appear to be very intentionally testing that
the options are persisted in that config file). I'm also hesitant to
update the existing tests as part of this series (primarily due to a
lack of familiarity, and partially to avoid scope creep of the series).

How concerned are you about the current implementation? Does it make
sense to you to defer this suggestion to a future series that cleans up
the tests to do more user-oriented checks?

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-02-23  6:23     ` Brian Lyles
@ 2024-02-23 17:41       ` Junio C Hamano
  2024-02-25 16:58       ` phillip.wood123
  1 sibling, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-02-23 17:41 UTC (permalink / raw)
  To: Brian Lyles; +Cc: phillip.wood, git, newren, me

"Brian Lyles" <brianmlyles@gmail.com> writes:

>> Well spotted, do we really need a new test file just for this though? I 
>> wonder if the new test would be better off living in 
>> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh
>
> I was modelling this off of 't3422-rebase-incompatible-options.sh'.
> Additionally, I do feel like these tests are only tangentially related
> to the tests that actually exercise the features themselves. Notably,
> the setup requirements are drastically different (simpler) since the
> test should fail long before any setup actually matters.

It is exactly the reason why we do not want to waste a scarce
resource, the test file number, when we do not have to.

Sanity checking the command line options and failing when they do
not make sense is a small part of what a command needs to do; you
are exactly right to say "they do not really exercise the main
feature".

As these "we expect this to fail" tests do not depend on and do not
have to modify the state of the test repository, we do not even have
to do any extra set-up over what the existing test script already
establishes, no?  No additional set-up is even better than a simpler
but non-zero set-up we need to newly do, right?

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch
  2024-02-23  5:28     ` Brian Lyles
@ 2024-02-25 16:57       ` phillip.wood123
  0 siblings, 0 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-02-25 16:57 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, newren, me, gitster

Hi Brian

On 23/02/2024 05:28, Brian Lyles wrote:
> On Thu, Feb 22, 2024 at 10:34 AM <phillip.wood123@gmail.com> wrote:
>>> +test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
>>> +	git checkout main &&
>>
>> I'm a bit confused by this - are we already on the branch "unborn" and
>> so need to move away from it to delete it?
> 
> Yes, the previous test leaves us on that branch. In v3, I will update
> this to instead just use `git checkout --detach`, as that does seem a
> little less confusing than switching to some other branch that is only
> relevant because it's not `unborn`. If there is a cleaner way to do
> this, I'd be happy to switch to it.

I think "git checkout --detach" is probably the best we can do. It would 
be nice to be able to do "git switch -C --orphan unborn" but "-C" does 
not work with "--orphan"

>>> +	git branch -D unborn &&
>>> +	git checkout --orphan unborn &&
>>> +	git rm --cached -r . &&
>>> +	rm -rf * &&
>>
>> "git switch --orphan" leaves us with an empty index and working copy
>> without having to remove the files ourselves.
> 
> Thanks for pointing this out. Using git-switch(1) here instead of
> git-checkout(1) allows us to drop the `rm -rf *` call form both the
> existing 'cherry-pick on unborn branch' test as well as my new test. It
> appears that the `git rm --cached -r .` call is still necessary in the
> existing test.

It looks like the previous test 'revert forbidden on dirty working tree' 
fails to clean up properly and so "git switch" is carrying the 
uncommitted changes across to the new orphan branch. I think that "git 
switch --discard-changes --orphan unborn" ought to clean the worktree 
and index but it doesn't seem to work.

>>> +	git cherry-pick initial --allow-empty &&
>>> +	git diff --quiet initial &&
>>
>> I'd drop "--quiet" here as it makes debugging easier if we can see the
>> diff if the test fails.
> 
> This makes sense. In v3, I will update this new test as well as the
> existing test to not use `--quiet`.
> 
> Combining the above suggestions, here's the version of the existing and
> new tests that I intend to use in v3. Let me know if this isn't what you
> had in mind!
> 
>      test_expect_success 'cherry-pick on unborn branch' '
>      	git switch --orphan unborn &&
>      	git rm --cached -r . &&
>      	git cherry-pick initial &&
>      	git diff initial &&
>      	test_cmp_rev ! initial HEAD
>      '
>      
>      test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
>      	git checkout --detach &&
>      	git branch -D unborn &&
>      	git switch --orphan unborn &&
>      	git cherry-pick initial --allow-empty &&
>      	git diff initial &&
>      	test_cmp_rev ! initial HEAD
>      '

These look good

Thanks

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-23  6:58     ` Brian Lyles
@ 2024-02-25 16:57       ` phillip.wood123
  2024-02-26  2:21         ` Brian Lyles
  2024-02-26  3:32         ` Brian Lyles
  0 siblings, 2 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-02-25 16:57 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, newren, me, gitster

Hi Brian

On 23/02/2024 06:58, Brian Lyles wrote:
> 
> On Thu, Feb 22, 2024 at 10:36 AM <phillip.wood123@gmail.com> wrote:
> 
>> I'm leaning towards leaving `--keep-redundant-commits` alone. That
>> introduces an inconsistency between `--keep-redundant-commits` and
>> `--empty=keep` as the latter does not imply `--allow-empty` but it does
>> avoid breaking existing users. We could document
>> `--keep-redundant-commits` as predating `--empty` and behaving like
>> `--empty=keep --allow-empty`. The documentation and implementation of
>> the new option look good modulo the typo that has already been pointed
>> out and a couple of small comments below.
> 
> I think I'm on board with leaving `--keep-redundant-commits` alone. I'm
> on the fence about having `--empty=keep` imply `--allow-empty` after
> seeing Junio's concerns. I laid out the options that I see in a reply to
> patch 6/8[1] and would appreciate input there. I'll adjust the details
> of this commit in v3 based on what we decide there.

I'll take a look at that in the next couple of days

> [1]: https://lore.kernel.org/git/17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk/
>>
>>> +enum empty_action {
>>> +	EMPTY_COMMIT_UNSPECIFIED = 0,
>>
>> We tend to use -1 for unspecified options
> 
> Thanks, I'll update this in v3.
> 
>>> +test_expect_success 'cherry-pick persists --empty=stop correctly' '
>>> +	pristine_detach initial &&
>>> +	# to make sure that the session to cherry-pick a sequence
>>> +	# gets interrupted, use a high-enough number that is larger
>>> +	# than the number of parents of any commit we have created
>>> +	mainline=4 &&
>>> +	test_expect_code 128 git cherry-pick -s -m $mainline --empty=stop initial..anotherpick &&
>>> +	test_path_is_file .git/sequencer/opts &&
>>> +	test_must_fail git config --file=.git/sequencer/opts --get-all options.keep-redundant-commits &&
>>> +	test_must_fail git config --file=.git/sequencer/opts --get-all options.drop-redundant-commits
>>> +'
>>
>> Thanks for adding these tests to check that the --empty option persists.
>> Usually for tests like this we prefer to check the user visible behavior
>> rather than the implementation details (I suspect we have some older
>> tests that do the latter). To check the behavior we usually arrange for
>> a merge conflict but using -m is a creative alternative, then we'd run
>> "git cherry-pick --continue" and check that the commits that become
>> empty have been preserved or dropped or that the cherry-pick stops.
> 
> Indeed, I was modelling these new tests after other existing tests in
> this file. While I agree with you in theory, I am hesitant to make these
> new tests drastically different from the existing tests that are testing
> the same mechanisms (and appear to be very intentionally testing that
> the options are persisted in that config file). I'm also hesitant to
> update the existing tests as part of this series (primarily due to a
> lack of familiarity, and partially to avoid scope creep of the series).

I certainly don't think it should be up to you to update the existing 
tests. I'm not sure adding more tests in the same pattern is a good idea 
though. Apart from the fact that they are testing an implementation 
detail rather than the user facing behavior they don't actually check 
that the option is respected by "git cherry-pick --continue", only that 
we save it when stopping for a conflict resolution.

> How concerned are you about the current implementation? Does it make
> sense to you to defer this suggestion to a future series that cleans up
> the tests to do more user-oriented checks?

I think adding tests that follow a pattern we want to change is just 
storing up work for the future and makes it less likely we'll improve 
things because it will be more work to do so.

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-02-23  6:23     ` Brian Lyles
  2024-02-23 17:41       ` Junio C Hamano
@ 2024-02-25 16:58       ` phillip.wood123
  2024-02-26  3:04         ` Brian Lyles
  1 sibling, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-02-25 16:58 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, newren, me, gitster

Hi Brian

On 23/02/2024 06:23, Brian Lyles wrote:
> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> 
>> Well spotted, do we really need a new test file just for this though? I
>> wonder if the new test would be better off living in
>> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh
> 
> I was modelling this off of 't3422-rebase-incompatible-options.sh'.

The rebase case is more complicated due to different options being 
supported by the two different backends. Thankfully here we only have to 
worry about options that are incompatible with "--continue/--abort" and 
so adding "--continue rejects --foo" into the file that tests option 
"--foo" keeps everything together.

> Additionally, I do feel like these tests are only tangentially related
> to the tests that actually exercise the features themselves. Notably,
> the setup requirements are drastically different (simpler) since the
> test should fail long before any setup actually matters. For that
> reason, I think a separate file where other future tests for
> incompatible options can also live does make sense.
> 
> Is there any particular downside to the new file that I am unaware of?

The main downside is that it spreads the tests for a particular option 
over several test files. There is also an overhead in setting up the 
repository at the start of each test file.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-25 16:57       ` phillip.wood123
@ 2024-02-26  2:21         ` Brian Lyles
  2024-02-26  3:32         ` Brian Lyles
  1 sibling, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-26  2:21 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster

Hi Phillip

On Sun, Feb 25, 2024 at 10:57 AM <phillip.wood123@gmail.com> wrote:

>> How concerned are you about the current implementation? Does it make
>> sense to you to defer this suggestion to a future series that cleans up
>> the tests to do more user-oriented checks?
> 
> I think adding tests that follow a pattern we want to change is just 
> storing up work for the future and makes it less likely we'll improve 
> things because it will be more work to do so.

That's fair. I'll rework these in v3. Thanks!

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-02-25 16:58       ` phillip.wood123
@ 2024-02-26  3:04         ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-02-26  3:04 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster

Hi Phillip

On Sun, Feb 25, 2024 at 10:58 AM <phillip.wood123@gmail.com> wrote:

> Hi Brian
> 
> On 23/02/2024 06:23, Brian Lyles wrote:
>> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> 
>>> Well spotted, do we really need a new test file just for this though? I
>>> wonder if the new test would be better off living in
>>> t3505-cherry-pick-empty.sh or t3507-cherry-pick-conflict.sh
>> 
>> I was modelling this off of 't3422-rebase-incompatible-options.sh'.
> 
> The rebase case is more complicated due to different options being 
> supported by the two different backends. Thankfully here we only have to 
> worry about options that are incompatible with "--continue/--abort" and 
> so adding "--continue rejects --foo" into the file that tests option 
> "--foo" keeps everything together.
> 
>> Additionally, I do feel like these tests are only tangentially related
>> to the tests that actually exercise the features themselves. Notably,
>> the setup requirements are drastically different (simpler) since the
>> test should fail long before any setup actually matters. For that
>> reason, I think a separate file where other future tests for
>> incompatible options can also live does make sense.
>> 
>> Is there any particular downside to the new file that I am unaware of?
> 
> The main downside is that it spreads the tests for a particular option 
> over several test files. There is also an overhead in setting up the 
> repository at the start of each test file.

That makes sense. I'll move these tests into
`t3505-cherry-pick-empty.sh` for v3, along with the corresponding
incompatibility tests for `--empty` introduced in the ultimate commit
for the series.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-25 16:57       ` phillip.wood123
  2024-02-26  2:21         ` Brian Lyles
@ 2024-02-26  3:32         ` Brian Lyles
  2024-02-27 10:39           ` phillip.wood123
  1 sibling, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-02-26  3:32 UTC (permalink / raw)
  To: phillip.wood, gitster; +Cc: git, newren, me

Hi Phillip and Junio

On Sun, Feb 25, 2024 at 10:57 AM <phillip.wood123@gmail.com> wrote:

> On 23/02/2024 06:58, Brian Lyles wrote:

>> I think I'm on board with leaving `--keep-redundant-commits` alone. I'm
>> on the fence about having `--empty=keep` imply `--allow-empty` after
>> seeing Junio's concerns. I laid out the options that I see in a reply to
>> patch 6/8[1] and would appreciate input there. I'll adjust the details
>> of this commit in v3 based on what we decide there.
> 
> I'll take a look at that in the next couple of days
>
>> [1]: https://lore.kernel.org/git/17b666ca6c4b7561.70b1dd9aae081c6e.203dcd72f6563036@zivdesk/

I'm not quite sure what happened here, but it seems that:

- The above link is to the wrong email, and
- The email I meant to link to isn't showing up in the archive for some
  reason, despite clearly showing as sent in my mailbox

Apologies for the confusion -- I'm not sure what happened.

In an attempt to keep this conversation on track, I've copied my
original attempted reply to Phillip's[2] and Junio's[3] replies below.

[2]: https://lore.kernel.org/git/3f276e10-7b03-4480-a157-47a7648e7f19@gmail.com/
[3]: https://lore.kernel.org/git/xmqqwmqwcpf7.fsf@gitster.g/

On Fri, Feb 23, 2024 at 12:08 AM Brian Lyles <brianmlyles@gmail.com> wrote:
>
> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> > I agree that if we were starting from scratch there would be no reason
> > to tie --apply-empty and --keep-redundant-commits together but I'm not
> > sure it is worth the disruption of changing it now. We're about to add
> > empty=keep which won't imply --allow-empty for anyone who wants that
> > behavior and I still tend to think the practical effect of implying
> > --allow-empty with --keep-redundant-commits is largely beneficial as I'm
> > skeptical that users want to keep commits that become empty but not the
> > ones that started empty.
>
> I think that's fair. I am okay dropping this potentially disruptive
> change.
>
> It sounds like you are on board with `--empty=keep` not having the same
> implication?
>
> That said...
>
> On Thu, Feb 22, 2024 at 12:41 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> > I do not quite see a good reason to do the opposite, dropping
> > commits that started out as empty but keeping the ones that have
> > become empty.  Such a behaviour has additional downside that after
> > such a cherry-pick, when you cherry-pick the resulting history onto
> > yet another base, your precious "were not empty but have become so
> > during the initial cherry-pick" commits will appear as commits that
> > were empty from the start.  So I do not see much reason to allow the
> > decoupling, even with the new "empty=keep" thing that does not imply
> > "allow-empty".
>
> Junio -- can you clarify this part?
>
> > So I do not see much reason to allow the decoupling, even with the new
> > "empty=keep" thing that does not imply "allow-empty"
>
> I'm not 100% sure if you are saying that you want `--empty=keep` to
> *also* imply `--allow-empty`, or that you simply want
> `--keep-redundant-commits` to continue implying `--allow-empty`
> *despite* the new `--empty=keep` no implying the same.
>
> On the one hand, I agree with Phillip's sentiment of "if we were
> starting from scratch there would be no reason to tie --apply-empty and
> --keep-redundant-commits together" (though your points perhaps provide
> such a reason). On the other, if both `--keep-redundant-commits` and
> `--empty=keep` behave the same way, it makes sense to soft-deprecate
> `--keep-redundant-commits` as I have currently done later in this
> series. If they do not behave the same way, that deprecation makes less
> sense and we have two very similar (but not quite identical) options.
>
> Just to make sure we're on the same page, the options I see are:
>
> - (A): Neither `--keep-redundant-commits` nor `--empty=keep` imply
>   `--allow-empty`, `--keep-redundant-commits` is soft-deprecated
> - (B): Both `--keep-redundant-commits` and `--empty=keep` imply
>   `--allow-empty`, `--keep-redundant-commits` is soft-deprecated
> - (C): Both `--keep-redundant-commits` and `--empty=keep` imply
>   `--allow-empty`, `--keep-redundant-commits` is *not* soft-deprecated
>   as it is more descriptive as noted by Junio here[1]
> - (D): `--keep-redundant-commits` continues to imply `--allow-empty` but
>   `--empty=keep` does not. `--keep-redundant-commits` is *not*
>   soft-deprecated as it behaves differently.
>
> (A) represents this v2 of the patch.
>
> I'm coming around to (B) based on Junio's workflow concerns, but to be
> honest I am fine with any of these options. Junio, I *think* you're
> saying you'd prefer (B) or (C)? Phillip, it sounds like you are okay
> with (D) based on your response -- how do you feel about (B)?
>
> [1]: https://lore.kernel.org/git/xmqq8r4gnd3c.fsf@gitster.g/

Thank you both for your review and insight!

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-26  3:32         ` Brian Lyles
@ 2024-02-27 10:39           ` phillip.wood123
  2024-02-27 17:33             ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-02-27 10:39 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood, gitster; +Cc: git, newren, me

Hi Brian

On 26/02/2024 03:32, Brian Lyles wrote:
> Hi Phillip and Junio
> On Fri, Feb 23, 2024 at 12:08 AM Brian Lyles <brianmlyles@gmail.com> wrote:
>>
>> On Thu, Feb 22, 2024 at 10:35 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>>> I agree that if we were starting from scratch there would be no reason
>>> to tie --apply-empty and --keep-redundant-commits together but I'm not
>>> sure it is worth the disruption of changing it now. We're about to add
>>> empty=keep which won't imply --allow-empty for anyone who wants that
>>> behavior and I still tend to think the practical effect of implying
>>> --allow-empty with --keep-redundant-commits is largely beneficial as I'm
>>> skeptical that users want to keep commits that become empty but not the
>>> ones that started empty.
>>
>> I think that's fair. I am okay dropping this potentially disruptive
>> change.
>>
>> It sounds like you are on board with `--empty=keep` not having the same
>> implication?

Yes indeed

>> That said...
>>
>> On Thu, Feb 22, 2024 at 12:41 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>>> I do not quite see a good reason to do the opposite, dropping
>>> commits that started out as empty but keeping the ones that have
>>> become empty.  Such a behaviour has additional downside that after
>>> such a cherry-pick, when you cherry-pick the resulting history onto
>>> yet another base, your precious "were not empty but have become so
>>> during the initial cherry-pick" commits will appear as commits that
>>> were empty from the start.  So I do not see much reason to allow the
>>> decoupling, even with the new "empty=keep" thing that does not imply
>>> "allow-empty".
>>
>> Junio -- can you clarify this part?
>>
>>> So I do not see much reason to allow the decoupling, even with the new
>>> "empty=keep" thing that does not imply "allow-empty"
>>
>> I'm not 100% sure if you are saying that you want `--empty=keep` to
>> *also* imply `--allow-empty`, or that you simply want
>> `--keep-redundant-commits` to continue implying `--allow-empty`
>> *despite* the new `--empty=keep` no implying the same.

FWIW I read it as the latter, but I can't claim to know what was in 
Junio's mind when he wrote it.

>> On the one hand, I agree with Phillip's sentiment of "if we were
>> starting from scratch there would be no reason to tie --apply-empty and
>> --keep-redundant-commits together" (though your points perhaps provide
>> such a reason). On the other, if both `--keep-redundant-commits` and
>> `--empty=keep` behave the same way, it makes sense to soft-deprecate
>> `--keep-redundant-commits` as I have currently done later in this
>> series. If they do not behave the same way, that deprecation makes less
>> sense and we have two very similar (but not quite identical) options.
>>
>> Just to make sure we're on the same page, the options I see are:
>>
>> - (A): Neither `--keep-redundant-commits` nor `--empty=keep` imply
>>    `--allow-empty`, `--keep-redundant-commits` is soft-deprecated
>> - (B): Both `--keep-redundant-commits` and `--empty=keep` imply
>>    `--allow-empty`, `--keep-redundant-commits` is soft-deprecated
>> - (C): Both `--keep-redundant-commits` and `--empty=keep` imply
>>    `--allow-empty`, `--keep-redundant-commits` is *not* soft-deprecated
>>    as it is more descriptive as noted by Junio here[1]
>> - (D): `--keep-redundant-commits` continues to imply `--allow-empty` but
>>    `--empty=keep` does not. `--keep-redundant-commits` is *not*
>>    soft-deprecated as it behaves differently.
>>
>> (A) represents this v2 of the patch.
>>
>> I'm coming around to (B) based on Junio's workflow concerns, but to be
>> honest I am fine with any of these options. Junio, I *think* you're
>> saying you'd prefer (B) or (C)? Phillip, it sounds like you are okay
>> with (D) based on your response -- how do you feel about (B)?

Yes, I'd prefer (D) as I think it gets confusing if some values of 
--empty imply --allow-empty and others don't. I could live with (B) though.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-02-27 10:39           ` phillip.wood123
@ 2024-02-27 17:33             ` Junio C Hamano
  0 siblings, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-02-27 17:33 UTC (permalink / raw)
  To: phillip.wood123; +Cc: Brian Lyles, phillip.wood, git, newren, me

phillip.wood123@gmail.com writes:

>>>> I do not quite see a good reason to do the opposite, dropping
>>>> commits that started out as empty but keeping the ones that have
>>>> become empty.  Such a behaviour has additional downside that after
>>>> such a cherry-pick, when you cherry-pick the resulting history onto
>>>> yet another base, your precious "were not empty but have become so
>>>> during the initial cherry-pick" commits will appear as commits that
>>>> were empty from the start.  So I do not see much reason to allow the
>>>> decoupling, even with the new "empty=keep" thing that does not imply
>>>> "allow-empty".
>>>
>>> Junio -- can you clarify this part?
>>>
>>>> So I do not see much reason to allow the decoupling, even with the new
>>>> "empty=keep" thing that does not imply "allow-empty"
>>>
>>> I'm not 100% sure if you are saying that you want `--empty=keep` to
>>> *also* imply `--allow-empty`, or that you simply want
>>> `--keep-redundant-commits` to continue implying `--allow-empty`
>>> *despite* the new `--empty=keep` no implying the same.
>
> FWIW I read it as the latter, but I can't claim to know what was in
> Junio's mind when he wrote it.

Given that "drop what was empty originally while keeping what became
empty" would "lose" what it wanted to keep (i.e. the one that has
become empty") when used to cherry-pick the result of doing such a
cherry-pick, I do not think allowing such combination makes as much
sense as the opposite "keep what was empty originally while dropping
what became empty", which does not have such a property.

And it does not matter if that is expressed via the combination of
existing --allow-empty and --keep-redundant-commits options, or the
newly proposed --empty=keep option.  If we start allowing the "drop
what was originally empty and keep what has become empty"
combination if we make empty=keep not to imply --allow-empty, I do
not think it is a good idea.

That was what was on my mind when I wrote it.  It may be that I was
not following the discussion correctly, and making "empty=keep" not
to imply "allow-empty" does *not* allow a request to "drop what was
originally empty, keep what has become empty".  If that is the case,
then I have no objection to making "empty=keep" not to imply
"allow-empty".

Thanks.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v3 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (14 preceding siblings ...)
  2024-02-10  7:43 ` [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
@ 2024-03-10 18:41 ` Brian Lyles
  2024-03-13 16:12   ` phillip.wood123
  2024-03-10 18:42 ` [PATCH v3 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
                   ` (22 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:41 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

The ultimate goal of this series is to allow git-cherry-pick(1) to
automatically drop redundant commits. The mechanism chosen is an
`--empty` option that provides the same flexibility as the `--empty`
options for git-rebase(1) and git-am(1).

Some secondary goals are to improve the consistency in the values and
documentation for this option across the three commands.

See "Does extending `--empty` to git-cherry-pick make sense?" [1] for
some context for why this option is desired in git-cherry-pick(1).

[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com

Along the way, I (with some help from Elijah and Phillip) found a few
other things in the docs and related sequencer code to clean up.

The primary difference from v2 of this patch is that I no longer make
any attempt to change the behavior of `--keep-redundant-commits`
implying `--allow-empty`, and the new `--empty=keep` will likewise also
imply `--allow-empty`. See "Re: [PATCH v2 8/8] cherry-pick: add
`--empty` for more robust redundant commit handling" [2] and the
previous messages in that thread for more context. Patch 6/8 from v2 is
dropped entirely, with some adjustments to the ultimate patch in this
series as well.

[2]: https://lore.kernel.org/git/xmqqttltu7zs.fsf@gitster.g/

Brian Lyles (7):
  docs: address inaccurate `--empty` default with `--exec`
  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  rebase: update `--empty=ask` to `--empty=stop`
  sequencer: treat error reading HEAD as unborn branch
  sequencer: do not require `allow_empty` for redundant commit options
  cherry-pick: enforce `--keep-redundant-commits` incompatibility
  cherry-pick: add `--empty` for more robust redundant commit handling

 Documentation/git-am.txt          | 20 ++++++----
 Documentation/git-cherry-pick.txt | 30 +++++++++++----
 Documentation/git-rebase.txt      | 26 ++++++++-----
 builtin/rebase.c                  | 16 +++++---
 builtin/revert.c                  | 38 +++++++++++++++++-
 sequencer.c                       | 64 ++++++++++++++++---------------
 t/t3424-rebase-empty.sh           | 55 ++++++++++++++++++++++++--
 t/t3501-revert-cherry-pick.sh     | 14 +++++--
 t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++
 10 files changed, 279 insertions(+), 67 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v3 1/7] docs: address inaccurate `--empty` default with `--exec`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (15 preceding siblings ...)
  2024-03-10 18:41 ` [PATCH v3 0/7] " Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-10 18:42 ` [PATCH v3 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
                   ` (21 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

The documentation for git-rebase(1) indicates that using the `--exec`
option will use `--empty=drop`. This is inaccurate: when `--interactive`
is not explicitly provided, `--exec` results in `--empty=keep`
behaviors.

Correctly indicate the behavior of `--exec` using `--empty=keep` when
`--interactive` is not specified.

Reported-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt | 10 +++++-----
 t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 06206521fc..3334e85356 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.
 	empty after rebasing (because they contain a subset of already
 	upstream changes).  With drop (the default), commits that
 	become empty are dropped.  With keep, such commits are kept.
-	With ask (implied by `--interactive`), the rebase will halt when
-	an empty commit is applied allowing you to choose whether to
-	drop it, edit files more, or just commit the empty changes.
-	Other options, like `--exec`, will use the default of drop unless
-	`-i`/`--interactive` is explicitly specified.
+	With ask, the rebase will halt when an empty commit is applied
+	allowing you to choose whether to drop it, edit files more, or just
+	commit the empty changes.
+	When the `-i`/`--interactive` option is used, the default becomes ask.
+	Otherwise, when the `--exec` option is used, the default becomes keep.
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a0af..73ff35ced2 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
 	test_path_is_missing .git/MERGE_MSG
 '
 
+test_expect_success 'rebase --exec --empty=drop' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=drop upstream &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=keep upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=ask' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v3 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (16 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-10 18:42 ` [PATCH v3 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
                   ` (20 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

Both of these pages document very similar `--empty` options, but with
different styles. The exact behavior of these `--empty` options differs
somewhat, but consistent styling in the docs is still beneficial. This
commit aims to make them more consistent.

Break the possible values for `--empty` into separate sections for
readability. Alphabetical order is chosen for consistency.

In a future commit, we'll be documenting a new `--empty` option for
git-cherry-pick(1), making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-am.txt     | 20 +++++++++++++-------
 Documentation/git-rebase.txt | 25 ++++++++++++++++---------
 2 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e080458d6c..f852e0ba79 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -66,13 +66,19 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
---empty=(stop|drop|keep)::
-	By default, or when the option is set to 'stop', the command
-	errors out on an input e-mail message lacking a patch
-	and stops in the middle of the current am session. When this
-	option is set to 'drop', skip such an e-mail message instead.
-	When this option is set to 'keep', create an empty commit,
-	recording the contents of the e-mail message as its log.
+--empty=(drop|keep|stop)::
+	How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+	The e-mail message will be skipped.
+`keep`;;
+	An empty commit will be created, with the contents of the e-mail
+	message as its log.
+`stop`;;
+	The command will fail, stopping in the middle of the current `am`
+	session. This is the default behavior.
+--
 
 -m::
 --message-id::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 3334e85356..0b0d0ccb80 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(drop|keep|ask)::
+--empty=(ask|drop|keep)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
-	upstream changes).  With drop (the default), commits that
-	become empty are dropped.  With keep, such commits are kept.
-	With ask, the rebase will halt when an empty commit is applied
-	allowing you to choose whether to drop it, edit files more, or just
-	commit the empty changes.
-	When the `-i`/`--interactive` option is used, the default becomes ask.
-	Otherwise, when the `--exec` option is used, the default becomes keep.
+	upstream changes):
++
+--
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified.
+`drop`;;
+	The commit will be dropped. This is the default behavior.
+`keep`;;
+	The commit will be kept. This option is implied when `--exec` is
+	specified unless `-i`/`--interactive` is also specified.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
@@ -704,7 +711,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(drop|keep|ask)` option for changing the behavior
+also has an `--empty=(ask|drop|keep)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v3 3/7] rebase: update `--empty=ask` to `--empty=stop`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (17 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-10 18:42 ` [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch Brian Lyles
                   ` (19 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09), `stop` was used
instead of `ask`. `stop` is a more accurate term for describing what
really happens, and consistency is good.

Update git-rebase(1) to also use `stop`, while keeping `ask` as a
deprecated synonym. Update the tests to primarily use `stop`, but also
ensure that `ask` is still allowed.

In a future commit, we'll be adding a new `--empty` option for
git-cherry-pick(1) as well, making the consistency even more relevant.

Reported-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt | 15 ++++++++-------
 builtin/rebase.c             | 16 ++++++++++------
 t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 0b0d0ccb80..67dd0a533e 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(ask|drop|keep)::
+--empty=(drop|keep|stop)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
 	upstream changes):
 +
 --
-`ask`;;
-	The rebase will halt when the commit is applied, allowing you to
-	choose whether to drop it, edit files more, or just commit the empty
-	changes. This option is implied when `-i`/`--interactive` is
-	specified.
 `drop`;;
 	The commit will be dropped. This is the default behavior.
 `keep`;;
 	The commit will be kept. This option is implied when `--exec` is
 	specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified. `ask` is a deprecated synonym of `stop`.
 --
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
@@ -711,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(ask|drop|keep)` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 5b086f651a..a4916781ce 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -58,7 +58,7 @@ enum empty_type {
 	EMPTY_UNSPECIFIED = -1,
 	EMPTY_DROP,
 	EMPTY_KEEP,
-	EMPTY_ASK
+	EMPTY_STOP
 };
 
 enum action {
@@ -951,10 +951,14 @@ static enum empty_type parse_empty_value(const char *value)
 		return EMPTY_DROP;
 	else if (!strcasecmp(value, "keep"))
 		return EMPTY_KEEP;
-	else if (!strcasecmp(value, "ask"))
-		return EMPTY_ASK;
+	else if (!strcasecmp(value, "stop"))
+		return EMPTY_STOP;
+	else if (!strcasecmp(value, "ask")) {
+		warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+		return EMPTY_STOP;
+	}
 
-	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
 }
 
 static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1133,7 +1137,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				 "instead of ignoring them"),
 			      1, PARSE_OPT_HIDDEN),
 		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
+		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
 			       N_("how to handle commits that become empty"),
 			       PARSE_OPT_NONEG, parse_opt_empty),
 		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1550,7 +1554,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (options.empty == EMPTY_UNSPECIFIED) {
 		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-			options.empty = EMPTY_ASK;
+			options.empty = EMPTY_STOP;
 		else if (options.exec.nr > 0)
 			options.empty = EMPTY_KEEP;
 		else
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 73ff35ced2..1ee6b00fd5 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --merge --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --interactive --empty=ask upstream &&
+	test_must_fail git rebase --interactive --empty=stop upstream &&
 
 	git rebase --skip &&
 
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --interactive upstream &&
 
@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --exec --empty=ask' '
+test_expect_success 'rebase --exec --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+	test_must_fail git rebase --exec "true" --empty=stop upstream &&
 
 	git rebase --skip &&
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (18 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-11  0:07   ` Junio C Hamano
  2024-03-13 15:10   ` phillip.wood123
  2024-03-10 18:42 ` [PATCH v3 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
                   ` (18 subsequent siblings)
  38 siblings, 2 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

When using git-cherry-pick(1) with `--allow-empty` while on an unborn
branch, an error is thrown. This is inconsistent with the same
cherry-pick when `--allow-empty` is not specified.

Treat a failure reading HEAD as an unborn branch in
`is_index_unchanged`. This is consistent with other sequencer logic such
as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as
the tree to compare against.

Add a new test to cover this scenario. While modelled off of the
existing 'cherry-pick on unborn branch' test, some improvements can be
made:

- Use `git switch --orphan unborn` instead of `git checkout --orphan
  unborn` to avoid the need for a separate `rm -rf *` call
- Avoid using `--quiet` in the `git diff` call to make debugging easier
  in the event of a failure

Make these improvements to the existing test as well as the new test.

Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Differences from v2:

- Minor code and test cleanup per [1] and the other replies in that
  thread

[1]: https://lore.kernel.org/git/62247a1c-0249-4ce1-8626-fe97b89c23dc@gmail.com/

 sequencer.c                   | 35 +++++++++++++++++++++--------------
 t/t3501-revert-cherry-pick.sh | 14 +++++++++++---
 2 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index f49a871ac0..a62ce244c1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -770,29 +770,36 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
 static int is_index_unchanged(struct repository *r)
 {
 	struct object_id head_oid, *cache_tree_oid;
+	const struct object_id *head_tree_oid;
 	struct commit *head_commit;
 	struct index_state *istate = r->index;
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-		return error(_("could not resolve HEAD commit"));
+	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+		/*
+		 * Treat an error reading HEAD as an unborn branch.
+		 */
+		head_tree_oid = the_hash_algo->empty_tree;
+	} else {
+		head_commit = lookup_commit(r, &head_oid);
 
-	head_commit = lookup_commit(r, &head_oid);
+		/*
+		 * If head_commit is NULL, check_commit, called from
+		 * lookup_commit, would have indicated that head_commit is not
+		 * a commit object already.  repo_parse_commit() will return failure
+		 * without further complaints in such a case.  Otherwise, if
+		 * the commit is invalid, repo_parse_commit() will complain.  So
+		 * there is nothing for us to say here.  Just return failure.
+		 */
+		if (repo_parse_commit(r, head_commit))
+			return -1;
 
-	/*
-	 * If head_commit is NULL, check_commit, called from
-	 * lookup_commit, would have indicated that head_commit is not
-	 * a commit object already.  repo_parse_commit() will return failure
-	 * without further complaints in such a case.  Otherwise, if
-	 * the commit is invalid, repo_parse_commit() will complain.  So
-	 * there is nothing for us to say here.  Just return failure.
-	 */
-	if (repo_parse_commit(r, head_commit))
-		return -1;
+		head_tree_oid = get_commit_tree_oid(head_commit);
+	}
 
 	if (!(cache_tree_oid = get_cache_tree_oid(istate)))
 		return -1;
 
-	return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+	return oideq(cache_tree_oid, head_tree_oid);
 }
 
 static int write_author_script(const char *message)
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index aeab689a98..8a1d154ca6 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '
 '
 
 test_expect_success 'cherry-pick on unborn branch' '
-	git checkout --orphan unborn &&
+	git switch --orphan unborn &&
 	git rm --cached -r . &&
-	rm -rf * &&
 	git cherry-pick initial &&
-	git diff --quiet initial &&
+	git diff initial &&
+	test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+	git checkout --detach &&
+	git branch -D unborn &&
+	git switch --orphan unborn &&
+	git cherry-pick initial --allow-empty &&
+	git diff initial &&
 	test_cmp_rev ! initial HEAD
 '
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v3 5/7] sequencer: do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (19 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-10 18:42 ` [PATCH v3 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
                   ` (17 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

A consumer of the sequencer that wishes to take advantage of either the
`keep_redundant_commits` or `drop_redundant_commits` feature must also
specify `allow_empty`. However, these refer to two distinct types of
empty commits:

- `allow_empty` refers specifically to commits which start empty
- `keep_redundant_commits` refers specifically to commits that do not
  start empty, but become empty due to the content already existing in
  the target history

Conceptually, there is no reason that the behavior for handling one of
these should be entangled with the other. It is particularly unintuitive
to require `allow_empty` in order for `drop_redundant_commits` to have
an effect: in order to prevent redundant commits automatically,
initially-empty commits would need to be kept automatically as well.

Instead, rewrite the `allow_empty()` logic to remove the over-arching
requirement that `allow_empty` be specified in order to reach any of the
keep/drop behaviors. Only if the commit was originally empty will
`allow_empty` have an effect.

Note that no behavioral changes should result from this commit -- it
merely sets the stage for future commits. In one such future commit, an
`--empty` option will be added to git-cherry-pick(1), meaning that
`drop_redundant_commits` will be used by that command.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 sequencer.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index a62ce244c1..8dce175f2e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1726,34 +1726,25 @@ static int allow_empty(struct repository *r,
 	int index_unchanged, originally_empty;
 
 	/*
-	 * Four cases:
+	 * For a commit that is initially empty, allow_empty determines if it
+	 * should be kept or not
 	 *
-	 * (1) we do not allow empty at all and error out.
-	 *
-	 * (2) we allow ones that were initially empty, and
-	 *     just drop the ones that become empty
-	 *
-	 * (3) we allow ones that were initially empty, but
-	 *     halt for the ones that become empty;
-	 *
-	 * (4) we allow both.
+	 * For a commit that becomes empty, keep_redundant_commits and
+	 * drop_redundant_commits determine whether the commit should be kept or
+	 * dropped. If neither is specified, halt.
 	 */
-	if (!opts->allow_empty)
-		return 0; /* let "git commit" barf as necessary */
-
 	index_unchanged = is_index_unchanged(r);
 	if (index_unchanged < 0)
 		return index_unchanged;
 	if (!index_unchanged)
 		return 0; /* we do not have to say --allow-empty */
 
-	if (opts->keep_redundant_commits)
-		return 1;
-
 	originally_empty = is_original_commit_empty(commit);
 	if (originally_empty < 0)
 		return originally_empty;
 	if (originally_empty)
+		return opts->allow_empty;
+	else if (opts->keep_redundant_commits)
 		return 1;
 	else if (opts->drop_redundant_commits)
 		return 2;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v3 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (20 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-10 18:42 ` [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
                   ` (16 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When `--keep-redundant-commits` was added in  b27cfb0d8d
(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was
not marked as incompatible with the various operations needed to
continue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and
`--quit`).

Enforce this incompatibility via `verify_opt_compatible` like we do for
the other various options.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Differences from v2:

- New tests are added to t3505-cherry-pick-empty.sh instead of a new
  test file

 builtin/revert.c             |  1 +
 t/t3505-cherry-pick-empty.sh | 14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/builtin/revert.c b/builtin/revert.c
index 89821bab95..a1936ef70e 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -167,6 +167,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--ff", opts->allow_ff,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+				"--keep-redundant-commits", opts->keep_redundant_commits,
 				NULL);
 	}

diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38d5a..61f91aaa0a 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -99,4 +99,18 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
 	test_cmp expect actual
 '

+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
 test_done
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (21 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
@ 2024-03-10 18:42 ` Brian Lyles
  2024-03-13 16:10   ` phillip.wood123
  2024-03-20 23:36 ` [PATCH v4 0/7] " Brian Lyles
                   ` (15 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-10 18:42 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a
commit being made redundant if the content from the picked commit is
already present in the target history. However, git-cherry-pick(1) does
not have the same options available that git-rebase(1) and git-am(1) have.

There are three things that can be done with these redundant commits:
drop them, keep them, or have the cherry-pick stop and wait for the user
to take an action. git-rebase(1) has the `--empty` option added in commit
e98c4269c8 (rebase (interactive-backend): fix handling of commits that
become empty, 2020-02-15), which handles all three of these scenarios.
Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09).

git-cherry-pick(1), on the other hand, only supports two of the three
possiblities: Keep the redundant commits via `--keep-redundant-commits`,
or have the cherry-pick fail by not specifying that option. There is no
way to automatically drop redundant commits.

In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and
git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It
has the same three options (keep, drop, and stop), and largely behaves
the same. The notable difference is that for git-cherry-pick(1), the
default will be `stop`, which maintains the current behavior when the
option is not specified.

Like the existing `--keep-redundant-commits`, `--empty=keep` will imply
`--allow-empty`.

The `--keep-redundant-commits` option will be documented as a deprecated
synonym of `--empty=keep`, and will be supported for backwards
compatibility for the time being.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Differences from v2:

- `--empty=keep` will now imply `--allow-empty`, consistent with
  `--keep-redundant-commits`. See [1] for more information.
- Tests for persistence of the new behaviors after `--continue`, etc.
  are more focused on user-visible behaviors rather than implementation
  details.
- The new empty_action enum uses -1 for unspecified instead of 0.

[1]: https://lore.kernel.org/git/xmqqttltu7zs.fsf@gitster.g/

 Documentation/git-cherry-pick.txt | 30 +++++++++++++++++++------
 builtin/revert.c                  | 37 ++++++++++++++++++++++++++++++-
 sequencer.c                       |  6 +++++
 t/t3505-cherry-pick-empty.sh      | 37 ++++++++++++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++++++++++++
 5 files changed, 133 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d200..e6a61503c7 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,20 +131,36 @@ effect to your index in a row.
 	even without this option.  Note also, that use of this option only
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
-	previous commit are dropped.  To force the inclusion of those commits
-	use `--keep-redundant-commits`.
+	previous commit will cause the cherry-pick to fail.  To force the
+	inclusion of those commits, use `--empty=keep`.
 
 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
 	This option overrides that behavior, allowing commits with empty
 	messages to be cherry picked.
 
+--empty=(drop|keep|stop)::
+	How to handle commits being cherry-picked that are redundant with
+	changes already in the current history.
++
+--
+`drop`;;
+	The commit will be dropped.
+`keep`;;
+	The commit will be kept. Implies `--allow-empty`.
+`stop`;;
+	The cherry-pick will stop when the commit is applied, allowing
+	you to examine the commit. This is the default behavior.
+--
++
+Note that this option specifies how to handle a commit that was not initially
+empty, but rather became empty due to a previous commit. Commits that were
+initially empty will cause the cherry-pick to fail. To force the inclusion of
+those commits, use `--allow-empty`.
++
+
 --keep-redundant-commits::
-	If a commit being cherry picked duplicates a commit already in the
-	current history, it will become empty.  By default these
-	redundant commits cause `cherry-pick` to stop so the user can
-	examine the commit. This option overrides that behavior and
-	creates an empty commit object.  Implies `--allow-empty`.
+	Deprecated synonym for `--empty=keep`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index a1936ef70e..53935d2c68 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
 	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
+enum empty_action {
+	EMPTY_COMMIT_UNSPECIFIED = -1,
+	STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "stop"))
+		*opt_value = STOP_ON_EMPTY_COMMIT;
+	else if (!strcmp(arg, "drop"))
+		*opt_value = DROP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "keep"))
+		*opt_value = KEEP_EMPTY_COMMIT;
+	else
+		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+	return 0;
+}
+
 static int option_parse_m(const struct option *opt,
 			  const char *arg, int unset)
 {
@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	const char *cleanup_arg = NULL;
+	enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+				       N_("how to handle commits that become empty"),
+				       PARSE_OPT_NONEG, parse_opt_empty),
 			OPT_END(),
 		};
 		options = parse_options_concat(options, cp_extra);
@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
+	if (opts->action == REPLAY_PICK) {
+		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+	}
+
 	/* implies allow_empty */
 	if (opts->keep_redundant_commits)
 		opts->allow_empty = 1;
@@ -168,6 +202,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
 				"--keep-redundant-commits", opts->keep_redundant_commits,
+				"--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
 				NULL);
 	}
 
diff --git a/sequencer.c b/sequencer.c
index 8dce175f2e..a3c73ecb9f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2924,6 +2924,9 @@ static int populate_opts_cb(const char *key, const char *value,
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+	else if (!strcmp(key, "options.drop-redundant-commits"))
+		opts->drop_redundant_commits =
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3468,6 +3471,9 @@ static int save_opts(struct replay_opts *opts)
 	if (opts->allow_empty_message)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.allow-empty-message", "true");
+	if (opts->drop_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.drop-redundant-commits", "true");
 	if (opts->keep_redundant_commits)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.keep-redundant-commits", "true");
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 61f91aaa0a..9748443530 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
 	git commit -m "add file2 on the side"
 '
 
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
 	git reset --hard &&
 	git checkout fork^0 &&
 	test_must_fail git cherry-pick main
@@ -113,4 +113,39 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '
 	git cherry-pick --abort
 '
 
+test_expect_success '--empty is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick --empty=stop main 2>output &&
+	test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=drop main &&
+	test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=keep main &&
+	test_commit_message HEAD -m "add file2 on main"
+'
+
 test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 72020a51c4..7eb52b12ed 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+	test_must_fail git cherry-pick --skip 2>msg &&
+	test_grep "The previous cherry-pick is now empty" msg &&
+	rm msg &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD^
+'
+
 test_expect_success 'revert persists opts correctly' '
 	pristine_detach initial &&
 	# to make sure that the session to revert a sequence
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-10 18:42 ` [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch Brian Lyles
@ 2024-03-11  0:07   ` Junio C Hamano
  2024-03-11 16:54     ` Junio C Hamano
  2024-03-13 15:10   ` phillip.wood123
  1 sibling, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-03-11  0:07 UTC (permalink / raw)
  To: Brian Lyles; +Cc: git, newren, me, phillip.wood123, Phillip Wood

Brian Lyles <brianmlyles@gmail.com> writes:

> When using git-cherry-pick(1) with `--allow-empty` while on an unborn
> branch, an error is thrown. This is inconsistent with the same
> cherry-pick when `--allow-empty` is not specified.

When cherry-picking on top of an unborn branch without the option,
how does the code figure out that we are on an unborn branch?  We
must be doing the detection, as we'd need to at least know the fact
that we are on an unborn in order to make the resulting commit a
root commit and also in order to apply the cherry-picked changes
against an empty tree.

> Treat a failure reading HEAD as an unborn branch in
> `is_index_unchanged`. This is consistent with other sequencer logic such
> as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as
> the tree to compare against.

It is not a good code hygiene to assume that a failure to read HEAD
always means we are on an unborn branch, even if that is the most
likely cause of the failure.  We may instead want to positively
determine that we are on an unborn state, by seeing if the HEAD is a
symbolic ref that points at a ref in refs/heads/* hierarchy, and
that ref does not exist.

But if the existing sequencer code is littered with such loose logic
everywhere, perhaps imitating the looseness for now with a NEEDSWORK
comment to later clean them all up would be the least we could do
right now.  If we want to be more ambitious, a few clean-up patches
that refactors existing code paths that detect that we are on an
unborn branch into a call to a single helper function, and then
tightens its implementation, before doing this step would be even
better (but I offhand do not know how bad the current code is, so I
would understand it if it may turn out to be a lot more work than
you would want to invest in).

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-11  0:07   ` Junio C Hamano
@ 2024-03-11 16:54     ` Junio C Hamano
  2024-03-12  2:04       ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-03-11 16:54 UTC (permalink / raw)
  To: Brian Lyles; +Cc: git, newren, me, phillip.wood123, Phillip Wood

Junio C Hamano <gitster@pobox.com> writes:

> It is not a good code hygiene to assume that a failure to read HEAD
> always means we are on an unborn branch, even if that is the most
> likely cause of the failure.  We may instead want to positively
> determine that we are on an unborn state, by seeing if the HEAD is a
> symbolic ref that points at a ref in refs/heads/* hierarchy, and
> that ref does not exist.

I suspect that you are almost there.

+	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+		/*
+		 * Treat an error reading HEAD as an unborn branch.
+		 */

After we see this error, if we make a call to resolve_ref_unsafe()
with RESOLVE_REF_NO_RECURSE, the call should return the branch that
we are on but is not yet born, and &oid will get the null_oid.  I am
not sure if there is a way to combine the two calls into one, but
because the failure case (i.e. doing anything on an unborn branch)
is a rare case that happens only once before actually giving birth
to a new branch, it probably is not worth spending extra brain
cycles on it and just use a simple and stupid "when resolving fails,
see if we are in a rare salvageable case with extra code" approach.


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-11 16:54     ` Junio C Hamano
@ 2024-03-12  2:04       ` Brian Lyles
  2024-03-12 22:25         ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-12  2:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, newren, me, phillip.wood123, Phillip Wood

Hi Junio,

On Mon, Mar 11, 2024 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:

> Junio C Hamano <gitster@pobox.com> writes:
> 
>> It is not a good code hygiene to assume that a failure to read HEAD
>> always means we are on an unborn branch, even if that is the most
>> likely cause of the failure.  We may instead want to positively
>> determine that we are on an unborn state, by seeing if the HEAD is a
>> symbolic ref that points at a ref in refs/heads/* hierarchy, and
>> that ref does not exist.
> 
> I suspect that you are almost there.
> 
> +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
> +		/*
> +		 * Treat an error reading HEAD as an unborn branch.
> +		 */
> 
> After we see this error, if we make a call to resolve_ref_unsafe()
> with RESOLVE_REF_NO_RECURSE, the call should return the branch that
> we are on but is not yet born, and &oid will get the null_oid.  I am
> not sure if there is a way to combine the two calls into one, but
> because the failure case (i.e. doing anything on an unborn branch)
> is a rare case that happens only once before actually giving birth
> to a new branch, it probably is not worth spending extra brain
> cycles on it and just use a simple and stupid "when resolving fails,
> see if we are in a rare salvageable case with extra code" approach.

If I'm understanding you correctly, it sounds like you're hinting at
something like this:

	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
		/*
		 * Check to see if this is an unborn branch
		 */
		if (resolve_ref_unsafe("HEAD", RESOLVE_REF_NO_RECURSE, &head_oid, NULL)
			&& is_null_oid(&head_oid)) {
			head_tree_oid = the_hash_algo->empty_tree;
		} else {
			return error(_("could not resolve HEAD commit"));
		}
	} else {
		...
	}

This does in fact seem to have the same effect as the original patch in
this case. If this is in line with what you are looking for, I can
include this in v4. The commit message would be adjusted slightly to be:

	sequencer: handle unborn branch with `--allow-empty`

	When using git-cherry-pick(1) with `--allow-empty` while on an
	unborn branch, an error is thrown. This is inconsistent with the
	same cherry-pick when `--allow-empty` is not specified.

	Detect unborn branches in `is_index_unchanged`. When on an unborn
	branch, use the `empty_tree` as the tree to compare against.

	...

Do these changes look good?

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-12  2:04       ` Brian Lyles
@ 2024-03-12 22:25         ` Junio C Hamano
  2024-03-16  3:05           ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-03-12 22:25 UTC (permalink / raw)
  To: Brian Lyles; +Cc: git, newren, me, phillip.wood123, Phillip Wood

"Brian Lyles" <brianmlyles@gmail.com> writes:

> If I'm understanding you correctly, it sounds like you're hinting at
> something like this:

You may also want to add _READING (I do not know offhand).  Also
you'd want to make sure resolve_ref_unsafe() returned a plausible
looking refname (i.e. passes starts_with("refs/heads/").

But other than that, yeah, doing these as "extra checks" only after
we see the primary resolve_ref("HEAD") fails was what I had in mind.

Thanks.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-10 18:42 ` [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch Brian Lyles
  2024-03-11  0:07   ` Junio C Hamano
@ 2024-03-13 15:10   ` phillip.wood123
  2024-03-16  3:07     ` Brian Lyles
  1 sibling, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-03-13 15:10 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster, Phillip Wood

Hi Brian

On 10/03/2024 18:42, Brian Lyles wrote:
> - Avoid using `--quiet` in the `git diff` call to make debugging easier
>    in the event of a failure

Sorry, I forgot when I was reviewing v2 that we need to replace --quiet 
with --exit-code otherwise the diff will never fail. Apart from that I 
don't have anything to add to Junio's comments on this patch.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-10 18:42 ` [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
@ 2024-03-13 16:10   ` phillip.wood123
  2024-03-13 17:17     ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-03-13 16:10 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/03/2024 18:42, Brian Lyles wrote:
> As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a
> commit being made redundant if the content from the picked commit is
> already present in the target history. However, git-cherry-pick(1) does
> not have the same options available that git-rebase(1) and git-am(1) have.
> 
> There are three things that can be done with these redundant commits:
> drop them, keep them, or have the cherry-pick stop and wait for the user
> to take an action. git-rebase(1) has the `--empty` option added in commit
> e98c4269c8 (rebase (interactive-backend): fix handling of commits that
> become empty, 2020-02-15), which handles all three of these scenarios.
> Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support
> --empty=<option> to handle empty patches, 2021-12-09).
> 
> git-cherry-pick(1), on the other hand, only supports two of the three
> possiblities: Keep the redundant commits via `--keep-redundant-commits`,
> or have the cherry-pick fail by not specifying that option. There is no
> way to automatically drop redundant commits.
> 
> In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and
> git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It
> has the same three options (keep, drop, and stop), and largely behaves
> the same. The notable difference is that for git-cherry-pick(1), the
> default will be `stop`, which maintains the current behavior when the
> option is not specified.
> 
> Like the existing `--keep-redundant-commits`, `--empty=keep` will imply
> `--allow-empty`.

I think this is reasonable. git rebase defaults to keeping commits that 
start empty so now that "git cherry-pick --empty=keep" implies 
"--allow-empty" it will behave the same as "git rebase --empty=keep" as 
well as matching the behavior of "git cherry-pick --keep-redundant-commits".

> The `--keep-redundant-commits` option will be documented as a deprecated
> synonym of `--empty=keep`, and will be supported for backwards
> compatibility for the time being.
> 
> Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
> ---
> 
> Differences from v2:
> 
> - `--empty=keep` will now imply `--allow-empty`, consistent with
>    `--keep-redundant-commits`. See [1] for more information.
> - Tests for persistence of the new behaviors after `--continue`, etc.
>    are more focused on user-visible behaviors rather than implementation
>    details.
> - The new empty_action enum uses -1 for unspecified instead of 0.

Thanks for reworking the tests. I've left a small comment below but this 
is looking good.

> +--empty=(drop|keep|stop)::
> +	How to handle commits being cherry-picked that are redundant with
> +	changes already in the current history.
> ++
> +--
> +`drop`;;
> +	The commit will be dropped.
> +`keep`;;
> +	The commit will be kept. Implies `--allow-empty`.
> +`stop`;;
> +	The cherry-pick will stop when the commit is applied, allowing
> +	you to examine the commit. This is the default behavior.
> +--
> ++
> +Note that this option specifies how to handle a commit that was not initially
> +empty, but rather became empty due to a previous commit. Commits that were
> +initially empty will cause the cherry-pick to fail. To force the inclusion of
> +those commits, use `--allow-empty`.

I found this last paragraph is slightly confusing now --empty=keep 
implies --allow-empty. Maybe we could change the middle sentence to say 
something like

     With the exception of `--empty=keep` commits that were initially
     empty will cause the cherry-pick to fail.

> +	if (opts->action == REPLAY_PICK) {
> +		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
> +		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
> +	}
> +
>   	/* implies allow_empty */
>   	if (opts->keep_redundant_commits)
>   		opts->allow_empty = 1;

--empty=keep sets opts->keep_redundant_commits above so this makes it 
imply --allow-empty - good.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-10 18:41 ` [PATCH v3 0/7] " Brian Lyles
@ 2024-03-13 16:12   ` phillip.wood123
  0 siblings, 0 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-03-13 16:12 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 10/03/2024 18:41, Brian Lyles wrote:
> The ultimate goal of this series is to allow git-cherry-pick(1) to
> automatically drop redundant commits. The mechanism chosen is an
> `--empty` option that provides the same flexibility as the `--empty`
> options for git-rebase(1) and git-am(1).
> 
> Some secondary goals are to improve the consistency in the values and
> documentation for this option across the three commands.
> 
> See "Does extending `--empty` to git-cherry-pick make sense?" [1] for
> some context for why this option is desired in git-cherry-pick(1).
> 
> [1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com
> 
> Along the way, I (with some help from Elijah and Phillip) found a few
> other things in the docs and related sequencer code to clean up.
> 
> The primary difference from v2 of this patch is that I no longer make
> any attempt to change the behavior of `--keep-redundant-commits`
> implying `--allow-empty`, and the new `--empty=keep` will likewise also
> imply `--allow-empty`. See "Re: [PATCH v2 8/8] cherry-pick: add
> `--empty` for more robust redundant commit handling" [2] and the
> previous messages in that thread for more context. Patch 6/8 from v2 is
> dropped entirely, with some adjustments to the ultimate patch in this
> series as well.

This is looking good, I've left a couple of comments but there is 
nothing major.

Thanks for working on it

Phillip

> [2]: https://lore.kernel.org/git/xmqqttltu7zs.fsf@gitster.g/
> 
> Brian Lyles (7):
>    docs: address inaccurate `--empty` default with `--exec`
>    docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
>    rebase: update `--empty=ask` to `--empty=stop`
>    sequencer: treat error reading HEAD as unborn branch
>    sequencer: do not require `allow_empty` for redundant commit options
>    cherry-pick: enforce `--keep-redundant-commits` incompatibility
>    cherry-pick: add `--empty` for more robust redundant commit handling
> 
>   Documentation/git-am.txt          | 20 ++++++----
>   Documentation/git-cherry-pick.txt | 30 +++++++++++----
>   Documentation/git-rebase.txt      | 26 ++++++++-----
>   builtin/rebase.c                  | 16 +++++---
>   builtin/revert.c                  | 38 +++++++++++++++++-
>   sequencer.c                       | 64 ++++++++++++++++---------------
>   t/t3424-rebase-empty.sh           | 55 ++++++++++++++++++++++++--
>   t/t3501-revert-cherry-pick.sh     | 14 +++++--
>   t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++++-
>   t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++
>   10 files changed, 279 insertions(+), 67 deletions(-)
> 

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-13 16:10   ` phillip.wood123
@ 2024-03-13 17:17     ` Junio C Hamano
  2024-03-16  5:20       ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-03-13 17:17 UTC (permalink / raw)
  To: phillip.wood123; +Cc: Brian Lyles, git, newren, me

phillip.wood123@gmail.com writes:

>> +Note that this option specifies how to handle a commit that was not initially
>> +empty, but rather became empty due to a previous commit. Commits that were
>> +initially empty will cause the cherry-pick to fail. To force the inclusion of
>> +those commits, use `--allow-empty`.
>
> I found this last paragraph is slightly confusing now --empty=keep
> implies --allow-empty. Maybe we could change the middle sentence to
> say something like
>
>     With the exception of `--empty=keep` commits that were initially
>     empty will cause the cherry-pick to fail.

That is certainly easier to read and much less confusing.

Thanks, both.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-12 22:25         ` Junio C Hamano
@ 2024-03-16  3:05           ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-16  3:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, newren, me, phillip.wood123, Phillip Wood


On Tue, Mar 12, 2024 at 5:25 PM Junio C Hamano <gitster@pobox.com> wrote:

> "Brian Lyles" <brianmlyles@gmail.com> writes:
> 
>> If I'm understanding you correctly, it sounds like you're hinting at
>> something like this:
> 
> You may also want to add _READING (I do not know offhand).  Also
> you'd want to make sure resolve_ref_unsafe() returned a plausible
> looking refname (i.e. passes starts_with("refs/heads/").
> 
> But other than that, yeah, doing these as "extra checks" only after
> we see the primary resolve_ref("HEAD") fails was what I had in mind.
> 
> Thanks.

Makes sense to me. From reading the documentation for
RESOLVE_REF_READING, I think we do want that as well, and the
starts_with("refs/heads/") check works as expected too. I'll incorporate
those into v4.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch
  2024-03-13 15:10   ` phillip.wood123
@ 2024-03-16  3:07     ` Brian Lyles
  0 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-16  3:07 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster

Hi Phillip

On Wed, Mar 13, 2024 at 10:10 AM <phillip.wood123@gmail.com> wrote:

> Hi Brian
> 
> On 10/03/2024 18:42, Brian Lyles wrote:
>> - Avoid using `--quiet` in the `git diff` call to make debugging easier
>>    in the event of a failure
> 
> Sorry, I forgot when I was reviewing v2 that we need to replace --quiet 
> with --exit-code otherwise the diff will never fail. Apart from that I 
> don't have anything to add to Junio's comments on this patch.

Ah, of course -- thank you for catching that. I'll include that in v4.

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-13 17:17     ` Junio C Hamano
@ 2024-03-16  5:20       ` Brian Lyles
  2024-03-20 19:35         ` phillip.wood123
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-16  5:20 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: phillip.wood123, git, newren, me

Hi Phillip and Junio

Apologies in advance if this is a duplicate message -- it appears that my reply
never showed up on the public archive at https://lore.kernel.org/git/ for some
reason, and I'm unsure if those CC'd received it either. As such, I am resending
it.

On Wed, Mar 13, 2024 at 12:17 PM Junio C Hamano <gitster@pobox.com> wrote:

> phillip.wood123@gmail.com writes:
>
>>> +Note that this option specifies how to handle a commit that was not initially
>>> +empty, but rather became empty due to a previous commit. Commits that were
>>> +initially empty will cause the cherry-pick to fail. To force the inclusion of
>>> +those commits, use `--allow-empty`.
>>
>> I found this last paragraph is slightly confusing now --empty=keep
>> implies --allow-empty. Maybe we could change the middle sentence to
>> say something like
>>
>>     With the exception of `--empty=keep` commits that were initially
>>     empty will cause the cherry-pick to fail.
>
> That is certainly easier to read and much less confusing.

I agree that this paragraph is slightly confusing. I tried this
suggestion on but found it to not sit quite right, I think because the
two exceptions (--empty=keep and --allow-empty) were not part of the
same sentence, so it felt a little disjointed. How would you feel about
the following instead, which aims to be more clear and specific about
the behavior?

        Note that `--empty=drop` and `--empty=stop` only specify how to
        handle a commit that was not initially empty, but rather became
        empty due to a previous commit. Commits that were initially empty
        will still cause the cherry-pick to fail unless one of
        `--empty=keep` or `--allow-empty` are specified.

Thank you both again for your time reviewing this!

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-16  5:20       ` Brian Lyles
@ 2024-03-20 19:35         ` phillip.wood123
  0 siblings, 0 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-03-20 19:35 UTC (permalink / raw)
  To: Brian Lyles, Junio C Hamano; +Cc: git, newren, me

Hi Brian

On 16/03/2024 05:20, Brian Lyles wrote:
> On Wed, Mar 13, 2024 at 12:17 PM Junio C Hamano <gitster@pobox.com> wrote:
>> phillip.wood123@gmail.com writes:
>>>> +Note that this option specifies how to handle a commit that was not initially
>>>> +empty, but rather became empty due to a previous commit. Commits that were
>>>> +initially empty will cause the cherry-pick to fail. To force the inclusion of
>>>> +those commits, use `--allow-empty`.
>>>
>>> I found this last paragraph is slightly confusing now --empty=keep
>>> implies --allow-empty. Maybe we could change the middle sentence to
>>> say something like
>>>
>>>      With the exception of `--empty=keep` commits that were initially
>>>      empty will cause the cherry-pick to fail.
>>
>> That is certainly easier to read and much less confusing.
> 
> I agree that this paragraph is slightly confusing. I tried this
> suggestion on but found it to not sit quite right, I think because the
> two exceptions (--empty=keep and --allow-empty) were not part of the
> same sentence, so it felt a little disjointed. How would you feel about
> the following instead, which aims to be more clear and specific about
> the behavior?
> 
>          Note that `--empty=drop` and `--empty=stop` only specify how to
>          handle a commit that was not initially empty, but rather became
>          empty due to a previous commit. Commits that were initially empty
>          will still cause the cherry-pick to fail unless one of
>          `--empty=keep` or `--allow-empty` are specified.

That looks fine to me

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (22 preceding siblings ...)
  2024-03-10 18:42 ` [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
@ 2024-03-20 23:36 ` Brian Lyles
  2024-03-25 14:38   ` phillip.wood123
  2024-03-20 23:36 ` [PATCH v4 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
                   ` (14 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:36 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

The ultimate goal of this series is to allow git-cherry-pick(1) to
automatically drop redundant commits. The mechanism chosen is an
`--empty` option that provides the same flexibility as the `--empty`
options for git-rebase(1) and git-am(1).

Some secondary goals are to improve the consistency in the values and
documentation for this option across the three commands.

See "Does extending `--empty` to git-cherry-pick make sense?" [1] for
some context for why this option is desired in git-cherry-pick(1).

[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com

Along the way, I (with some help from Elijah and Phillip) found a few
other things in the docs and related sequencer code to clean up.

This re-roll contains only minor changes from v3.

Brian Lyles (7):
  docs: address inaccurate `--empty` default with `--exec`
  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  rebase: update `--empty=ask` to `--empty=stop`
  sequencer: handle unborn branch with `--allow-empty`
  sequencer: do not require `allow_empty` for redundant commit options
  cherry-pick: enforce `--keep-redundant-commits` incompatibility
  cherry-pick: add `--empty` for more robust redundant commit handling

 Documentation/git-am.txt          | 20 +++++----
 Documentation/git-cherry-pick.txt | 30 ++++++++++----
 Documentation/git-rebase.txt      | 26 ++++++++----
 builtin/rebase.c                  | 16 +++++---
 builtin/revert.c                  | 38 ++++++++++++++++-
 sequencer.c                       | 68 +++++++++++++++++--------------
 t/t3424-rebase-empty.sh           | 55 +++++++++++++++++++++++--
 t/t3501-revert-cherry-pick.sh     | 14 +++++--
 t/t3505-cherry-pick-empty.sh      | 51 ++++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh   | 32 +++++++++++++++
 10 files changed, 283 insertions(+), 67 deletions(-)

-- 
2.43.2


^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v4 1/7] docs: address inaccurate `--empty` default with `--exec`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (23 preceding siblings ...)
  2024-03-20 23:36 ` [PATCH v4 0/7] " Brian Lyles
@ 2024-03-20 23:36 ` Brian Lyles
  2024-03-20 23:36 ` [PATCH v4 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
                   ` (13 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:36 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

The documentation for git-rebase(1) indicates that using the `--exec`
option will use `--empty=drop`. This is inaccurate: when `--interactive`
is not explicitly provided, `--exec` results in `--empty=keep`
behaviors.

Correctly indicate the behavior of `--exec` using `--empty=keep` when
`--interactive` is not specified.

Reported-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt | 10 +++++-----
 t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 06206521fc..3334e85356 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.
 	empty after rebasing (because they contain a subset of already
 	upstream changes).  With drop (the default), commits that
 	become empty are dropped.  With keep, such commits are kept.
-	With ask (implied by `--interactive`), the rebase will halt when
-	an empty commit is applied allowing you to choose whether to
-	drop it, edit files more, or just commit the empty changes.
-	Other options, like `--exec`, will use the default of drop unless
-	`-i`/`--interactive` is explicitly specified.
+	With ask, the rebase will halt when an empty commit is applied
+	allowing you to choose whether to drop it, edit files more, or just
+	commit the empty changes.
+	When the `-i`/`--interactive` option is used, the default becomes ask.
+	Otherwise, when the `--exec` option is used, the default becomes keep.
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a0af..73ff35ced2 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
 	test_path_is_missing .git/MERGE_MSG
 '
 
+test_expect_success 'rebase --exec --empty=drop' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=drop upstream &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=keep upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=ask' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v4 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (24 preceding siblings ...)
  2024-03-20 23:36 ` [PATCH v4 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
@ 2024-03-20 23:36 ` Brian Lyles
  2024-03-20 23:36 ` [PATCH v4 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
                   ` (12 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:36 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

Both of these pages document very similar `--empty` options, but with
different styles. The exact behavior of these `--empty` options differs
somewhat, but consistent styling in the docs is still beneficial. This
commit aims to make them more consistent.

Break the possible values for `--empty` into separate sections for
readability. Alphabetical order is chosen for consistency.

In a future commit, we'll be documenting a new `--empty` option for
git-cherry-pick(1), making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-am.txt     | 20 +++++++++++++-------
 Documentation/git-rebase.txt | 25 ++++++++++++++++---------
 2 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e080458d6c..f852e0ba79 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -66,13 +66,19 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
---empty=(stop|drop|keep)::
-	By default, or when the option is set to 'stop', the command
-	errors out on an input e-mail message lacking a patch
-	and stops in the middle of the current am session. When this
-	option is set to 'drop', skip such an e-mail message instead.
-	When this option is set to 'keep', create an empty commit,
-	recording the contents of the e-mail message as its log.
+--empty=(drop|keep|stop)::
+	How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+	The e-mail message will be skipped.
+`keep`;;
+	An empty commit will be created, with the contents of the e-mail
+	message as its log.
+`stop`;;
+	The command will fail, stopping in the middle of the current `am`
+	session. This is the default behavior.
+--
 
 -m::
 --message-id::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 3334e85356..0b0d0ccb80 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(drop|keep|ask)::
+--empty=(ask|drop|keep)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
-	upstream changes).  With drop (the default), commits that
-	become empty are dropped.  With keep, such commits are kept.
-	With ask, the rebase will halt when an empty commit is applied
-	allowing you to choose whether to drop it, edit files more, or just
-	commit the empty changes.
-	When the `-i`/`--interactive` option is used, the default becomes ask.
-	Otherwise, when the `--exec` option is used, the default becomes keep.
+	upstream changes):
++
+--
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified.
+`drop`;;
+	The commit will be dropped. This is the default behavior.
+`keep`;;
+	The commit will be kept. This option is implied when `--exec` is
+	specified unless `-i`/`--interactive` is also specified.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
@@ -704,7 +711,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(drop|keep|ask)` option for changing the behavior
+also has an `--empty=(ask|drop|keep)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v4 3/7] rebase: update `--empty=ask` to `--empty=stop`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (25 preceding siblings ...)
  2024-03-20 23:36 ` [PATCH v4 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
@ 2024-03-20 23:36 ` Brian Lyles
  2024-03-20 23:36 ` [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
                   ` (11 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:36 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09), `stop` was used
instead of `ask`. `stop` is a more accurate term for describing what
really happens, and consistency is good.

Update git-rebase(1) to also use `stop`, while keeping `ask` as a
deprecated synonym. Update the tests to primarily use `stop`, but also
ensure that `ask` is still allowed.

In a future commit, we'll be adding a new `--empty` option for
git-cherry-pick(1) as well, making the consistency even more relevant.

Reported-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt | 15 ++++++++-------
 builtin/rebase.c             | 16 ++++++++++------
 t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 0b0d0ccb80..67dd0a533e 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(ask|drop|keep)::
+--empty=(drop|keep|stop)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
 	upstream changes):
 +
 --
-`ask`;;
-	The rebase will halt when the commit is applied, allowing you to
-	choose whether to drop it, edit files more, or just commit the empty
-	changes. This option is implied when `-i`/`--interactive` is
-	specified.
 `drop`;;
 	The commit will be dropped. This is the default behavior.
 `keep`;;
 	The commit will be kept. This option is implied when `--exec` is
 	specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified. `ask` is a deprecated synonym of `stop`.
 --
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
@@ -711,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(ask|drop|keep)` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 5b086f651a..a4916781ce 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -58,7 +58,7 @@ enum empty_type {
 	EMPTY_UNSPECIFIED = -1,
 	EMPTY_DROP,
 	EMPTY_KEEP,
-	EMPTY_ASK
+	EMPTY_STOP
 };
 
 enum action {
@@ -951,10 +951,14 @@ static enum empty_type parse_empty_value(const char *value)
 		return EMPTY_DROP;
 	else if (!strcasecmp(value, "keep"))
 		return EMPTY_KEEP;
-	else if (!strcasecmp(value, "ask"))
-		return EMPTY_ASK;
+	else if (!strcasecmp(value, "stop"))
+		return EMPTY_STOP;
+	else if (!strcasecmp(value, "ask")) {
+		warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+		return EMPTY_STOP;
+	}
 
-	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
 }
 
 static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1133,7 +1137,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				 "instead of ignoring them"),
 			      1, PARSE_OPT_HIDDEN),
 		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
+		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
 			       N_("how to handle commits that become empty"),
 			       PARSE_OPT_NONEG, parse_opt_empty),
 		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1550,7 +1554,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (options.empty == EMPTY_UNSPECIFIED) {
 		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-			options.empty = EMPTY_ASK;
+			options.empty = EMPTY_STOP;
 		else if (options.exec.nr > 0)
 			options.empty = EMPTY_KEEP;
 		else
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 73ff35ced2..1ee6b00fd5 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --merge --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --interactive --empty=ask upstream &&
+	test_must_fail git rebase --interactive --empty=stop upstream &&
 
 	git rebase --skip &&
 
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --interactive upstream &&
 
@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --exec --empty=ask' '
+test_expect_success 'rebase --exec --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+	test_must_fail git rebase --exec "true" --empty=stop upstream &&
 
 	git rebase --skip &&
 
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (26 preceding siblings ...)
  2024-03-20 23:36 ` [PATCH v4 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
@ 2024-03-20 23:36 ` Brian Lyles
  2024-03-21  9:52   ` Dirk Gouders
  2024-03-20 23:37 ` [PATCH v4 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
                   ` (10 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:36 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

When using git-cherry-pick(1) with `--allow-empty` while on an unborn
branch, an error is thrown. This is inconsistent with the same
cherry-pick when `--allow-empty` is not specified.

Detect unborn branches in `is_index_unchanged`. When on an unborn
branch, use the `empty_tree` as the tree to compare against.

Add a new test to cover this scenario. While modelled off of the
existing 'cherry-pick on unborn branch' test, some improvements can be
made:

- Use `git switch --orphan unborn` instead of `git checkout --orphan
  unborn` to avoid the need for a separate `rm -rf *` call
- Avoid using `--quiet` in the `git diff` call to make debugging easier
  in the event of a failure. Use simply `--exit-code` instead.

Make these improvements to the existing test as well as the new test.

Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Changes from v3:

- More robustly validate that we are on an unborn branch, rather than
  assuming that an error while reading the HEAD implies an unborn branch
- Replace `--quiet` with `--exit-code` in the tests rather than just
  removing `--quiet`, to ensure that the test still fails appropriately
  if any differences are found.

 sequencer.c                   | 39 ++++++++++++++++++++++-------------
 t/t3501-revert-cherry-pick.sh | 14 ++++++++++---
 2 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index f49a871ac0..f31d71ebad 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -770,29 +770,40 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
 static int is_index_unchanged(struct repository *r)
 {
 	struct object_id head_oid, *cache_tree_oid;
+	const struct object_id *head_tree_oid;
 	struct commit *head_commit;
 	struct index_state *istate = r->index;
+	const char *head_name;
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-		return error(_("could not resolve HEAD commit"));
+	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+		/*
+		 * Check to see if this is an unborn branch
+		 */
+		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
+		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
+			return error(_("could not resolve HEAD commit"));
+		head_tree_oid = the_hash_algo->empty_tree;
+	} else {
+		head_commit = lookup_commit(r, &head_oid);
 
-	head_commit = lookup_commit(r, &head_oid);
+		/*
+		 * If head_commit is NULL, check_commit, called from
+		 * lookup_commit, would have indicated that head_commit is not
+		 * a commit object already.  repo_parse_commit() will return failure
+		 * without further complaints in such a case.  Otherwise, if
+		 * the commit is invalid, repo_parse_commit() will complain.  So
+		 * there is nothing for us to say here.  Just return failure.
+		 */
+		if (repo_parse_commit(r, head_commit))
+			return -1;
 
-	/*
-	 * If head_commit is NULL, check_commit, called from
-	 * lookup_commit, would have indicated that head_commit is not
-	 * a commit object already.  repo_parse_commit() will return failure
-	 * without further complaints in such a case.  Otherwise, if
-	 * the commit is invalid, repo_parse_commit() will complain.  So
-	 * there is nothing for us to say here.  Just return failure.
-	 */
-	if (repo_parse_commit(r, head_commit))
-		return -1;
+		head_tree_oid = get_commit_tree_oid(head_commit);
+	}
 
 	if (!(cache_tree_oid = get_cache_tree_oid(istate)))
 		return -1;
 
-	return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+	return oideq(cache_tree_oid, head_tree_oid);
 }
 
 static int write_author_script(const char *message)
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index aeab689a98..af73227512 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '
 '
 
 test_expect_success 'cherry-pick on unborn branch' '
-	git checkout --orphan unborn &&
+	git switch --orphan unborn &&
 	git rm --cached -r . &&
-	rm -rf * &&
 	git cherry-pick initial &&
-	git diff --quiet initial &&
+	git diff --exit-code initial &&
+	test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+	git checkout --detach &&
+	git branch -D unborn &&
+	git switch --orphan unborn &&
+	git cherry-pick initial --allow-empty &&
+	git diff --exit-code initial &&
 	test_cmp_rev ! initial HEAD
 '
 
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v4 5/7] sequencer: do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (27 preceding siblings ...)
  2024-03-20 23:36 ` [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
@ 2024-03-20 23:37 ` Brian Lyles
  2024-03-20 23:37 ` [PATCH v4 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
                   ` (9 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:37 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

A consumer of the sequencer that wishes to take advantage of either the
`keep_redundant_commits` or `drop_redundant_commits` feature must also
specify `allow_empty`. However, these refer to two distinct types of
empty commits:

- `allow_empty` refers specifically to commits which start empty
- `keep_redundant_commits` refers specifically to commits that do not
  start empty, but become empty due to the content already existing in
  the target history

Conceptually, there is no reason that the behavior for handling one of
these should be entangled with the other. It is particularly unintuitive
to require `allow_empty` in order for `drop_redundant_commits` to have
an effect: in order to prevent redundant commits automatically,
initially-empty commits would need to be kept automatically as well.

Instead, rewrite the `allow_empty()` logic to remove the over-arching
requirement that `allow_empty` be specified in order to reach any of the
keep/drop behaviors. Only if the commit was originally empty will
`allow_empty` have an effect.

Note that no behavioral changes should result from this commit -- it
merely sets the stage for future commits. In one such future commit, an
`--empty` option will be added to git-cherry-pick(1), meaning that
`drop_redundant_commits` will be used by that command.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 sequencer.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index f31d71ebad..b8d8f15e65 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1730,34 +1730,25 @@ static int allow_empty(struct repository *r,
 	int index_unchanged, originally_empty;
 
 	/*
-	 * Four cases:
+	 * For a commit that is initially empty, allow_empty determines if it
+	 * should be kept or not
 	 *
-	 * (1) we do not allow empty at all and error out.
-	 *
-	 * (2) we allow ones that were initially empty, and
-	 *     just drop the ones that become empty
-	 *
-	 * (3) we allow ones that were initially empty, but
-	 *     halt for the ones that become empty;
-	 *
-	 * (4) we allow both.
+	 * For a commit that becomes empty, keep_redundant_commits and
+	 * drop_redundant_commits determine whether the commit should be kept or
+	 * dropped. If neither is specified, halt.
 	 */
-	if (!opts->allow_empty)
-		return 0; /* let "git commit" barf as necessary */
-
 	index_unchanged = is_index_unchanged(r);
 	if (index_unchanged < 0)
 		return index_unchanged;
 	if (!index_unchanged)
 		return 0; /* we do not have to say --allow-empty */
 
-	if (opts->keep_redundant_commits)
-		return 1;
-
 	originally_empty = is_original_commit_empty(commit);
 	if (originally_empty < 0)
 		return originally_empty;
 	if (originally_empty)
+		return opts->allow_empty;
+	else if (opts->keep_redundant_commits)
 		return 1;
 	else if (opts->drop_redundant_commits)
 		return 2;
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v4 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (28 preceding siblings ...)
  2024-03-20 23:37 ` [PATCH v4 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
@ 2024-03-20 23:37 ` Brian Lyles
  2024-03-20 23:37 ` [PATCH v4 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
                   ` (8 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:37 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When `--keep-redundant-commits` was added in  b27cfb0d8d
(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was
not marked as incompatible with the various operations needed to
continue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and
`--quit`).

Enforce this incompatibility via `verify_opt_compatible` like we do for
the other various options.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 builtin/revert.c             |  1 +
 t/t3505-cherry-pick-empty.sh | 14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/builtin/revert.c b/builtin/revert.c
index 89821bab95..a1936ef70e 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -167,6 +167,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--ff", opts->allow_ff,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+				"--keep-redundant-commits", opts->keep_redundant_commits,
 				NULL);
 	}
 
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38d5a..61f91aaa0a 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -99,4 +99,18 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
 	test_cmp expect actual
 '
 
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
 test_done
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v4 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (29 preceding siblings ...)
  2024-03-20 23:37 ` [PATCH v4 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
@ 2024-03-20 23:37 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 0/7] " Brian Lyles
                   ` (7 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-20 23:37 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a
commit being made redundant if the content from the picked commit is
already present in the target history. However, git-cherry-pick(1) does
not have the same options available that git-rebase(1) and git-am(1) have.

There are three things that can be done with these redundant commits:
drop them, keep them, or have the cherry-pick stop and wait for the user
to take an action. git-rebase(1) has the `--empty` option added in commit
e98c4269c8 (rebase (interactive-backend): fix handling of commits that
become empty, 2020-02-15), which handles all three of these scenarios.
Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09).

git-cherry-pick(1), on the other hand, only supports two of the three
possiblities: Keep the redundant commits via `--keep-redundant-commits`,
or have the cherry-pick fail by not specifying that option. There is no
way to automatically drop redundant commits.

In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and
git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It
has the same three options (keep, drop, and stop), and largely behaves
the same. The notable difference is that for git-cherry-pick(1), the
default will be `stop`, which maintains the current behavior when the
option is not specified.

Like the existing `--keep-redundant-commits`, `--empty=keep` will imply
`--allow-empty`.

The `--keep-redundant-commits` option will be documented as a deprecated
synonym of `--empty=keep`, and will be supported for backwards
compatibility for the time being.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Changes from v3:

- Re-worded the note about initially-empty commits in the documentation
  for the new `--empty` option.

 Documentation/git-cherry-pick.txt | 30 +++++++++++++++++++------
 builtin/revert.c                  | 37 ++++++++++++++++++++++++++++++-
 sequencer.c                       |  6 +++++
 t/t3505-cherry-pick-empty.sh      | 37 ++++++++++++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++++++++++++
 5 files changed, 133 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d200..81ace900fc 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,20 +131,36 @@ effect to your index in a row.
 	even without this option.  Note also, that use of this option only
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
-	previous commit are dropped.  To force the inclusion of those commits
-	use `--keep-redundant-commits`.
+	previous commit will cause the cherry-pick to fail.  To force the
+	inclusion of those commits, use `--empty=keep`.
 
 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
 	This option overrides that behavior, allowing commits with empty
 	messages to be cherry picked.
 
+--empty=(drop|keep|stop)::
+	How to handle commits being cherry-picked that are redundant with
+	changes already in the current history.
++
+--
+`drop`;;
+	The commit will be dropped.
+`keep`;;
+	The commit will be kept. Implies `--allow-empty`.
+`stop`;;
+	The cherry-pick will stop when the commit is applied, allowing
+	you to examine the commit. This is the default behavior.
+--
++
+Note that `--empty=drop` and `--empty=stop` only specify how to handle a
+commit that was not initially empty, but rather became empty due to a previous
+commit. Commits that were initially empty will still cause the cherry-pick to
+fail unless one of `--empty=keep` or `--allow-empty` are specified.
++
+
 --keep-redundant-commits::
-	If a commit being cherry picked duplicates a commit already in the
-	current history, it will become empty.  By default these
-	redundant commits cause `cherry-pick` to stop so the user can
-	examine the commit. This option overrides that behavior and
-	creates an empty commit object.  Implies `--allow-empty`.
+	Deprecated synonym for `--empty=keep`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index a1936ef70e..53935d2c68 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
 	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
+enum empty_action {
+	EMPTY_COMMIT_UNSPECIFIED = -1,
+	STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "stop"))
+		*opt_value = STOP_ON_EMPTY_COMMIT;
+	else if (!strcmp(arg, "drop"))
+		*opt_value = DROP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "keep"))
+		*opt_value = KEEP_EMPTY_COMMIT;
+	else
+		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+	return 0;
+}
+
 static int option_parse_m(const struct option *opt,
 			  const char *arg, int unset)
 {
@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	const char *cleanup_arg = NULL;
+	enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+				       N_("how to handle commits that become empty"),
+				       PARSE_OPT_NONEG, parse_opt_empty),
 			OPT_END(),
 		};
 		options = parse_options_concat(options, cp_extra);
@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
+	if (opts->action == REPLAY_PICK) {
+		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+	}
+
 	/* implies allow_empty */
 	if (opts->keep_redundant_commits)
 		opts->allow_empty = 1;
@@ -168,6 +202,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
 				"--keep-redundant-commits", opts->keep_redundant_commits,
+				"--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
 				NULL);
 	}
 
diff --git a/sequencer.c b/sequencer.c
index b8d8f15e65..42bac5ebd7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2928,6 +2928,9 @@ static int populate_opts_cb(const char *key, const char *value,
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+	else if (!strcmp(key, "options.drop-redundant-commits"))
+		opts->drop_redundant_commits =
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3472,6 +3475,9 @@ static int save_opts(struct replay_opts *opts)
 	if (opts->allow_empty_message)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.allow-empty-message", "true");
+	if (opts->drop_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.drop-redundant-commits", "true");
 	if (opts->keep_redundant_commits)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.keep-redundant-commits", "true");
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 61f91aaa0a..9748443530 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
 	git commit -m "add file2 on the side"
 '
 
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
 	git reset --hard &&
 	git checkout fork^0 &&
 	test_must_fail git cherry-pick main
@@ -113,4 +113,39 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '
 	git cherry-pick --abort
 '
 
+test_expect_success '--empty is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick --empty=stop main 2>output &&
+	test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=drop main &&
+	test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=keep main &&
+	test_commit_message HEAD -m "add file2 on main"
+'
+
 test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 72020a51c4..7eb52b12ed 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+	test_must_fail git cherry-pick --skip 2>msg &&
+	test_grep "The previous cherry-pick is now empty" msg &&
+	rm msg &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD^
+'
+
 test_expect_success 'revert persists opts correctly' '
 	pristine_detach initial &&
 	# to make sure that the session to revert a sequence
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`
  2024-03-20 23:36 ` [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
@ 2024-03-21  9:52   ` Dirk Gouders
  2024-03-21 16:22     ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: Dirk Gouders @ 2024-03-21  9:52 UTC (permalink / raw)
  To: Brian Lyles; +Cc: git, newren, me, phillip.wood123, gitster, Phillip Wood

Brian Lyles <brianmlyles@gmail.com> writes:

> +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
> +		/*
> +		 * Check to see if this is an unborn branch
> +		 */
> +		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
> +		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
> +			return error(_("could not resolve HEAD commit"));
> +		head_tree_oid = the_hash_algo->empty_tree;
> +	} else {
> +		head_commit = lookup_commit(r, &head_oid);
>  
> -	head_commit = lookup_commit(r, &head_oid);
> +		/*
> +		 * If head_commit is NULL, check_commit, called from
> +		 * lookup_commit, would have indicated that head_commit is not
> +		 * a commit object already.  repo_parse_commit() will return failure
> +		 * without further complaints in such a case.  Otherwise, if
> +		 * the commit is invalid, repo_parse_commit() will complain.  So
> +		 * there is nothing for us to say here.  Just return failure.
> +		 */
> +		if (repo_parse_commit(r, head_commit))
> +			return -1;

Not that I am qualified to do a review of your changes but I am in a
situation where I am trying to understand Git code in general and
(perhaps normal for this situation) wondering about varying styles of
commenting code -- could be that I am just too new to the code base and
do not yet understand the obvious things that don't need comments.

In the above example, there is a short but outstanding comment that
announces a check (and if I understood correctly by [1] it is a kind of
trick that could deserve some more information) and it does _not_
comment on the result.  Of course, I have an idea where the correct
place for a comment /* This is an unborn branch -- handle it as if... */
could be, but I'm not sure.

So, my intention is by no means to trigger another spin of this series
-- it is just a view from someone trying to understand not just this
code ;-)

Dirk

[1] https://lore.kernel.org/git/xmqqh6hcu2tg.fsf@gitster.g/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`
  2024-03-21  9:52   ` Dirk Gouders
@ 2024-03-21 16:22     ` Junio C Hamano
  2024-03-21 19:45       ` Dirk Gouders
  0 siblings, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-03-21 16:22 UTC (permalink / raw)
  To: Dirk Gouders; +Cc: Brian Lyles, git, newren, me, phillip.wood123, Phillip Wood

Dirk Gouders <dirk@gouders.net> writes:

> Brian Lyles <brianmlyles@gmail.com> writes:
>
>> +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
>> +		/*
>> +		 * Check to see if this is an unborn branch
>> +		 */
> In the above example, there is a short but outstanding comment that
> announces a check (and if I understood correctly by [1] it is a kind of
> trick that could deserve some more information) and it does _not_
> comment on the result.  Of course, I have an idea where the correct
> place for a comment /* This is an unborn branch -- handle it as if... */
> could be, but I'm not sure.

You mean "Check to see if this is an unborn branch, and if so, use
an empty tree to compare against, instead of the tree of the HEAD
that does not yet exist"?

I think that is possible, but the use of the_hash_algo->empty_tree
indicates that clearly enough.  But we need to stop somewhere and
what we see above may be a reasonable place to do so.

If anything, we may want to say why we want to continue as if we had
an empty tree (as opposed to fail and return with an error()), or
the tree to compare with is computed here for what purpose.  But the
name of the function may tell what this whole computation and
comparison is for, so it probably is not needed, either.



^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty`
  2024-03-21 16:22     ` Junio C Hamano
@ 2024-03-21 19:45       ` Dirk Gouders
  0 siblings, 0 replies; 118+ messages in thread
From: Dirk Gouders @ 2024-03-21 19:45 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brian Lyles, git, newren, me, phillip.wood123, Phillip Wood

Junio C Hamano <gitster@pobox.com> writes:

> Dirk Gouders <dirk@gouders.net> writes:
>
>> Brian Lyles <brianmlyles@gmail.com> writes:
>>
>>> +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
>>> +		/*
>>> +		 * Check to see if this is an unborn branch
>>> +		 */
>> In the above example, there is a short but outstanding comment that
>> announces a check (and if I understood correctly by [1] it is a kind of
>> trick that could deserve some more information) and it does _not_
>> comment on the result.  Of course, I have an idea where the correct
>> place for a comment /* This is an unborn branch -- handle it as if... */
>> could be, but I'm not sure.
>
> You mean "Check to see if this is an unborn branch, and if so, use
> an empty tree to compare against, instead of the tree of the HEAD
> that does not yet exist"?
>
> I think that is possible, but the use of the_hash_algo->empty_tree
> indicates that clearly enough.  But we need to stop somewhere and
> what we see above may be a reasonable place to do so.
>
> If anything, we may want to say why we want to continue as if we had
> an empty tree (as opposed to fail and return with an error()), or
> the tree to compare with is computed here for what purpose.  But the
> name of the function may tell what this whole computation and
> comparison is for, so it probably is not needed, either.

Thank you for the reply.

I guess, the hidden question in my comment was: "Do experienced
Git developers understand the code as something obvious?".
And I read your answer as a "Yes, no problem.".

Now, I can put this subject aside and later, after more reading, check
if my understanding improved sufficiently to now understand that code
without additional comments, as you already do.

Dirk

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-20 23:36 ` [PATCH v4 0/7] " Brian Lyles
@ 2024-03-25 14:38   ` phillip.wood123
  2024-03-25 16:12     ` Brian Lyles
  0 siblings, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-03-25 14:38 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 20/03/2024 23:36, Brian Lyles wrote:
> This re-roll contains only minor changes from v3.

Here is the range diff with a couple of comments

1:  5a503a7026 = 1:  5aa3f7cec2 docs: address inaccurate `--empty` default with `--exec`
2:  36278e0d40 = 2:  abb7d7233a docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
3:  a2f603da50 = 3:  ef8224d286 rebase: update `--empty=ask` to `--empty=stop`
4:  0701f9f9fc ! 4:  21ed2dfb65 sequencer: treat error reading HEAD as unborn branch
     @@ Metadata
      Author: Brian Lyles <brianmlyles@gmail.com>
      
       ## Commit message ##
     -    sequencer: treat error reading HEAD as unborn branch
     +    sequencer: handle unborn branch with `--allow-empty`
      
          When using git-cherry-pick(1) with `--allow-empty` while on an unborn
          branch, an error is thrown. This is inconsistent with the same
          cherry-pick when `--allow-empty` is not specified.
      
     -    Treat a failure reading HEAD as an unborn branch in
     -    `is_index_unchanged`. This is consistent with other sequencer logic such
     -    as `do_pick_commit`. When on an unborn branch, use the `empty_tree` as
     -    the tree to compare against.
     +    Detect unborn branches in `is_index_unchanged`. When on an unborn
     +    branch, use the `empty_tree` as the tree to compare against.
      
          Add a new test to cover this scenario. While modelled off of the
          existing 'cherry-pick on unborn branch' test, some improvements can be
     @@ Commit message
          - Use `git switch --orphan unborn` instead of `git checkout --orphan
            unborn` to avoid the need for a separate `rm -rf *` call
          - Avoid using `--quiet` in the `git diff` call to make debugging easier
     -      in the event of a failure
     +      in the event of a failure. Use simply `--exit-code` instead.
      
          Make these improvements to the existing test as well as the new test.
      
          Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
     +    Helped-by: Junio C Hamano <gitster@pobox.com>
          Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
      
       ## sequencer.c ##
     @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist
      +	const struct object_id *head_tree_oid;
       	struct commit *head_commit;
       	struct index_state *istate = r->index;
     -
     +-
      -	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
      -		return error(_("could not resolve HEAD commit"));
      -
     @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist
      -	 */
      -	if (repo_parse_commit(r, head_commit))
      -		return -1;
     ++	const char *head_name;
     ++
      +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
      +		/*
     -+		 * Treat an error reading HEAD as an unborn branch.
     ++		 * Check to see if this is an unborn branch
      +		 */

You are only editing an existing comment here and it is not worth a
re-roll on its own, but for one line comments we prefer

	/* Check to see if this is an unborn branch */

     ++		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
     ++		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))

While we don't mind the occasional line that is a little over 80
characters these really are rather long.

     ++			return error(_("could not resolve HEAD commit"));
      +		head_tree_oid = the_hash_algo->empty_tree;
      +	} else {
      +		head_commit = lookup_commit(r, &head_oid);
     @@ t/t3501-revert-cherry-pick.sh: test_expect_success 'revert forbidden on dirty wo
      -	rm -rf * &&
       	git cherry-pick initial &&
      -	git diff --quiet initial &&
     -+	git diff initial &&
     ++	git diff --exit-code initial &&
      +	test_cmp_rev ! initial HEAD
      +'
      +
     @@ t/t3501-revert-cherry-pick.sh: test_expect_success 'revert forbidden on dirty wo
      +	git branch -D unborn &&
      +	git switch --orphan unborn &&
      +	git cherry-pick initial --allow-empty &&
     -+	git diff initial &&
     ++	git diff --exit-code initial &&
       	test_cmp_rev ! initial HEAD
       '

Thanks for updating these tests
       
5:  3634da0f70 = 5:  5c16a01056 sequencer: do not require `allow_empty` for redundant commit options
6:  e98b414697 = 6:  3ee057e485 cherry-pick: enforce `--keep-redundant-commits` incompatibility
7:  cc4030d8ec ! 7:  0cd4620ef7 cherry-pick: add `--empty` for more robust redundant commit handling
     @@ Documentation/git-cherry-pick.txt: effect to your index in a row.
      +	you to examine the commit. This is the default behavior.
      +--
      ++
     -+Note that this option specifies how to handle a commit that was not initially
     -+empty, but rather became empty due to a previous commit. Commits that were
     -+initially empty will cause the cherry-pick to fail. To force the inclusion of
     -+those commits, use `--allow-empty`.
     ++Note that `--empty=drop` and `--empty=stop` only specify how to handle a
     ++commit that was not initially empty, but rather became empty due to a previous
     ++commit. Commits that were initially empty will still cause the cherry-pick to
     ++fail unless one of `--empty=keep` or `--allow-empty` are specified.
      ++
      +
       --keep-redundant-commits::

The new wording here looks good

Apart from the minor style issues this all looks good to me, thanks
for working on it, it will be a useful addition to be able to drop
cherry-picks that become empty.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-25 14:38   ` phillip.wood123
@ 2024-03-25 16:12     ` Brian Lyles
  2024-03-25 19:36       ` phillip.wood123
  0 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 16:12 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, newren, me, gitster

Hi Phillip

On Mon, Mar 25, 2024 at 9:38 AM <phillip.wood123@gmail.com> wrote:

>      @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist
>       -	 */
>       -	if (repo_parse_commit(r, head_commit))
>       -		return -1;
>      ++	const char *head_name;
>      ++
>       +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
>       +		/*
>      -+		 * Treat an error reading HEAD as an unborn branch.
>      ++		 * Check to see if this is an unborn branch
>       +		 */
> 
> You are only editing an existing comment here and it is not worth a
> re-roll on its own, but for one line comments we prefer
> 
> 	/* Check to see if this is an unborn branch */

Existing in v3, but new with this series. Since it sounds like a re-roll
is desired anyway to address the line length issue below, I can go ahead
and fix this in v5 as well.

>      ++		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
>      ++		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
> 
> While we don't mind the occasional line that is a little over 80
> characters these really are rather long.
> 

You're right, these got a little long. I wasn't able to identify a
definitive wrapping style for these cases, so I'll include my proposed
update here just to avoid another re-roll. Does the following diff from
v4 to a proposed v5 work for you?

@@ -776,11 +776,13 @@ static int is_index_unchanged(struct repository *r)
 	const char *head_name;
 
 	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
-		/*
-		 * Check to see if this is an unborn branch
-		 */
-		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
-		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
+		/* Check to see if this is an unborn branch */
+		head_name = resolve_ref_unsafe("HEAD",
+			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			&head_oid, NULL);
+		if (!head_name
+			|| !starts_with(head_name, "refs/heads/")
+			|| !is_null_oid(&head_oid))
 			return error(_("could not resolve HEAD commit"));
 		head_tree_oid = the_hash_algo->empty_tree;
 	} else {

> Apart from the minor style issues this all looks good to me, thanks
> for working on it, it will be a useful addition to be able to drop
> cherry-picks that become empty.

Thanks, I really appreciate your help with this series!

-- 
Thank you,
Brian Lyles

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-25 16:12     ` Brian Lyles
@ 2024-03-25 19:36       ` phillip.wood123
  2024-03-25 20:57         ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-03-25 19:36 UTC (permalink / raw)
  To: Brian Lyles, phillip.wood; +Cc: git, newren, me, gitster

Hi Brian

On 25/03/2024 16:12, Brian Lyles wrote:
>>       ++		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
>>       ++		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
>>
>> While we don't mind the occasional line that is a little over 80
>> characters these really are rather long.
>>
> 
> You're right, these got a little long. I wasn't able to identify a
> definitive wrapping style for these cases, so I'll include my proposed
> update here just to avoid another re-roll. Does the following diff from
> v4 to a proposed v5 work for you?
> 
> @@ -776,11 +776,13 @@ static int is_index_unchanged(struct repository *r)
>   	const char *head_name;
>   
>   	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
> -		/*
> -		 * Check to see if this is an unborn branch
> -		 */
> -		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
> -		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
> +		/* Check to see if this is an unborn branch */
> +		head_name = resolve_ref_unsafe("HEAD",
> +			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
> +			&head_oid, NULL);
> +		if (!head_name
> +			|| !starts_with(head_name, "refs/heads/")
> +			|| !is_null_oid(&head_oid))
>   			return error(_("could not resolve HEAD commit"));

Normally we'd write this as

	if (!head_name ||
	    starts_with(head_name, "refs/heads/") ||
	    !is_null_oid(&head_oid))
		return error(...)

breaking lines after an operator and indenting to the open bracket after 
the if. The rest looks good. Junio was talking about merging this to 
next in the latest "what's cooking" email so I'd double check he hasn't 
done that yet before re-rolling.

>   		head_tree_oid = the_hash_algo->empty_tree;
>   	} else {
> 
>> Apart from the minor style issues this all looks good to me, thanks
>> for working on it, it will be a useful addition to be able to drop
>> cherry-picks that become empty.
> 
> Thanks, I really appreciate your help with this series!

Thank you for working on it, I've enjoyed reading your patches

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v4 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-25 19:36       ` phillip.wood123
@ 2024-03-25 20:57         ` Junio C Hamano
  0 siblings, 0 replies; 118+ messages in thread
From: Junio C Hamano @ 2024-03-25 20:57 UTC (permalink / raw)
  To: phillip.wood123; +Cc: Brian Lyles, phillip.wood, git, newren, me

phillip.wood123@gmail.com writes:

>> +		/* Check to see if this is an unborn branch */
>> +		head_name = resolve_ref_unsafe("HEAD",
>> +			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
>> +			&head_oid, NULL);
>> +		if (!head_name
>> +			|| !starts_with(head_name, "refs/heads/")
>> +			|| !is_null_oid(&head_oid))
>>   			return error(_("could not resolve HEAD commit"));
>
> Normally we'd write this as
>
> 	if (!head_name ||
> 	    starts_with(head_name, "refs/heads/") ||
> 	    !is_null_oid(&head_oid))
> 		return error(...)
>
> breaking lines after an operator and indenting to the open bracket
> after the if.

> The rest looks good. Junio was talking about merging
> this to next in the latest "what's cooking" email so I'd double check
> he hasn't done that yet before re-rolling.

I do not want to rush things---if we find that this is worth
touching up (and obviously we do---otherwise we would not be having
this conversation), then let's do have a quick v5 with minimal
range-diff.

> Thank you for working on it, I've enjoyed reading your patches

Likewise.  Thanks, both.


^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (30 preceding siblings ...)
  2024-03-20 23:37 ` [PATCH v4 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-26 14:45   ` phillip.wood123
  2024-03-25 23:16 ` [PATCH v5 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
                   ` (6 subsequent siblings)
  38 siblings, 1 reply; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

The ultimate goal of this series is to allow git-cherry-pick(1) to
automatically drop redundant commits. The mechanism chosen is an
`--empty` option that provides the same flexibility as the `--empty`
options for git-rebase(1) and git-am(1).

Some secondary goals are to improve the consistency in the values and
documentation for this option across the three commands.

See "Does extending `--empty` to git-cherry-pick make sense?" [1] for
some context for why this option is desired in git-cherry-pick(1).

[1]: https://lore.kernel.org/git/CAHPHrSevBdQF0BisR8VK=jM=wj1dTUYEVrv31gLerAzL9=Cd8Q@mail.gmail.com

Along the way, I (with some help from Elijah and Phillip) found a few
other things in the docs and related sequencer code to clean up.

This is the final planned re-roll of this series, addressing two minor
style concerns with commit 4/7 as noted in this [2] thread. All other
commits are left unchanged.

[2]: https://lore.kernel.org/git/xmqqa5mmhvx5.fsf@gitster.g

Range-diff from v4:

1:  f6b8a655cd = 1:  f6b8a655cd docs: address inaccurate `--empty` default with `--exec`
2:  401de76c0b = 2:  401de76c0b docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
3:  031b3bb7bb = 3:  031b3bb7bb rebase: update `--empty=ask` to `--empty=stop`
4:  fd53c39482 ! 4:  d3bfe41819 sequencer: handle unborn branch with `--allow-empty`
    @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist
      	struct commit *head_commit;
      	struct index_state *istate = r->index;
     +	const char *head_name;
    -
    --	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
    --		return error(_("could not resolve HEAD commit"));
    ++
     +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
    -+		/*
    -+		 * Check to see if this is an unborn branch
    -+		 */
    -+		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
    -+		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
    ++		/* Check to see if this is an unborn branch */
    ++		head_name = resolve_ref_unsafe("HEAD",
    ++			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
    ++			&head_oid, NULL);
    ++		if (!head_name ||
    ++			!starts_with(head_name, "refs/heads/") ||
    ++			!is_null_oid(&head_oid))
     +			return error(_("could not resolve HEAD commit"));
     +		head_tree_oid = the_hash_algo->empty_tree;
     +	} else {
     +		head_commit = lookup_commit(r, &head_oid);

    +-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
    +-		return error(_("could not resolve HEAD commit"));
    +-
     -	head_commit = lookup_commit(r, &head_oid);
     +		/*
     +		 * If head_commit is NULL, check_commit, called from
5:  90dca45c12 = 5:  5e690bca6e sequencer: do not require `allow_empty` for redundant commit options
6:  ab3b6afc97 = 6:  ed03908e9e cherry-pick: enforce `--keep-redundant-commits` incompatibility
7:  0e2577ea56 = 7:  d3cf068c45 cherry-pick: add `--empty` for more robust redundant commit handling


Brian Lyles (7):
  docs: address inaccurate `--empty` default with `--exec`
  docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  rebase: update `--empty=ask` to `--empty=stop`
  sequencer: handle unborn branch with `--allow-empty`
  sequencer: do not require `allow_empty` for redundant commit options
  cherry-pick: enforce `--keep-redundant-commits` incompatibility
  cherry-pick: add `--empty` for more robust redundant commit handling

 Documentation/git-am.txt          | 20 ++++++---
 Documentation/git-cherry-pick.txt | 30 ++++++++++---
 Documentation/git-rebase.txt      | 26 +++++++----
 builtin/rebase.c                  | 16 ++++---
 builtin/revert.c                  | 38 +++++++++++++++-
 sequencer.c                       | 72 ++++++++++++++++++-------------
 t/t3424-rebase-empty.sh           | 55 +++++++++++++++++++++--
 t/t3501-revert-cherry-pick.sh     | 14 ++++--
 t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++
 10 files changed, 286 insertions(+), 68 deletions(-)

-- 
2.43.2


^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH v5 1/7] docs: address inaccurate `--empty` default with `--exec`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (31 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 0/7] " Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
                   ` (5 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

The documentation for git-rebase(1) indicates that using the `--exec`
option will use `--empty=drop`. This is inaccurate: when `--interactive`
is not explicitly provided, `--exec` results in `--empty=keep`
behaviors.

Correctly indicate the behavior of `--exec` using `--empty=keep` when
`--interactive` is not specified.

Reported-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt | 10 +++++-----
 t/t3424-rebase-empty.sh      | 38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 06206521fc..3334e85356 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -295,11 +295,11 @@ See also INCOMPATIBLE OPTIONS below.
 	empty after rebasing (because they contain a subset of already
 	upstream changes).  With drop (the default), commits that
 	become empty are dropped.  With keep, such commits are kept.
-	With ask (implied by `--interactive`), the rebase will halt when
-	an empty commit is applied allowing you to choose whether to
-	drop it, edit files more, or just commit the empty changes.
-	Other options, like `--exec`, will use the default of drop unless
-	`-i`/`--interactive` is explicitly specified.
+	With ask, the rebase will halt when an empty commit is applied
+	allowing you to choose whether to drop it, edit files more, or just
+	commit the empty changes.
+	When the `-i`/`--interactive` option is used, the default becomes ask.
+	Otherwise, when the `--exec` option is used, the default becomes keep.
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a0af..73ff35ced2 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -167,4 +167,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
 	test_path_is_missing .git/MERGE_MSG
 '
 
+test_expect_success 'rebase --exec --empty=drop' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=drop upstream &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" --empty=keep upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+	git checkout -B testing localmods &&
+	git rebase --exec "true" upstream &&
+
+	test_write_lines D C2 C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=ask' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v5 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (32 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
                   ` (4 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

Both of these pages document very similar `--empty` options, but with
different styles. The exact behavior of these `--empty` options differs
somewhat, but consistent styling in the docs is still beneficial. This
commit aims to make them more consistent.

Break the possible values for `--empty` into separate sections for
readability. Alphabetical order is chosen for consistency.

In a future commit, we'll be documenting a new `--empty` option for
git-cherry-pick(1), making the consistency even more relevant.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-am.txt     | 20 +++++++++++++-------
 Documentation/git-rebase.txt | 25 ++++++++++++++++---------
 2 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e080458d6c..f852e0ba79 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -66,13 +66,19 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
---empty=(stop|drop|keep)::
-	By default, or when the option is set to 'stop', the command
-	errors out on an input e-mail message lacking a patch
-	and stops in the middle of the current am session. When this
-	option is set to 'drop', skip such an e-mail message instead.
-	When this option is set to 'keep', create an empty commit,
-	recording the contents of the e-mail message as its log.
+--empty=(drop|keep|stop)::
+	How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+	The e-mail message will be skipped.
+`keep`;;
+	An empty commit will be created, with the contents of the e-mail
+	message as its log.
+`stop`;;
+	The command will fail, stopping in the middle of the current `am`
+	session. This is the default behavior.
+--
 
 -m::
 --message-id::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 3334e85356..0b0d0ccb80 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,17 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(drop|keep|ask)::
+--empty=(ask|drop|keep)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
-	upstream changes).  With drop (the default), commits that
-	become empty are dropped.  With keep, such commits are kept.
-	With ask, the rebase will halt when an empty commit is applied
-	allowing you to choose whether to drop it, edit files more, or just
-	commit the empty changes.
-	When the `-i`/`--interactive` option is used, the default becomes ask.
-	Otherwise, when the `--exec` option is used, the default becomes keep.
+	upstream changes):
++
+--
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified.
+`drop`;;
+	The commit will be dropped. This is the default behavior.
+`keep`;;
+	The commit will be kept. This option is implied when `--exec` is
+	specified unless `-i`/`--interactive` is also specified.
+--
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
@@ -704,7 +711,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(drop|keep|ask)` option for changing the behavior
+also has an `--empty=(ask|drop|keep)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v5 3/7] rebase: update `--empty=ask` to `--empty=stop`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (33 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
                   ` (3 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When git-am(1) got its own `--empty` option in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09), `stop` was used
instead of `ask`. `stop` is a more accurate term for describing what
really happens, and consistency is good.

Update git-rebase(1) to also use `stop`, while keeping `ask` as a
deprecated synonym. Update the tests to primarily use `stop`, but also
ensure that `ask` is still allowed.

In a future commit, we'll be adding a new `--empty` option for
git-cherry-pick(1) as well, making the consistency even more relevant.

Reported-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-rebase.txt | 15 ++++++++-------
 builtin/rebase.c             | 16 ++++++++++------
 t/t3424-rebase-empty.sh      | 21 ++++++++++++++++-----
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 0b0d0ccb80..67dd0a533e 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -289,23 +289,24 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
---empty=(ask|drop|keep)::
+--empty=(drop|keep|stop)::
 	How to handle commits that are not empty to start and are not
 	clean cherry-picks of any upstream commit, but which become
 	empty after rebasing (because they contain a subset of already
 	upstream changes):
 +
 --
-`ask`;;
-	The rebase will halt when the commit is applied, allowing you to
-	choose whether to drop it, edit files more, or just commit the empty
-	changes. This option is implied when `-i`/`--interactive` is
-	specified.
 `drop`;;
 	The commit will be dropped. This is the default behavior.
 `keep`;;
 	The commit will be kept. This option is implied when `--exec` is
 	specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+	The rebase will halt when the commit is applied, allowing you to
+	choose whether to drop it, edit files more, or just commit the empty
+	changes. This option is implied when `-i`/`--interactive` is
+	specified. `ask` is a deprecated synonym of `stop`.
 --
 +
 Note that commits which start empty are kept (unless `--no-keep-empty`
@@ -711,7 +712,7 @@ be dropped automatically with `--no-keep-empty`).
 Similar to the apply backend, by default the merge backend drops
 commits that become empty unless `-i`/`--interactive` is specified (in
 which case it stops and asks the user what to do).  The merge backend
-also has an `--empty=(ask|drop|keep)` option for changing the behavior
+also has an `--empty=(drop|keep|stop)` option for changing the behavior
 of handling commits that become empty.
 
 Directory rename detection
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 5b086f651a..a4916781ce 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -58,7 +58,7 @@ enum empty_type {
 	EMPTY_UNSPECIFIED = -1,
 	EMPTY_DROP,
 	EMPTY_KEEP,
-	EMPTY_ASK
+	EMPTY_STOP
 };
 
 enum action {
@@ -951,10 +951,14 @@ static enum empty_type parse_empty_value(const char *value)
 		return EMPTY_DROP;
 	else if (!strcasecmp(value, "keep"))
 		return EMPTY_KEEP;
-	else if (!strcasecmp(value, "ask"))
-		return EMPTY_ASK;
+	else if (!strcasecmp(value, "stop"))
+		return EMPTY_STOP;
+	else if (!strcasecmp(value, "ask")) {
+		warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+		return EMPTY_STOP;
+	}
 
-	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+	die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
 }
 
 static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1133,7 +1137,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				 "instead of ignoring them"),
 			      1, PARSE_OPT_HIDDEN),
 		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)",
+		OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|stop)",
 			       N_("how to handle commits that become empty"),
 			       PARSE_OPT_NONEG, parse_opt_empty),
 		OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1550,7 +1554,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	if (options.empty == EMPTY_UNSPECIFIED) {
 		if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-			options.empty = EMPTY_ASK;
+			options.empty = EMPTY_STOP;
 		else if (options.exec.nr > 0)
 			options.empty = EMPTY_KEEP;
 		else
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 73ff35ced2..1ee6b00fd5 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
 	test_cmp expect actual
 '
 
+test_expect_success 'rebase --merge --empty=stop' '
+	git checkout -B testing localmods &&
+	test_must_fail git rebase --merge --empty=stop upstream &&
+
+	git rebase --skip &&
+
+	test_write_lines D C B A >expect &&
+	git log --format=%s >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'rebase --merge --empty=ask' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --interactive --empty=ask upstream &&
+	test_must_fail git rebase --interactive --empty=stop upstream &&
 
 	git rebase --skip &&
 
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
 	git checkout -B testing localmods &&
 	test_must_fail git rebase --interactive upstream &&
 
@@ -194,9 +205,9 @@ test_expect_success 'rebase --exec uses default of --empty=keep' '
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --exec --empty=ask' '
+test_expect_success 'rebase --exec --empty=stop' '
 	git checkout -B testing localmods &&
-	test_must_fail git rebase --exec "true" --empty=ask upstream &&
+	test_must_fail git rebase --exec "true" --empty=stop upstream &&
 
 	git rebase --skip &&
 
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v5 4/7] sequencer: handle unborn branch with `--allow-empty`
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (34 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
                   ` (2 subsequent siblings)
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster, Phillip Wood

When using git-cherry-pick(1) with `--allow-empty` while on an unborn
branch, an error is thrown. This is inconsistent with the same
cherry-pick when `--allow-empty` is not specified.

Detect unborn branches in `is_index_unchanged`. When on an unborn
branch, use the `empty_tree` as the tree to compare against.

Add a new test to cover this scenario. While modelled off of the
existing 'cherry-pick on unborn branch' test, some improvements can be
made:

- Use `git switch --orphan unborn` instead of `git checkout --orphan
  unborn` to avoid the need for a separate `rm -rf *` call
- Avoid using `--quiet` in the `git diff` call to make debugging easier
  in the event of a failure. Use simply `--exit-code` instead.

Make these improvements to the existing test as well as the new test.

Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---

Changes from v4:

- Use single-line block comment style since the comment is a single line
  of text.
- Wrap two longer lines of code.

 sequencer.c                   | 43 +++++++++++++++++++++++------------
 t/t3501-revert-cherry-pick.sh | 14 +++++++++---
 2 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index f49a871ac0..e3f0a52f72 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -770,29 +770,42 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
 static int is_index_unchanged(struct repository *r)
 {
 	struct object_id head_oid, *cache_tree_oid;
+	const struct object_id *head_tree_oid;
 	struct commit *head_commit;
 	struct index_state *istate = r->index;
+	const char *head_name;
+
+	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+		/* Check to see if this is an unborn branch */
+		head_name = resolve_ref_unsafe("HEAD",
+			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+			&head_oid, NULL);
+		if (!head_name ||
+			!starts_with(head_name, "refs/heads/") ||
+			!is_null_oid(&head_oid))
+			return error(_("could not resolve HEAD commit"));
+		head_tree_oid = the_hash_algo->empty_tree;
+	} else {
+		head_commit = lookup_commit(r, &head_oid);
 
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-		return error(_("could not resolve HEAD commit"));
-
-	head_commit = lookup_commit(r, &head_oid);
+		/*
+		 * If head_commit is NULL, check_commit, called from
+		 * lookup_commit, would have indicated that head_commit is not
+		 * a commit object already.  repo_parse_commit() will return failure
+		 * without further complaints in such a case.  Otherwise, if
+		 * the commit is invalid, repo_parse_commit() will complain.  So
+		 * there is nothing for us to say here.  Just return failure.
+		 */
+		if (repo_parse_commit(r, head_commit))
+			return -1;
 
-	/*
-	 * If head_commit is NULL, check_commit, called from
-	 * lookup_commit, would have indicated that head_commit is not
-	 * a commit object already.  repo_parse_commit() will return failure
-	 * without further complaints in such a case.  Otherwise, if
-	 * the commit is invalid, repo_parse_commit() will complain.  So
-	 * there is nothing for us to say here.  Just return failure.
-	 */
-	if (repo_parse_commit(r, head_commit))
-		return -1;
+		head_tree_oid = get_commit_tree_oid(head_commit);
+	}
 
 	if (!(cache_tree_oid = get_cache_tree_oid(istate)))
 		return -1;
 
-	return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+	return oideq(cache_tree_oid, head_tree_oid);
 }
 
 static int write_author_script(const char *message)
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index aeab689a98..af73227512 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '
 '
 
 test_expect_success 'cherry-pick on unborn branch' '
-	git checkout --orphan unborn &&
+	git switch --orphan unborn &&
 	git rm --cached -r . &&
-	rm -rf * &&
 	git cherry-pick initial &&
-	git diff --quiet initial &&
+	git diff --exit-code initial &&
+	test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+	git checkout --detach &&
+	git branch -D unborn &&
+	git switch --orphan unborn &&
+	git cherry-pick initial --allow-empty &&
+	git diff --exit-code initial &&
 	test_cmp_rev ! initial HEAD
 '
 
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v5 5/7] sequencer: do not require `allow_empty` for redundant commit options
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (35 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

A consumer of the sequencer that wishes to take advantage of either the
`keep_redundant_commits` or `drop_redundant_commits` feature must also
specify `allow_empty`. However, these refer to two distinct types of
empty commits:

- `allow_empty` refers specifically to commits which start empty
- `keep_redundant_commits` refers specifically to commits that do not
  start empty, but become empty due to the content already existing in
  the target history

Conceptually, there is no reason that the behavior for handling one of
these should be entangled with the other. It is particularly unintuitive
to require `allow_empty` in order for `drop_redundant_commits` to have
an effect: in order to prevent redundant commits automatically,
initially-empty commits would need to be kept automatically as well.

Instead, rewrite the `allow_empty()` logic to remove the over-arching
requirement that `allow_empty` be specified in order to reach any of the
keep/drop behaviors. Only if the commit was originally empty will
`allow_empty` have an effect.

Note that no behavioral changes should result from this commit -- it
merely sets the stage for future commits. In one such future commit, an
`--empty` option will be added to git-cherry-pick(1), meaning that
`drop_redundant_commits` will be used by that command.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 sequencer.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index e3f0a52f72..04ee94bd81 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1732,34 +1732,25 @@ static int allow_empty(struct repository *r,
 	int index_unchanged, originally_empty;
 
 	/*
-	 * Four cases:
+	 * For a commit that is initially empty, allow_empty determines if it
+	 * should be kept or not
 	 *
-	 * (1) we do not allow empty at all and error out.
-	 *
-	 * (2) we allow ones that were initially empty, and
-	 *     just drop the ones that become empty
-	 *
-	 * (3) we allow ones that were initially empty, but
-	 *     halt for the ones that become empty;
-	 *
-	 * (4) we allow both.
+	 * For a commit that becomes empty, keep_redundant_commits and
+	 * drop_redundant_commits determine whether the commit should be kept or
+	 * dropped. If neither is specified, halt.
 	 */
-	if (!opts->allow_empty)
-		return 0; /* let "git commit" barf as necessary */
-
 	index_unchanged = is_index_unchanged(r);
 	if (index_unchanged < 0)
 		return index_unchanged;
 	if (!index_unchanged)
 		return 0; /* we do not have to say --allow-empty */
 
-	if (opts->keep_redundant_commits)
-		return 1;
-
 	originally_empty = is_original_commit_empty(commit);
 	if (originally_empty < 0)
 		return originally_empty;
 	if (originally_empty)
+		return opts->allow_empty;
+	else if (opts->keep_redundant_commits)
 		return 1;
 	else if (opts->drop_redundant_commits)
 		return 2;
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v5 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (36 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  2024-03-25 23:16 ` [PATCH v5 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

When `--keep-redundant-commits` was added in  b27cfb0d8d
(git-cherry-pick: Add keep-redundant-commits option, 2012-04-20), it was
not marked as incompatible with the various operations needed to
continue or exit a cherry-pick (`--continue`, `--skip`, `--abort`, and
`--quit`).

Enforce this incompatibility via `verify_opt_compatible` like we do for
the other various options.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 builtin/revert.c             |  1 +
 t/t3505-cherry-pick-empty.sh | 14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/builtin/revert.c b/builtin/revert.c
index 89821bab95..a1936ef70e 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -167,6 +167,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--ff", opts->allow_ff,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+				"--keep-redundant-commits", opts->keep_redundant_commits,
 				NULL);
 	}
 
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38d5a..61f91aaa0a 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -99,4 +99,18 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
 	test_cmp expect actual
 '
 
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+	test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
 test_done
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH v5 7/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
                   ` (37 preceding siblings ...)
  2024-03-25 23:16 ` [PATCH v5 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
@ 2024-03-25 23:16 ` Brian Lyles
  38 siblings, 0 replies; 118+ messages in thread
From: Brian Lyles @ 2024-03-25 23:16 UTC (permalink / raw)
  To: git; +Cc: Brian Lyles, newren, me, phillip.wood123, gitster

As with git-rebase(1) and git-am(1), git-cherry-pick(1) can result in a
commit being made redundant if the content from the picked commit is
already present in the target history. However, git-cherry-pick(1) does
not have the same options available that git-rebase(1) and git-am(1) have.

There are three things that can be done with these redundant commits:
drop them, keep them, or have the cherry-pick stop and wait for the user
to take an action. git-rebase(1) has the `--empty` option added in commit
e98c4269c8 (rebase (interactive-backend): fix handling of commits that
become empty, 2020-02-15), which handles all three of these scenarios.
Similarly, git-am(1) got its own `--empty` in 7c096b8d61 (am: support
--empty=<option> to handle empty patches, 2021-12-09).

git-cherry-pick(1), on the other hand, only supports two of the three
possiblities: Keep the redundant commits via `--keep-redundant-commits`,
or have the cherry-pick fail by not specifying that option. There is no
way to automatically drop redundant commits.

In order to bring git-cherry-pick(1) more in-line with git-rebase(1) and
git-am(1), this commit adds an `--empty` option to git-cherry-pick(1). It
has the same three options (keep, drop, and stop), and largely behaves
the same. The notable difference is that for git-cherry-pick(1), the
default will be `stop`, which maintains the current behavior when the
option is not specified.

Like the existing `--keep-redundant-commits`, `--empty=keep` will imply
`--allow-empty`.

The `--keep-redundant-commits` option will be documented as a deprecated
synonym of `--empty=keep`, and will be supported for backwards
compatibility for the time being.

Signed-off-by: Brian Lyles <brianmlyles@gmail.com>
---
 Documentation/git-cherry-pick.txt | 30 +++++++++++++++++++------
 builtin/revert.c                  | 37 ++++++++++++++++++++++++++++++-
 sequencer.c                       |  6 +++++
 t/t3505-cherry-pick-empty.sh      | 37 ++++++++++++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++++++++++++++
 5 files changed, 133 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d200..81ace900fc 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,20 +131,36 @@ effect to your index in a row.
 	even without this option.  Note also, that use of this option only
 	keeps commits that were initially empty (i.e. the commit recorded the
 	same tree as its parent).  Commits which are made empty due to a
-	previous commit are dropped.  To force the inclusion of those commits
-	use `--keep-redundant-commits`.
+	previous commit will cause the cherry-pick to fail.  To force the
+	inclusion of those commits, use `--empty=keep`.
 
 --allow-empty-message::
 	By default, cherry-picking a commit with an empty message will fail.
 	This option overrides that behavior, allowing commits with empty
 	messages to be cherry picked.
 
+--empty=(drop|keep|stop)::
+	How to handle commits being cherry-picked that are redundant with
+	changes already in the current history.
++
+--
+`drop`;;
+	The commit will be dropped.
+`keep`;;
+	The commit will be kept. Implies `--allow-empty`.
+`stop`;;
+	The cherry-pick will stop when the commit is applied, allowing
+	you to examine the commit. This is the default behavior.
+--
++
+Note that `--empty=drop` and `--empty=stop` only specify how to handle a
+commit that was not initially empty, but rather became empty due to a previous
+commit. Commits that were initially empty will still cause the cherry-pick to
+fail unless one of `--empty=keep` or `--allow-empty` are specified.
++
+
 --keep-redundant-commits::
-	If a commit being cherry picked duplicates a commit already in the
-	current history, it will become empty.  By default these
-	redundant commits cause `cherry-pick` to stop so the user can
-	examine the commit. This option overrides that behavior and
-	creates an empty commit object.  Implies `--allow-empty`.
+	Deprecated synonym for `--empty=keep`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index a1936ef70e..53935d2c68 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -43,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
 	return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
 }
 
+enum empty_action {
+	EMPTY_COMMIT_UNSPECIFIED = -1,
+	STOP_ON_EMPTY_COMMIT,      /* output errors and stop in the middle of a cherry-pick */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "stop"))
+		*opt_value = STOP_ON_EMPTY_COMMIT;
+	else if (!strcmp(arg, "drop"))
+		*opt_value = DROP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "keep"))
+		*opt_value = KEEP_EMPTY_COMMIT;
+	else
+		return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+	return 0;
+}
+
 static int option_parse_m(const struct option *opt,
 			  const char *arg, int unset)
 {
@@ -85,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	const char *cleanup_arg = NULL;
+	enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -114,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 			OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+			OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+				       N_("how to handle commits that become empty"),
+				       PARSE_OPT_NONEG, parse_opt_empty),
 			OPT_END(),
 		};
 		options = parse_options_concat(options, cp_extra);
@@ -134,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
+	if (opts->action == REPLAY_PICK) {
+		opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+		opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+	}
+
 	/* implies allow_empty */
 	if (opts->keep_redundant_commits)
 		opts->allow_empty = 1;
@@ -168,6 +202,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 				"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
 				"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
 				"--keep-redundant-commits", opts->keep_redundant_commits,
+				"--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
 				NULL);
 	}
 
diff --git a/sequencer.c b/sequencer.c
index 04ee94bd81..72ca483459 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2930,6 +2930,9 @@ static int populate_opts_cb(const char *key, const char *value,
 	else if (!strcmp(key, "options.allow-empty-message"))
 		opts->allow_empty_message =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+	else if (!strcmp(key, "options.drop-redundant-commits"))
+		opts->drop_redundant_commits =
+			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
 	else if (!strcmp(key, "options.keep-redundant-commits"))
 		opts->keep_redundant_commits =
 			git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3474,6 +3477,9 @@ static int save_opts(struct replay_opts *opts)
 	if (opts->allow_empty_message)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.allow-empty-message", "true");
+	if (opts->drop_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.drop-redundant-commits", "true");
 	if (opts->keep_redundant_commits)
 		res |= git_config_set_in_file_gently(opts_file,
 				"options.keep-redundant-commits", "true");
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 61f91aaa0a..9748443530 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
 	git commit -m "add file2 on the side"
 '
 
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
 	git reset --hard &&
 	git checkout fork^0 &&
 	test_must_fail git cherry-pick main
@@ -113,4 +113,39 @@ test_expect_success '--keep-redundant-commits is incompatible with operations' '
 	git cherry-pick --abort
 '
 
+test_expect_success '--empty is incompatible with operations' '
+	test_must_fail git cherry-pick HEAD 2>output &&
+	test_grep "The previous cherry-pick is now empty" output &&
+	test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+	test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+	test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+	test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+	test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	test_must_fail git cherry-pick --empty=stop main 2>output &&
+	test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=drop main &&
+	test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+	git reset --hard &&
+	git checkout fork^0 &&
+	git cherry-pick --empty=keep main &&
+	test_commit_message HEAD -m "add file2 on main"
+'
+
 test_done
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 72020a51c4..7eb52b12ed 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+	test_must_fail git cherry-pick --skip 2>msg &&
+	test_grep "The previous cherry-pick is now empty" msg &&
+	rm msg &&
+	git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+	pristine_detach yetanotherpick &&
+	# Picking `anotherpick` forces a conflict so that we stop. That
+	# commit is then skipped, after which we pick `yetanotherpick`
+	# while already on `yetanotherpick` to cause an empty commit
+	test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+	git cherry-pick --skip &&
+	test_cmp_rev yetanotherpick HEAD^
+'
+
 test_expect_success 'revert persists opts correctly' '
 	pristine_detach initial &&
 	# to make sure that the session to revert a sequence
-- 
2.43.2


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-25 23:16 ` [PATCH v5 0/7] " Brian Lyles
@ 2024-03-26 14:45   ` phillip.wood123
  2024-03-26 18:28     ` Junio C Hamano
  0 siblings, 1 reply; 118+ messages in thread
From: phillip.wood123 @ 2024-03-26 14:45 UTC (permalink / raw)
  To: Brian Lyles, git; +Cc: newren, me, gitster

Hi Brian

On 25/03/2024 23:16, Brian Lyles wrote:
> This is the final planned re-roll of this series, addressing two minor
> style concerns with commit 4/7 as noted in this [2] thread. All other
> commits are left unchanged.
> 
> [2]: https://lore.kernel.org/git/xmqqa5mmhvx5.fsf@gitster.g
> 
> Range-diff from v4:
> 
> 1:  f6b8a655cd = 1:  f6b8a655cd docs: address inaccurate `--empty` default with `--exec`
> 2:  401de76c0b = 2:  401de76c0b docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
> 3:  031b3bb7bb = 3:  031b3bb7bb rebase: update `--empty=ask` to `--empty=stop`
> 4:  fd53c39482 ! 4:  d3bfe41819 sequencer: handle unborn branch with `--allow-empty`
>      @@ sequencer.c: static struct object_id *get_cache_tree_oid(struct index_state *ist
>        	struct commit *head_commit;
>        	struct index_state *istate = r->index;
>       +	const char *head_name;
>      -
>      --	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
>      --		return error(_("could not resolve HEAD commit"));
>      ++
>       +	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
>      -+		/*
>      -+		 * Check to see if this is an unborn branch
>      -+		 */
>      -+		head_name = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &head_oid, NULL);
>      -+		if (!head_name || !starts_with(head_name, "refs/heads/") || !is_null_oid(&head_oid))
>      ++		/* Check to see if this is an unborn branch */
>      ++		head_name = resolve_ref_unsafe("HEAD",
>      ++			RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
>      ++			&head_oid, NULL);
>      ++		if (!head_name ||
>      ++			!starts_with(head_name, "refs/heads/") ||
>      ++			!is_null_oid(&head_oid))
>       +			return error(_("could not resolve HEAD commit"));
>       +		head_tree_oid = the_hash_algo->empty_tree;
>       +	} else {
>       +		head_commit = lookup_commit(r, &head_oid);

This version is definitely more readable, thanks

>      +-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
>      +-		return error(_("could not resolve HEAD commit"));
>      +-
>       -	head_commit = lookup_commit(r, &head_oid);
>       +		/*
>       +		 * If head_commit is NULL, check_commit, called from

This looks strange, but if I do a range-diff locally from v4 to v5 I 
only see the line wrapping changes above so I don't think it is anything 
to worry about.

Thanks for working on this

Phillip

> 5:  90dca45c12 = 5:  5e690bca6e sequencer: do not require `allow_empty` for redundant commit options
> 6:  ab3b6afc97 = 6:  ed03908e9e cherry-pick: enforce `--keep-redundant-commits` incompatibility
> 7:  0e2577ea56 = 7:  d3cf068c45 cherry-pick: add `--empty` for more robust redundant commit handling
> 
> 
> Brian Lyles (7):
>    docs: address inaccurate `--empty` default with `--exec`
>    docs: clean up `--empty` formatting in git-rebase(1) and git-am(1)
>    rebase: update `--empty=ask` to `--empty=stop`
>    sequencer: handle unborn branch with `--allow-empty`
>    sequencer: do not require `allow_empty` for redundant commit options
>    cherry-pick: enforce `--keep-redundant-commits` incompatibility
>    cherry-pick: add `--empty` for more robust redundant commit handling
> 
>   Documentation/git-am.txt          | 20 ++++++---
>   Documentation/git-cherry-pick.txt | 30 ++++++++++---
>   Documentation/git-rebase.txt      | 26 +++++++----
>   builtin/rebase.c                  | 16 ++++---
>   builtin/revert.c                  | 38 +++++++++++++++-
>   sequencer.c                       | 72 ++++++++++++++++++-------------
>   t/t3424-rebase-empty.sh           | 55 +++++++++++++++++++++--
>   t/t3501-revert-cherry-pick.sh     | 14 ++++--
>   t/t3505-cherry-pick-empty.sh      | 51 +++++++++++++++++++++-
>   t/t3510-cherry-pick-sequence.sh   | 32 ++++++++++++++
>   10 files changed, 286 insertions(+), 68 deletions(-)
> 

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-26 14:45   ` phillip.wood123
@ 2024-03-26 18:28     ` Junio C Hamano
  2024-03-27 16:37       ` phillip.wood123
  0 siblings, 1 reply; 118+ messages in thread
From: Junio C Hamano @ 2024-03-26 18:28 UTC (permalink / raw)
  To: phillip.wood123; +Cc: Brian Lyles, git, newren, me

phillip.wood123@gmail.com writes:

> Hi Brian
> ...
>>       +		head_commit = lookup_commit(r, &head_oid);
>
> This version is definitely more readable, thanks
>
>...
>
> This looks strange, but if I do a range-diff locally from v4 to v5 I
> only see the line wrapping changes above so I don't think it is
> anything to worry about.
>
> Thanks for working on this

So, I take it as an Ack from the person who acted as the main
reviewer for this series?  If so, I'll mark the topic for 'next'.

Tanks, both of you.  The resulting series does look very good.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH v5 0/7] cherry-pick: add `--empty` for more robust redundant commit handling
  2024-03-26 18:28     ` Junio C Hamano
@ 2024-03-27 16:37       ` phillip.wood123
  0 siblings, 0 replies; 118+ messages in thread
From: phillip.wood123 @ 2024-03-27 16:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Brian Lyles, git, newren, me

On 26/03/2024 18:28, Junio C Hamano wrote:
> phillip.wood123@gmail.com writes:
> 
>> Hi Brian
>> ...
>>>        +		head_commit = lookup_commit(r, &head_oid);
>>
>> This version is definitely more readable, thanks
>>
>> ...
>>
>> This looks strange, but if I do a range-diff locally from v4 to v5 I
>> only see the line wrapping changes above so I don't think it is
>> anything to worry about.
>>
>> Thanks for working on this
> 
> So, I take it as an Ack from the person who acted as the main
> reviewer for this series? 

Yes I'm happy

> If so, I'll mark the topic for 'next'.

Excellent

> Tanks, both of you.  The resulting series does look very good.

All credit to Brian - it has been a well structured and well written 
series since the first iteration.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 118+ messages in thread

end of thread, other threads:[~2024-03-27 16:37 UTC | newest]

Thread overview: 118+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-19  5:59 [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options brianmlyles
2024-01-19  5:59 ` [PATCH 2/4] docs: Clean up `--empty` formatting in `git-rebase` and `git-am` brianmlyles
2024-01-23 14:24   ` Phillip Wood
2024-01-27 21:22     ` Brian Lyles
2024-02-01 14:02       ` Phillip Wood
2024-01-19  5:59 ` [PATCH 3/4] rebase: Update `--empty=ask` to `--empty=drop` brianmlyles
2024-01-23 14:24   ` Phillip Wood
2024-01-27 21:49     ` Brian Lyles
2024-02-01 14:02       ` Phillip Wood
2024-01-19  5:59 ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling brianmlyles
2024-01-20 20:24   ` Kristoffer Haugsbakk
2024-01-21 18:28     ` Brian Lyles
2024-01-21 22:05       ` Kristoffer Haugsbakk
2024-01-21 22:41       ` Junio C Hamano
2024-01-22 10:40       ` Phillip Wood
2024-01-22 20:55       ` Kristoffer Haugsbakk
2024-01-23  5:23         ` Brian Lyles
2024-01-23  7:11           ` Kristoffer Haugsbakk
2024-01-23 17:32           ` Junio C Hamano
2024-01-23 18:41             ` Subject: [PATCH] CoC: whitespace fix Junio C Hamano
2024-01-24  3:06               ` Elijah Newren
2024-01-23 18:49             ` [PATCH 4/4] cherry-pick: Add `--empty` for more robust redundant commit handling Junio C Hamano
2024-01-23 14:25   ` Phillip Wood
2024-01-23 18:01     ` Junio C Hamano
2024-01-28  0:07       ` Brian Lyles
2024-01-27 23:56     ` Brian Lyles
2024-01-20 21:38 ` [PATCH 1/4] sequencer: Do not require `allow_empty` for redundant commit options Kristoffer Haugsbakk
2024-01-21 18:19   ` Brian Lyles
2024-01-23 14:23 ` Phillip Wood
2024-01-23 18:18   ` Junio C Hamano
2024-01-24 11:01   ` Phillip Wood
2024-01-24 11:01 ` Phillip Wood
2024-01-27 23:30   ` Brian Lyles
2024-01-28 16:36     ` Brian Lyles
2024-01-29 10:55       ` Phillip Wood
2024-02-10  5:50         ` Brian Lyles
2024-02-01 10:57     ` Phillip Wood
2024-02-10  4:34       ` Brian Lyles
2024-02-10  7:43 ` [PATCH v2 0/8] cherry-pick: add `--empty` Brian Lyles
2024-02-22 16:39   ` phillip.wood123
2024-02-10  7:43 ` [PATCH v2 1/8] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
2024-02-10  7:43 ` [PATCH v2 2/8] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
2024-02-10  7:43 ` [PATCH v2 3/8] rebase: update `--empty=ask` to `--empty=drop` Brian Lyles
2024-02-11  4:54   ` Brian Lyles
2024-02-14 11:05     ` Phillip Wood
2024-02-22 16:34   ` phillip.wood123
2024-02-22 18:27     ` Junio C Hamano
2024-02-10  7:43 ` [PATCH v2 4/8] sequencer: treat error reading HEAD as unborn branch Brian Lyles
2024-02-22 16:34   ` phillip.wood123
2024-02-23  5:28     ` Brian Lyles
2024-02-25 16:57       ` phillip.wood123
2024-02-10  7:43 ` [PATCH v2 5/8] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
2024-02-22 16:35   ` phillip.wood123
2024-02-10  7:43 ` [PATCH v2 6/8] cherry-pick: decouple `--allow-empty` and `--keep-redundant-commits` Brian Lyles
2024-02-22 16:35   ` Phillip Wood
2024-02-22 18:41     ` Junio C Hamano
2024-02-10  7:43 ` [PATCH v2 7/8] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
2024-02-22 16:35   ` Phillip Wood
2024-02-23  6:23     ` Brian Lyles
2024-02-23 17:41       ` Junio C Hamano
2024-02-25 16:58       ` phillip.wood123
2024-02-26  3:04         ` Brian Lyles
2024-02-10  7:43 ` [PATCH v2 8/8] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
2024-02-11 20:50   ` Jean-Noël AVILA
2024-02-12  1:35     ` Brian Lyles
2024-02-22 16:36   ` phillip.wood123
2024-02-23  6:58     ` Brian Lyles
2024-02-25 16:57       ` phillip.wood123
2024-02-26  2:21         ` Brian Lyles
2024-02-26  3:32         ` Brian Lyles
2024-02-27 10:39           ` phillip.wood123
2024-02-27 17:33             ` Junio C Hamano
2024-03-10 18:41 ` [PATCH v3 0/7] " Brian Lyles
2024-03-13 16:12   ` phillip.wood123
2024-03-10 18:42 ` [PATCH v3 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
2024-03-10 18:42 ` [PATCH v3 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
2024-03-10 18:42 ` [PATCH v3 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
2024-03-10 18:42 ` [PATCH v3 4/7] sequencer: treat error reading HEAD as unborn branch Brian Lyles
2024-03-11  0:07   ` Junio C Hamano
2024-03-11 16:54     ` Junio C Hamano
2024-03-12  2:04       ` Brian Lyles
2024-03-12 22:25         ` Junio C Hamano
2024-03-16  3:05           ` Brian Lyles
2024-03-13 15:10   ` phillip.wood123
2024-03-16  3:07     ` Brian Lyles
2024-03-10 18:42 ` [PATCH v3 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
2024-03-10 18:42 ` [PATCH v3 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
2024-03-10 18:42 ` [PATCH v3 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
2024-03-13 16:10   ` phillip.wood123
2024-03-13 17:17     ` Junio C Hamano
2024-03-16  5:20       ` Brian Lyles
2024-03-20 19:35         ` phillip.wood123
2024-03-20 23:36 ` [PATCH v4 0/7] " Brian Lyles
2024-03-25 14:38   ` phillip.wood123
2024-03-25 16:12     ` Brian Lyles
2024-03-25 19:36       ` phillip.wood123
2024-03-25 20:57         ` Junio C Hamano
2024-03-20 23:36 ` [PATCH v4 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
2024-03-20 23:36 ` [PATCH v4 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
2024-03-20 23:36 ` [PATCH v4 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
2024-03-20 23:36 ` [PATCH v4 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
2024-03-21  9:52   ` Dirk Gouders
2024-03-21 16:22     ` Junio C Hamano
2024-03-21 19:45       ` Dirk Gouders
2024-03-20 23:37 ` [PATCH v4 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
2024-03-20 23:37 ` [PATCH v4 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
2024-03-20 23:37 ` [PATCH v4 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles
2024-03-25 23:16 ` [PATCH v5 0/7] " Brian Lyles
2024-03-26 14:45   ` phillip.wood123
2024-03-26 18:28     ` Junio C Hamano
2024-03-27 16:37       ` phillip.wood123
2024-03-25 23:16 ` [PATCH v5 1/7] docs: address inaccurate `--empty` default with `--exec` Brian Lyles
2024-03-25 23:16 ` [PATCH v5 2/7] docs: clean up `--empty` formatting in git-rebase(1) and git-am(1) Brian Lyles
2024-03-25 23:16 ` [PATCH v5 3/7] rebase: update `--empty=ask` to `--empty=stop` Brian Lyles
2024-03-25 23:16 ` [PATCH v5 4/7] sequencer: handle unborn branch with `--allow-empty` Brian Lyles
2024-03-25 23:16 ` [PATCH v5 5/7] sequencer: do not require `allow_empty` for redundant commit options Brian Lyles
2024-03-25 23:16 ` [PATCH v5 6/7] cherry-pick: enforce `--keep-redundant-commits` incompatibility Brian Lyles
2024-03-25 23:16 ` [PATCH v5 7/7] cherry-pick: add `--empty` for more robust redundant commit handling Brian Lyles

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.