git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] am: support --always option to am empty commits
@ 2021-11-12  4:58 Aleen via GitGitGadget
  2021-11-12  4:58 ` [PATCH 1/2] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
                   ` (3 more replies)
  0 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  4:58 UTC (permalink / raw)
  To: git; +Cc: Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with empty commit message, git-am should support
applying and committing with empty patches.

Aleen (2):
  doc: git-format-patch: specify the option --always
  am: support --always option to am empty commits

 Documentation/git-am.txt           |  5 +++++
 Documentation/git-format-patch.txt |  5 +++++
 builtin/am.c                       | 18 ++++++++++++++++--
 t/t4150-am.sh                      | 25 +++++++++++++++++++++++++
 4 files changed, 51 insertions(+), 2 deletions(-)


base-commit: b550198c73edd4cc058832dcf74b41aeec2adba2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1076
-- 
gitgitgadget

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

* [PATCH 1/2] doc: git-format-patch: specify the option --always
  2021-11-12  4:58 [PATCH 0/2] am: support --always option to am empty commits Aleen via GitGitGadget
@ 2021-11-12  4:58 ` Aleen via GitGitGadget
  2021-11-12  4:58 ` [PATCH 2/2] am: support --always option to am empty commits Aleen via GitGitGadget
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  4:58 UTC (permalink / raw)
  To: git; +Cc: Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..a9f2bf94182 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -30,6 +30,7 @@ SYNOPSIS
 		   [--range-diff=<previous> [--creation-factor=<percent>]]
 		   [--filename-max-length=<n>]
 		   [--progress]
+		   [--always]
 		   [<common diff options>]
 		   [ <since> | <revision range> ]
 
@@ -388,6 +389,10 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
 --progress::
 	Show progress reports on stderr as patches are generated.
 
+--always::
+	Patch commits with detailed commit messages,
+	even if they emit no changes. (see linkgit:git-diff-tree[1])
+
 CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message,
-- 
gitgitgadget


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

* [PATCH 2/2] am: support --always option to am empty commits
  2021-11-12  4:58 [PATCH 0/2] am: support --always option to am empty commits Aleen via GitGitGadget
  2021-11-12  4:58 ` [PATCH 1/2] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
@ 2021-11-12  4:58 ` Aleen via GitGitGadget
  2021-11-12  6:17 ` [PATCH 0/2] " René Scharfe
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  4:58 UTC (permalink / raw)
  To: git; +Cc: Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  5 +++++
 builtin/am.c             | 18 ++++++++++++++++--
 t/t4150-am.sh            | 25 +++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..de5d11e404c 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--always]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -159,6 +160,10 @@ default.   You can use `--no-utf8` to override this.
 	countermand both `commit.gpgSign` configuration variable, and
 	earlier `--gpg-sign`.
 
+--always::
+	Apply patches of commits with detailed commit messages,
+	even if they emit no changes. (see linkgit:git-format-patch[1])
+
 --continue::
 -r::
 --resolved::
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..d11efc16f92 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -124,6 +124,8 @@ struct am_state {
 	int ignore_date;
 	int allow_rerere_autoupdate;
 	const char *sign_commit;
+	int always;
+	int empty_commit;
 	int rebasing;
 };
 
@@ -1249,8 +1251,12 @@ static int parse_mail(struct am_state *state, const char *mail)
 	}
 
 	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
+		if (state->always) {
+			state->empty_commit = 1;
+		} else {
+			printf_ln(_("Patch is empty."));
+			die_user_resolve(state);
+		}
 	}
 
 	strbuf_addstr(&msg, "\n\n");
@@ -1792,6 +1798,9 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		if (state->empty_commit)
+			goto commit;
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
 
@@ -1827,6 +1836,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2367,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_BOOL(0, "always", &state.always,
+			N_("always apply patch event if the patch is empty")),
+		OPT_HIDDEN_BOOL(0, "empty-commit", &state.empty_commit,
+			N_("(internal use for skipping git-apply to empty commits)")),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..5b3617857a8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	git format-patch --stdout empty-commit^ >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,23 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'am a real empty patch with the --always option' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	test_must_fail git am --always empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'am a patch with empty commits' '
+	grep "empty commit" empty-commit.patch &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout empty-commit^ &&
+	git am --always empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git cat-file commit HEAD >actual &&
+	test_i18ngrep "empty commit" actual
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH 0/2] am: support --always option to am empty commits
  2021-11-12  4:58 [PATCH 0/2] am: support --always option to am empty commits Aleen via GitGitGadget
  2021-11-12  4:58 ` [PATCH 1/2] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
  2021-11-12  4:58 ` [PATCH 2/2] am: support --always option to am empty commits Aleen via GitGitGadget
@ 2021-11-12  6:17 ` René Scharfe
  2021-11-12  6:42   ` Aleen
  2021-11-12  6:47   ` Junio C Hamano
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
  3 siblings, 2 replies; 129+ messages in thread
From: René Scharfe @ 2021-11-12  6:17 UTC (permalink / raw)
  To: Aleen via GitGitGadget, git; +Cc: Aleen

Am 12.11.21 um 05:58 schrieb Aleen via GitGitGadget:
> Since that git has supported the --always option for the git-format-patch
> command to create a patch with empty commit message, git-am should support
> applying and committing with empty patches.

The symmetry is compelling, but "always" is quite generic.  I can see
e.g. someone expecting "git am --always" to imply --keep-non-patch.

git commit and cherry-pick have --allow-empty, which is (a bit) more
specific.  That seems to me a better option name to copy for a commit-
creating command like git am.

René

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

* RE: [PATCH 0/2] am: support --always option to am empty commits
  2021-11-12  6:17 ` [PATCH 0/2] " René Scharfe
@ 2021-11-12  6:42   ` Aleen
  2021-11-12  6:47   ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: Aleen @ 2021-11-12  6:42 UTC (permalink / raw)
  To: René Scharfe, Aleen via GitGitGadget, git

Dears René,

> The symmetry is compelling, but "always" is quite generic.  I can see
> e.g. someone expecting "git am --always" to imply --keep-non-patch.

> git commit and cherry-pick have --allow-empty, which is (a bit) more
> specific.  That seems to me a better option name to copy for a commit-
> creating command like git am.

It was designed corresponding to --always option in git-format-patch, which
will be pssed into git-diff-tree. As a commit-creating command, --allow-empty
is apparently a better choice. I will re-submit it, thank you.

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

* Re: [PATCH 0/2] am: support --always option to am empty commits
  2021-11-12  6:17 ` [PATCH 0/2] " René Scharfe
  2021-11-12  6:42   ` Aleen
@ 2021-11-12  6:47   ` Junio C Hamano
  2021-11-12  7:10     ` Aleen 徐沛文
  2021-11-12 15:28     ` René Scharfe
  1 sibling, 2 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-12  6:47 UTC (permalink / raw)
  To: René Scharfe; +Cc: Aleen via GitGitGadget, git, Aleen

René Scharfe <l.s.r@web.de> writes:

> Am 12.11.21 um 05:58 schrieb Aleen via GitGitGadget:
>> Since that git has supported the --always option for the git-format-patch
>> command to create a patch with empty commit message, git-am should support
>> applying and committing with empty patches.
>
> The symmetry is compelling, but "always" is quite generic.  I can see
> e.g. someone expecting "git am --always" to imply --keep-non-patch.

What symmetry?

> git commit and cherry-pick have --allow-empty, which is (a bit) more
> specific.  That seems to me a better option name to copy for a commit-
> creating command like git am.

That one I can believe, even though I do not necessarily think it is
a good idea to add such an option.

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

* [PATCH v2 0/4] am: support --allow-empty option to am empty commits
  2021-11-12  4:58 [PATCH 0/2] am: support --always option to am empty commits Aleen via GitGitGadget
                   ` (2 preceding siblings ...)
  2021-11-12  6:17 ` [PATCH 0/2] " René Scharfe
@ 2021-11-12  6:53 ` Aleen via GitGitGadget
  2021-11-12  6:53   ` [PATCH v2 1/4] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
                     ` (4 more replies)
  3 siblings, 5 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  6:53 UTC (permalink / raw)
  To: git; +Cc: René Scharfe, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with empty commit message, git-am should support
applying and committing with empty patches.

Changes since v1:

 * test: am: add the case when not passing the --always option
 * chore: am: rename the --always option to --allow-empty

Aleen (4):
  doc: git-format-patch: specify the option --always
  am: support --always option to am empty commits
  test: am: add the case when not passing the --always option
  chore: am: rename the --always option to --allow-empty

 Documentation/git-am.txt           |  5 +++++
 Documentation/git-format-patch.txt |  5 +++++
 builtin/am.c                       | 18 +++++++++++++--
 t/t4150-am.sh                      | 35 ++++++++++++++++++++++++++++++
 4 files changed, 61 insertions(+), 2 deletions(-)


base-commit: b550198c73edd4cc058832dcf74b41aeec2adba2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v1:

 1:  71e6989375c = 1:  71e6989375c doc: git-format-patch: specify the option --always
 2:  59b1417da37 = 2:  59b1417da37 am: support --always option to am empty commits
 -:  ----------- > 3:  da024ced668 test: am: add the case when not passing the --always option
 -:  ----------- > 4:  45e9720f40b chore: am: rename the --always option to --allow-empty

-- 
gitgitgadget

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

* [PATCH v2 1/4] doc: git-format-patch: specify the option --always
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
@ 2021-11-12  6:53   ` Aleen via GitGitGadget
  2021-11-12 22:17     ` Junio C Hamano
  2021-11-12  6:53   ` [PATCH v2 2/4] am: support --always option to am empty commits Aleen via GitGitGadget
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  6:53 UTC (permalink / raw)
  To: git; +Cc: René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..a9f2bf94182 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -30,6 +30,7 @@ SYNOPSIS
 		   [--range-diff=<previous> [--creation-factor=<percent>]]
 		   [--filename-max-length=<n>]
 		   [--progress]
+		   [--always]
 		   [<common diff options>]
 		   [ <since> | <revision range> ]
 
@@ -388,6 +389,10 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
 --progress::
 	Show progress reports on stderr as patches are generated.
 
+--always::
+	Patch commits with detailed commit messages,
+	even if they emit no changes. (see linkgit:git-diff-tree[1])
+
 CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message,
-- 
gitgitgadget


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

* [PATCH v2 2/4] am: support --always option to am empty commits
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
  2021-11-12  6:53   ` [PATCH v2 1/4] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
@ 2021-11-12  6:53   ` Aleen via GitGitGadget
  2021-11-12 22:23     ` Junio C Hamano
  2021-11-12  6:53   ` [PATCH v2 3/4] test: am: add the case when not passing the --always option Aleen via GitGitGadget
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  6:53 UTC (permalink / raw)
  To: git; +Cc: René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  5 +++++
 builtin/am.c             | 18 ++++++++++++++++--
 t/t4150-am.sh            | 25 +++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..de5d11e404c 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--always]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -159,6 +160,10 @@ default.   You can use `--no-utf8` to override this.
 	countermand both `commit.gpgSign` configuration variable, and
 	earlier `--gpg-sign`.
 
+--always::
+	Apply patches of commits with detailed commit messages,
+	even if they emit no changes. (see linkgit:git-format-patch[1])
+
 --continue::
 -r::
 --resolved::
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..d11efc16f92 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -124,6 +124,8 @@ struct am_state {
 	int ignore_date;
 	int allow_rerere_autoupdate;
 	const char *sign_commit;
+	int always;
+	int empty_commit;
 	int rebasing;
 };
 
@@ -1249,8 +1251,12 @@ static int parse_mail(struct am_state *state, const char *mail)
 	}
 
 	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
+		if (state->always) {
+			state->empty_commit = 1;
+		} else {
+			printf_ln(_("Patch is empty."));
+			die_user_resolve(state);
+		}
 	}
 
 	strbuf_addstr(&msg, "\n\n");
@@ -1792,6 +1798,9 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		if (state->empty_commit)
+			goto commit;
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
 
@@ -1827,6 +1836,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2367,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_BOOL(0, "always", &state.always,
+			N_("always apply patch event if the patch is empty")),
+		OPT_HIDDEN_BOOL(0, "empty-commit", &state.empty_commit,
+			N_("(internal use for skipping git-apply to empty commits)")),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..5b3617857a8 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	git format-patch --stdout empty-commit^ >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,23 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'am a real empty patch with the --always option' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	test_must_fail git am --always empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'am a patch with empty commits' '
+	grep "empty commit" empty-commit.patch &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout empty-commit^ &&
+	git am --always empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git cat-file commit HEAD >actual &&
+	test_i18ngrep "empty commit" actual
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v2 3/4] test: am: add the case when not passing the --always option
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
  2021-11-12  6:53   ` [PATCH v2 1/4] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
  2021-11-12  6:53   ` [PATCH v2 2/4] am: support --always option to am empty commits Aleen via GitGitGadget
@ 2021-11-12  6:53   ` Aleen via GitGitGadget
  2021-11-12  6:54   ` [PATCH v2 4/4] chore: am: rename the --always option to --allow-empty Aleen via GitGitGadget
  2021-11-15 10:39   ` [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  4 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  6:53 UTC (permalink / raw)
  To: git; +Cc: René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 t/t4150-am.sh | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 5b3617857a8..364c61ba198 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1166,7 +1166,17 @@ test_expect_success 'am a real empty patch with the --always option' '
 	test_cmp expected actual
 '
 
-test_expect_success 'am a patch with empty commits' '
+test_expect_success 'am a patch of empty commits without the --always option' '
+	grep "empty commit" empty-commit.patch &&
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err
+'
+
+test_expect_success 'am a patch of empty commits with the --always option' '
 	grep "empty commit" empty-commit.patch &&
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
-- 
gitgitgadget


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

* [PATCH v2 4/4] chore: am: rename the --always option to --allow-empty
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
                     ` (2 preceding siblings ...)
  2021-11-12  6:53   ` [PATCH v2 3/4] test: am: add the case when not passing the --always option Aleen via GitGitGadget
@ 2021-11-12  6:54   ` Aleen via GitGitGadget
  2021-11-15 10:39   ` [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  4 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-12  6:54 UTC (permalink / raw)
  To: git; +Cc: René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  4 ++--
 builtin/am.c             |  8 ++++----
 t/t4150-am.sh            | 10 +++++-----
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index de5d11e404c..6ed844af9c6 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,7 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
-	 [--always]
+	 [--allow-empty]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -160,7 +160,7 @@ default.   You can use `--no-utf8` to override this.
 	countermand both `commit.gpgSign` configuration variable, and
 	earlier `--gpg-sign`.
 
---always::
+--allow-empty::
 	Apply patches of commits with detailed commit messages,
 	even if they emit no changes. (see linkgit:git-format-patch[1])
 
diff --git a/builtin/am.c b/builtin/am.c
index d11efc16f92..a8661098acf 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -124,7 +124,7 @@ struct am_state {
 	int ignore_date;
 	int allow_rerere_autoupdate;
 	const char *sign_commit;
-	int always;
+	int allow_empty;
 	int empty_commit;
 	int rebasing;
 };
@@ -1251,7 +1251,7 @@ static int parse_mail(struct am_state *state, const char *mail)
 	}
 
 	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		if (state->always) {
+		if (state->allow_empty) {
 			state->empty_commit = 1;
 		} else {
 			printf_ln(_("Patch is empty."));
@@ -2367,8 +2367,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-		OPT_BOOL(0, "always", &state.always,
-			N_("always apply patch event if the patch is empty")),
+		OPT_BOOL(0, "allow-empty", &state.allow_empty,
+			N_("allow to apply patches of empty commits")),
 		OPT_HIDDEN_BOOL(0, "empty-commit", &state.empty_commit,
 			N_("(internal use for skipping git-apply to empty commits)")),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 364c61ba198..b47a0fa41e7 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1158,15 +1158,15 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
-test_expect_success 'am a real empty patch with the --always option' '
+test_expect_success 'am a real empty patch with the --allow-empty option' '
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
-	test_must_fail git am --always empty.patch 2>actual &&
+	test_must_fail git am --allow-empty empty.patch 2>actual &&
 	echo Patch format detection failed. >expected &&
 	test_cmp expected actual
 '
 
-test_expect_success 'am a patch of empty commits without the --always option' '
+test_expect_success 'am a patch of empty commits without the --allow-empty option' '
 	grep "empty commit" empty-commit.patch &&
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
@@ -1176,12 +1176,12 @@ test_expect_success 'am a patch of empty commits without the --always option' '
 	test_i18ngrep "Patch is empty." err
 '
 
-test_expect_success 'am a patch of empty commits with the --always option' '
+test_expect_success 'am a patch of empty commits with the --allow-empty option' '
 	grep "empty commit" empty-commit.patch &&
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
 	git checkout empty-commit^ &&
-	git am --always empty-commit.patch &&
+	git am --allow-empty empty-commit.patch &&
 	test_path_is_missing .git/rebase-apply &&
 	git cat-file commit HEAD >actual &&
 	test_i18ngrep "empty commit" actual
-- 
gitgitgadget

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

* Re: [PATCH 0/2] am: support --always option to am empty commits
  2021-11-12  6:47   ` Junio C Hamano
@ 2021-11-12  7:10     ` Aleen 徐沛文
  2021-11-12 15:28     ` René Scharfe
  1 sibling, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-12  7:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: René Scharfe, Aleen via GitGitGadget, git, Aleen

Dears Junio C Hamano:

> René Scharfe <l.s.r@web.de> writes:
> 
> > Am 12.11.21 um 05:58 schrieb Aleen via GitGitGadget:
> >> Since that git has supported the --always option for the git-format-patch
> >> command to create a patch with empty commit message, git-am should support
> >> applying and committing with empty patches.
> >
> > The symmetry is compelling, but "always" is quite generic.  I can see
> > e.g. someone expecting "git am --always" to imply --keep-non-patch.
> 
> What symmetry?
> 
> > git commit and cherry-pick have --allow-empty, which is (a bit) more
> > specific.  That seems to me a better option name to copy for a commit-
> > creating command like git am.
> 
> That one I can believe, even though I do not necessarily think it is
> a good idea to add such an option.

In some cases when we need to migrate commits from one branch to another branch,
from one repo to another repo, it should be necessary.

Aleen

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

* Re: [PATCH 0/2] am: support --always option to am empty commits
  2021-11-12  6:47   ` Junio C Hamano
  2021-11-12  7:10     ` Aleen 徐沛文
@ 2021-11-12 15:28     ` René Scharfe
  2021-11-12 16:08       ` Junio C Hamano
  1 sibling, 1 reply; 129+ messages in thread
From: René Scharfe @ 2021-11-12 15:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Aleen via GitGitGadget, git, Aleen

Am 12.11.21 um 07:47 schrieb Junio C Hamano:
> René Scharfe <l.s.r@web.de> writes:
>
>> Am 12.11.21 um 05:58 schrieb Aleen via GitGitGadget:
>>> Since that git has supported the --always option for the git-format-patch
>>> command to create a patch with empty commit message, git-am should support
>>> applying and committing with empty patches.
>>
>> The symmetry is compelling, but "always" is quite generic.  I can see
>> e.g. someone expecting "git am --always" to imply --keep-non-patch.
>
> What symmetry?

To have the same option in both producer and consumer.

René

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

* Re: [PATCH 0/2] am: support --always option to am empty commits
  2021-11-12 15:28     ` René Scharfe
@ 2021-11-12 16:08       ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-12 16:08 UTC (permalink / raw)
  To: René Scharfe; +Cc: Aleen via GitGitGadget, git, Aleen

René Scharfe <l.s.r@web.de> writes:

>>> The symmetry is compelling, but "always" is quite generic.  I can see
>>> e.g. someone expecting "git am --always" to imply --keep-non-patch.
>>
>> What symmetry?
>
> To have the same option in both producer and consumer.

Well, "--always" that stands for "--always-show-header" seeps
through to the command line parser from revision.c layer but that is
by accident, and is not even documented.  We may want to file it as
a bug and fix it later.

As you mentioned, allow-empty-commit does sound like a much better
name.



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

* Re: [PATCH v2 1/4] doc: git-format-patch: specify the option --always
  2021-11-12  6:53   ` [PATCH v2 1/4] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
@ 2021-11-12 22:17     ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-12 22:17 UTC (permalink / raw)
  To: Aleen via GitGitGadget; +Cc: git, René Scharfe, Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Aleen <aleen42@vip.qq.com>
> Subject: Re: [PATCH v2 1/4] doc: git-format-patch: specify the option --always

"specify" -> "describe", perhaps?

> Signed-off-by: Aleen <aleen42@vip.qq.com>
> ---
>  Documentation/git-format-patch.txt | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
> index 113eabc107c..a9f2bf94182 100644
> --- a/Documentation/git-format-patch.txt
> +++ b/Documentation/git-format-patch.txt
> @@ -30,6 +30,7 @@ SYNOPSIS
>  		   [--range-diff=<previous> [--creation-factor=<percent>]]
>  		   [--filename-max-length=<n>]
>  		   [--progress]
> +		   [--always]
>  		   [<common diff options>]
>  		   [ <since> | <revision range> ]
>  
> @@ -388,6 +389,10 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
>  --progress::
>  	Show progress reports on stderr as patches are generated.
>  
> +--always::
> +	Patch commits with detailed commit messages,
> +	even if they emit no changes. (see linkgit:git-diff-tree[1])

What does the verb "Patch" mean here?  It cannot be what the command
"patch" does, i.e. apply a diff to working tree files, as
format-patch does not apply any patch.  

Who guarantees that commit messages are detailed?  If I write a
commit that does not change anything with a one-liner message, would
this option make that message more detailed?  I doubt that it would
be the case.

This option may sit much better near the --ignore-if-in-upstream
option.  The primary way the command is told which commits to show
is via its argument (<since> or <revision-range>), and we do not
have many options to affect the selection of commits, but this one
and --ignore-if-in-upstream are such options.  Borrowing from the
way the description of that other option is phrased, perhaps

    Include patches for commits that do not introduce any change,
    which are omitted by default.

Do not refer to 'git-diff-tree'; I do not think the reader who is
learning how to drive format-patch will learn anything new by
reading that page.

I have a feeling that if we were to endorse and promote this option
by documenting (note: it was an accident and a mistake that this
option is understood by underlying revision.c parse machinery, and
not a designed behaviour for format-patch to do anything to empty
commits), we should give users a better synonym to invoke it, but
that can and should be outside of this step.

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

* Re: [PATCH v2 2/4] am: support --always option to am empty commits
  2021-11-12  6:53   ` [PATCH v2 2/4] am: support --always option to am empty commits Aleen via GitGitGadget
@ 2021-11-12 22:23     ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-12 22:23 UTC (permalink / raw)
  To: Aleen via GitGitGadget; +Cc: git, René Scharfe, Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Aleen <aleen42@vip.qq.com>
> Subject: Re: [PATCH v2 2/4] am: support --always option to am empty commits

As the inventor of "format-patch" and "am", I probably should wish
that "to am" were by now a valid verb, but no, it is not.

More importantly, when an empty patch comes, there can be many
different ways for the "am" command to handle it.  "to am empty
commits" does not say how the patch chooses to so and does not make
a very useful title for this commit.

Right now, we error out, simply because it is an easy mistake to
save a non-patch e-mail to the mailbox when intending to save a
series of patches belonging to a topic, and the user is expected to
say "git am --skip" to skip over it when it happens.  The above
"Subject:" can be read to mean that the new option instead allows
such an empty message to be skipped without stopping and forcing the
user to say "am --skip", which may be a useful thing to do.  Or it
may mean that the new option creates an empty commit, using the
contents of the e-mail as the commit log message.  Does this patch
offer both behaviour?  If so, "to am", even though it does not
convey a bit of information, might be an acceptable compromise.  If
the patch implements only one of the behaviours, then we should say
so.  Either one of these two:

    am: --always option skips empty patches
    am: --always option records empty patches as empty commits

Also, I thought that the previous round saw a conclusion that --always
is a bad name for the option.  If we are making the second round,
let's not start with a bad name and the "fix the mistake" of
starting with a bad name in a later step.  Just start with the final
name from the beginning.

> +--always::
> +	Apply patches of commits with detailed commit messages,
> +	even if they emit no changes. (see linkgit:git-format-patch[1])

Almost the same comment as 1/4 applies to the above description.

    --empty-patch=(skip|asis|die)::
	The command usually errors out when seeing an input e-mail
	message that lacks a patch.  When this option is set to
	'skip', skip such an e-mail message without erroring out.
	When this option is set to 'asis', create an empty commit,
	recording the contents of the e-mail message as its log.
	'die' is the default.


perhaps?  Assuming that 'skip' would make a useful addition to the
mix in the future.

> diff --git a/builtin/am.c b/builtin/am.c
> index 8677ea2348a..d11efc16f92 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -124,6 +124,8 @@ struct am_state {
>  	int ignore_date;
>  	int allow_rerere_autoupdate;
>  	const char *sign_commit;
> +	int always;

OK, so here is where the parse_options() records the command line
option.

> +	int empty_commit;

I do not think this addtion of empty_commit member to this structure
is welcome or necessary.

>  	int rebasing;
>  };

> @@ -1249,8 +1251,12 @@ static int parse_mail(struct am_state *state, const char *mail)
>  	}

>  	if (is_empty_or_missing_file(am_path(state, "patch"))) {
> -		printf_ln(_("Patch is empty."));
> -		die_user_resolve(state);
> +		if (state->always) {
> +			state->empty_commit = 1;
> +		} else {
> +			printf_ln(_("Patch is empty."));
> +			die_user_resolve(state);
> +		}
>  	}

I am only thinking aloud, but I suspect that the whole "if 'patch'
is empty, do something special" code logically belongs to the
caller.  Perhaps we should remove this block altogether and let the
code continue the rest of this function.  And return 0, as this is
not like mail-system-internal-data that we want to pretend did not
even exist, and have the caller check if "patch" file is empty and
act accordingly.

> @@ -1792,6 +1798,9 @@ static void am_run(struct am_state *state, int resume)
>  		if (state->interactive && do_interactive(state))
>  			goto next;
>  
> +		if (state->empty_commit)
> +			goto commit;
> +

This is probably a wrong place to jump from.  You are bypassing
applypatch-msg-hook that may be serving as a gate to catch typos
if you are going to create a commit.

So, perhaps check if "patch" is empty here, using the code you'd
lift from parse_mail(), and if it is empty then:

  - if --empty-commit is set to die (or left default), do the
    printf_ln(_("Patch is empty.")) followed by a call to
    die_user_resolve(state), just like before.

  - if it is set to skip, jump to "next", just like when
    parse_mail() returned 1.

  - otherwise (i.e. you are told to create an empty commit),
    remember the fact that current e-mail has no patch, but continue
    to the next step to run the hook.

>  		if (run_applypatch_msg_hook(state))
>  			exit(1);

And after passing the hook, if your earlier check says that there is
no patch and you are to create an empty commit, jump to "commit"
label from here.

> diff --git a/t/t4150-am.sh b/t/t4150-am.sh
> index 2aaaa0d7ded..5b3617857a8 100755
> --- a/t/t4150-am.sh
> +++ b/t/t4150-am.sh
> @@ -196,6 +196,12 @@ test_expect_success setup '
>  
>  	git format-patch -M --stdout lorem^ >rename-add.patch &&
>  
> +	git checkout -b empty-commit &&
> +	git commit -m "empty commit" --allow-empty &&
> +
> +	git format-patch --stdout empty-commit^ >empty.patch &&
> +	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
> +
>  	# reset time
>  	sane_unset test_tick &&
>  	test_tick
> @@ -1152,4 +1158,23 @@ test_expect_success 'apply binary blob in partial clone' '
>  	git -C client am ../patch
>  '
>  
> +test_expect_success 'am a real empty patch with the --always option' '
> +	rm -fr .git/rebase-apply &&

What is this one about?  If this is trying to clean up the cruft the
previous step made, it may be better to do the clean-up in the
previous step using test_when_finished.

> +	git reset --hard &&
> +	test_must_fail git am --always empty.patch 2>actual &&
> +	echo Patch format detection failed. >expected &&
> +	test_cmp expected actual
> +'

It is curious that the error message the patch touched said "Patch
is empty." but the test checks for a different message.  Are we
testing the right failure mode?

> +test_expect_success 'am a patch with empty commits' '
> +	grep "empty commit" empty-commit.patch &&

What is this testing?  If it is checking the sanity of test data we
created earlier, shouldn't we do so where we generated the data
(i.e. the "setup" block that we earlier saw)?

> +	rm -fr .git/rebase-apply &&
> +	git reset --hard &&

These are trying to clean up the cruft the previous step (added by
this patch) may have left.  Perhaps these should be done inside
test_when_finished of the previous step?

> +	git checkout empty-commit^ &&
> +	git am --always empty-commit.patch &&
> +	test_path_is_missing .git/rebase-apply &&

We should trust "git am"'s exit status here, I would think, rather
than be so intimate with the internal implementation detail like the
name of the temporary directory the command uses.

> +	git cat-file commit HEAD >actual &&
> +	test_i18ngrep "empty commit" actual

test_i18ngrep -> grep

The input (i.e. the commit that resulted in this empty patch) said.
"empty commit", and we are making sure that string appears, but we
are not making sure that is the only string appears in the log
message.  Is it because we will later enhance the command to
automatically extend the single-liner "empty patch" log message into
a lot more detailed one?  I doubt it ;-)

More importantly, the above checks if (part of) the log message is
recorded, but does not check if the resulting commit is what is
expected, i.e. an empty one.

Perhaps checking with "grep" is way too loose a test.  Shouldn't we
do something like

	git show -1 --format='%B' >actual

and compare it with expected "the log is recorded as-is, and there
is no change between HEAD^ and HEAD"?

> +'
> +
>  test_done

Thanks.

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

* [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits
  2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
                     ` (3 preceding siblings ...)
  2021-11-12  6:54   ` [PATCH v2 4/4] chore: am: rename the --always option to --allow-empty Aleen via GitGitGadget
@ 2021-11-15 10:39   ` Aleen via GitGitGadget
  2021-11-15 10:39     ` [PATCH v3 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                       ` (2 more replies)
  4 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-15 10:39 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Aleen 徐沛文,
	René Scharfe, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option
 2. rename the --always option to --allow-empty

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Aleen (2):
  doc: git-format-patch: describe the option --always
  am: support --empty-commit option to handle empty patches

 Documentation/git-am.txt           |  9 +++++
 Documentation/git-format-patch.txt |  6 +++-
 builtin/am.c                       | 48 ++++++++++++++++++++++---
 t/t4150-am.sh                      | 58 ++++++++++++++++++++++++++++++
 4 files changed, 115 insertions(+), 6 deletions(-)


base-commit: b550198c73edd4cc058832dcf74b41aeec2adba2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v2:

 1:  71e6989375c ! 1:  9f1b3dd6d0b doc: git-format-patch: specify the option --always
     @@ Metadata
      Author: Aleen <aleen42@vip.qq.com>
      
       ## Commit message ##
     -    doc: git-format-patch: specify the option --always
     +    doc: git-format-patch: describe the option --always
      
          Signed-off-by: Aleen <aleen42@vip.qq.com>
      
       ## Documentation/git-format-patch.txt ##
      @@ Documentation/git-format-patch.txt: SYNOPSIS
     - 		   [--range-diff=<previous> [--creation-factor=<percent>]]
     - 		   [--filename-max-length=<n>]
     - 		   [--progress]
     -+		   [--always]
     - 		   [<common diff options>]
     - 		   [ <since> | <revision range> ]
     - 
     -@@ Documentation/git-format-patch.txt: you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
     - --progress::
     - 	Show progress reports on stderr as patches are generated.
     + 		   [-n | --numbered | -N | --no-numbered]
     + 		   [--start-number <n>] [--numbered-files]
     + 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
     +-		   [--ignore-if-in-upstream]
     ++		   [--ignore-if-in-upstream] [--always]
     + 		   [--cover-from-description=<mode>]
     + 		   [--rfc] [--subject-prefix=<subject prefix>]
     + 		   [(--reroll-count|-v) <n>]
     +@@ Documentation/git-format-patch.txt: will want to ensure that threading is disabled for `git send-email`.
     + 	patches being generated, and any patch that matches is
     + 	ignored.
       
      +--always::
     -+	Patch commits with detailed commit messages,
     -+	even if they emit no changes. (see linkgit:git-diff-tree[1])
     ++	Include patches for commits that do not introduce any change,
     ++	which are omitted by default.
      +
     - CONFIGURATION
     - -------------
     - You can specify extra mail header lines to be added to each message,
     + --cover-from-description=<mode>::
     + 	Controls which parts of the cover letter will be automatically
     + 	populated using the branch's description.
 2:  59b1417da37 ! 2:  ef33ce8c6f9 am: support --always option to am empty commits
     @@ Metadata
      Author: Aleen <aleen42@vip.qq.com>
      
       ## Commit message ##
     -    am: support --always option to am empty commits
     +    am: support --empty-commit option to handle empty patches
      
          Signed-off-by: Aleen <aleen42@vip.qq.com>
      
     @@ Documentation/git-am.txt: SYNOPSIS
       	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
       	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
       	 [--quoted-cr=<action>]
     -+	 [--always]
     ++	 [--empty-commit=(die|skip|asis)]
       	 [(<mbox> | <Maildir>)...]
       'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
       
     -@@ Documentation/git-am.txt: default.   You can use `--no-utf8` to override this.
     - 	countermand both `commit.gpgSign` configuration variable, and
     - 	earlier `--gpg-sign`.
     +@@ Documentation/git-am.txt: OPTIONS
     + --quoted-cr=<action>::
     + 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
       
     -+--always::
     -+	Apply patches of commits with detailed commit messages,
     -+	even if they emit no changes. (see linkgit:git-format-patch[1])
     ++--empty-commit=(die|skip|asis)::
     ++	The command usually errors out when seeing an input e-mail
     ++	message that lacks a patch. When this option is set to
     ++	'skip', skip such an e-mail message without outputting error.
     ++	When this option is set to 'asis', create an empty commit,
     ++	recording the contents of the e-mail message as its log.
     ++	'die' is specified by default.
      +
     - --continue::
     - -r::
     - --resolved::
     + -m::
     + --message-id::
     + 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
      
       ## builtin/am.c ##
     -@@ builtin/am.c: struct am_state {
     - 	int ignore_date;
     - 	int allow_rerere_autoupdate;
     - 	const char *sign_commit;
     -+	int always;
     -+	int empty_commit;
     - 	int rebasing;
     +@@ builtin/am.c: enum show_patch_type {
     + 	SHOW_PATCH_DIFF = 1,
       };
       
     ++enum empty_commit_action {
     ++	DIE_EMPTY_COMMIT = 0,  /* output errors */
     ++	SKIP_EMPTY_COMMIT,     /* skip without outputting errors */
     ++	ASIS_EMPTY_COMMIT      /* keep recording as empty commits */
     ++};
     ++
     + struct am_state {
     + 	/* state directory path */
     + 	char *dir;
     +@@ builtin/am.c: struct am_state {
     + 	int message_id;
     + 	int scissors; /* enum scissors_type */
     + 	int quoted_cr; /* enum quoted_cr_action */
     ++	int empty_commit; /* enum empty_commit_action */
     + 	struct strvec git_apply_opts;
     + 	const char *resolvemsg;
     + 	int committer_date_is_author_date;
     +@@ builtin/am.c: static int am_option_parse_quoted_cr(const struct option *opt,
     + 	return 0;
     + }
     + 
     ++static int am_option_parse_empty_commit(const struct option *opt,
     ++				     const char *arg, int unset)
     ++{
     ++	int *opt_value = opt->value;
     ++
     ++	if (unset || !strcmp(arg, "die"))
     ++		*opt_value = DIE_EMPTY_COMMIT;
     ++	else if (!strcmp(arg, "skip"))
     ++		*opt_value = SKIP_EMPTY_COMMIT;
     ++	else if (!strcmp(arg, "asis"))
     ++		*opt_value = ASIS_EMPTY_COMMIT;
     ++	else
     ++		return error(_("Invalid value for --empty-commit: %s"), arg);
     ++
     ++	return 0;
     ++}
     ++
     + /**
     +  * Returns path relative to the am_state directory.
     +  */
      @@ builtin/am.c: static int parse_mail(struct am_state *state, const char *mail)
     + 		goto finish;
       	}
       
     - 	if (is_empty_or_missing_file(am_path(state, "patch"))) {
     +-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
      -		printf_ln(_("Patch is empty."));
      -		die_user_resolve(state);
     -+		if (state->always) {
     -+			state->empty_commit = 1;
     -+		} else {
     -+			printf_ln(_("Patch is empty."));
     -+			die_user_resolve(state);
     -+		}
     - 	}
     - 
     +-	}
     +-
       	strbuf_addstr(&msg, "\n\n");
     + 	strbuf_addbuf(&msg, &mi.log_message);
     + 	strbuf_stripspace(&msg, 0);
      @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
       		if (state->interactive && do_interactive(state))
       			goto next;
       
     -+		if (state->empty_commit)
     -+			goto commit;
     ++		if (is_empty_or_missing_file(am_path(state, "patch"))) {
     ++			if (state->empty_commit == SKIP_EMPTY_COMMIT)
     ++				goto next;
     ++			else if (state->empty_commit == ASIS_EMPTY_COMMIT) {
     ++				if (run_applypatch_msg_hook(state))
     ++					exit(1);
     ++				else
     ++					goto commit;
     ++			} else if (state->empty_commit == DIE_EMPTY_COMMIT) {
     ++				printf_ln(_("Patch is empty."));
     ++				die_user_resolve(state);
     ++			}
     ++		}
      +
       		if (run_applypatch_msg_hook(state))
       			exit(1);
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
       		  N_("GPG-sign commits"),
       		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
     -+		OPT_BOOL(0, "always", &state.always,
     -+			N_("always apply patch event if the patch is empty")),
     -+		OPT_HIDDEN_BOOL(0, "empty-commit", &state.empty_commit,
     -+			N_("(internal use for skipping git-apply to empty commits)")),
     ++		{ OPTION_CALLBACK, 0, "empty-commit", &state.empty_commit,
     ++		  "(die|skip|asis)",
     ++		  N_("specify how to handle empty patches"),
     ++		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
       		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
       			N_("(internal use for git-rebase)")),
       		OPT_END()
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
       	git -C client am ../patch
       '
       
     -+test_expect_success 'am a real empty patch with the --always option' '
     -+	rm -fr .git/rebase-apply &&
     -+	git reset --hard &&
     -+	test_must_fail git am --always empty.patch 2>actual &&
     ++test_expect_success 'still output error with --empty-commit when meeting empty files' '
     ++	test_must_fail git am --empty-commit=skip empty.patch 2>actual &&
      +	echo Patch format detection failed. >expected &&
      +	test_cmp expected actual
      +'
      +
     -+test_expect_success 'am a patch with empty commits' '
     -+	grep "empty commit" empty-commit.patch &&
     -+	rm -fr .git/rebase-apply &&
     -+	git reset --hard &&
     ++test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
      +	git checkout empty-commit^ &&
     -+	git am --always empty-commit.patch &&
     ++	test_must_fail git am empty-commit.patch >err &&
     ++	test_path_is_dir .git/rebase-apply &&
     ++	test_i18ngrep "Patch is empty." err &&
     ++	rm -fr .git/rebase-apply &&
     ++
     ++	test_must_fail git am --empty-commit=die empty-commit.patch >err &&
     ++	test_path_is_dir .git/rebase-apply &&
     ++	test_i18ngrep "Patch is empty." err &&
     ++	rm -fr .git/rebase-apply &&
     ++
     ++	test_must_fail git am --empty-commit=die cover-letter.patch >err &&
     ++	test_path_is_dir .git/rebase-apply &&
     ++	test_i18ngrep "Patch is empty." err &&
     ++	rm -fr .git/rebase-apply
     ++'
     ++
     ++test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
     ++	git am --empty-commit=skip empty-commit.patch >err &&
     ++	test_path_is_missing .git/rebase-apply &&
     ++	git rev-parse empty-commit^ >expected &&
     ++	git rev-parse HEAD >actual &&
     ++	test_cmp expected actual &&
     ++
     ++	git am --empty-commit=skip cover-letter.patch >err &&
     ++	test_path_is_missing .git/rebase-apply &&
     ++	git rev-parse empty-commit^ >expected &&
     ++	git rev-parse HEAD >actual &&
     ++	test_cmp expected actual
     ++'
     ++
     ++test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
     ++	git am --empty-commit=asis empty-commit.patch &&
     ++	test_path_is_missing .git/rebase-apply &&
     ++	git show empty-commit --format="%B" >expected &&
     ++	git show HEAD --format="%B" >actual &&
     ++	grep -f actual expected &&
     ++
     ++	git am --empty-commit=asis cover-letter.patch &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	git cat-file commit HEAD >actual &&
     -+	test_i18ngrep "empty commit" actual
     ++	git show empty-commit --format="%B" >expected &&
     ++	git show HEAD --format="%B" >actual &&
     ++	grep -f actual expected
      +'
      +
       test_done
 3:  da024ced668 < -:  ----------- test: am: add the case when not passing the --always option
 4:  45e9720f40b < -:  ----------- chore: am: rename the --always option to --allow-empty

-- 
gitgitgadget

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

* [PATCH v3 1/2] doc: git-format-patch: describe the option --always
  2021-11-15 10:39   ` [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
@ 2021-11-15 10:39     ` Aleen via GitGitGadget
  2021-11-15 10:39     ` [PATCH v3 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
  2021-11-16  5:18     ` [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-15 10:39 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Aleen 徐沛文,
	René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v3 2/2] am: support --empty-commit option to handle empty patches
  2021-11-15 10:39   ` [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2021-11-15 10:39     ` [PATCH v3 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-15 10:39     ` Aleen via GitGitGadget
  2021-11-15 11:13       ` Aleen 徐沛文
  2021-11-16  5:18     ` [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-15 10:39 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Aleen 徐沛文,
	René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++++
 builtin/am.c             | 48 +++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 58 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..d8d3bf202d7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty-commit=(die|skip|asis)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty-commit=(die|skip|asis)::
+	The command usually errors out when seeing an input e-mail
+	message that lacks a patch. When this option is set to
+	'skip', skip such an e-mail message without outputting error.
+	When this option is set to 'asis', create an empty commit,
+	recording the contents of the e-mail message as its log.
+	'die' is specified by default.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..e7755c1377e 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_commit_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	SKIP_EMPTY_COMMIT,     /* skip without outputting errors */
+	ASIS_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_commit; /* enum empty_commit_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,23 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty_commit(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	if (unset || !strcmp(arg, "die"))
+		*opt_value = DIE_EMPTY_COMMIT;
+	else if (!strcmp(arg, "skip"))
+		*opt_value = SKIP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "asis"))
+		*opt_value = ASIS_EMPTY_COMMIT;
+	else
+		return error(_("Invalid value for --empty-commit: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1272,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1792,6 +1811,20 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			if (state->empty_commit == SKIP_EMPTY_COMMIT)
+				goto next;
+			else if (state->empty_commit == ASIS_EMPTY_COMMIT) {
+				if (run_applypatch_msg_hook(state))
+					exit(1);
+				else
+					goto commit;
+			} else if (state->empty_commit == DIE_EMPTY_COMMIT) {
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
 
@@ -1827,6 +1860,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2391,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		{ OPTION_CALLBACK, 0, "empty-commit", &state.empty_commit,
+		  "(die|skip|asis)",
+		  N_("specify how to handle empty patches"),
+		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..13fdf2f86c6 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	git format-patch --stdout empty-commit^ >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,56 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'still output error with --empty-commit when meeting empty files' '
+	test_must_fail git am --empty-commit=skip empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty-commit=die empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty-commit=die cover-letter.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply
+'
+
+test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
+	git am --empty-commit=skip empty-commit.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+
+	git am --empty-commit=skip cover-letter.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty-commit=asis empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected &&
+
+	git am --empty-commit=asis cover-letter.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH v3 2/2] am: support --empty-commit option to handle empty patches
  2021-11-15 10:39     ` [PATCH v3 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
@ 2021-11-15 11:13       ` Aleen 徐沛文
  0 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-15 11:13 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, René Scharfe, Aleen, Aleen via GitGitGadget

Dears Hamano,

Sorry about the late submission, and I have modified it to support the `--empty-commit=(die|skip|asis)` pattern.

> +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
> +	git am --empty-commit=asis empty-commit.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	git show empty-commit --format="%B" >expected &&
> +	git show HEAD --format="%B" >actual &&
> +	grep -f actual expected &&
> +
> +	git am --empty-commit=asis cover-letter.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	git show empty-commit --format="%B" >expected &&
> +	git show HEAD --format="%B" >actual &&
> +	grep -f actual expected
> +'

When it comes to the last test case, there are some accidental contents recognized as commit messages inside an empty patch, like:

1. version signatures:
  > -- 
  > 2.34.0.rc2.390.gef33ce8c6f
2. information of cover letter:
  > *** BLURB HERE ***
  >
  > A U Thor (1):
  >   empty commit

I don't think it is necessary to fix it within `mailinfo.c`. Can you give me some suggestions on whether this case should be handled?

Aleen

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

* [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits
  2021-11-15 10:39   ` [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2021-11-15 10:39     ` [PATCH v3 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-15 10:39     ` [PATCH v3 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
@ 2021-11-16  5:18     ` Aleen via GitGitGadget
  2021-11-16  5:18       ` [PATCH v4 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                         ` (2 more replies)
  2 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-16  5:18 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Aleen 徐沛文,
	René Scharfe, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option
 2. rename the --always option to --allow-empty

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases
 2. grep -f cannot be used under Mac OS

----------------------------------------------------------------------------

Aleen (2):
  doc: git-format-patch: describe the option --always
  am: support --empty-commit option to handle empty patches

 Documentation/git-am.txt           |  9 ++++
 Documentation/git-format-patch.txt |  6 ++-
 builtin/am.c                       | 48 ++++++++++++++++++--
 t/t4150-am.sh                      | 73 ++++++++++++++++++++++++++++++
 4 files changed, 130 insertions(+), 6 deletions(-)


base-commit: b550198c73edd4cc058832dcf74b41aeec2adba2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v3:

 1:  9f1b3dd6d0b = 1:  9f1b3dd6d0b doc: git-format-patch: describe the option --always
 2:  ef33ce8c6f9 ! 2:  b7e30c9b7ab am: support --empty-commit option to handle empty patches
     @@ t/t4150-am.sh: test_expect_success setup '
      +	git commit -m "empty commit" --allow-empty &&
      +
      +	git format-patch --stdout empty-commit^ >empty.patch &&
     ++	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&
      +	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
      +
       	# reset time
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +
      +	git am --empty-commit=skip cover-letter.patch >err &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	git rev-parse empty-commit^ >expected &&
     -+	git rev-parse HEAD >actual &&
     -+	test_cmp expected actual
     ++	test_cmp_rev empty-commit^ HEAD
      +'
      +
      +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
      +	git am --empty-commit=asis empty-commit.patch &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	git show empty-commit --format="%B" >expected &&
     ++	{
     ++		git show empty-commit --format="%B" &&
     ++		echo "--" &&
     ++		git version | sed -e "s/^git version //" &&
     ++		echo
     ++	} >expected &&
      +	git show HEAD --format="%B" >actual &&
     -+	grep -f actual expected &&
     ++	test_cmp actual expected &&
      +
      +	git am --empty-commit=asis cover-letter.patch &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	git show empty-commit --format="%B" >expected &&
     ++	{
     ++		echo "*** SUBJECT HERE ***" &&
     ++		echo &&
     ++		echo "*** BLURB HERE ***" &&
     ++		echo &&
     ++		echo "A U Thor (1):" &&
     ++		printf "  " &&
     ++		git show empty-commit --format="%B" &&
     ++		echo "--" &&
     ++		git version | sed -e "s/^git version //" &&
     ++		echo
     ++	} >expected &&
      +	git show HEAD --format="%B" >actual &&
     -+	grep -f actual expected
     ++	test_cmp actual expected
      +'
      +
       test_done

-- 
gitgitgadget

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

* [PATCH v4 1/2] doc: git-format-patch: describe the option --always
  2021-11-16  5:18     ` [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
@ 2021-11-16  5:18       ` Aleen via GitGitGadget
  2021-11-16  5:18       ` [PATCH v4 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
  2021-11-17  9:33       ` [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-16  5:18 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Aleen 徐沛文,
	René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v4 2/2] am: support --empty-commit option to handle empty patches
  2021-11-16  5:18     ` [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2021-11-16  5:18       ` [PATCH v4 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-16  5:18       ` Aleen via GitGitGadget
  2021-11-16 10:07         ` Phillip Wood
  2021-11-17  9:33       ` [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-16  5:18 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Aleen 徐沛文,
	René Scharfe, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++
 builtin/am.c             | 48 +++++++++++++++++++++++---
 t/t4150-am.sh            | 73 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..d8d3bf202d7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty-commit=(die|skip|asis)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty-commit=(die|skip|asis)::
+	The command usually errors out when seeing an input e-mail
+	message that lacks a patch. When this option is set to
+	'skip', skip such an e-mail message without outputting error.
+	When this option is set to 'asis', create an empty commit,
+	recording the contents of the e-mail message as its log.
+	'die' is specified by default.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..e7755c1377e 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_commit_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	SKIP_EMPTY_COMMIT,     /* skip without outputting errors */
+	ASIS_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_commit; /* enum empty_commit_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,23 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty_commit(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	if (unset || !strcmp(arg, "die"))
+		*opt_value = DIE_EMPTY_COMMIT;
+	else if (!strcmp(arg, "skip"))
+		*opt_value = SKIP_EMPTY_COMMIT;
+	else if (!strcmp(arg, "asis"))
+		*opt_value = ASIS_EMPTY_COMMIT;
+	else
+		return error(_("Invalid value for --empty-commit: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1272,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1792,6 +1811,20 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			if (state->empty_commit == SKIP_EMPTY_COMMIT)
+				goto next;
+			else if (state->empty_commit == ASIS_EMPTY_COMMIT) {
+				if (run_applypatch_msg_hook(state))
+					exit(1);
+				else
+					goto commit;
+			} else if (state->empty_commit == DIE_EMPTY_COMMIT) {
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
 
@@ -1827,6 +1860,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2391,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		{ OPTION_CALLBACK, 0, "empty-commit", &state.empty_commit,
+		  "(die|skip|asis)",
+		  N_("specify how to handle empty patches"),
+		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..e657180c201 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,13 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	git format-patch --stdout empty-commit^ >empty.patch &&
+	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1159,70 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'still output error with --empty-commit when meeting empty files' '
+	test_must_fail git am --empty-commit=skip empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty-commit=die empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty-commit=die cover-letter.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply
+'
+
+test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
+	git am --empty-commit=skip empty-commit.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+
+	git am --empty-commit=skip cover-letter.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	test_cmp_rev empty-commit^ HEAD
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty-commit=asis empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	{
+		git show empty-commit --format="%B" &&
+		echo "--" &&
+		git version | sed -e "s/^git version //" &&
+		echo
+	} >expected &&
+	git show HEAD --format="%B" >actual &&
+	test_cmp actual expected &&
+
+	git am --empty-commit=asis cover-letter.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	{
+		echo "*** SUBJECT HERE ***" &&
+		echo &&
+		echo "*** BLURB HERE ***" &&
+		echo &&
+		echo "A U Thor (1):" &&
+		printf "  " &&
+		git show empty-commit --format="%B" &&
+		echo "--" &&
+		git version | sed -e "s/^git version //" &&
+		echo
+	} >expected &&
+	git show HEAD --format="%B" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH v4 2/2] am: support --empty-commit option to handle empty patches
  2021-11-16  5:18       ` [PATCH v4 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
@ 2021-11-16 10:07         ` Phillip Wood
  2021-11-16 10:31           ` Aleen 徐沛文
  2021-11-17  8:39           ` Junio C Hamano
  0 siblings, 2 replies; 129+ messages in thread
From: Phillip Wood @ 2021-11-16 10:07 UTC (permalink / raw)
  To: Aleen via GitGitGadget, git
  Cc: René Scharfe, Aleen 徐沛文, Aleen, Junio C Hamano

Hi Aleen

Thanks for working on this

On 16/11/2021 05:18, Aleen via GitGitGadget wrote:
> From: Aleen <aleen42@vip.qq.com>
> 
> Signed-off-by: Aleen <aleen42@vip.qq.com>
> ---
>   Documentation/git-am.txt |  9 +++++
>   builtin/am.c             | 48 +++++++++++++++++++++++---
>   t/t4150-am.sh            | 73 ++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 125 insertions(+), 5 deletions(-)
> 
> diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
> index 0a4a984dfde..d8d3bf202d7 100644
> --- a/Documentation/git-am.txt
> +++ b/Documentation/git-am.txt
> @@ -16,6 +16,7 @@ SYNOPSIS
>   	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
>   	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
>   	 [--quoted-cr=<action>]
> +	 [--empty-commit=(die|skip|asis)]
>   	 [(<mbox> | <Maildir>)...]
>   'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
>   
> @@ -63,6 +64,14 @@ OPTIONS
>   --quoted-cr=<action>::
>   	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
>   
> +--empty-commit=(die|skip|asis)::
> +	The command usually errors out when seeing an input e-mail
> +	message that lacks a patch. When this option is set to
> +	'skip', skip such an e-mail message without outputting error.
> +	When this option is set to 'asis', create an empty commit,
> +	recording the contents of the e-mail message as its log.
> +	'die' is specified by default.

This feels sufficiently similar to the case of handling empty commits in 
'git rebase' that it is worth trying to have a similar user interface. 
Otherwise the two commands have two different option names doing more or 
less the same thing. 'git rebase' has --empty=[drop,keep,ask] where drop 
is the default. If am were to accept --empty=[drop,keep,die] it would 
offer a similar user experience.

Best Wishes

Phillip

>   -m::
>   --message-id::
>   	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
> diff --git a/builtin/am.c b/builtin/am.c
> index 8677ea2348a..e7755c1377e 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -87,6 +87,12 @@ enum show_patch_type {
>   	SHOW_PATCH_DIFF = 1,
>   };
>   
> +enum empty_commit_action {
> +	DIE_EMPTY_COMMIT = 0,  /* output errors */
> +	SKIP_EMPTY_COMMIT,     /* skip without outputting errors */
> +	ASIS_EMPTY_COMMIT      /* keep recording as empty commits */
> +};
> +
>   struct am_state {
>   	/* state directory path */
>   	char *dir;
> @@ -118,6 +124,7 @@ struct am_state {
>   	int message_id;
>   	int scissors; /* enum scissors_type */
>   	int quoted_cr; /* enum quoted_cr_action */
> +	int empty_commit; /* enum empty_commit_action */
>   	struct strvec git_apply_opts;
>   	const char *resolvemsg;
>   	int committer_date_is_author_date;
> @@ -178,6 +185,23 @@ static int am_option_parse_quoted_cr(const struct option *opt,
>   	return 0;
>   }
>   
> +static int am_option_parse_empty_commit(const struct option *opt,
> +				     const char *arg, int unset)
> +{
> +	int *opt_value = opt->value;
> +
> +	if (unset || !strcmp(arg, "die"))
> +		*opt_value = DIE_EMPTY_COMMIT;
> +	else if (!strcmp(arg, "skip"))
> +		*opt_value = SKIP_EMPTY_COMMIT;
> +	else if (!strcmp(arg, "asis"))
> +		*opt_value = ASIS_EMPTY_COMMIT;
> +	else
> +		return error(_("Invalid value for --empty-commit: %s"), arg);
> +
> +	return 0;
> +}
> +
>   /**
>    * Returns path relative to the am_state directory.
>    */
> @@ -1248,11 +1272,6 @@ static int parse_mail(struct am_state *state, const char *mail)
>   		goto finish;
>   	}
>   
> -	if (is_empty_or_missing_file(am_path(state, "patch"))) {
> -		printf_ln(_("Patch is empty."));
> -		die_user_resolve(state);
> -	}
> -
>   	strbuf_addstr(&msg, "\n\n");
>   	strbuf_addbuf(&msg, &mi.log_message);
>   	strbuf_stripspace(&msg, 0);
> @@ -1792,6 +1811,20 @@ static void am_run(struct am_state *state, int resume)
>   		if (state->interactive && do_interactive(state))
>   			goto next;
>   
> +		if (is_empty_or_missing_file(am_path(state, "patch"))) {
> +			if (state->empty_commit == SKIP_EMPTY_COMMIT)
> +				goto next;
> +			else if (state->empty_commit == ASIS_EMPTY_COMMIT) {
> +				if (run_applypatch_msg_hook(state))
> +					exit(1);
> +				else
> +					goto commit;
> +			} else if (state->empty_commit == DIE_EMPTY_COMMIT) {
> +				printf_ln(_("Patch is empty."));
> +				die_user_resolve(state);
> +			}
> +		}
> +
>   		if (run_applypatch_msg_hook(state))
>   			exit(1);
>   
> @@ -1827,6 +1860,7 @@ static void am_run(struct am_state *state, int resume)
>   			die_user_resolve(state);
>   		}
>   
> +commit:
>   		do_commit(state);
>   
>   next:
> @@ -2357,6 +2391,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
>   		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
>   		  N_("GPG-sign commits"),
>   		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> +		{ OPTION_CALLBACK, 0, "empty-commit", &state.empty_commit,
> +		  "(die|skip|asis)",
> +		  N_("specify how to handle empty patches"),
> +		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
>   		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
>   			N_("(internal use for git-rebase)")),
>   		OPT_END()
> diff --git a/t/t4150-am.sh b/t/t4150-am.sh
> index 2aaaa0d7ded..e657180c201 100755
> --- a/t/t4150-am.sh
> +++ b/t/t4150-am.sh
> @@ -196,6 +196,13 @@ test_expect_success setup '
>   
>   	git format-patch -M --stdout lorem^ >rename-add.patch &&
>   
> +	git checkout -b empty-commit &&
> +	git commit -m "empty commit" --allow-empty &&
> +
> +	git format-patch --stdout empty-commit^ >empty.patch &&
> +	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&
> +	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
> +
>   	# reset time
>   	sane_unset test_tick &&
>   	test_tick
> @@ -1152,4 +1159,70 @@ test_expect_success 'apply binary blob in partial clone' '
>   	git -C client am ../patch
>   '
>   
> +test_expect_success 'still output error with --empty-commit when meeting empty files' '
> +	test_must_fail git am --empty-commit=skip empty.patch 2>actual &&
> +	echo Patch format detection failed. >expected &&
> +	test_cmp expected actual
> +'
> +
> +test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
> +	git checkout empty-commit^ &&
> +	test_must_fail git am empty-commit.patch >err &&
> +	test_path_is_dir .git/rebase-apply &&
> +	test_i18ngrep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply &&
> +
> +	test_must_fail git am --empty-commit=die empty-commit.patch >err &&
> +	test_path_is_dir .git/rebase-apply &&
> +	test_i18ngrep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply &&
> +
> +	test_must_fail git am --empty-commit=die cover-letter.patch >err &&
> +	test_path_is_dir .git/rebase-apply &&
> +	test_i18ngrep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply
> +'
> +
> +test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
> +	git am --empty-commit=skip empty-commit.patch >err &&
> +	test_path_is_missing .git/rebase-apply &&
> +	git rev-parse empty-commit^ >expected &&
> +	git rev-parse HEAD >actual &&
> +	test_cmp expected actual &&
> +
> +	git am --empty-commit=skip cover-letter.patch >err &&
> +	test_path_is_missing .git/rebase-apply &&
> +	test_cmp_rev empty-commit^ HEAD
> +'
> +
> +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
> +	git am --empty-commit=asis empty-commit.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	{
> +		git show empty-commit --format="%B" &&
> +		echo "--" &&
> +		git version | sed -e "s/^git version //" &&
> +		echo
> +	} >expected &&
> +	git show HEAD --format="%B" >actual &&
> +	test_cmp actual expected &&
> +
> +	git am --empty-commit=asis cover-letter.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	{
> +		echo "*** SUBJECT HERE ***" &&
> +		echo &&
> +		echo "*** BLURB HERE ***" &&
> +		echo &&
> +		echo "A U Thor (1):" &&
> +		printf "  " &&
> +		git show empty-commit --format="%B" &&
> +		echo "--" &&
> +		git version | sed -e "s/^git version //" &&
> +		echo
> +	} >expected &&
> +	git show HEAD --format="%B" >actual &&
> +	test_cmp actual expected
> +'
> +
>   test_done
> 

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

* Re: [PATCH v4 2/2] am: support --empty-commit option to handle empty patches
  2021-11-16 10:07         ` Phillip Wood
@ 2021-11-16 10:31           ` Aleen 徐沛文
  2021-11-17  8:39           ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-16 10:31 UTC (permalink / raw)
  To: phillip.wood
  Cc: Aleen via GitGitGadget, git, René Scharfe, Aleen, Junio C Hamano

> > +--empty-commit=(die|skip|asis)::
> > +	The command usually errors out when seeing an input e-mail
> > +	message that lacks a patch. When this option is set to
> > +	'skip', skip such an e-mail message without outputting error.
> > +	When this option is set to 'asis', create an empty commit,
> > +	recording the contents of the e-mail message as its log.
> > +	'die' is specified by default.
> 
> This feels sufficiently similar to the case of handling empty commits in 
> 'git rebase' that it is worth trying to have a similar user interface. 
> Otherwise the two commands have two different option names doing more or 
> less the same thing. 'git rebase' has --empty=[drop,keep,ask] where drop 
> is the default. If am were to accept --empty=[drop,keep,die] it would 
> offer a similar user experience.
> 
> Best Wishes
> 
> Phillip

Dears Phillip,

It seems a good idea. Can Hamano make a decision?

Aleen

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

* Re: [PATCH v4 2/2] am: support --empty-commit option to handle empty patches
  2021-11-16 10:07         ` Phillip Wood
  2021-11-16 10:31           ` Aleen 徐沛文
@ 2021-11-17  8:39           ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-17  8:39 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Aleen via GitGitGadget, git, René Scharfe,
	Aleen 徐沛文,
	Aleen

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

>>   +--empty-commit=(die|skip|asis)::
>> +	The command usually errors out when seeing an input e-mail
>> +	message that lacks a patch. When this option is set to
>> +	'skip', skip such an e-mail message without outputting error.
>> +	When this option is set to 'asis', create an empty commit,
>> +	recording the contents of the e-mail message as its log.
>> +	'die' is specified by default.
>
> This feels sufficiently similar to the case of handling empty commits
> in 'git rebase' that it is worth trying to have a similar user
> interface. Otherwise the two commands have two different option names
> doing more or less the same thing. 'git rebase' has
> --empty=[drop,keep,ask] where drop is the default. If am were to
>  accept --empty=[drop,keep,die] it would offer a similar user
> experience.

Ah, thanks for noticing.  I like the three words you suggest.  If we
already have a similar option, we definitely should follow suit, and
I think "--empty" would be a better fit than "--empty-commit" in the
context of talking about the _input_ to "am".


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

* [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits
  2021-11-16  5:18     ` [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2021-11-16  5:18       ` [PATCH v4 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-16  5:18       ` [PATCH v4 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
@ 2021-11-17  9:33       ` Aleen via GitGitGadget
  2021-11-17  9:33         ` [PATCH v5 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                           ` (2 more replies)
  2 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-17  9:33 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Aleen (2):
  doc: git-format-patch: describe the option --always
  am: support --empty option to handle empty patches

 Documentation/git-am.txt           |  9 ++++
 Documentation/git-format-patch.txt |  6 ++-
 builtin/am.c                       | 48 ++++++++++++++++++--
 t/t4150-am.sh                      | 73 ++++++++++++++++++++++++++++++
 4 files changed, 130 insertions(+), 6 deletions(-)


base-commit: b550198c73edd4cc058832dcf74b41aeec2adba2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v4:

 1:  9f1b3dd6d0b = 1:  9f1b3dd6d0b doc: git-format-patch: describe the option --always
 2:  b7e30c9b7ab ! 2:  96d8573dc80 am: support --empty-commit option to handle empty patches
     @@ Metadata
      Author: Aleen <aleen42@vip.qq.com>
      
       ## Commit message ##
     -    am: support --empty-commit option to handle empty patches
     +    am: support --empty option to handle empty patches
      
          Signed-off-by: Aleen <aleen42@vip.qq.com>
      
     @@ Documentation/git-am.txt: SYNOPSIS
       	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
       	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
       	 [--quoted-cr=<action>]
     -+	 [--empty-commit=(die|skip|asis)]
     ++	 [--empty=(die|drop|keep)]
       	 [(<mbox> | <Maildir>)...]
       'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
       
     @@ Documentation/git-am.txt: OPTIONS
       --quoted-cr=<action>::
       	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
       
     -+--empty-commit=(die|skip|asis)::
     ++--empty-commit=(die|drop|keep)::
      +	The command usually errors out when seeing an input e-mail
      +	message that lacks a patch. When this option is set to
     -+	'skip', skip such an e-mail message without outputting error.
     -+	When this option is set to 'asis', create an empty commit,
     ++	'drop', skip such an e-mail message without outputting error.
     ++	When this option is set to 'keep', create an empty commit,
      +	recording the contents of the e-mail message as its log.
      +	'die' is specified by default.
      +
     @@ builtin/am.c: enum show_patch_type {
       	SHOW_PATCH_DIFF = 1,
       };
       
     -+enum empty_commit_action {
     ++enum empty_action {
      +	DIE_EMPTY_COMMIT = 0,  /* output errors */
     -+	SKIP_EMPTY_COMMIT,     /* skip without outputting errors */
     -+	ASIS_EMPTY_COMMIT      /* keep recording as empty commits */
     ++	DROP_EMPTY_COMMIT,     /* skip without outputting errors */
     ++	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
      +};
      +
       struct am_state {
     @@ builtin/am.c: struct am_state {
       	int message_id;
       	int scissors; /* enum scissors_type */
       	int quoted_cr; /* enum quoted_cr_action */
     -+	int empty_commit; /* enum empty_commit_action */
     ++	int empty_type; /* enum empty_action */
       	struct strvec git_apply_opts;
       	const char *resolvemsg;
       	int committer_date_is_author_date;
     @@ builtin/am.c: static int am_option_parse_quoted_cr(const struct option *opt,
      +
      +	if (unset || !strcmp(arg, "die"))
      +		*opt_value = DIE_EMPTY_COMMIT;
     -+	else if (!strcmp(arg, "skip"))
     -+		*opt_value = SKIP_EMPTY_COMMIT;
     -+	else if (!strcmp(arg, "asis"))
     -+		*opt_value = ASIS_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 --empty-commit: %s"), arg);
     ++		return error(_("Invalid value for --empty: %s"), arg);
      +
      +	return 0;
      +}
     @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
       			goto next;
       
      +		if (is_empty_or_missing_file(am_path(state, "patch"))) {
     -+			if (state->empty_commit == SKIP_EMPTY_COMMIT)
     ++			if (state->empty_type == DROP_EMPTY_COMMIT)
      +				goto next;
     -+			else if (state->empty_commit == ASIS_EMPTY_COMMIT) {
     ++			else if (state->empty_type == KEEP_EMPTY_COMMIT) {
      +				if (run_applypatch_msg_hook(state))
      +					exit(1);
      +				else
      +					goto commit;
     -+			} else if (state->empty_commit == DIE_EMPTY_COMMIT) {
     ++			} else if (state->empty_type == DIE_EMPTY_COMMIT) {
      +				printf_ln(_("Patch is empty."));
      +				die_user_resolve(state);
      +			}
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
       		  N_("GPG-sign commits"),
       		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
     -+		{ OPTION_CALLBACK, 0, "empty-commit", &state.empty_commit,
     -+		  "(die|skip|asis)",
     ++		{ OPTION_CALLBACK, 0, "empty", &state.empty_type,
     ++		  "(die|drop|keep)",
      +		  N_("specify how to handle empty patches"),
      +		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
       		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
       	git -C client am ../patch
       '
       
     -+test_expect_success 'still output error with --empty-commit when meeting empty files' '
     -+	test_must_fail git am --empty-commit=skip empty.patch 2>actual &&
     ++test_expect_success 'still output error with --empty when meeting empty files' '
     ++	test_must_fail git am --empty=drop empty.patch 2>actual &&
      +	echo Patch format detection failed. >expected &&
      +	test_cmp expected actual
      +'
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +	test_i18ngrep "Patch is empty." err &&
      +	rm -fr .git/rebase-apply &&
      +
     -+	test_must_fail git am --empty-commit=die empty-commit.patch >err &&
     ++	test_must_fail git am --empty=die empty-commit.patch >err &&
      +	test_path_is_dir .git/rebase-apply &&
      +	test_i18ngrep "Patch is empty." err &&
      +	rm -fr .git/rebase-apply &&
      +
     -+	test_must_fail git am --empty-commit=die cover-letter.patch >err &&
     ++	test_must_fail git am --empty=die cover-letter.patch >err &&
      +	test_path_is_dir .git/rebase-apply &&
      +	test_i18ngrep "Patch is empty." err &&
      +	rm -fr .git/rebase-apply
      +'
      +
      +test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
     -+	git am --empty-commit=skip empty-commit.patch >err &&
     ++	git am --empty=drop empty-commit.patch >err &&
      +	test_path_is_missing .git/rebase-apply &&
      +	git rev-parse empty-commit^ >expected &&
      +	git rev-parse HEAD >actual &&
      +	test_cmp expected actual &&
      +
     -+	git am --empty-commit=skip cover-letter.patch >err &&
     ++	git am --empty=drop cover-letter.patch >err &&
      +	test_path_is_missing .git/rebase-apply &&
      +	test_cmp_rev empty-commit^ HEAD
      +'
      +
      +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
     -+	git am --empty-commit=asis empty-commit.patch &&
     ++	git am --empty=keep empty-commit.patch &&
      +	test_path_is_missing .git/rebase-apply &&
      +	{
      +		git show empty-commit --format="%B" &&
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +	git show HEAD --format="%B" >actual &&
      +	test_cmp actual expected &&
      +
     -+	git am --empty-commit=asis cover-letter.patch &&
     ++	git am --empty=keep cover-letter.patch &&
      +	test_path_is_missing .git/rebase-apply &&
      +	{
      +		echo "*** SUBJECT HERE ***" &&

-- 
gitgitgadget

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

* [PATCH v5 1/2] doc: git-format-patch: describe the option --always
  2021-11-17  9:33       ` [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
@ 2021-11-17  9:33         ` Aleen via GitGitGadget
  2021-11-17  9:33         ` [PATCH v5 2/2] am: support --empty option to handle empty patches Aleen via GitGitGadget
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-17  9:33 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v5 2/2] am: support --empty option to handle empty patches
  2021-11-17  9:33       ` [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2021-11-17  9:33         ` [PATCH v5 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-17  9:33         ` Aleen via GitGitGadget
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-17  9:33 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++
 builtin/am.c             | 48 +++++++++++++++++++++++---
 t/t4150-am.sh            | 73 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..665bc89ca9f 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty-commit=(die|drop|keep)::
+	The command usually errors out when seeing an input e-mail
+	message that lacks a patch. When this option is set to
+	'drop', skip such an e-mail message without outputting error.
+	When this option is set to 'keep', create an empty commit,
+	recording the contents of the e-mail message as its log.
+	'die' is specified by default.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..1a3ed87b445 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip without outputting errors */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,23 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty_commit(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	if (unset || !strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1272,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1792,6 +1811,20 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			if (state->empty_type == DROP_EMPTY_COMMIT)
+				goto next;
+			else if (state->empty_type == KEEP_EMPTY_COMMIT) {
+				if (run_applypatch_msg_hook(state))
+					exit(1);
+				else
+					goto commit;
+			} else if (state->empty_type == DIE_EMPTY_COMMIT) {
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
 
@@ -1827,6 +1860,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2391,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		{ OPTION_CALLBACK, 0, "empty", &state.empty_type,
+		  "(die|drop|keep)",
+		  N_("specify how to handle empty patches"),
+		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..3119556884d 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,13 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	git format-patch --stdout empty-commit^ >empty.patch &&
+	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1159,70 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'still output error with --empty when meeting empty files' '
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty=die cover-letter.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply
+'
+
+test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
+	git am --empty=drop empty-commit.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+
+	git am --empty=drop cover-letter.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	test_cmp_rev empty-commit^ HEAD
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	{
+		git show empty-commit --format="%B" &&
+		echo "--" &&
+		git version | sed -e "s/^git version //" &&
+		echo
+	} >expected &&
+	git show HEAD --format="%B" >actual &&
+	test_cmp actual expected &&
+
+	git am --empty=keep cover-letter.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	{
+		echo "*** SUBJECT HERE ***" &&
+		echo &&
+		echo "*** BLURB HERE ***" &&
+		echo &&
+		echo "A U Thor (1):" &&
+		printf "  " &&
+		git show empty-commit --format="%B" &&
+		echo "--" &&
+		git version | sed -e "s/^git version //" &&
+		echo
+	} >expected &&
+	git show HEAD --format="%B" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-17  9:33       ` [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
  2021-11-17  9:33         ` [PATCH v5 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-17  9:33         ` [PATCH v5 2/2] am: support --empty option to handle empty patches Aleen via GitGitGadget
@ 2021-11-18 10:50         ` Aleen via GitGitGadget
  2021-11-18 10:50           ` [PATCH v6 1/3] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                             ` (4 more replies)
  2 siblings, 5 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-18 10:50 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Aleen (3):
  doc: git-format-patch: describe the option --always
  am: support --empty option to handle empty patches
  am: throw an error when passing --empty option without value

 Documentation/git-am.txt           |  9 ++++
 Documentation/git-format-patch.txt |  6 ++-
 builtin/am.c                       | 49 ++++++++++++++++--
 t/t4150-am.sh                      | 79 ++++++++++++++++++++++++++++++
 4 files changed, 137 insertions(+), 6 deletions(-)


base-commit: b550198c73edd4cc058832dcf74b41aeec2adba2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v5:

 1:  9f1b3dd6d0b = 1:  9f1b3dd6d0b doc: git-format-patch: describe the option --always
 2:  96d8573dc80 = 2:  96d8573dc80 am: support --empty option to handle empty patches
 -:  ----------- > 3:  e907a2b2faa am: throw an error when passing --empty option without value

-- 
gitgitgadget

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

* [PATCH v6 1/3] doc: git-format-patch: describe the option --always
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
@ 2021-11-18 10:50           ` Aleen via GitGitGadget
  2021-11-18 10:50           ` [PATCH v6 2/3] am: support --empty option to handle empty patches Aleen via GitGitGadget
                             ` (3 subsequent siblings)
  4 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-18 10:50 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
  2021-11-18 10:50           ` [PATCH v6 1/3] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-18 10:50           ` Aleen via GitGitGadget
  2021-11-19  0:56             ` Junio C Hamano
  2021-11-19 10:33             ` Bagas Sanjaya
  2021-11-18 10:50           ` [PATCH v6 3/3] am: throw an error when passing --empty option without value Aleen via GitGitGadget
                             ` (2 subsequent siblings)
  4 siblings, 2 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-18 10:50 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++
 builtin/am.c             | 48 +++++++++++++++++++++++---
 t/t4150-am.sh            | 73 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..665bc89ca9f 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty-commit=(die|drop|keep)::
+	The command usually errors out when seeing an input e-mail
+	message that lacks a patch. When this option is set to
+	'drop', skip such an e-mail message without outputting error.
+	When this option is set to 'keep', create an empty commit,
+	recording the contents of the e-mail message as its log.
+	'die' is specified by default.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..1a3ed87b445 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip without outputting errors */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,23 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty_commit(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	if (unset || !strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1272,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1792,6 +1811,20 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			if (state->empty_type == DROP_EMPTY_COMMIT)
+				goto next;
+			else if (state->empty_type == KEEP_EMPTY_COMMIT) {
+				if (run_applypatch_msg_hook(state))
+					exit(1);
+				else
+					goto commit;
+			} else if (state->empty_type == DIE_EMPTY_COMMIT) {
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
 
@@ -1827,6 +1860,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2391,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		{ OPTION_CALLBACK, 0, "empty", &state.empty_type,
+		  "(die|drop|keep)",
+		  N_("specify how to handle empty patches"),
+		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..3119556884d 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,13 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	git format-patch --stdout empty-commit^ >empty.patch &&
+	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1159,70 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'still output error with --empty when meeting empty files' '
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply &&
+
+	test_must_fail git am --empty=die cover-letter.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	test_i18ngrep "Patch is empty." err &&
+	rm -fr .git/rebase-apply
+'
+
+test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
+	git am --empty=drop empty-commit.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+
+	git am --empty=drop cover-letter.patch >err &&
+	test_path_is_missing .git/rebase-apply &&
+	test_cmp_rev empty-commit^ HEAD
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	{
+		git show empty-commit --format="%B" &&
+		echo "--" &&
+		git version | sed -e "s/^git version //" &&
+		echo
+	} >expected &&
+	git show HEAD --format="%B" >actual &&
+	test_cmp actual expected &&
+
+	git am --empty=keep cover-letter.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	{
+		echo "*** SUBJECT HERE ***" &&
+		echo &&
+		echo "*** BLURB HERE ***" &&
+		echo &&
+		echo "A U Thor (1):" &&
+		printf "  " &&
+		git show empty-commit --format="%B" &&
+		echo "--" &&
+		git version | sed -e "s/^git version //" &&
+		echo
+	} >expected &&
+	git show HEAD --format="%B" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v6 3/3] am: throw an error when passing --empty option without value
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
  2021-11-18 10:50           ` [PATCH v6 1/3] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-18 10:50           ` [PATCH v6 2/3] am: support --empty option to handle empty patches Aleen via GitGitGadget
@ 2021-11-18 10:50           ` Aleen via GitGitGadget
  2021-11-19  1:13             ` Junio C Hamano
  2021-11-18 23:47           ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches Junio C Hamano
  2021-11-19  5:04           ` [PATCH v7 0/2] " Aleen via GitGitGadget
  4 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-18 10:50 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 builtin/am.c  | 13 +++++++------
 t/t4150-am.sh |  8 +++++++-
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 1a3ed87b445..5d487b5256b 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -185,12 +185,14 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
-static int am_option_parse_empty_commit(const struct option *opt,
+static int am_option_parse_empty(const struct option *opt,
 				     const char *arg, int unset)
 {
 	int *opt_value = opt->value;
 
-	if (unset || !strcmp(arg, "die"))
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
 		*opt_value = DIE_EMPTY_COMMIT;
 	else if (!strcmp(arg, "drop"))
 		*opt_value = DROP_EMPTY_COMMIT;
@@ -2391,10 +2393,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-		{ OPTION_CALLBACK, 0, "empty", &state.empty_type,
-		  "(die|drop|keep)",
-		  N_("specify how to handle empty patches"),
-		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 3119556884d..c32d21e80da 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1165,8 +1165,14 @@ test_expect_success 'still output error with --empty when meeting empty files' '
 	test_cmp expected actual
 '
 
-test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
+test_expect_success 'invalid when passing no value for the --empty option' '
 	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
 	test_must_fail git am empty-commit.patch >err &&
 	test_path_is_dir .git/rebase-apply &&
 	test_i18ngrep "Patch is empty." err &&
-- 
gitgitgadget

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
                             ` (2 preceding siblings ...)
  2021-11-18 10:50           ` [PATCH v6 3/3] am: throw an error when passing --empty option without value Aleen via GitGitGadget
@ 2021-11-18 23:47           ` Junio C Hamano
  2021-11-19  1:45             ` Aleen 徐沛文
  2021-11-19  4:16             ` Aleen 徐沛文
  2021-11-19  5:04           ` [PATCH v7 0/2] " Aleen via GitGitGadget
  4 siblings, 2 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-18 23:47 UTC (permalink / raw)
  To: Aleen via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood,
	Aleen 徐沛文,
	Aleen


As per https://git-scm.com/docs/SubmittingPatches#sign-off:

   Please don’t hide your real name.

I suspect your real name is not Aleen, but something with x and w in
it.



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

* Re: [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-18 10:50           ` [PATCH v6 2/3] am: support --empty option to handle empty patches Aleen via GitGitGadget
@ 2021-11-19  0:56             ` Junio C Hamano
  2021-11-19 10:33             ` Bagas Sanjaya
  1 sibling, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19  0:56 UTC (permalink / raw)
  To: Aleen via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood,
	Aleen 徐沛文,
	Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Aleen <aleen42@vip.qq.com>

Here is a place to explain to readers what it exactly means to
"support --empty option to handle empty patches".  You know what you
meant, I happen to know what you meant because I haven't forgotten
yet, but we are writing for developers in the future who need to fix
bugs in 6 months down the road to decide if this commit is involved
in the bug in "git am" they are hunting.

> Signed-off-by: Aleen <aleen42@vip.qq.com>
> ---
>  Documentation/git-am.txt |  9 +++++
>  builtin/am.c             | 48 +++++++++++++++++++++++---
>  t/t4150-am.sh            | 73 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 125 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
> index 0a4a984dfde..665bc89ca9f 100644
> --- a/Documentation/git-am.txt
> +++ b/Documentation/git-am.txt
> @@ -16,6 +16,7 @@ SYNOPSIS
>  	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
>  	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
>  	 [--quoted-cr=<action>]
> +	 [--empty=(die|drop|keep)]
>  	 [(<mbox> | <Maildir>)...]
>  'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
>  
> @@ -63,6 +64,14 @@ OPTIONS
>  --quoted-cr=<action>::
>  	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
>  
> +--empty-commit=(die|drop|keep)::
> +	The command usually errors out when seeing an input e-mail
> +	message that lacks a patch. When this option is set to
> +	'drop', skip such an e-mail message without outputting error.

"without outputting error" -> "instead".

The first sentence used "to error" as a verb to mean "stop the
operation in the middle with a message that diagnoses what is
wrong".  Saying "outputting error" to use the same word as a noun is
not a good idea, especially the use of verb "to output", as it makes
the word "error" look as if it is only the "message" part, and it
becomes unclear if the "stopping due to an error" is also skipped.

I actually think it makes sense to give a notice/info message when
"drop" skips an empty patch, unless "--quiet" is given.

> +	When this option is set to 'keep', create an empty commit,
> +	recording the contents of the e-mail message as its log.
> +	'die' is specified by default.

"is specified by default" sounds a bit strange.  Perhaps lose the
last sentence, and update the first line like so:

    By default, or when the option is set to 'die', the command
    errors out on an input e-mail message that lacks a patch.

> diff --git a/builtin/am.c b/builtin/am.c
> index 8677ea2348a..1a3ed87b445 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -87,6 +87,12 @@ enum show_patch_type {
>  	SHOW_PATCH_DIFF = 1,
>  };
>  
> +enum empty_action {
> +	DIE_EMPTY_COMMIT = 0,  /* output errors */
> +	DROP_EMPTY_COMMIT,     /* skip without outputting errors */

I think the user deserves "info:" (unless "--quiet").

> +	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
> +};
> +
>  struct am_state {
>  	/* state directory path */
>  	char *dir;
> @@ -118,6 +124,7 @@ struct am_state {
>  	int message_id;
>  	int scissors; /* enum scissors_type */
>  	int quoted_cr; /* enum quoted_cr_action */
> +	int empty_type; /* enum empty_action */
>  	struct strvec git_apply_opts;
>  	const char *resolvemsg;
>  	int committer_date_is_author_date;
> @@ -178,6 +185,23 @@ static int am_option_parse_quoted_cr(const struct option *opt,
>  	return 0;
>  }
>  
> +static int am_option_parse_empty_commit(const struct option *opt,
> +				     const char *arg, int unset)
> +{
> +	int *opt_value = opt->value;
> +
> +	if (unset || !strcmp(arg, "die"))
> +		*opt_value = DIE_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 --empty: %s"), arg);
> +
> +	return 0;
> +}

OK.

> @@ -1792,6 +1811,20 @@ static void am_run(struct am_state *state, int resume)
>  		if (state->interactive && do_interactive(state))
>  			goto next;
>  
> +		if (is_empty_or_missing_file(am_path(state, "patch"))) {
> +			if (state->empty_type == DROP_EMPTY_COMMIT)
> +				goto next;
> +			else if (state->empty_type == KEEP_EMPTY_COMMIT) {
> +				if (run_applypatch_msg_hook(state))
> +					exit(1);
> +				else
> +					goto commit;

It probably is easier to read without "else", i.e.

			else if (state->empty_type == KEEP_EMPTY_COMMIT) {
				if (run_applypatch_msg_hook(state))
					exit(1);
				goto commit;

Running hook here and outside this if() block a bit ugly.  Can't we
instead set a flag variable or something and let the existing call
to run_applypatch_msg_hook() used?

> +			} else if (state->empty_type == DIE_EMPTY_COMMIT) {
> +				printf_ln(_("Patch is empty."));
> +				die_user_resolve(state);
> +			}

Also, if developers ever add another possible value for the
state->empty_type member (by the way, "empty" in its name is good,
but "type" is iffy), it would be a bug if they do not consider what
should happen inside this "if (is_empty_or_missing_file())" block,
no?  In such a case, instead of if/else if/... cascade, consider
using switch().  Taking them together.
 
	while (state->cur <= state->last) {
        	const char *mail = am_path(state, msgnum(state));
		int apply_status;
		int to_keep;
		...
		to_keep = 0;
		if (is_empty_or_missing_file(am_path(state, "patch"))) {
			switch (state->empty_type) {
			case DROP_EMPTY_COMMIT:
                                say(state, stdout, _("Skipping empty patch '%.*s",
					linelen(state->msg0, state->msg);
				goto next;
				break;
			case KEEP_EMPTY_COMMIT:
				to_keep = 1;
				break;
			case DIE_EMPTY_COMMIT:
				pritnf_ln(_("Patch is empty."));
				die_user_resolve(state);
				break;
			}
		}

		if (run_applypatch_msg_hook(state))
			exit(1);
		if (to_keep)
			goto commit; /* empty: just record a message */

or something like that.

> diff --git a/t/t4150-am.sh b/t/t4150-am.sh
> index 2aaaa0d7ded..3119556884d 100755
> --- a/t/t4150-am.sh
> +++ b/t/t4150-am.sh
> @@ -196,6 +196,13 @@ test_expect_success setup '
>  
>  	git format-patch -M --stdout lorem^ >rename-add.patch &&
>  
> +	git checkout -b empty-commit &&
> +	git commit -m "empty commit" --allow-empty &&
> +
> +	git format-patch --stdout empty-commit^ >empty.patch &&

Unless this is a test for "format-patch" command to make sure that
the above command will not output a single byte, I would rather see
the above line written like this:

	: >empty.patch &&

> +	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&

This only creates a cover letter without anything, which sort of
looks like an empty commit.

> +	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&

And this gives another empty patch with only a message.

I would sort-of understand if there is

	git format-patch --stdout --always --cover-letter HEAD^ >both.patch

that has two e-mail messages in it, but what's the significance of
testing both cover-letter.patch and empty-commit.patch?

The receiving end would not care what's in the actual e-mail
message, as long as it lacks the patch part, no?  Or am I missing
some subtle reason why we want to test both?

> @@ -1152,4 +1159,70 @@ test_expect_success 'apply binary blob in partial clone' '
>  	git -C client am ../patch
>  '
>  
> +test_expect_success 'still output error with --empty when meeting empty files' '
> +	test_must_fail git am --empty=drop empty.patch 2>actual &&

Nobody will understand "still", unless they know it used to be
unconditionally a failure to feed an e-mail message that lacks a
patch.

test_expect_success 'An empty input file is error regardless of --empty option' '

or something like that?

> +	echo Patch format detection failed. >expected &&
> +	test_cmp expected actual
> +'
> +
> +test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
> +	git checkout empty-commit^ &&
> +	test_must_fail git am empty-commit.patch >err &&
> +	test_path_is_dir .git/rebase-apply &&
> +	test_i18ngrep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply &&

If any command before "rm" fails, it won't be cleaned up.

    test_expect_success 'a message without a patch is an error (default)' '
	test_when_finished "git am --abort || :" &&
	git checkout --detach empty-commit^ &&
	test_must_fail git am empty-commit.patch >err &&
	grep "Patch is empty" err
    '

or something like that.  The title says it tests the default case,
so do not test --empty=die case in the same test.  Use a separate
test to do the same for the case where an explicit "--empty=die"
is given.

As I said, I do not quite see the point of testing with both
empty-commit.patch and cover-letter.patch (unless the latter has an
empty commit and a cover letter, totally in two messages).

> +	test_must_fail git am --empty=die empty-commit.patch >err &&
> +	test_path_is_dir .git/rebase-apply &&
> +	test_i18ngrep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply &&
> +
> +	test_must_fail git am --empty=die cover-letter.patch >err &&
> +	test_path_is_dir .git/rebase-apply &&
> +	test_i18ngrep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply
> +'
> +
> +test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
> +	git am --empty=drop empty-commit.patch >err &&

The output 'err' is used by nobody; do not redirect it and just
leave the standard output alone.

> +	test_path_is_missing .git/rebase-apply &&

Don't test this condition when testing success or failure from
"am".  That is too much implementation detail.

> +	git rev-parse empty-commit^ >expected &&
> +	git rev-parse HEAD >actual &&
> +	test_cmp expected actual &&

OK, we make sure that HEAD does not move.

> +	git am --empty=drop cover-letter.patch >err &&
> +	test_path_is_missing .git/rebase-apply &&
> +	test_cmp_rev empty-commit^ HEAD

Lose this one?  As I said, I may be missing some subtle reason why
both need to be tested---and if it is the case, test it in a
separate test, not 2 of them in a single test like this.

> +'
> +
> +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
> +	git am --empty=keep empty-commit.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	{
> +		git show empty-commit --format="%B" &&
> +		echo "--" &&

Did this test pass?  Without a trailing whitespace after double-dash,
I do not think this shell snippet reproduces a patch e-mail correctly

In any case, don't do this.  Imagine a future developer may want to
improve the patch output by adding other things after the "-- "
signature line, in addition to the "git version".  Or perhaps the
"git version" trailer gets removed by default for privacy concerns.

Should such changes be prevented from being made, only because this
test expects exact output we have today?  No.

Verify what we _care_, e.g. "does the title of the resulting commit
reproduce the one-liner message given to '-m' when the empty commit
was made?" is a sensible question to ask.

> +		git version | sed -e "s/^git version //" &&
> +		echo

> +	} >expected &&
> +	git show HEAD --format="%B" >actual &&
> +	test_cmp actual expected &&
> +
> +	git am --empty=keep cover-letter.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	{
> +		echo "*** SUBJECT HERE ***" &&
> +		echo &&
> +		echo "*** BLURB HERE ***" &&
> +		echo &&
> +		echo "A U Thor (1):" &&
> +		printf "  " &&
> +		git show empty-commit --format="%B" &&
> +		echo "--" &&
> +		git version | sed -e "s/^git version //" &&
> +		echo

Don't do this.  Imagine a future developer may want to improve the
cover letter template.  Should that change be prevented from being
made, because this test expects exact output we have today?  No.

> +	} >expected &&
> +	git show HEAD --format="%B" >actual &&
> +	test_cmp actual expected
> +'
> +
>  test_done

Thanks.

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

* Re: [PATCH v6 3/3] am: throw an error when passing --empty option without value
  2021-11-18 10:50           ` [PATCH v6 3/3] am: throw an error when passing --empty option without value Aleen via GitGitGadget
@ 2021-11-19  1:13             ` Junio C Hamano
  2021-11-19  2:11               ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19  1:13 UTC (permalink / raw)
  To: Aleen via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood,
	Aleen 徐沛文,
	Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Aleen <aleen42@vip.qq.com>

Here is a place for you to explain why this change might be a good
idea to defend it.  If we applied 1/3 and 2/3 but not this one, what
happens?  "am --empty" (not "am --empty=<choice>") is accepted and
behaves just like "am --empty=die"?  Is that a bad thing?

If it is unconditonally a bad thing, then this patch does not stand
on its own.  It should be squashed into the previous step; otherwise
it would be "oops, we didn't think it through and failed to detect
an argument-less --error as an error in the previous step, and here
is a fix for that mistake".  We try not to deliberately commit a
mistake and then fix it to gain commit counts in this project (or
putting it differently, we try to pretend we are better than we
really are and did not make a mistake in the first place).

Or are you giving list participants and reviewers a choice between
"'--empty' is a synonym to '--empty=die'" and "'--empty' alone is an
error" because you cannot decide yourself?  If that is the case, you
really really need to make it clear in this place between "From:"
and "Signed-off-by:".  Why would we want to use this patch?  Why
would we be better without this patch?

I haven't thought things through, but my gut feeling is that the
semantics of the --empty=<choice> option is better _with_ this,
in other words, it makes more sense to forbid "--empty" alone.  So
if I were doing this series, I would probably have squashed this
into the previous one, making it a two-patch series.

Thanks.


> Signed-off-by: Aleen <aleen42@vip.qq.com>
> ---
>  builtin/am.c  | 13 +++++++------
>  t/t4150-am.sh |  8 +++++++-
>  2 files changed, 14 insertions(+), 7 deletions(-)


> diff --git a/builtin/am.c b/builtin/am.c
> index 1a3ed87b445..5d487b5256b 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -185,12 +185,14 @@ static int am_option_parse_quoted_cr(const struct option *opt,
>  	return 0;
>  }
>  
> -static int am_option_parse_empty_commit(const struct option *opt,
> +static int am_option_parse_empty(const struct option *opt,
>  				     const char *arg, int unset)
>  {
>  	int *opt_value = opt->value;
>  
> -	if (unset || !strcmp(arg, "die"))
> +	BUG_ON_OPT_NEG(unset);



> +	if (!strcmp(arg, "die"))
>  		*opt_value = DIE_EMPTY_COMMIT;
>  	else if (!strcmp(arg, "drop"))
>  		*opt_value = DROP_EMPTY_COMMIT;
> @@ -2391,10 +2393,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
>  		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
>  		  N_("GPG-sign commits"),
>  		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> -		{ OPTION_CALLBACK, 0, "empty", &state.empty_type,
> -		  "(die|drop|keep)",
> -		  N_("specify how to handle empty patches"),
> -		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
> +		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
> +		  N_("how to handle empty patches"),
> +		  PARSE_OPT_NONEG, am_option_parse_empty),
>  		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
>  			N_("(internal use for git-rebase)")),
>  		OPT_END()
> diff --git a/t/t4150-am.sh b/t/t4150-am.sh
> index 3119556884d..c32d21e80da 100755
> --- a/t/t4150-am.sh
> +++ b/t/t4150-am.sh
> @@ -1165,8 +1165,14 @@ test_expect_success 'still output error with --empty when meeting empty files' '
>  	test_cmp expected actual
>  '
>  
> -test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
> +test_expect_success 'invalid when passing no value for the --empty option' '
>  	git checkout empty-commit^ &&
> +	test_must_fail git am --empty empty-commit.patch 2>err &&
> +	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
> +	test_cmp expected err
> +'
> +
> +test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
>  	test_must_fail git am empty-commit.patch >err &&
>  	test_path_is_dir .git/rebase-apply &&
>  	test_i18ngrep "Patch is empty." err &&

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-18 23:47           ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches Junio C Hamano
@ 2021-11-19  1:45             ` Aleen 徐沛文
  2021-11-19  5:46               ` Junio C Hamano
  2021-11-19  4:16             ` Aleen 徐沛文
  1 sibling, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-19  1:45 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood, Aleen

> -----Original Messages-----From:"Junio C Hamano" <gitster@pobox.com>Sent Time:2021-11-19 07:47:11 (Friday)To:"Aleen via GitGitGadget" <gitgitgadget@gmail.com>Cc:git@vger.kernel.org, "René Scharfe" <l.s.r@web.de>, "Phillip Wood" <phillip.wood123@gmail.com>, "Aleen 徐沛文" <pwxu@coremail.cn>, Aleen <aleen42@vip.qq.com>Subject:Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
> 
> 
> As per https://git-scm.com/docs/SubmittingPatches#sign-off:
> 
>    Please don’t hide your real name.
> 
> I suspect your real name is not Aleen, but something with x and w in
> it.
>

I have used "Aleen" <aleen42@vip.qq.com> as my GitHub account to send this pull request, but the e-mail service disgusts me that it is slow to send emails to the domain "vger.kernel.org". So I decided to use another mail service to discuss, "Aleen 徐沛文" <pwxu@coremail.cn> as you saw.

In the open-source world, I usually signed off "Aleen" rather than "Aleen 徐沛文", and is that necessary to change?

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

* Re: [PATCH v6 3/3] am: throw an error when passing --empty option without value
  2021-11-19  1:13             ` Junio C Hamano
@ 2021-11-19  2:11               ` Aleen 徐沛文
  0 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-19  2:11 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood, Aleen

> Here is a place for you to explain why this change might be a good
> idea to defend it.  If we applied 1/3 and 2/3 but not this one, what
> happens?  "am --empty" (not "am --empty=<choice>") is accepted and
> behaves just like "am --empty=die"?  Is that a bad thing?
> 
> If it is unconditonally a bad thing, then this patch does not stand
> on its own.  It should be squashed into the previous step; otherwise
> it would be "oops, we didn't think it through and failed to detect
> an argument-less --error as an error in the previous step, and here
> is a fix for that mistake".  We try not to deliberately commit a
> mistake and then fix it to gain commit counts in this project (or
> putting it differently, we try to pretend we are better than we
> really are and did not make a mistake in the first place).
> 
> Or are you giving list participants and reviewers a choice between
> "'--empty' is a synonym to '--empty=die'" and "'--empty' alone is an
> error" because you cannot decide yourself?  If that is the case, you
> really really need to make it clear in this place between "From:"
> and "Signed-off-by:".  Why would we want to use this patch?  Why
> would we be better without this patch?
> 
> I haven't thought things through, but my gut feeling is that the
> semantics of the --empty=<choice> option is better _with_ this,
> in other words, it makes more sense to forbid "--empty" alone.  So
> if I were doing this series, I would probably have squashed this
> into the previous one, making it a two-patch series.
> 
> Thanks.

Hamano, I could not make this decision like what you said between "'--empty' is a synonym to '--empty=die'" and "'--empty' alone is an error". In my opinion, it is a little wired that '--empty' should not act as "died" semantically. Reversely, it may be better to support a synonym between '--quiet' and '--empty=skip'.

I will squash the commit into the previous one later and hope all participants and reviewers make a decision.

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-18 23:47           ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches Junio C Hamano
  2021-11-19  1:45             ` Aleen 徐沛文
@ 2021-11-19  4:16             ` Aleen 徐沛文
  1 sibling, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-19  4:16 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood, Aleen

> As per https://git-scm.com/docs/SubmittingPatches#sign-off:
> 
>    Please don’t hide your real name.
> 
> I suspect your real name is not Aleen, but something with x and w in
> it.

BTW, gitgitgadget has defined that developers should sign off the user name
of the GitHub account, and I can't change it at all.

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

* [PATCH v7 0/2] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
                             ` (3 preceding siblings ...)
  2021-11-18 23:47           ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches Junio C Hamano
@ 2021-11-19  5:04           ` Aleen via GitGitGadget
  2021-11-19  5:04             ` [PATCH v7 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                               ` (2 more replies)
  4 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-19  5:04 UTC (permalink / raw)
  To: git; +Cc: Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

cc: René Scharfe l.s.r@web.de cc: Phillip Wood phillip.wood123@gmail.com cc:
Aleen 徐沛文 pwxu@coremail.cn

Aleen (2):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches

 Documentation/git-am.txt           |  8 +++++
 Documentation/git-format-patch.txt |  6 +++-
 builtin/am.c                       | 55 +++++++++++++++++++++++++++---
 po/bg.po                           |  2 +-
 po/ca.po                           |  2 +-
 po/de.po                           |  2 +-
 po/el.po                           |  2 +-
 po/es.po                           |  2 +-
 po/fr.po                           |  2 +-
 po/git.pot                         |  2 +-
 po/id.po                           |  2 +-
 po/it.po                           |  2 +-
 po/ko.po                           |  2 +-
 po/pl.po                           |  2 +-
 po/pt_PT.po                        |  8 ++---
 po/ru.po                           |  4 +--
 po/sv.po                           |  2 +-
 po/tr.po                           |  2 +-
 po/vi.po                           |  2 +-
 po/zh_CN.po                        |  9 +++--
 po/zh_TW.po                        |  7 +++-
 t/t4150-am.sh                      | 49 ++++++++++++++++++++++++++
 22 files changed, 145 insertions(+), 29 deletions(-)


base-commit: ca35af825273b98fc8dc11527488952f5db8eb80
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v7
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v6:

 1:  9f1b3dd6d0b ! 1:  d612bc49d57 doc: git-format-patch: describe the option --always
     @@ Metadata
       ## Commit message ##
          doc: git-format-patch: describe the option --always
      
     +    This commit has described how to use '--always' option in the command
     +    'git-format-patch' to include patches for commits that emit no changes.
     +
          Signed-off-by: Aleen <aleen42@vip.qq.com>
      
       ## Documentation/git-format-patch.txt ##
 2:  96d8573dc80 ! 2:  9e60e77c041 am: support --empty option to handle empty patches
     @@ Metadata
      Author: Aleen <aleen42@vip.qq.com>
      
       ## Commit message ##
     -    am: support --empty option to handle empty patches
     +    am: support --empty=<option> to handle empty patches
     +
     +    Since that the command 'git-format-patch' can include patches of
     +    commits that emit no changes, the 'git-am' command should also
     +    support an option, named as '--empty', to specify how to handle
     +    those empty patches. In this commit, we have implemented three
     +    valid options ('die', 'drop' and 'keep').
      
          Signed-off-by: Aleen <aleen42@vip.qq.com>
      
     @@ Documentation/git-am.txt: OPTIONS
       	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
       
      +--empty-commit=(die|drop|keep)::
     -+	The command usually errors out when seeing an input e-mail
     -+	message that lacks a patch. When this option is set to
     -+	'drop', skip such an e-mail message without outputting error.
     ++	By default, or when the option is set to 'die', the command
     ++	errors out on an input e-mail message that lacks a patch. 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.
     -+	'die' is specified by default.
      +
       -m::
       --message-id::
     @@ builtin/am.c: enum show_patch_type {
       
      +enum empty_action {
      +	DIE_EMPTY_COMMIT = 0,  /* output errors */
     -+	DROP_EMPTY_COMMIT,     /* skip without outputting errors */
     ++	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
      +	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
      +};
      +
     @@ builtin/am.c: static int am_option_parse_quoted_cr(const struct option *opt,
       	return 0;
       }
       
     -+static int am_option_parse_empty_commit(const struct option *opt,
     ++static int am_option_parse_empty(const struct option *opt,
      +				     const char *arg, int unset)
      +{
      +	int *opt_value = opt->value;
      +
     -+	if (unset || !strcmp(arg, "die"))
     ++	BUG_ON_OPT_NEG(unset);
     ++
     ++	if (!strcmp(arg, "die"))
      +		*opt_value = DIE_EMPTY_COMMIT;
      +	else if (!strcmp(arg, "drop"))
      +		*opt_value = DROP_EMPTY_COMMIT;
     @@ builtin/am.c: static int parse_mail(struct am_state *state, const char *mail)
       	strbuf_addstr(&msg, "\n\n");
       	strbuf_addbuf(&msg, &mi.log_message);
       	strbuf_stripspace(&msg, 0);
     +@@ builtin/am.c: static void am_run(struct am_state *state, int resume)
     + 	while (state->cur <= state->last) {
     + 		const char *mail = am_path(state, msgnum(state));
     + 		int apply_status;
     ++		int to_keep;
     + 
     + 		reset_ident_date();
     + 
      @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
       		if (state->interactive && do_interactive(state))
       			goto next;
       
     ++		to_keep = 0;
      +		if (is_empty_or_missing_file(am_path(state, "patch"))) {
     -+			if (state->empty_type == DROP_EMPTY_COMMIT)
     ++			switch (state->empty_type) {
     ++			case DROP_EMPTY_COMMIT:
     ++				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
      +				goto next;
     -+			else if (state->empty_type == KEEP_EMPTY_COMMIT) {
     -+				if (run_applypatch_msg_hook(state))
     -+					exit(1);
     -+				else
     -+					goto commit;
     -+			} else if (state->empty_type == DIE_EMPTY_COMMIT) {
     ++				break;
     ++			case KEEP_EMPTY_COMMIT:
     ++				to_keep = 1;
     ++				break;
     ++			case DIE_EMPTY_COMMIT:
      +				printf_ln(_("Patch is empty."));
      +				die_user_resolve(state);
     ++				break;
      +			}
      +		}
      +
       		if (run_applypatch_msg_hook(state))
       			exit(1);
     ++		if (to_keep)
     ++			goto commit;
     + 
     + 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
       
      @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
       			die_user_resolve(state);
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
       		  N_("GPG-sign commits"),
       		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
     -+		{ OPTION_CALLBACK, 0, "empty", &state.empty_type,
     -+		  "(die|drop|keep)",
     -+		  N_("specify how to handle empty patches"),
     -+		  PARSE_OPT_OPTARG, am_option_parse_empty_commit },
     ++		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
     ++		  N_("how to handle empty patches"),
     ++		  PARSE_OPT_NONEG, am_option_parse_empty),
       		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
       			N_("(internal use for git-rebase)")),
       		OPT_END()
      
     + ## po/bg.po ##
     +@@ po/bg.po: msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr ""
     + "Индексът не е чист: кръпките не може да бъдат приложени (замърсени са: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Прилагане: %.*s"
     +
     + ## po/ca.po ##
     +@@ po/ca.po: msgstr "no s'ha pogut escriure el fitxer d'índex"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Índex brut: no es poden aplicar pedaços (bruts: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "S'està aplicant: %.*s"
     +
     + ## po/de.po ##
     +@@ po/de.po: msgstr "Konnte Index-Datei nicht schreiben."
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Geänderter Index: kann Patches nicht anwenden (geändert: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Wende an: %.*s"
     +
     + ## po/el.po ##
     +@@ po/el.po: msgstr "Να γίνει εφαρμογή; [y]es/[n]o/[e]dit/[v]iew patch/[a]ccep
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr ""
     + 
     +-#: builtin/am.c:1749 builtin/am.c:1817
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr ""
     +
     + ## po/es.po ##
     +@@ po/es.po: msgstr "no es posible escribir el archivo índice"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Índice sucio: no se puede aplicar parches (sucio: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Aplicando: %.*s"
     +
     + ## po/fr.po ##
     +@@ po/fr.po: msgstr "impossible d'écrire le fichier d'index"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Index sale : impossible d'appliquer des patchs (sales : %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Application de  %.*s"
     +
     + ## po/git.pot ##
     +@@ po/git.pot: msgstr ""
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr ""
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr ""
     +
     + ## po/id.po ##
     +@@ po/id.po: msgstr "tidak dapat menulis berkas indeks"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Indeks kotor: tidak dapat menerapkan tambalan (kotor: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Menerapkan: %.*s"
     +
     + ## po/it.po ##
     +@@ po/it.po: msgstr "impossibile scrivere il file indice"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Indice sporco: impossibile applicare le patch (elemento sporco: %s)"
     + 
     +-#: builtin/am.c:1761 builtin/am.c:1829
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Applicazione in corso: %.*s"
     +
     + ## po/ko.po ##
     +@@ po/ko.po: msgstr "적용? 예[y]/아니오[n]/편집[e]/패치 보기[v]/모두 적용[a]:
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "변경된 인덱스: 패치를 적용할 수 없습니다 (dirty: %s)"
     + 
     +-#: builtin/am.c:1808 builtin/am.c:1879
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "적용하는 중: %.*s"
     +
     + ## po/pl.po ##
     +@@ po/pl.po: msgstr "nie można zapisać pliku indeksu"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Brudny indeks: nie można zastosować łatek (brudny: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Stosowanie: %.*s"
     +
     + ## po/pt_PT.po ##
     +@@
     + #   bisect                           |  bisetar
     + #   blame                            |  blame
     + #   blob object                      |  objeto-blob
     +-#   branch                           |  ramo 
     ++#   branch                           |  ramo
     + #   bug                              |  bug
     + #   bundle                           |  conjunto
     + #   bypass                           |  desviar
     +@@
     + #   loose refs                       |  refs soltas
     + #   mark                             |  marca
     + #   master                           |  master
     +-#   merge                            |  junção 
     ++#   merge                            |  junção
     + #   mergetag                         |  etiqueta-junção
     + #   object                           |  objeto
     + #   object database                  |  base dados de objeto
     +@@
     + #   token                            |  token
     + #   unset                            |  desdefinir
     + #   untrack                          |  desmonitorizar
     +-#   
     ++#
     + #
     + msgid ""
     + msgstr ""
     +@@ po/pt_PT.po: msgstr "incapaz escrever ficheiro de index"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Index sujo: incapaz aplicar patches (sujo: %s)"
     + 
     +-#: builtin/am.c:1797 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "A aplicar: %.*s"
     +
     + ## po/ru.po ##
     +@@
     + # SOME DESCRIPTIVE TITLE.
     + # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
     + # This file is distributed under the same license as the PACKAGE package.
     +-# 
     ++#
     + # Translators:
     + # Alexander Golubev <fatzer2@gmail.com>, 2020
     + # Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2021
     +@@ po/ru.po: msgstr "не удалось записать индекс"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Индекс изменён: нельзя применять патчи (изменено: %s)"
     + 
     +-#: builtin/am.c:1748 builtin/am.c:1816
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Применение: %.*s"
     +
     + ## po/sv.po ##
     +@@ po/sv.po: msgstr "kan inte skriva indexfil"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Smutsigt index: kan inte tillämpa patchar (smutsiga: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Tillämpar: %.*s"
     +
     + ## po/tr.po ##
     +@@ po/tr.po: msgstr "indeks dosyası yazılamıyor"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Kirli indeks: Yamalar uygulanamıyor (kirli: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Uygulanıyor: %.*s"
     +
     + ## po/vi.po ##
     +@@ po/vi.po: msgstr "không thể ghi tập tin lưu mục lục"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "Bảng mục lục bẩn: không thể áp dụng các miếng vá (bẩn: %s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "Áp dụng: %.*s"
     +
     + ## po/zh_CN.po ##
     +@@ po/zh_CN.po: msgstr "坏的索引文件 sha1 签名"
     + msgid "index uses %.4s extension, which we do not understand"
     + msgstr "索引使用不被支持的 %.4s 扩展"
     + 
     +-# 	
     ++#
     + #: read-cache.c:1834
     + #, c-format
     + msgid "ignoring %.4s extension"
     +@@ po/zh_CN.po: msgstr "无法写入索引文件"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "脏索引:不能应用补丁(脏文件:%s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "应用:%.*s"
     + 
     ++#: builtin/am.c:1818
     ++#, c-format
     ++msgid "Skipping: %.*s"
     ++msgstr "跳过:%.*s"
     ++
     + #: builtin/am.c:1815
     + msgid "No changes -- Patch already applied."
     + msgstr "没有变更 —— 补丁已经应用过。"
     +
     + ## po/zh_TW.po ##
     +@@ po/zh_TW.po: msgstr "無法寫入索引檔案"
     + msgid "Dirty index: cannot apply patches (dirty: %s)"
     + msgstr "髒索引:不能套用修補檔(髒檔案:%s)"
     + 
     +-#: builtin/am.c:1798 builtin/am.c:1865
     ++#: builtin/am.c:1834 builtin/am.c:1902
     + #, c-format
     + msgid "Applying: %.*s"
     + msgstr "套用:%.*s"
     + 
     ++#: builtin/am.c:1818
     ++#, c-format
     ++msgid "Skipping: %.*s"
     ++msgstr "忽略:%.*s"
     ++
     + #: builtin/am.c:1815
     + msgid "No changes -- Patch already applied."
     + msgstr "沒有變更——修補檔已經套用過。"
     +
       ## t/t4150-am.sh ##
      @@ t/t4150-am.sh: test_expect_success setup '
       
     @@ t/t4150-am.sh: test_expect_success setup '
      +	git checkout -b empty-commit &&
      +	git commit -m "empty commit" --allow-empty &&
      +
     -+	git format-patch --stdout empty-commit^ >empty.patch &&
     -+	git format-patch --stdout --cover-letter empty-commit^ >cover-letter.patch &&
     ++	: >empty.patch &&
      +	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
      +
       	# reset time
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
       	git -C client am ../patch
       '
       
     -+test_expect_success 'still output error with --empty when meeting empty files' '
     ++test_expect_success 'An empty input file is error regardless of --empty option' '
      +	test_must_fail git am --empty=drop empty.patch 2>actual &&
      +	echo Patch format detection failed. >expected &&
      +	test_cmp expected actual
      +'
      +
     -+test_expect_success 'error when meeting e-mail message that lacks a patch by default' '
     ++test_expect_success 'invalid when passing the --empty option alone' '
      +	git checkout empty-commit^ &&
     ++	test_must_fail git am --empty empty-commit.patch 2>err &&
     ++	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
     ++	test_cmp expected err
     ++'
     ++
     ++test_expect_success 'a message without a patch is an error (default)' '
     ++	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am empty-commit.patch >err &&
     -+	test_path_is_dir .git/rebase-apply &&
     -+	test_i18ngrep "Patch is empty." err &&
     -+	rm -fr .git/rebase-apply &&
     ++	grep "Patch is empty" err &&
     ++	rm -fr .git/rebase-apply
     ++'
      +
     ++test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
     ++	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am --empty=die empty-commit.patch >err &&
     -+	test_path_is_dir .git/rebase-apply &&
     -+	test_i18ngrep "Patch is empty." err &&
     -+	rm -fr .git/rebase-apply &&
     -+
     -+	test_must_fail git am --empty=die cover-letter.patch >err &&
     -+	test_path_is_dir .git/rebase-apply &&
     -+	test_i18ngrep "Patch is empty." err &&
     ++	grep "Patch is empty." err &&
      +	rm -fr .git/rebase-apply
      +'
      +
     -+test_expect_success 'skip without error when meeting e-mail message that lacks a patch' '
     -+	git am --empty=drop empty-commit.patch >err &&
     -+	test_path_is_missing .git/rebase-apply &&
     ++test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
     ++	git am --empty=drop empty-commit.patch >output &&
      +	git rev-parse empty-commit^ >expected &&
      +	git rev-parse HEAD >actual &&
      +	test_cmp expected actual &&
     -+
     -+	git am --empty=drop cover-letter.patch >err &&
     -+	test_path_is_missing .git/rebase-apply &&
     -+	test_cmp_rev empty-commit^ HEAD
     ++	grep "Skipping: empty commit" output
      +'
      +
      +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
      +	git am --empty=keep empty-commit.patch &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	{
     -+		git show empty-commit --format="%B" &&
     -+		echo "--" &&
     -+		git version | sed -e "s/^git version //" &&
     -+		echo
     -+	} >expected &&
     -+	git show HEAD --format="%B" >actual &&
     -+	test_cmp actual expected &&
     -+
     -+	git am --empty=keep cover-letter.patch &&
     -+	test_path_is_missing .git/rebase-apply &&
     -+	{
     -+		echo "*** SUBJECT HERE ***" &&
     -+		echo &&
     -+		echo "*** BLURB HERE ***" &&
     -+		echo &&
     -+		echo "A U Thor (1):" &&
     -+		printf "  " &&
     -+		git show empty-commit --format="%B" &&
     -+		echo "--" &&
     -+		git version | sed -e "s/^git version //" &&
     -+		echo
     -+	} >expected &&
     -+	git show HEAD --format="%B" >actual &&
     ++	git show empty-commit --format="%s" >expected &&
     ++	git show HEAD --format="%s" >actual &&
      +	test_cmp actual expected
      +'
      +
 3:  e907a2b2faa < -:  ----------- am: throw an error when passing --empty option without value

-- 
gitgitgadget

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

* [PATCH v7 1/2] doc: git-format-patch: describe the option --always
  2021-11-19  5:04           ` [PATCH v7 0/2] " Aleen via GitGitGadget
@ 2021-11-19  5:04             ` Aleen via GitGitGadget
  2021-11-19  5:04             ` [PATCH v7 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
  2021-11-22  6:46             ` [PATCH v8 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-19  5:04 UTC (permalink / raw)
  To: git; +Cc: Aleen 徐沛文, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v7 2/2] am: support --empty=<option> to handle empty patches
  2021-11-19  5:04           ` [PATCH v7 0/2] " Aleen via GitGitGadget
  2021-11-19  5:04             ` [PATCH v7 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-19  5:04             ` Aleen via GitGitGadget
  2021-11-19 22:50               ` Junio C Hamano
  2021-11-19 23:07               ` Junio C Hamano
  2021-11-22  6:46             ` [PATCH v8 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 2 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-19  5:04 UTC (permalink / raw)
  To: git; +Cc: Aleen 徐沛文, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: Aleen <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 po/bg.po                 |  2 +-
 po/ca.po                 |  2 +-
 po/de.po                 |  2 +-
 po/el.po                 |  2 +-
 po/es.po                 |  2 +-
 po/fr.po                 |  2 +-
 po/git.pot               |  2 +-
 po/id.po                 |  2 +-
 po/it.po                 |  2 +-
 po/ko.po                 |  2 +-
 po/pl.po                 |  2 +-
 po/pt_PT.po              |  8 +++---
 po/ru.po                 |  4 +--
 po/sv.po                 |  2 +-
 po/tr.po                 |  2 +-
 po/vi.po                 |  2 +-
 po/zh_CN.po              |  9 +++++--
 po/zh_TW.po              |  7 ++++-
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 21 files changed, 140 insertions(+), 28 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..1e6a64bf2ed 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty-commit=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/po/bg.po b/po/bg.po
index 51d7c7242f2..17d05bc9d4e 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -11194,7 +11194,7 @@ msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr ""
 "Индексът не е чист: кръпките не може да бъдат приложени (замърсени са: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Прилагане: %.*s"
diff --git a/po/ca.po b/po/ca.po
index 556b028cd8b..4035055c28e 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -10931,7 +10931,7 @@ msgstr "no s'ha pogut escriure el fitxer d'índex"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Índex brut: no es poden aplicar pedaços (bruts: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "S'està aplicant: %.*s"
diff --git a/po/de.po b/po/de.po
index f00c21d896f..fbae81fc59f 100644
--- a/po/de.po
+++ b/po/de.po
@@ -10999,7 +10999,7 @@ msgstr "Konnte Index-Datei nicht schreiben."
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Geänderter Index: kann Patches nicht anwenden (geändert: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Wende an: %.*s"
diff --git a/po/el.po b/po/el.po
index 703f46d0c7c..5a3d4d8a61e 100644
--- a/po/el.po
+++ b/po/el.po
@@ -7801,7 +7801,7 @@ msgstr "Να γίνει εφαρμογή; [y]es/[n]o/[e]dit/[v]iew patch/[a]ccep
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr ""
 
-#: builtin/am.c:1749 builtin/am.c:1817
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr ""
diff --git a/po/es.po b/po/es.po
index ced2eb6d3d5..774a333cc64 100644
--- a/po/es.po
+++ b/po/es.po
@@ -10840,7 +10840,7 @@ msgstr "no es posible escribir el archivo índice"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Índice sucio: no se puede aplicar parches (sucio: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Aplicando: %.*s"
diff --git a/po/fr.po b/po/fr.po
index e950f87add0..30a8a1b3fd1 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -11013,7 +11013,7 @@ msgstr "impossible d'écrire le fichier d'index"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Index sale : impossible d'appliquer des patchs (sales : %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Application de  %.*s"
diff --git a/po/git.pot b/po/git.pot
index 78fea37065b..1367e12f3d0 100644
--- a/po/git.pot
+++ b/po/git.pot
@@ -9985,7 +9985,7 @@ msgstr ""
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr ""
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr ""
diff --git a/po/id.po b/po/id.po
index c1afc2a32f3..0821da2efee 100644
--- a/po/id.po
+++ b/po/id.po
@@ -10306,7 +10306,7 @@ msgstr "tidak dapat menulis berkas indeks"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Indeks kotor: tidak dapat menerapkan tambalan (kotor: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Menerapkan: %.*s"
diff --git a/po/it.po b/po/it.po
index c31560834d8..475a60dc1d1 100644
--- a/po/it.po
+++ b/po/it.po
@@ -10229,7 +10229,7 @@ msgstr "impossibile scrivere il file indice"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Indice sporco: impossibile applicare le patch (elemento sporco: %s)"
 
-#: builtin/am.c:1761 builtin/am.c:1829
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Applicazione in corso: %.*s"
diff --git a/po/ko.po b/po/ko.po
index 5d190e52380..5a9d7eb0305 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -5681,7 +5681,7 @@ msgstr "적용? 예[y]/아니오[n]/편집[e]/패치 보기[v]/모두 적용[a]:
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "변경된 인덱스: 패치를 적용할 수 없습니다 (dirty: %s)"
 
-#: builtin/am.c:1808 builtin/am.c:1879
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "적용하는 중: %.*s"
diff --git a/po/pl.po b/po/pl.po
index 0ec127e14cc..f9768843fb0 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -10811,7 +10811,7 @@ msgstr "nie można zapisać pliku indeksu"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Brudny indeks: nie można zastosować łatek (brudny: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Stosowanie: %.*s"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 70e5c6d9c16..e89894ca5dc 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -24,7 +24,7 @@
 #   bisect                           |  bisetar
 #   blame                            |  blame
 #   blob object                      |  objeto-blob
-#   branch                           |  ramo 
+#   branch                           |  ramo
 #   bug                              |  bug
 #   bundle                           |  conjunto
 #   bypass                           |  desviar
@@ -74,7 +74,7 @@
 #   loose refs                       |  refs soltas
 #   mark                             |  marca
 #   master                           |  master
-#   merge                            |  junção 
+#   merge                            |  junção
 #   mergetag                         |  etiqueta-junção
 #   object                           |  objeto
 #   object database                  |  base dados de objeto
@@ -211,7 +211,7 @@
 #   token                            |  token
 #   unset                            |  desdefinir
 #   untrack                          |  desmonitorizar
-#   
+#
 #
 msgid ""
 msgstr ""
@@ -10485,7 +10485,7 @@ msgstr "incapaz escrever ficheiro de index"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Index sujo: incapaz aplicar patches (sujo: %s)"
 
-#: builtin/am.c:1797 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "A aplicar: %.*s"
diff --git a/po/ru.po b/po/ru.po
index 993d106f1f9..69acd080d12 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -1,7 +1,7 @@
 # SOME DESCRIPTIVE TITLE.
 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
 # This file is distributed under the same license as the PACKAGE package.
-# 
+#
 # Translators:
 # Alexander Golubev <fatzer2@gmail.com>, 2020
 # Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2021
@@ -9398,7 +9398,7 @@ msgstr "не удалось записать индекс"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Индекс изменён: нельзя применять патчи (изменено: %s)"
 
-#: builtin/am.c:1748 builtin/am.c:1816
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Применение: %.*s"
diff --git a/po/sv.po b/po/sv.po
index e3b17b6a0ef..41f6be1c540 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -10746,7 +10746,7 @@ msgstr "kan inte skriva indexfil"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Smutsigt index: kan inte tillämpa patchar (smutsiga: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Tillämpar: %.*s"
diff --git a/po/tr.po b/po/tr.po
index 06870900c4e..6c4d7ee8337 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -10810,7 +10810,7 @@ msgstr "indeks dosyası yazılamıyor"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Kirli indeks: Yamalar uygulanamıyor (kirli: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Uygulanıyor: %.*s"
diff --git a/po/vi.po b/po/vi.po
index be197ab594f..5ed8c0239fe 100644
--- a/po/vi.po
+++ b/po/vi.po
@@ -10807,7 +10807,7 @@ msgstr "không thể ghi tập tin lưu mục lục"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "Bảng mục lục bẩn: không thể áp dụng các miếng vá (bẩn: %s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "Áp dụng: %.*s"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 60ffa45f7a0..13c4ae14a29 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -6463,7 +6463,7 @@ msgstr "坏的索引文件 sha1 签名"
 msgid "index uses %.4s extension, which we do not understand"
 msgstr "索引使用不被支持的 %.4s 扩展"
 
-# 	
+#
 #: read-cache.c:1834
 #, c-format
 msgid "ignoring %.4s extension"
@@ -10673,11 +10673,16 @@ msgstr "无法写入索引文件"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "脏索引:不能应用补丁(脏文件:%s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "应用:%.*s"
 
+#: builtin/am.c:1818
+#, c-format
+msgid "Skipping: %.*s"
+msgstr "跳过:%.*s"
+
 #: builtin/am.c:1815
 msgid "No changes -- Patch already applied."
 msgstr "没有变更 —— 补丁已经应用过。"
diff --git a/po/zh_TW.po b/po/zh_TW.po
index f1df8b60a91..d6259cc1cb6 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -10538,11 +10538,16 @@ msgstr "無法寫入索引檔案"
 msgid "Dirty index: cannot apply patches (dirty: %s)"
 msgstr "髒索引:不能套用修補檔(髒檔案:%s)"
 
-#: builtin/am.c:1798 builtin/am.c:1865
+#: builtin/am.c:1834 builtin/am.c:1902
 #, c-format
 msgid "Applying: %.*s"
 msgstr "套用:%.*s"
 
+#: builtin/am.c:1818
+#, c-format
+msgid "Skipping: %.*s"
+msgstr "忽略:%.*s"
+
 #: builtin/am.c:1815
 msgid "No changes -- Patch already applied."
 msgstr "沒有變更——修補檔已經套用過。"
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..16e966b2b73 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'An empty input file is error regardless of --empty option' '
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo Patch format detection failed. >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err &&
+	rm -fr .git/rebase-apply
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	rm -fr .git/rebase-apply
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19  1:45             ` Aleen 徐沛文
@ 2021-11-19  5:46               ` Junio C Hamano
  2021-11-19  7:23                 ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19  5:46 UTC (permalink / raw)
  To: Aleen 徐沛文
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood, Aleen

Aleen 徐沛文 <pwxu@coremail.cn> writes:

>> As per https://git-scm.com/docs/SubmittingPatches#sign-off:
>> 
>>    Please don’t hide your real name.
>> 
>> I suspect your real name is not Aleen, but something with x and w in
>> it.
>>
>
> I have used "Aleen" <aleen42@vip.qq.com> as my GitHub account to
> send this pull request, but the e-mail service disgusts me that it
> is slow to send emails to the domain "vger.kernel.org". So I
> decided to use another mail service to discuss, "Aleen 徐沛文"
> <pwxu@coremail.cn> as you saw.
>
> In the open-source world, I usually signed off "Aleen" rather than
> "Aleen 徐沛文", and is that necessary to change?

Yes.

As the URL you were referred to explains, the sign-off procedure is
to keep track of provenance of the code, which is a more "legal"
formal requirement than just "I use this pseudonym everywhere".
When a big company comes to us, claiming that "this code is our
intellectual property stolen from us" and pointing at code added by
a patch from you, we do not want to see us in the position to have
to say "eh, somebody who uses psuedonym X signed DCO, but we do not
even know their real name".

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19  5:46               ` Junio C Hamano
@ 2021-11-19  7:23                 ` Aleen 徐沛文
  2021-11-19  7:25                   ` =?gb18030?B?QWxlZW4=?=
  2021-11-19 16:54                   ` Junio C Hamano
  0 siblings, 2 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-19  7:23 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood, Aleen

> Yes.
> 
> As the URL you were referred to explains, the sign-off procedure is
> to keep track of provenance of the code, which is a more "legal"
> formal requirement than just "I use this pseudonym everywhere".
> When a big company comes to us, claiming that "this code is our
> intellectual property stolen from us" and pointing at code added by
> a patch from you, we do not want to see us in the position to have
> to say "eh, somebody who uses psuedonym X signed DCO, but we do not
> even know their real name".

I know it, and as I said before that gitgitgadget need PR creators to
sign off user name of GitHub account, according to the DCO check. I can
confirmed that "Aleen" and "Aleen 徐沛文" are both the real name of mine,
the committer. I can use the account aleen42@vip.qq.com to confirm this.

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

* RE: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19  7:23                 ` Aleen 徐沛文
@ 2021-11-19  7:25                   ` =?gb18030?B?QWxlZW4=?=
  2021-11-19 16:54                   ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: =?gb18030?B?QWxlZW4=?= @ 2021-11-19  7:25 UTC (permalink / raw)
  To: =?gb18030?B?QWxlZW4g0OzF5s7E?=, =?gb18030?B?SnVuaW8gQyBIYW1hbm8=?=
  Cc: =?gb18030?B?QWxlZW4gdmlhIEdpdEdpdEdhZGdldA==?=,
	=?gb18030?B?Z2l0?=, =?gb18030?B?UmVuqKYgU2NoYXJmZQ==?=,
	=?gb18030?B?UGhpbGxpcCBXb29k?=

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb18030", Size: 308 bytes --]

> I know it, and as I said before that gitgitgadget need PR creators to
> sign off user name of GitHub account, according to the DCO check. I can
> confirmed that "Aleen" and "Aleen ÐìÅæÎÄ" are both the real name of mine,
> the committer. I can use the account aleen42@vip.qq.com to confirm this.

Confirmed.

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

* Re: [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-18 10:50           ` [PATCH v6 2/3] am: support --empty option to handle empty patches Aleen via GitGitGadget
  2021-11-19  0:56             ` Junio C Hamano
@ 2021-11-19 10:33             ` Bagas Sanjaya
  2021-11-19 12:10               ` Ævar Arnfjörð Bjarmason
  2021-11-19 16:46               ` Junio C Hamano
  1 sibling, 2 replies; 129+ messages in thread
From: Bagas Sanjaya @ 2021-11-19 10:33 UTC (permalink / raw)
  To: Aleen via GitGitGadget, git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文, Aleen

On 18/11/21 17.50, Aleen via GitGitGadget wrote:
> +test_expect_success 'still output error with --empty when meeting empty files' '
> +	test_must_fail git am --empty=drop empty.patch 2>actual &&
> +	echo Patch format detection failed. >expected &&
> +	test_cmp expected actual
> +'

Why isn't the echo string quoted?

-- 
An old man doll... just what I always wanted! - Clara

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

* Re: [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-19 10:33             ` Bagas Sanjaya
@ 2021-11-19 12:10               ` Ævar Arnfjörð Bjarmason
  2021-11-19 12:20                 ` Eric Sunshine
  2021-11-19 16:46               ` Junio C Hamano
  1 sibling, 1 reply; 129+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-11-19 12:10 UTC (permalink / raw)
  To: Bagas Sanjaya
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood,
	Aleen 徐沛文,
	Aleen


On Fri, Nov 19 2021, Bagas Sanjaya wrote:

> On 18/11/21 17.50, Aleen via GitGitGadget wrote:
>> +test_expect_success 'still output error with --empty when meeting empty files' '
>> +	test_must_fail git am --empty=drop empty.patch 2>actual &&
>> +	echo Patch format detection failed. >expected &&
>> +	test_cmp expected actual
>> +'
>
> Why isn't the echo string quoted?

There's no need to quote the arguments given to echo in cases like
these.

It's not the same, i.e. it'll get N arguments on argv and not on, but it
makes it easier to spot things that do need quoting, rather than
over-quoting everything.

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

* Re: [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-19 12:10               ` Ævar Arnfjörð Bjarmason
@ 2021-11-19 12:20                 ` Eric Sunshine
  2021-11-19 16:49                   ` Junio C Hamano
  0 siblings, 1 reply; 129+ messages in thread
From: Eric Sunshine @ 2021-11-19 12:20 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Bagas Sanjaya, Aleen via GitGitGadget, Git List,
	René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen

On Fri, Nov 19, 2021 at 7:12 AM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> On Fri, Nov 19 2021, Bagas Sanjaya wrote:
> > On 18/11/21 17.50, Aleen via GitGitGadget wrote:
> >> +test_expect_success 'still output error with --empty when meeting empty files' '
> >> +    test_must_fail git am --empty=drop empty.patch 2>actual &&
> >> +    echo Patch format detection failed. >expected &&
> >> +    test_cmp expected actual
> >> +'
> >
> > Why isn't the echo string quoted?
>
> There's no need to quote the arguments given to echo in cases like
> these.
>
> It's not the same, i.e. it'll get N arguments on argv and not on, but it
> makes it easier to spot things that do need quoting, rather than
> over-quoting everything.

I recently expressed an opposing opinion in [1], stating effectively
that omitting the quotes like this is "an accident waiting to happen":

    ... the lack of quotes ... in the `echo ... >expect` statement
    gives me a moment's pause since it relies upon the fact that
    `echo` will insert exactly one space between the ... arguments
    (which happens to match the single space in the [command's output]
    ). For clarity and that extra bit of robustness, I'd probably have
    used a single double-quoted string argument with `echo`.

But, it's a fairly minor objection.

[1]: https://lore.kernel.org/git/CAPig+cQVSUg1aqry_hMydJ=Uo=-VhOog6TUTpG=0on0LUcw8Dg@mail.gmail.com/

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

* Re: [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-19 10:33             ` Bagas Sanjaya
  2021-11-19 12:10               ` Ævar Arnfjörð Bjarmason
@ 2021-11-19 16:46               ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19 16:46 UTC (permalink / raw)
  To: Bagas Sanjaya
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood,
	Aleen 徐沛文,
	Aleen

Bagas Sanjaya <bagasdotme@gmail.com> writes:

> On 18/11/21 17.50, Aleen via GitGitGadget wrote:
>> +test_expect_success 'still output error with --empty when meeting empty files' '
>> +	test_must_fail git am --empty=drop empty.patch 2>actual &&
>> +	echo Patch format detection failed. >expected &&
>> +	test_cmp expected actual
>> +'
>
> Why isn't the echo string quoted?

You may doing so without realizing, but such a "why does the code
not do X" question, especially without stating why you think the
code should do X, gets irritating quickly.

    I think it is better to quote the arguments to echo to make it a
    single string, because ...

would have helped the author of the patch.

If this were one-off, I would have let it go, but I thought it may
be worth commenting, as you seem to often ask this kind of
"questions".

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

* Re: [PATCH v6 2/3] am: support --empty option to handle empty patches
  2021-11-19 12:20                 ` Eric Sunshine
@ 2021-11-19 16:49                   ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19 16:49 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Ævar Arnfjörð Bjarmason, Bagas Sanjaya,
	Aleen via GitGitGadget, Git List, René Scharfe,
	Phillip Wood, Aleen 徐沛文,
	Aleen

Eric Sunshine <sunshine@sunshineco.com> writes:

> I recently expressed an opposing opinion in [1], stating effectively
> that omitting the quotes like this is "an accident waiting to happen":
>
>     ... the lack of quotes ... in the `echo ... >expect` statement
>     gives me a moment's pause since it relies upon the fact that
>     `echo` will insert exactly one space between the ... arguments
>     (which happens to match the single space in the [command's output]
>     ). For clarity and that extra bit of robustness, I'd probably have
>     used a single double-quoted string argument with `echo`.
>
> But, it's a fairly minor objection.

It indeed is minor enough that a patch to turn an existing

	echo A B C >expect &&
	test_cmp expect actual

into

	echo "A B C" >expect &&
	test_cmp expect actual

is not welcome.  But it still is worth pointing out and correcting
in a patch to add new code, I would think.  It all depends on what
we care about, and the use of test_cmp means we do care about exact
shape of the string, including the inter-word spacing.

Thanks.

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19  7:23                 ` Aleen 徐沛文
  2021-11-19  7:25                   ` =?gb18030?B?QWxlZW4=?=
@ 2021-11-19 16:54                   ` Junio C Hamano
  2021-11-19 17:14                     ` Aleen 徐沛文
  2021-11-22 11:57                     ` Johannes Schindelin
  1 sibling, 2 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19 16:54 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Aleen 徐沛文,
	git, René Scharfe, Phillip Wood, Aleen

Aleen 徐沛文 <pwxu@coremail.cn> writes:

>> Yes.
>> 
>> As the URL you were referred to explains, the sign-off procedure is
>> to keep track of provenance of the code, which is a more "legal"
>> formal requirement than just "I use this pseudonym everywhere".
>> When a big company comes to us, claiming that "this code is our
>> intellectual property stolen from us" and pointing at code added by
>> a patch from you, we do not want to see us in the position to have
>> to say "eh, somebody who uses psuedonym X signed DCO, but we do not
>> even know their real name".
>
> I know it, and as I said before that gitgitgadget need PR creators to
> sign off user name of GitHub account, according to the DCO check. I can
> confirmed that "Aleen" and "Aleen 徐沛文" are both the real name of mine,
> the committer. I can use the account aleen42@vip.qq.com to confirm this.

Can somebody from GGG land help this user?  I _think_ the easiest
workaround (other than not using GGG and sending e-mail in the old
fashioned way) is to commit and sign-off under the real name, and
push under whatever GitHub username to throw a GGG pull request,
which GGG should be able to take, as I have seen users forward other
authors commits just fine.



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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19 16:54                   ` Junio C Hamano
@ 2021-11-19 17:14                     ` Aleen 徐沛文
  2021-11-19 19:25                       ` Junio C Hamano
  2021-11-22 11:57                     ` Johannes Schindelin
  1 sibling, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-19 17:14 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, René Scharfe, Phillip Wood, Aleen

 
> Can somebody from GGG land help this user?  I _think_ the easiest
> workaround (other than not using GGG and sending e-mail in the old
> fashioned way) is to commit and sign-off under the real name, and
> push under whatever GitHub username to throw a GGG pull request,
> which GGG should be able to take, as I have seen users forward other
> authors commits just fine.

When it comes to GGG, I just want to know whether this is the only
way to contribute to Git? I think we can directly use GitHub to
run the reviewing procedure, rather than sending emails in this old
fashioned way, since that Git code has been maintained in GitHub.
E-mails does well in playing the role as a notification way. They
helps nothing in reviewing code, especially when we need to point out
problems or suggestions referred to certain code snippets.

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19 17:14                     ` Aleen 徐沛文
@ 2021-11-19 19:25                       ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19 19:25 UTC (permalink / raw)
  To: Aleen 徐沛文
  Cc: Johannes Schindelin, git, René Scharfe, Phillip Wood, Aleen

Aleen 徐沛文 <pwxu@coremail.cn> writes:

>> Can somebody from GGG land help this user?  I _think_ the easiest
>> workaround (other than not using GGG and sending e-mail in the old
>> fashioned way) is to commit and sign-off under the real name, and
>> push under whatever GitHub username to throw a GGG pull request,
>> which GGG should be able to take, as I have seen users forward other
>> authors commits just fine.
>
> When it comes to GGG, I just want to know whether this is the only
> way to contribute to Git? I think we can directly use GitHub to
> run the reviewing procedure, rather than sending emails in this old
> fashioned way, since that Git code has been maintained in GitHub.

No, we do not develop or maintain at GitHub at all.  The repository
at GitHub is one of the several publishing point and development is
done here.

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

* Re: [PATCH v7 2/2] am: support --empty=<option> to handle empty patches
  2021-11-19  5:04             ` [PATCH v7 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
@ 2021-11-19 22:50               ` Junio C Hamano
  2021-11-19 23:07               ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19 22:50 UTC (permalink / raw)
  To: Aleen via GitGitGadget; +Cc: git, Aleen 徐沛文, Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Aleen <aleen42@vip.qq.com>
>
> Since that the command 'git-format-patch' can include patches of
> commits that emit no changes, the 'git-am' command should also
> support an option, named as '--empty', to specify how to handle
> those empty patches. In this commit, we have implemented three
> valid options ('die', 'drop' and 'keep').
>
> Signed-off-by: Aleen <aleen42@vip.qq.com>
> ---
>  Documentation/git-am.txt |  8 ++++++
>  builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
>  t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++

>  po/bg.po                 |  2 +-
>  po/ca.po                 |  2 +-
>  po/de.po                 |  2 +-
>  po/el.po                 |  2 +-
>  po/es.po                 |  2 +-
>  po/fr.po                 |  2 +-
>  po/git.pot               |  2 +-
>  po/id.po                 |  2 +-
>  po/it.po                 |  2 +-
>  po/ko.po                 |  2 +-
>  po/pl.po                 |  2 +-
>  po/pt_PT.po              |  8 +++---
>  po/ru.po                 |  4 +--
>  po/sv.po                 |  2 +-
>  po/tr.po                 |  2 +-
>  po/vi.po                 |  2 +-
>  po/zh_CN.po              |  9 +++++--
>  po/zh_TW.po              |  7 ++++-

Please do not touch these po/ files.  They will be updated when i18n
coordinator decides to update the translation.


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

* Re: [PATCH v7 2/2] am: support --empty=<option> to handle empty patches
  2021-11-19  5:04             ` [PATCH v7 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
  2021-11-19 22:50               ` Junio C Hamano
@ 2021-11-19 23:07               ` Junio C Hamano
  1 sibling, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-19 23:07 UTC (permalink / raw)
  To: Aleen via GitGitGadget; +Cc: git, Aleen 徐沛文, Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +test_expect_success 'An empty input file is error regardless of --empty option' '

Titles of all the other tests seem to begin with lowercase, so "An
empty" -> "an empty", probably.

> +	test_must_fail git am --empty=drop empty.patch 2>actual &&
> +	echo Patch format detection failed. >expected &&

Quote for exactness, like the next test does.

    echo "Patch format detection failed." >expected

> +	test_cmp expected actual
> +'
> +
> +test_expect_success 'invalid when passing the --empty option alone' '
> +	git checkout empty-commit^ &&
> +	test_must_fail git am --empty empty-commit.patch 2>err &&
> +	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
> +	test_cmp expected err
> +'

This mode of failure of "am" may not leave ".git/rebase-apply"
behind right now, or it may leave one in the future.  We do not want
to worry about left-over cruft to interfere with the next test, so
cleaning after ourselves with test_when_finished would be a good
idea, i.e.

    test_expect_success 'invalid when passing the --empty option alone' '
            test_when_finished "git am --abort || :" &&
            git checkout empty-commit^ &&
            test_must_fail git am --empty empty-commit.patch 2>err &&
            echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
            test_cmp expected err
    '

> +test_expect_success 'a message without a patch is an error (default)' '
> +	test_when_finished "git am --abort || :" &&
> +	test_must_fail git am empty-commit.patch >err &&
> +	grep "Patch is empty" err &&
> +	rm -fr .git/rebase-apply
> +'

And the point of test_when_finished is to run the clean-up even when
other steps in the test fails.  For example, "test_must_fail git am"
may fail to fail for any reason.  Or "grep" after it may fail.
Because the pieces in a single test is strung together with &&, any
such failure means the control would NOT reach "rm -fr".  

Since we have test_when_finished that cleans up after ourselves even
in such a case, the last "rm -fr" step is unnecessary.

> +test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
> +	test_when_finished "git am --abort || :" &&
> +	test_must_fail git am --empty=die empty-commit.patch >err &&
> +	grep "Patch is empty." err &&
> +	rm -fr .git/rebase-apply
> +'

Likewise.

Other than that (and po/ that should not be part of this patch),
things are looking good.

Thanks.

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

* [PATCH v8 0/2] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19  5:04           ` [PATCH v7 0/2] " Aleen via GitGitGadget
  2021-11-19  5:04             ` [PATCH v7 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-19  5:04             ` [PATCH v7 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
@ 2021-11-22  6:46             ` Aleen via GitGitGadget
  2021-11-22  6:46               ` [PATCH v8 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                                 ` (2 more replies)
  2 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  6:46 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. update code according to the seen branch
 2. fix wrong document of git-am

----------------------------------------------------------------------------

Aleen (2):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches

 Documentation/git-am.txt           |  8 +++++
 Documentation/git-format-patch.txt |  6 +++-
 builtin/am.c                       | 55 +++++++++++++++++++++++++++---
 t/t4150-am.sh                      | 49 ++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 6 deletions(-)


base-commit: ca35af825273b98fc8dc11527488952f5db8eb80
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v8
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v8
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v7:

 1:  d612bc49d57 ! 1:  5d98a088e14 doc: git-format-patch: describe the option --always
     @@ Commit message
          This commit has described how to use '--always' option in the command
          'git-format-patch' to include patches for commits that emit no changes.
      
     -    Signed-off-by: Aleen <aleen42@vip.qq.com>
     +    Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
      
       ## Documentation/git-format-patch.txt ##
      @@ Documentation/git-format-patch.txt: SYNOPSIS
 2:  9e60e77c041 ! 2:  3ff18e16a7a am: support --empty=<option> to handle empty patches
     @@ Commit message
          those empty patches. In this commit, we have implemented three
          valid options ('die', 'drop' and 'keep').
      
     -    Signed-off-by: Aleen <aleen42@vip.qq.com>
     +    Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
      
       ## Documentation/git-am.txt ##
      @@ Documentation/git-am.txt: SYNOPSIS
     @@ Documentation/git-am.txt: OPTIONS
       --quoted-cr=<action>::
       	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
       
     -+--empty-commit=(die|drop|keep)::
     ++--empty=(die|drop|keep)::
      +	By default, or when the option is set to 'die', the command
      +	errors out on an input e-mail message that lacks a patch. When
      +	this option is set to 'drop', skip such an e-mail message instead.
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       			N_("(internal use for git-rebase)")),
       		OPT_END()
      
     - ## po/bg.po ##
     -@@ po/bg.po: msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr ""
     - "Индексът не е чист: кръпките не може да бъдат приложени (замърсени са: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Прилагане: %.*s"
     -
     - ## po/ca.po ##
     -@@ po/ca.po: msgstr "no s'ha pogut escriure el fitxer d'índex"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Índex brut: no es poden aplicar pedaços (bruts: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "S'està aplicant: %.*s"
     -
     - ## po/de.po ##
     -@@ po/de.po: msgstr "Konnte Index-Datei nicht schreiben."
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Geänderter Index: kann Patches nicht anwenden (geändert: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Wende an: %.*s"
     -
     - ## po/el.po ##
     -@@ po/el.po: msgstr "Να γίνει εφαρμογή; [y]es/[n]o/[e]dit/[v]iew patch/[a]ccep
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr ""
     - 
     --#: builtin/am.c:1749 builtin/am.c:1817
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr ""
     -
     - ## po/es.po ##
     -@@ po/es.po: msgstr "no es posible escribir el archivo índice"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Índice sucio: no se puede aplicar parches (sucio: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Aplicando: %.*s"
     -
     - ## po/fr.po ##
     -@@ po/fr.po: msgstr "impossible d'écrire le fichier d'index"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Index sale : impossible d'appliquer des patchs (sales : %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Application de  %.*s"
     -
     - ## po/git.pot ##
     -@@ po/git.pot: msgstr ""
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr ""
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr ""
     -
     - ## po/id.po ##
     -@@ po/id.po: msgstr "tidak dapat menulis berkas indeks"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Indeks kotor: tidak dapat menerapkan tambalan (kotor: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Menerapkan: %.*s"
     -
     - ## po/it.po ##
     -@@ po/it.po: msgstr "impossibile scrivere il file indice"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Indice sporco: impossibile applicare le patch (elemento sporco: %s)"
     - 
     --#: builtin/am.c:1761 builtin/am.c:1829
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Applicazione in corso: %.*s"
     -
     - ## po/ko.po ##
     -@@ po/ko.po: msgstr "적용? 예[y]/아니오[n]/편집[e]/패치 보기[v]/모두 적용[a]:
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "변경된 인덱스: 패치를 적용할 수 없습니다 (dirty: %s)"
     - 
     --#: builtin/am.c:1808 builtin/am.c:1879
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "적용하는 중: %.*s"
     -
     - ## po/pl.po ##
     -@@ po/pl.po: msgstr "nie można zapisać pliku indeksu"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Brudny indeks: nie można zastosować łatek (brudny: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Stosowanie: %.*s"
     -
     - ## po/pt_PT.po ##
     -@@
     - #   bisect                           |  bisetar
     - #   blame                            |  blame
     - #   blob object                      |  objeto-blob
     --#   branch                           |  ramo 
     -+#   branch                           |  ramo
     - #   bug                              |  bug
     - #   bundle                           |  conjunto
     - #   bypass                           |  desviar
     -@@
     - #   loose refs                       |  refs soltas
     - #   mark                             |  marca
     - #   master                           |  master
     --#   merge                            |  junção 
     -+#   merge                            |  junção
     - #   mergetag                         |  etiqueta-junção
     - #   object                           |  objeto
     - #   object database                  |  base dados de objeto
     -@@
     - #   token                            |  token
     - #   unset                            |  desdefinir
     - #   untrack                          |  desmonitorizar
     --#   
     -+#
     - #
     - msgid ""
     - msgstr ""
     -@@ po/pt_PT.po: msgstr "incapaz escrever ficheiro de index"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Index sujo: incapaz aplicar patches (sujo: %s)"
     - 
     --#: builtin/am.c:1797 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "A aplicar: %.*s"
     -
     - ## po/ru.po ##
     -@@
     - # SOME DESCRIPTIVE TITLE.
     - # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
     - # This file is distributed under the same license as the PACKAGE package.
     --# 
     -+#
     - # Translators:
     - # Alexander Golubev <fatzer2@gmail.com>, 2020
     - # Dimitriy Ryazantcev <DJm00n@mail.ru>, 2014-2021
     -@@ po/ru.po: msgstr "не удалось записать индекс"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Индекс изменён: нельзя применять патчи (изменено: %s)"
     - 
     --#: builtin/am.c:1748 builtin/am.c:1816
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Применение: %.*s"
     -
     - ## po/sv.po ##
     -@@ po/sv.po: msgstr "kan inte skriva indexfil"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Smutsigt index: kan inte tillämpa patchar (smutsiga: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Tillämpar: %.*s"
     -
     - ## po/tr.po ##
     -@@ po/tr.po: msgstr "indeks dosyası yazılamıyor"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Kirli indeks: Yamalar uygulanamıyor (kirli: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Uygulanıyor: %.*s"
     -
     - ## po/vi.po ##
     -@@ po/vi.po: msgstr "không thể ghi tập tin lưu mục lục"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "Bảng mục lục bẩn: không thể áp dụng các miếng vá (bẩn: %s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "Áp dụng: %.*s"
     -
     - ## po/zh_CN.po ##
     -@@ po/zh_CN.po: msgstr "坏的索引文件 sha1 签名"
     - msgid "index uses %.4s extension, which we do not understand"
     - msgstr "索引使用不被支持的 %.4s 扩展"
     - 
     --# 	
     -+#
     - #: read-cache.c:1834
     - #, c-format
     - msgid "ignoring %.4s extension"
     -@@ po/zh_CN.po: msgstr "无法写入索引文件"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "脏索引:不能应用补丁(脏文件:%s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "应用:%.*s"
     - 
     -+#: builtin/am.c:1818
     -+#, c-format
     -+msgid "Skipping: %.*s"
     -+msgstr "跳过:%.*s"
     -+
     - #: builtin/am.c:1815
     - msgid "No changes -- Patch already applied."
     - msgstr "没有变更 —— 补丁已经应用过。"
     -
     - ## po/zh_TW.po ##
     -@@ po/zh_TW.po: msgstr "無法寫入索引檔案"
     - msgid "Dirty index: cannot apply patches (dirty: %s)"
     - msgstr "髒索引:不能套用修補檔(髒檔案:%s)"
     - 
     --#: builtin/am.c:1798 builtin/am.c:1865
     -+#: builtin/am.c:1834 builtin/am.c:1902
     - #, c-format
     - msgid "Applying: %.*s"
     - msgstr "套用:%.*s"
     - 
     -+#: builtin/am.c:1818
     -+#, c-format
     -+msgid "Skipping: %.*s"
     -+msgstr "忽略:%.*s"
     -+
     - #: builtin/am.c:1815
     - msgid "No changes -- Patch already applied."
     - msgstr "沒有變更——修補檔已經套用過。"
     -
       ## t/t4150-am.sh ##
      @@ t/t4150-am.sh: test_expect_success setup '
       
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
       	git -C client am ../patch
       '
       
     -+test_expect_success 'An empty input file is error regardless of --empty option' '
     ++test_expect_success 'an empty input file is error regardless of --empty option' '
     ++	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am --empty=drop empty.patch 2>actual &&
     -+	echo Patch format detection failed. >expected &&
     ++	echo "Patch format detection failed." >expected &&
      +	test_cmp expected actual
      +'
      +
      +test_expect_success 'invalid when passing the --empty option alone' '
     ++	test_when_finished "git am --abort || :" &&
      +	git checkout empty-commit^ &&
      +	test_must_fail git am --empty empty-commit.patch 2>err &&
      +	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +test_expect_success 'a message without a patch is an error (default)' '
      +	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am empty-commit.patch >err &&
     -+	grep "Patch is empty" err &&
     -+	rm -fr .git/rebase-apply
     ++	grep "Patch is empty" err
      +'
      +
      +test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
      +	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am --empty=die empty-commit.patch >err &&
     -+	grep "Patch is empty." err &&
     -+	rm -fr .git/rebase-apply
     ++	grep "Patch is empty." err
      +'
      +
      +test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '

-- 
gitgitgadget

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

* [PATCH v8 1/2] doc: git-format-patch: describe the option --always
  2021-11-22  6:46             ` [PATCH v8 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
@ 2021-11-22  6:46               ` Aleen via GitGitGadget
  2021-11-22  6:46               ` [PATCH v8 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
  2021-11-22  7:02               ` [PATCH v9 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  6:46 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v8 2/2] am: support --empty=<option> to handle empty patches
  2021-11-22  6:46             ` [PATCH v8 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-22  6:46               ` [PATCH v8 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-22  6:46               ` Aleen via GitGitGadget
  2021-11-22  7:06                 ` Junio C Hamano
  2021-11-22  7:02               ` [PATCH v9 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  6:46 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..ba17063f621 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* [PATCH v9 0/2] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-22  6:46             ` [PATCH v8 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-22  6:46               ` [PATCH v8 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-22  6:46               ` [PATCH v8 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
@ 2021-11-22  7:02               ` Aleen via GitGitGadget
  2021-11-22  7:02                 ` [PATCH v9 1/2] doc: git-format-patch: describe the option --always Aleen 徐沛文 via GitGitGadget
                                   ` (2 more replies)
  2 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  7:02 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

Aleen 徐沛文 (2):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches

 Documentation/git-am.txt           |  8 +++++
 Documentation/git-format-patch.txt |  6 +++-
 builtin/am.c                       | 55 +++++++++++++++++++++++++++---
 t/t4150-am.sh                      | 49 ++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 6 deletions(-)


base-commit: ca35af825273b98fc8dc11527488952f5db8eb80
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v9
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v9
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v8:

 1:  5d98a088e14 ! 1:  3b41ca3dec7 doc: git-format-patch: describe the option --always
     @@
       ## Metadata ##
     -Author: Aleen <aleen42@vip.qq.com>
     +Author: Aleen 徐沛文 <aleen42@vip.qq.com>
      
       ## Commit message ##
          doc: git-format-patch: describe the option --always
 2:  3ff18e16a7a ! 2:  d2ec18b36af am: support --empty=<option> to handle empty patches
     @@
       ## Metadata ##
     -Author: Aleen <aleen42@vip.qq.com>
     +Author: Aleen 徐沛文 <aleen42@vip.qq.com>
      
       ## Commit message ##
          am: support --empty=<option> to handle empty patches

-- 
gitgitgadget

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

* [PATCH v9 1/2] doc: git-format-patch: describe the option --always
  2021-11-22  7:02               ` [PATCH v9 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
@ 2021-11-22  7:02                 ` Aleen 徐沛文 via GitGitGadget
  2021-11-22  7:02                 ` [PATCH v9 2/2] am: support --empty=<option> to handle empty patches Aleen 徐沛文 via GitGitGadget
  2021-11-22  7:51                 ` [PATCH v10 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 via GitGitGadget @ 2021-11-22  7:02 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen 徐沛文

From: =?UTF-8?q?Aleen=20=E5=BE=90=E6=B2=9B=E6=96=87?= <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v9 2/2] am: support --empty=<option> to handle empty patches
  2021-11-22  7:02               ` [PATCH v9 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-22  7:02                 ` [PATCH v9 1/2] doc: git-format-patch: describe the option --always Aleen 徐沛文 via GitGitGadget
@ 2021-11-22  7:02                 ` Aleen 徐沛文 via GitGitGadget
  2021-11-22  7:04                   ` Aleen 徐沛文
  2021-11-22  7:51                 ` [PATCH v10 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 via GitGitGadget @ 2021-11-22  7:02 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Aleen 徐沛文,
	Aleen, Aleen 徐沛文

From: =?UTF-8?q?Aleen=20=E5=BE=90=E6=B2=9B=E6=96=87?= <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..ba17063f621 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH v9 2/2] am: support --empty=<option> to handle empty patches
  2021-11-22  7:02                 ` [PATCH v9 2/2] am: support --empty=<option> to handle empty patches Aleen 徐沛文 via GitGitGadget
@ 2021-11-22  7:04                   ` Aleen 徐沛文
  0 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-22  7:04 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, René Scharfe, Phillip Wood, Aleen,
	Aleen 徐沛文 via GitGitGadget

> Can somebody from GGG land help this user?  I _think_ the easiest
> workaround (other than not using GGG and sending e-mail in the old
> fashioned way) is to commit and sign-off under the real name, and
> push under whatever GitHub username to throw a GGG pull request,
> which GGG should be able to take, as I have seen users forward other
> authors commits just fine.

Sorry for the wrong report, and I have checked that the DCO integrated by GGG has only checked
whether the committer name is the same as the signed name. So I have changed it, and re-submitted
it via version 9.

> `--empty-commit=(die|drop|keep)::`

Besides, I have also fixed a missed mistake, which also exists in the `seen` branch.

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

* Re: [PATCH v8 2/2] am: support --empty=<option> to handle empty patches
  2021-11-22  6:46               ` [PATCH v8 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
@ 2021-11-22  7:06                 ` Junio C Hamano
  2021-11-22  7:19                   ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Junio C Hamano @ 2021-11-22  7:06 UTC (permalink / raw)
  To: Aleen via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood,
	Aleen 徐沛文,
	Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Aleen <aleen42@vip.qq.com>
>
> Since that the command 'git-format-patch' can include patches of
> commits that emit no changes, the 'git-am' command should also
> support an option, named as '--empty', to specify how to handle
> those empty patches. In this commit, we have implemented three
> valid options ('die', 'drop' and 'keep').
>
> Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>

Perhaps this line should imitate what Hans Krentel did in
https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com/,
i.e. real name first, and then (nickname) in parentheses.

Also, the in-body "From:" line should match the sign off.

I corrected what has been queued in 'seen' manually when I applied
the previous round.

> +--empty=(die|drop|keep)::

This is the only change relative to what is queued (we had
"--empty-commit", which is remnant from an earlier iteration), and
it makes the documentation consistent with what the code does.

Good.

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

* Re: [PATCH v8 2/2] am: support --empty=<option> to handle empty patches
  2021-11-22  7:06                 ` Junio C Hamano
@ 2021-11-22  7:19                   ` Aleen 徐沛文
  0 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-22  7:19 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Aleen via GitGitGadget, git, René Scharfe, Phillip Wood, Aleen


> Perhaps this line should imitate what Hans Krentel did in
> https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com/,
> i.e. real name first, and then (nickname) in parentheses.
> 
> Also, the in-body "From:" line should match the sign off.
> 
> I corrected what has been queued in 'seen' manually when I applied
> the previous round.
> 
> > +--empty=(die|drop|keep)::
> 
> This is the only change relative to what is queued (we had
> "--empty-commit", which is remnant from an earlier iteration), and
> it makes the documentation consistent with what the code does.
> 
> Good.

It seems there is something wrong when submitting via "徐沛文 (Aleen) <aleen42@vip.qq.com>".

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

* [PATCH v10 0/2] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-22  7:02               ` [PATCH v9 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-22  7:02                 ` [PATCH v9 1/2] doc: git-format-patch: describe the option --always Aleen 徐沛文 via GitGitGadget
  2021-11-22  7:02                 ` [PATCH v9 2/2] am: support --empty=<option> to handle empty patches Aleen 徐沛文 via GitGitGadget
@ 2021-11-22  7:51                 ` Aleen via GitGitGadget
  2021-11-22  7:51                   ` [PATCH v10 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
                                     ` (2 more replies)
  2 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  7:51 UTC (permalink / raw)
  To: git; +Cc: Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

cc: René Scharfe l.s.r@web.de cc: Phillip Wood phillip.wood123@gmail.com cc:
Aleen 徐沛文 pwxu@coremail.cn

Aleen (2):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches

 Documentation/git-am.txt           |  8 +++++
 Documentation/git-format-patch.txt |  6 +++-
 builtin/am.c                       | 55 +++++++++++++++++++++++++++---
 t/t4150-am.sh                      | 49 ++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 6 deletions(-)


base-commit: ca35af825273b98fc8dc11527488952f5db8eb80
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v10
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v10
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v9:

 1:  3b41ca3dec7 ! 1:  59bce7131da doc: git-format-patch: describe the option --always
     @@
       ## Metadata ##
     -Author: Aleen 徐沛文 <aleen42@vip.qq.com>
     +Author: Aleen <aleen42@vip.qq.com>
      
       ## Commit message ##
          doc: git-format-patch: describe the option --always
     @@ Commit message
          This commit has described how to use '--always' option in the command
          'git-format-patch' to include patches for commits that emit no changes.
      
     -    Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
     +    Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
       ## Documentation/git-format-patch.txt ##
      @@ Documentation/git-format-patch.txt: SYNOPSIS
 2:  d2ec18b36af ! 2:  5025ad30ba7 am: support --empty=<option> to handle empty patches
     @@
       ## Metadata ##
     -Author: Aleen 徐沛文 <aleen42@vip.qq.com>
     +Author: Aleen <aleen42@vip.qq.com>
      
       ## Commit message ##
          am: support --empty=<option> to handle empty patches
     @@ Commit message
          those empty patches. In this commit, we have implemented three
          valid options ('die', 'drop' and 'keep').
      
     -    Signed-off-by: Aleen 徐沛文 <aleen42@vip.qq.com>
     +    Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
       ## Documentation/git-am.txt ##
      @@ Documentation/git-am.txt: SYNOPSIS

-- 
gitgitgadget

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

* [PATCH v10 1/2] doc: git-format-patch: describe the option --always
  2021-11-22  7:51                 ` [PATCH v10 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
@ 2021-11-22  7:51                   ` Aleen via GitGitGadget
  2021-11-22 12:00                     ` Johannes Schindelin
  2021-11-22  7:51                   ` [PATCH v10 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
  2021-11-23 15:26                   ` [PATCH v11 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  7:51 UTC (permalink / raw)
  To: git; +Cc: Aleen 徐沛文, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v10 2/2] am: support --empty=<option> to handle empty patches
  2021-11-22  7:51                 ` [PATCH v10 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-22  7:51                   ` [PATCH v10 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-22  7:51                   ` Aleen via GitGitGadget
  2021-11-23 15:26                   ` [PATCH v11 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-22  7:51 UTC (permalink / raw)
  To: git; +Cc: Aleen 徐沛文, Aleen, Aleen

From: Aleen <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..ba17063f621 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-19 16:54                   ` Junio C Hamano
  2021-11-19 17:14                     ` Aleen 徐沛文
@ 2021-11-22 11:57                     ` Johannes Schindelin
  1 sibling, 0 replies; 129+ messages in thread
From: Johannes Schindelin @ 2021-11-22 11:57 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Aleen 徐沛文,
	git, René Scharfe, Phillip Wood, Aleen

[-- Attachment #1: Type: text/plain, Size: 1698 bytes --]

Hi Junio & Aleen,

On Fri, 19 Nov 2021, Junio C Hamano wrote:

> Aleen 徐沛文 <pwxu@coremail.cn> writes:
>
> >> Yes.
> >>
> >> As the URL you were referred to explains, the sign-off procedure is
> >> to keep track of provenance of the code, which is a more "legal"
> >> formal requirement than just "I use this pseudonym everywhere".
> >> When a big company comes to us, claiming that "this code is our
> >> intellectual property stolen from us" and pointing at code added by
> >> a patch from you, we do not want to see us in the position to have
> >> to say "eh, somebody who uses psuedonym X signed DCO, but we do not
> >> even know their real name".
> >
> > I know it, and as I said before that gitgitgadget need PR creators to
> > sign off user name of GitHub account, according to the DCO check. I can
> > confirmed that "Aleen" and "Aleen 徐沛文" are both the real name of mine,
> > the committer. I can use the account aleen42@vip.qq.com to confirm this.
>
> Can somebody from GGG land help this user?  I _think_ the easiest
> workaround (other than not using GGG and sending e-mail in the old
> fashioned way) is to commit and sign-off under the real name, and
> push under whatever GitHub username to throw a GGG pull request,
> which GGG should be able to take, as I have seen users forward other
> authors commits just fine.

GitGitGadget looks at the author information, so you need to ensure that
the "Author:" line in the output of `git log` shows the desired name. If
it does not, you need to configure `user.name` (and user.email)` as
desired, and then re-commit with `git commit --amend --reset-author`, then
force-push.

Ciao,
Johannes

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

* Re: [PATCH v10 1/2] doc: git-format-patch: describe the option --always
  2021-11-22  7:51                   ` [PATCH v10 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
@ 2021-11-22 12:00                     ` Johannes Schindelin
  2021-11-23  1:25                       ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Johannes Schindelin @ 2021-11-22 12:00 UTC (permalink / raw)
  To: Aleen via GitGitGadget; +Cc: git, Aleen 徐沛文, Aleen, Aleen

[-- Attachment #1: Type: text/plain, Size: 468 bytes --]

Hi Aleen,

On Mon, 22 Nov 2021, Aleen via GitGitGadget wrote:

> From: Aleen <aleen42@vip.qq.com>

FWIW this information comes from your commit, specifically from the
author information recorded when you committed first. To re-set it in
these two patches, run something like this:

	git config --global user.name "徐沛文 (Aleen)"
	git rebase -x "git commit --amend --no-edit --reset-author" HEAD~2

and then force-push to your branch.

Ciao,
Dscho

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

* Re: [PATCH v10 1/2] doc: git-format-patch: describe the option --always
  2021-11-22 12:00                     ` Johannes Schindelin
@ 2021-11-23  1:25                       ` Aleen 徐沛文
  2021-11-23 12:30                         ` Johannes Schindelin
  0 siblings, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-23  1:25 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Aleen via GitGitGadget, git, Aleen, Junio C Hamano, Phillip Wood,
	René Scharfe

> Hi Aleen,
> 
> On Mon, 22 Nov 2021, Aleen via GitGitGadget wrote:
> 
> > From: Aleen <aleen42@vip.qq.com>
> 
> FWIW this information comes from your commit, specifically from the
> author information recorded when you committed first. To re-set it in
> these two patches, run something like this:
> 
> 	git config --global user.name "徐沛文 (Aleen)"
> 	git rebase -x "git commit --amend --no-edit --reset-author" HEAD~2
> 
> and then force-push to your branch.
> 
> Ciao,
> Dscho

The main problem is that it broke when I tried to submit via GGG:
https://github.com/gitgitgadget/git/pull/1076#issuecomment-976087421

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

* Re: [PATCH v10 1/2] doc: git-format-patch: describe the option --always
  2021-11-23  1:25                       ` Aleen 徐沛文
@ 2021-11-23 12:30                         ` Johannes Schindelin
  0 siblings, 0 replies; 129+ messages in thread
From: Johannes Schindelin @ 2021-11-23 12:30 UTC (permalink / raw)
  To: Aleen 徐沛文
  Cc: Aleen via GitGitGadget, git, Aleen, Junio C Hamano, Phillip Wood,
	René Scharfe

[-- Attachment #1: Type: text/plain, Size: 1044 bytes --]

Hi Aleen,

On Tue, 23 Nov 2021, Aleen 徐沛文 wrote:

> > Hi Aleen,
> >
> > On Mon, 22 Nov 2021, Aleen via GitGitGadget wrote:
> >
> > > From: Aleen <aleen42@vip.qq.com>
> >
> > FWIW this information comes from your commit, specifically from the
> > author information recorded when you committed first. To re-set it in
> > these two patches, run something like this:
> >
> > 	git config --global user.name "徐沛文 (Aleen)"
> > 	git rebase -x "git commit --amend --no-edit --reset-author" HEAD~2
> >
> > and then force-push to your branch.
> >
> > Ciao,
> > Dscho
>
> The main problem is that it broke when I tried to submit via GGG:
> https://github.com/gitgitgadget/git/pull/1076#issuecomment-976087421

Whoops. It seems that the `From:` header cannot be parsed by GitGitGadget.
Most likely because the `<` and `>` of the email address are on the next
line, not the same line as the `From:`.

I had a quick look and opened
https://github.com/gitgitgadget/gitgitgadget/pull/777 to fix this.

Ciao,
Dscho

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

* [PATCH v11 0/2] am: support --empty=(die|drop|keep) option to handle empty patches
  2021-11-22  7:51                 ` [PATCH v10 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-22  7:51                   ` [PATCH v10 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
  2021-11-22  7:51                   ` [PATCH v10 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
@ 2021-11-23 15:26                   ` Aleen via GitGitGadget
  2021-11-23 15:26                     ` [PATCH v11 1/2] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                       ` (2 more replies)
  2 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-23 15:26 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Aleen 徐沛文, Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

cc: René Scharfe l.s.r@web.de cc: Phillip Wood phillip.wood123@gmail.com cc:
Aleen 徐沛文 pwxu@coremail.cn

cc: Aleen 徐沛文 pwxu@coremail.cn

徐沛文 (Aleen) (2):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches

 Documentation/git-am.txt           |  8 +++++
 Documentation/git-format-patch.txt |  6 +++-
 builtin/am.c                       | 55 +++++++++++++++++++++++++++---
 t/t4150-am.sh                      | 49 ++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 6 deletions(-)


base-commit: f8b28837226f3932b867ca88a4f830bf203d2afe
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v11
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v11
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v10:

 1:  59bce7131da ! 1:  3d7e96ce2b3 doc: git-format-patch: describe the option --always
     @@
       ## Metadata ##
     -Author: Aleen <aleen42@vip.qq.com>
     +Author: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
       ## Commit message ##
          doc: git-format-patch: describe the option --always
 2:  5025ad30ba7 ! 2:  6051ad9440a am: support --empty=<option> to handle empty patches
     @@
       ## Metadata ##
     -Author: Aleen <aleen42@vip.qq.com>
     +Author: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
       ## Commit message ##
          am: support --empty=<option> to handle empty patches

-- 
gitgitgadget

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

* [PATCH v11 1/2] doc: git-format-patch: describe the option --always
  2021-11-23 15:26                   ` [PATCH v11 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
@ 2021-11-23 15:26                     ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-23 16:12                       ` Johannes Schindelin
  2021-11-23 15:26                     ` [PATCH v11 2/2] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-23 15:26 UTC (permalink / raw)
  To: git
  Cc: Johannes Schindelin, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-23 15:26                   ` [PATCH v11 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-23 15:26                     ` [PATCH v11 1/2] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-23 15:26                     ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-26 20:14                       ` Elijah Newren
  2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-23 15:26 UTC (permalink / raw)
  To: git
  Cc: Johannes Schindelin, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..ba17063f621 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget

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

* Re: [PATCH v11 1/2] doc: git-format-patch: describe the option --always
  2021-11-23 15:26                     ` [PATCH v11 1/2] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-23 16:12                       ` Johannes Schindelin
  2021-11-23 22:02                         ` Junio C Hamano
  0 siblings, 1 reply; 129+ messages in thread
From: Johannes Schindelin @ 2021-11-23 16:12 UTC (permalink / raw)
  To: 徐沛文 (Aleen) via GitGitGadget
  Cc: git, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

[-- Attachment #1: Type: text/plain, Size: 466 bytes --]

Hi,

On Tue, 23 Nov 2021, 徐沛文 (Aleen) via GitGitGadget wrote:

> From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
>  <aleen42@vip.qq.com>

I triggered the Azure Pipeline to submit this patch series after merging
the PR that fixed parsing of the `From:` line.

It looks a bit funny that this is in two lines, so I downloaded the mail
from https://lore.kernel.org/git/ and fed it to `git am`, which was just
fine with it.

Ciao,
Dscho

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

* Re: [PATCH v11 1/2] doc: git-format-patch: describe the option --always
  2021-11-23 16:12                       ` Johannes Schindelin
@ 2021-11-23 22:02                         ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-23 22:02 UTC (permalink / raw)
  To: Johannes Schindelin, Aleen 徐沛文, Aleen
  Cc: 徐沛文 (Aleen) via GitGitGadget, git

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> On Tue, 23 Nov 2021, 徐沛文 (Aleen) via GitGitGadget wrote:
>
>> From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
>>  <aleen42@vip.qq.com>
>
> I triggered the Azure Pipeline to submit this patch series after merging
> the PR that fixed parsing of the `From:` line.
>
> It looks a bit funny that this is in two lines, so I downloaded the mail
> from https://lore.kernel.org/git/ and fed it to `git am`, which was just
> fine with it.

;-)  Thanks for double-checking carefully.


These two are identical to what has been in my tree (as I
manually tweaked the patches from the previous rounds).

1:  6112004764 = 1:  e9181bd6fd doc: git-format-patch: describe the option --always
2:  b26f121395 = 2:  ca426b823b am: support --empty=<option> to handle empty patches

I'll pretend that I took v11 but please do not get alarmed if the
author and committer times are from v10.

Thanks, both.

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

* Re: [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-23 15:26                     ` [PATCH v11 2/2] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-26 20:14                       ` Elijah Newren
  2021-11-29  9:19                         ` Aleen 徐沛文
  2021-11-29 18:17                         ` [PATCH v11 2/2] am: support --empty=<option> to handle " Junio C Hamano
  0 siblings, 2 replies; 129+ messages in thread
From: Elijah Newren @ 2021-11-26 20:14 UTC (permalink / raw)
  To: 徐沛文 (Aleen) via GitGitGadget
  Cc: Git Mailing List, Johannes Schindelin,
	Aleen 徐沛文, 徐沛文 (Aleen)

On Tue, Nov 23, 2021 at 8:38 AM 徐沛文 (Aleen) via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
>  <aleen42@vip.qq.com>
>
> Since that the command 'git-format-patch' can include patches of
> commits that emit no changes, the 'git-am' command should also
> support an option, named as '--empty', to specify how to handle
> those empty patches. In this commit, we have implemented three
> valid options ('die', 'drop' and 'keep').
>
> Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
> ---
>  Documentation/git-am.txt |  8 ++++++
>  builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
>  t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
>  3 files changed, 107 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
> index 0a4a984dfde..ba17063f621 100644
> --- a/Documentation/git-am.txt
> +++ b/Documentation/git-am.txt
> @@ -16,6 +16,7 @@ SYNOPSIS
>          [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
>          [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
>          [--quoted-cr=<action>]
> +        [--empty=(die|drop|keep)]
>          [(<mbox> | <Maildir>)...]
>  'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
>
> @@ -63,6 +64,13 @@ OPTIONS
>  --quoted-cr=<action>::
>         This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
>
> +--empty=(die|drop|keep)::
> +       By default, or when the option is set to 'die', the command
> +       errors out on an input e-mail message that lacks a patch. 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.

What does 'errors out' mean?  Is the am operation aborted, and the
user return to the pre-am state?  Or is the am operation interrupted,
with the user being asked to choose whether to keep or drop the patch?
 Or something else (my first thought was "Are you going to leave the
index locked?")?  This description is not that clear.  To me, the
wording suggests aborted (or worse), but what you actually implemented
was an interrupt-and-ask.

Can I suggest using 'ask' instead of 'die'?  I think that will be
clearer, and it matches the term used by git rebase --empty.

Also, the only instructions given to the user when you interrupt
include how to skip the patch, but I don't see anything for how to
keep it.  The instructions are:
'''
Patch is empty.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
'''

I tried it manually, and it turns out "git am --continue" will just
spit out basically the same message again:
'''
Applying: empty commit
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
'''

And if I try to run `git commit --allow-empty` (which I happen to
remember is the command suggested by `git rebase --empty=ask` when it
stops), then I'm given an empty editor; it is not pre-populated with
the appropriate commit message.  Can the portion of the empty patch
corresponding to the commit message be added to .git/COMMIT_EDITMSG to
correct that?  Also, can some extra words be printed before
interrupting to explain what to do when you want to keep the empty
commit?  Something like:
"""
The current commit being applied is empty.  If you wish to commit it
anyway, use:
    git commit --allow-empty
"""

> +
>  -m::
>  --message-id::
>         Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
> diff --git a/builtin/am.c b/builtin/am.c
> index 8677ea2348a..cc6512275aa 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -87,6 +87,12 @@ enum show_patch_type {
>         SHOW_PATCH_DIFF = 1,
>  };
>
> +enum empty_action {
> +       DIE_EMPTY_COMMIT = 0,  /* output errors */
> +       DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
> +       KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
> +};
> +
>  struct am_state {
>         /* state directory path */
>         char *dir;
> @@ -118,6 +124,7 @@ struct am_state {
>         int message_id;
>         int scissors; /* enum scissors_type */
>         int quoted_cr; /* enum quoted_cr_action */
> +       int empty_type; /* enum empty_action */
>         struct strvec git_apply_opts;
>         const char *resolvemsg;
>         int committer_date_is_author_date;
> @@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
>         return 0;
>  }
>
> +static int am_option_parse_empty(const struct option *opt,
> +                                    const char *arg, int unset)
> +{
> +       int *opt_value = opt->value;
> +
> +       BUG_ON_OPT_NEG(unset);
> +
> +       if (!strcmp(arg, "die"))
> +               *opt_value = DIE_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 --empty: %s"), arg);
> +
> +       return 0;
> +}
> +
>  /**
>   * Returns path relative to the am_state directory.
>   */
> @@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
>                 goto finish;
>         }
>
> -       if (is_empty_or_missing_file(am_path(state, "patch"))) {
> -               printf_ln(_("Patch is empty."));
> -               die_user_resolve(state);
> -       }
> -
>         strbuf_addstr(&msg, "\n\n");
>         strbuf_addbuf(&msg, &mi.log_message);
>         strbuf_stripspace(&msg, 0);
> @@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
>         while (state->cur <= state->last) {
>                 const char *mail = am_path(state, msgnum(state));
>                 int apply_status;
> +               int to_keep;
>
>                 reset_ident_date();
>
> @@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
>                 if (state->interactive && do_interactive(state))
>                         goto next;
>
> +               to_keep = 0;
> +               if (is_empty_or_missing_file(am_path(state, "patch"))) {
> +                       switch (state->empty_type) {
> +                       case DROP_EMPTY_COMMIT:
> +                               say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
> +                               goto next;
> +                               break;
> +                       case KEEP_EMPTY_COMMIT:
> +                               to_keep = 1;
> +                               break;
> +                       case DIE_EMPTY_COMMIT:
> +                               printf_ln(_("Patch is empty."));
> +                               die_user_resolve(state);
> +                               break;
> +                       }
> +               }
> +
>                 if (run_applypatch_msg_hook(state))
>                         exit(1);
> +               if (to_keep)
> +                       goto commit;
>
>                 say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
>
> @@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
>                         die_user_resolve(state);
>                 }
>
> +commit:
>                 do_commit(state);
>
>  next:
> @@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
>                 { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
>                   N_("GPG-sign commits"),
>                   PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> +               OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
> +                 N_("how to handle empty patches"),
> +                 PARSE_OPT_NONEG, am_option_parse_empty),
>                 OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
>                         N_("(internal use for git-rebase)")),
>                 OPT_END()
> diff --git a/t/t4150-am.sh b/t/t4150-am.sh
> index 2aaaa0d7ded..8c8bd4db220 100755
> --- a/t/t4150-am.sh
> +++ b/t/t4150-am.sh
> @@ -196,6 +196,12 @@ test_expect_success setup '
>
>         git format-patch -M --stdout lorem^ >rename-add.patch &&
>
> +       git checkout -b empty-commit &&
> +       git commit -m "empty commit" --allow-empty &&
> +
> +       : >empty.patch &&
> +       git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
> +
>         # reset time
>         sane_unset test_tick &&
>         test_tick
> @@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
>         git -C client am ../patch
>  '
>
> +test_expect_success 'an empty input file is error regardless of --empty option' '
> +       test_when_finished "git am --abort || :" &&
> +       test_must_fail git am --empty=drop empty.patch 2>actual &&
> +       echo "Patch format detection failed." >expected &&
> +       test_cmp expected actual
> +'
> +
> +test_expect_success 'invalid when passing the --empty option alone' '
> +       test_when_finished "git am --abort || :" &&
> +       git checkout empty-commit^ &&
> +       test_must_fail git am --empty empty-commit.patch 2>err &&
> +       echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
> +       test_cmp expected err
> +'
> +
> +test_expect_success 'a message without a patch is an error (default)' '
> +       test_when_finished "git am --abort || :" &&
> +       test_must_fail git am empty-commit.patch >err &&
> +       grep "Patch is empty" err
> +'
> +
> +test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
> +       test_when_finished "git am --abort || :" &&
> +       test_must_fail git am --empty=die empty-commit.patch >err &&
> +       grep "Patch is empty." err
> +'
> +
> +test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
> +       git am --empty=drop empty-commit.patch >output &&
> +       git rev-parse empty-commit^ >expected &&
> +       git rev-parse HEAD >actual &&
> +       test_cmp expected actual &&
> +       grep "Skipping: empty commit" output
> +'
> +
> +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
> +       git am --empty=keep empty-commit.patch &&
> +       test_path_is_missing .git/rebase-apply &&
> +       git show empty-commit --format="%s" >expected &&
> +       git show HEAD --format="%s" >actual &&
> +       test_cmp actual expected
> +'
> +
>  test_done
> --
> gitgitgadget

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

* Re: [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-26 20:14                       ` Elijah Newren
@ 2021-11-29  9:19                         ` Aleen 徐沛文
  2021-11-29 10:00                           ` Aleen 徐沛文
  2021-11-29 18:17                         ` [PATCH v11 2/2] am: support --empty=<option> to handle " Junio C Hamano
  1 sibling, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-29  9:19 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: 徐沛文 (Aleen) via GitGitGadget,
	Git Mailing List, Johannes Schindelin,
	徐沛文 (Aleen),
	René Scharfe, Elijah Newren

Dears Hamano,

   Elijah Newren has given two better suggestions:

       1. Use 'ask' rather than 'die'
       2. When erroring out 'Patch is empty', print out a tutorial information
          to help users using 'git commit --allow-empty' to keep recording as 
          an empty commit.

   Should we continue to implement these features in current PR?

Aleen

> > +--empty=(die|drop|keep)::
> > +       By default, or when the option is set to 'die', the command
> > +       errors out on an input e-mail message that lacks a patch. 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.
> 
> What does 'errors out' mean?  Is the am operation aborted, and the
> user return to the pre-am state?  Or is the am operation interrupted,
> with the user being asked to choose whether to keep or drop the patch?
>  Or something else (my first thought was "Are you going to leave the
> index locked?")?  This description is not that clear.  To me, the
> wording suggests aborted (or worse), but what you actually implemented
> was an interrupt-and-ask.
> 
> Can I suggest using 'ask' instead of 'die'?  I think that will be
> clearer, and it matches the term used by git rebase --empty.
> 
> Also, the only instructions given to the user when you interrupt
> include how to skip the patch, but I don't see anything for how to
> keep it.  The instructions are:
> '''
> Patch is empty.
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".
> '''
> 
> I tried it manually, and it turns out "git am --continue" will just
> spit out basically the same message again:
> '''
> Applying: empty commit
> No changes - did you forget to use 'git add'?
> If there is nothing left to stage, chances are that something else
> already introduced the same changes; you might want to skip this patch.
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".
> '''
> 
> And if I try to run `git commit --allow-empty` (which I happen to
> remember is the command suggested by `git rebase --empty=ask` when it
> stops), then I'm given an empty editor; it is not pre-populated with
> the appropriate commit message.  Can the portion of the empty patch
> corresponding to the commit message be added to .git/COMMIT_EDITMSG to
> correct that?  Also, can some extra words be printed before
> interrupting to explain what to do when you want to keep the empty
> commit?  Something like:
> """
> The current commit being applied is empty.  If you wish to commit it
> anyway, use:
>     git commit --allow-empty
> """

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

* Re: Re: [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-29  9:19                         ` Aleen 徐沛文
@ 2021-11-29 10:00                           ` Aleen 徐沛文
  2021-11-29 17:10                             ` Elijah Newren
  0 siblings, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-29 10:00 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: 徐沛文 (Aleen) via GitGitGadget,
	Git Mailing List, Johannes Schindelin,
	徐沛文 (Aleen),
	René Scharfe, Elijah Newren

It is quite complicated to support 'git commit --allow-empty' extracting messages
from an empty patch, because 'git am' has to find somewhere to store the parsed state
for 'git commit' to read. How about directly supporting another interactive option
'--allow-empty' for 'git am' to keep recording?

> Dears Hamano,
> 
>    Elijah Newren has given two better suggestions:
> 
>        1. Use 'ask' rather than 'die'
>        2. When erroring out 'Patch is empty', print out a tutorial information
>           to help users using 'git commit --allow-empty' to keep recording as 
>           an empty commit.
> 
>    Should we continue to implement these features in current PR?
> 
> Aleen

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

* Re: Re: [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-29 10:00                           ` Aleen 徐沛文
@ 2021-11-29 17:10                             ` Elijah Newren
  2021-11-30  5:45                               ` [PATCH v12 3/3] am: support --allow-empty to record specific " Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Elijah Newren @ 2021-11-29 17:10 UTC (permalink / raw)
  To: Aleen 徐沛文
  Cc: Junio C Hamano,
	徐沛文 (Aleen) via GitGitGadget,
	Git Mailing List, Johannes Schindelin,
	徐沛文 (Aleen),
	René Scharfe

On Mon, Nov 29, 2021 at 2:00 AM Aleen 徐沛文 <pwxu@coremail.cn> wrote:
>
> It is quite complicated to support 'git commit --allow-empty' extracting messages
> from an empty patch, because 'git am' has to find somewhere to store the parsed state
> for 'git commit' to read.

.git/COMMIT_EDITMSG is such a place that already exists, assuming you
are just extracting a commit message.

> How about directly supporting another interactive option
> '--allow-empty' for 'git am' to keep recording?

--empty=keep was already part of your patch series; I don't see why
you'd need to add a synonym (--allow-empty) for it.

I think you were trying to ask if you could just leave out the
implementation of --empty=ask (or --empty=die) from your patches.
That's usually fine, but there is a small wrinkle here since it was
your chosen default.  You'll have to pick a different default, and
possibly alert users in the documentation that the default may change
in the future if/when the other option is implemented.

> > Dears Hamano,
> >
> >    Elijah Newren has given two better suggestions:
> >
> >        1. Use 'ask' rather than 'die'
> >        2. When erroring out 'Patch is empty', print out a tutorial information
> >           to help users using 'git commit --allow-empty' to keep recording as
> >           an empty commit.
> >
> >    Should we continue to implement these features in current PR?
> >
> > Aleen

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

* Re: [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-26 20:14                       ` Elijah Newren
  2021-11-29  9:19                         ` Aleen 徐沛文
@ 2021-11-29 18:17                         ` Junio C Hamano
  2021-11-29 18:57                           ` Elijah Newren
  1 sibling, 1 reply; 129+ messages in thread
From: Junio C Hamano @ 2021-11-29 18:17 UTC (permalink / raw)
  To: Elijah Newren
  Cc: 徐沛文 (Aleen) via GitGitGadget,
	Git Mailing List, Johannes Schindelin,
	Aleen 徐沛文, 徐沛文 (Aleen)

Elijah Newren <newren@gmail.com> writes:

>> +--empty=(die|drop|keep)::
>> +       By default, or when the option is set to 'die', the command
>> +       errors out on an input e-mail message that lacks a patch. 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.
>
> What does 'errors out' mean?  Is the am operation aborted, and the
> user return to the pre-am state?  Or is the am operation interrupted,
> with the user being asked to choose whether to keep or drop the patch?

I think it is the same as how "git am" without this sees a piece of
e-mail without any patch in it.  It exits with non-zero status, but
keeps the contents of .git/rebase-apply directory intact so that the
user can decide what the next action should be.  "the command stops
with non-zero status and gives control back to the user" might be a
better explanation.

As to the name of the option's value, I think 'die' is a much better
word than 'ask' for this behaviour.  It is not like we retain
control, give "do you want to do X or Y?"  prompt and do either X or
Y ourselves, which is what I expect out of 'ask'.  If we just exit
with non-zero value and give the control back to the user, then we
are not asking.

This is a tangent, but in the current Documentation/git-am.txt, the
phrasing for "--skip" and "--resolved" are a bit inconsistent.

 - The "--skip" operation calls the situation it is designed to handle
   "an aborted patch".

 - The "--resolved" operation calls it "a patch failure".

The "--abort" operation interestingly does not use any word to
explain the situation it is designed to be used in.  It just
assumes that the user knows "git am" session is somehow still in
effect.



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

* Re: [PATCH v11 2/2] am: support --empty=<option> to handle empty patches
  2021-11-29 18:17                         ` [PATCH v11 2/2] am: support --empty=<option> to handle " Junio C Hamano
@ 2021-11-29 18:57                           ` Elijah Newren
  0 siblings, 0 replies; 129+ messages in thread
From: Elijah Newren @ 2021-11-29 18:57 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: 徐沛文 (Aleen) via GitGitGadget,
	Git Mailing List, Johannes Schindelin,
	Aleen 徐沛文, 徐沛文 (Aleen)

On Mon, Nov 29, 2021 at 10:17 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Elijah Newren <newren@gmail.com> writes:
>
> >> +--empty=(die|drop|keep)::
> >> +       By default, or when the option is set to 'die', the command
> >> +       errors out on an input e-mail message that lacks a patch. 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.
> >
> > What does 'errors out' mean?  Is the am operation aborted, and the
> > user return to the pre-am state?  Or is the am operation interrupted,
> > with the user being asked to choose whether to keep or drop the patch?
>
> I think it is the same as how "git am" without this sees a piece of
> e-mail without any patch in it.  It exits with non-zero status, but
> keeps the contents of .git/rebase-apply directory intact so that the
> user can decide what the next action should be.  "the command stops
> with non-zero status and gives control back to the user" might be a
> better explanation.
>
> As to the name of the option's value, I think 'die' is a much better
> word than 'ask' for this behaviour.  It is not like we retain
> control, give "do you want to do X or Y?"  prompt and do either X or
> Y ourselves, which is what I expect out of 'ask'.  If we just exit
> with non-zero value and give the control back to the user, then we
> are not asking.

Well, I still think 'die' implies aborting the entire overall `am`
operation, and is thus a confusing term.  To me, 'stop' or 'interrupt'
would be better if you want a one-word term and don't like 'ask'.

If you don't like the term 'ask' here, perhaps we should also change
rebase?  rebase --empty has 'ask' as a choice for
stopping/interrupting the rebase operation and letting the user decide
how to handle it.  (There is a very subtly different context for
rebase's --empty=ask, namely in that it is only triggered for patches
that were not originally empty but become so do the the new base the
patches are being applied upon already having the changes from the
patch in question, but that doesn't seem like a big enough difference
to suggest it should have a different name.)


There's another bit to my query on the semantics, though.  The second
half of my critique was that the current form of the series does not
inform the user how to handle the situation when `git-am` stops.  It
only tells the user how to drop the patch.  (If there's only one
option, why doesn't git just take it automatically?)  I think it
should also tell the user how to keep the patch.  Now, if both options
are presented to the user when the operation stops, would that qualify
as the user having been 'asked' what to do?

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

* [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-11-23 15:26                   ` [PATCH v11 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
  2021-11-23 15:26                     ` [PATCH v11 1/2] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-11-23 15:26                     ` [PATCH v11 2/2] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  5:37                     ` Aleen via GitGitGadget
  2021-11-30  5:37                       ` [PATCH v12 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                         ` (3 more replies)
  2 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-30  5:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           | 14 +++++-
 Documentation/git-format-patch.txt |  6 ++-
 builtin/am.c                       | 77 ++++++++++++++++++++++++++----
 t/t4150-am.sh                      | 61 +++++++++++++++++++++++
 t/t7512-status-help.sh             |  1 +
 wt-status.c                        |  3 ++
 6 files changed, 151 insertions(+), 11 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v12
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v12
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v11:

 1:  3d7e96ce2b3 = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  6051ad9440a = 2:  a3e850bab7d am: support --empty=<option> to handle empty patches
 -:  ----------- > 3:  d44dac09c87 am: support --allow-empty to record specific empty patches

-- 
gitgitgadget

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

* [PATCH v12 1/3] doc: git-format-patch: describe the option --always
  2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
@ 2021-11-30  5:37                       ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  5:37                       ` [PATCH v12 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-30  5:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v12 2/3] am: support --empty=<option> to handle empty patches
  2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
  2021-11-30  5:37                       ` [PATCH v12 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  5:37                       ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  5:37                       ` [PATCH v12 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-30  5:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..ba17063f621 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v12 3/3] am: support --allow-empty to record specific empty patches
  2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
  2021-11-30  5:37                       ` [PATCH v12 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  5:37                       ` [PATCH v12 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  5:37                       ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  7:21                         ` Junio C Hamano
  2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-30  5:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  6 +++++-
 builtin/am.c             | 24 +++++++++++++++++++-----
 t/t4150-am.sh            | 12 ++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  3 +++
 5 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index ba17063f621..fe3af32f7f7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -199,6 +199,10 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	Keep recording the empty patch as an empty commit with
+	the contents of the e-mail message as its log.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index cc6512275aa..2ae6fabb28a 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
 				to_keep = 1;
 				break;
 			case DIE_EMPTY_COMMIT:
-				printf_ln(_("Patch is empty."));
+				printf_ln(_("Patch is empty.\n"
+							"If you want to keep recording it, run \"git am --allow-empty\"."));
 				die_user_resolve(state);
 				break;
 			}
@@ -1898,10 +1899,15 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
+	if (allow_empty) {
+		goto commit;
+	}
+
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
@@ -1928,6 +1934,7 @@ static void am_resolve(struct am_state *state)
 
 	repo_rerere(the_repository, 0);
 
+commit:
 	do_commit(state);
 
 next:
@@ -2237,7 +2244,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY
 };
 
 struct resume_mode {
@@ -2390,6 +2398,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("keep recording the empty patch as empty commits"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2498,7 +2509,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+		am_resolve(&state, 0);
+		break;
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, 1);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 8c8bd4db220..cab21411f00 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1201,4 +1201,16 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	test_cmp actual expected
 '
 
+
+test_expect_success 'record as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to keep recording it, run \"git am --allow-empty\"." err &&
+	git am --allow-empty &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..5513018154e 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
 On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
+  (use "git am --allow-empty" to keep recording this empty patch)
   (use "git am --skip" to skip this patch)
   (use "git am --abort" to restore the original branch)
 
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..d036f88a6b9 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
 		if (!s->state.am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to keep recording this empty patch)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
-- 
gitgitgadget

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

* Re: [PATCH v12 3/3] am: support --allow-empty to record specific empty patches
  2021-11-29 17:10                             ` Elijah Newren
@ 2021-11-30  5:45                               ` Aleen 徐沛文
  0 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-11-30  5:45 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Junio C Hamano,
	徐沛文 (Aleen) via GitGitGadget,
	Git Mailing List, Johannes Schindelin,
	徐沛文 (Aleen),
	René Scharfe

I have introduced an interactive option `--allow-empty` in a new independent commit,
and it is not the synonym of the `--empty` option because it is only for the case in
a middle am session where users can resolve "died" problems by choosing whether to
record current empty patch. The `--empty` option acts like a strategy option to tell
`git-am` how to handle when meeting any empty patches.

> On Mon, Nov 29, 2021 at 2:00 AM Aleen 徐沛文 <pwxu@coremail.cn> wrote:
> >
> > It is quite complicated to support 'git commit --allow-empty' extracting messages
> > from an empty patch, because 'git am' has to find somewhere to store the parsed state
> > for 'git commit' to read.
> 
> .git/COMMIT_EDITMSG is such a place that already exists, assuming you
> are just extracting a commit message.
> 
> > How about directly supporting another interactive option
> > '--allow-empty' for 'git am' to keep recording?
> 
> --empty=keep was already part of your patch series; I don't see why
> you'd need to add a synonym (--allow-empty) for it.
> 
> I think you were trying to ask if you could just leave out the
> implementation of --empty=ask (or --empty=die) from your patches.
> That's usually fine, but there is a small wrinkle here since it was
> your chosen default.  You'll have to pick a different default, and
> possibly alert users in the documentation that the default may change
> in the future if/when the other option is implemented.
> 
> > > Dears Hamano,
> > >
> > >    Elijah Newren has given two better suggestions:
> > >
> > >        1. Use 'ask' rather than 'die'
> > >        2. When erroring out 'Patch is empty', print out a tutorial information
> > >           to help users using 'git commit --allow-empty' to keep recording as
> > >           an empty commit.
> > >
> > >    Should we continue to implement these features in current PR?
> > >
> > > Aleen

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

* Re: [PATCH v12 3/3] am: support --allow-empty to record specific empty patches
  2021-11-30  5:37                       ` [PATCH v12 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  7:21                         ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-11-30  7:21 UTC (permalink / raw)
  To: ""徐沛文  via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

""徐沛文 (Aleen)" via GitGitGadget"  <gitgitgadget@gmail.com>
writes:

> From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
>  <aleen42@vip.qq.com>
>
> This option helps to record specific empty patches in the middle
> of an am session.
>
> Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
> ---
>  Documentation/git-am.txt |  6 +++++-
>  builtin/am.c             | 24 +++++++++++++++++++-----
>  t/t4150-am.sh            | 12 ++++++++++++
>  t/t7512-status-help.sh   |  1 +
>  wt-status.c              |  3 +++
>  5 files changed, 40 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
> index ba17063f621..fe3af32f7f7 100644
> --- a/Documentation/git-am.txt
> +++ b/Documentation/git-am.txt
> @@ -18,7 +18,7 @@ SYNOPSIS
>  	 [--quoted-cr=<action>]
>  	 [--empty=(die|drop|keep)]
>  	 [(<mbox> | <Maildir>)...]
> -'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
> +'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
>  
>  DESCRIPTION
>  -----------
> @@ -199,6 +199,10 @@ default.   You can use `--no-utf8` to override this.
>  	the e-mail message; if `diff`, show the diff portion only.
>  	Defaults to `raw`.
>  
> +--allow-empty::
> +	Keep recording the empty patch as an empty commit with
> +	the contents of the e-mail message as its log.
> +
>  DISCUSSION
>  ----------
>  
> diff --git a/builtin/am.c b/builtin/am.c
> index cc6512275aa..2ae6fabb28a 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
>  				to_keep = 1;
>  				break;
>  			case DIE_EMPTY_COMMIT:
> -				printf_ln(_("Patch is empty."));
> +				printf_ln(_("Patch is empty.\n"
> +							"If you want to keep recording it, run \"git am --allow-empty\"."));

An overly deep indentation.  Make "If" align with "Patch",
probably.  Also "to keep recording it" -> "to record it".

"If you want to record the e-mail text as the log message of an
empty commit" may be a bit too much, but just saying "record it" may
not be clear enough to explain what gets recorded how.

> @@ -1898,10 +1899,15 @@ next:
>  /**
>   * Resume the current am session after patch application failure. The user did
>   * all the hard work, and we do not have to do any patch application. Just
> - * trust and commit what the user has in the index and working tree.
> + * trust and commit what the user has in the index and working tree. If `allow_empty`
> + * is true, commit as an empty commit.
>   */
> -static void am_resolve(struct am_state *state)
> +static void am_resolve(struct am_state *state, int allow_empty)
>  {
> +	if (allow_empty) {
> +		goto commit;
> +	}

Lose {} around a single statement block.

But more importantly, doesn't this break "git am" when the user
gives "--allow-empty" in cases where you are not expecting?

Like when the patch application (with or without -3) fails, either
during "git am -i" or "git am" without "-i"?

The parameter claims to be "allow empty", implying that the option
is about allowing to end up in an empty commit (and depending on the
input, the resulting commit may not be empty and that is OK).  But
what the code does is to _ignore_ the unmerged check, not recording
the manual resolution the user did for later reuse, and force a call
to do_commit() fail if the index is still unmerged.

>  	validate_resume_state(state);
>  
>  	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);

I suspect that the real code that wants to be changed is below this
line, where we say "if the index does not have changes (relative to
the HEAD), then complain and die".  Perhaps it should become
something along this line:

	if (!repo_index_has_changes(...)) {
		if (allow_empty) {
			printf_ln(_("No changes - recording an empty commit.");
		} else {
			printf_ln(_("No changes - did you forget ..."
				    "..."));
			die_user_resolve(state);
		}
	}

i.e. "when the index does not have changes, tell the user that we'd
create an empty commit, if allow-empty is in effect. otherwise,
complain and die".

I suspect that the worst failure mode may be "git am" (without -3)
first fails to apply a patch (hence the index is unchanged wrt
HEAD), then "git am" that is run with "--allow-empty" ignores the
failure and creates an empty commit.  Even with the "something along
this line" change above, such a failure mode still exists.  You
really need to see if we had an input without any patch and do the
skipping only when it is the case, I think.


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

* [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
                                         ` (2 preceding siblings ...)
  2021-11-30  5:37                       ` [PATCH v12 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  9:55                       ` Aleen via GitGitGadget
  2021-11-30  9:55                         ` [PATCH v13 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                           ` (3 more replies)
  3 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-11-30  9:55 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           | 14 ++++-
 Documentation/git-format-patch.txt |  6 ++-
 builtin/am.c                       | 82 +++++++++++++++++++++++++-----
 t/t4150-am.sh                      | 73 ++++++++++++++++++++++++++
 t/t7512-status-help.sh             |  1 +
 wt-status.c                        |  3 ++
 6 files changed, 164 insertions(+), 15 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v13
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v13
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v12:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  a3e850bab7d = 2:  a3e850bab7d am: support --empty=<option> to handle empty patches
 3:  d44dac09c87 ! 3:  08bd397ae7a am: support --allow-empty to record specific empty patches
     @@ Documentation/git-am.txt: default.   You can use `--no-utf8` to override this.
       	Defaults to `raw`.
       
      +--allow-empty::
     -+	Keep recording the empty patch as an empty commit with
     ++	Record the empty patch as an empty commit with
      +	the contents of the e-mail message as its log.
      +
       DISCUSSION
     @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
       			case DIE_EMPTY_COMMIT:
      -				printf_ln(_("Patch is empty."));
      +				printf_ln(_("Patch is empty.\n"
     -+							"If you want to keep recording it, run \"git am --allow-empty\"."));
     ++					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
       				die_user_resolve(state);
       				break;
       			}
     @@ builtin/am.c: next:
        * all the hard work, and we do not have to do any patch application. Just
      - * trust and commit what the user has in the index and working tree.
      + * trust and commit what the user has in the index and working tree. If `allow_empty`
     -+ * is true, commit as an empty commit.
     ++ * is true, commit as an empty commit when there is no changes.
        */
      -static void am_resolve(struct am_state *state)
      +static void am_resolve(struct am_state *state, int allow_empty)
       {
     -+	if (allow_empty) {
     -+		goto commit;
     -+	}
     -+
       	validate_resume_state(state);
       
       	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
     -@@ builtin/am.c: static void am_resolve(struct am_state *state)
     - 
     - 	repo_rerere(the_repository, 0);
       
     -+commit:
     - 	do_commit(state);
     + 	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
     +-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
     +-			"If there is nothing left to stage, chances are that something else\n"
     +-			"already introduced the same changes; you might want to skip this patch."));
     +-		die_user_resolve(state);
     ++		if (allow_empty)
     ++			printf_ln(_("No changes - record it as an empty commit."));
     ++		else {
     ++			printf_ln(_("No changes - did you forget to use 'git add'?\n"
     ++				    "If there is nothing left to stage, chances are that something else\n"
     ++				    "already introduced the same changes; you might want to skip this patch."));
     ++			die_user_resolve(state);
     ++		}
     + 	}
       
     - next:
     + 	if (unmerged_cache()) {
      @@ builtin/am.c: enum resume_type {
       	RESUME_SKIP,
       	RESUME_ABORT,
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
       		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
      +		OPT_CMDMODE(0, "allow-empty", &resume.mode,
     -+			N_("keep recording the empty patch as empty commits"),
     ++			N_("record the empty patch as an empty commit"),
      +			RESUME_ALLOW_EMPTY),
       		OPT_BOOL(0, "committer-date-is-author-date",
       			&state.committer_date_is_author_date,
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		break;
       	case RESUME_RESOLVED:
      -		am_resolve(&state);
     -+		am_resolve(&state, 0);
     -+		break;
      +	case RESUME_ALLOW_EMPTY:
     -+		am_resolve(&state, 1);
     ++		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
       		break;
       	case RESUME_SKIP:
       		am_skip(&state);
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
       	test_cmp actual expected
       '
       
     ++test_expect_success 'skip an empty patch in the middle of an am session' '
     ++	git checkout empty-commit^ &&
     ++	test_must_fail git am empty-commit.patch >err &&
     ++	grep "Patch is empty." err &&
     ++	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
     ++	git am --skip &&
     ++	test_path_is_missing .git/rebase-apply &&
     ++	git rev-parse empty-commit^ >expected &&
     ++	git rev-parse HEAD >actual &&
     ++	test_cmp expected actual
     ++'
      +
     -+test_expect_success 'record as an empty commit in the middle of an am session' '
     ++test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
      +	git checkout empty-commit^ &&
      +	test_must_fail git am empty-commit.patch >err &&
      +	grep "Patch is empty." err &&
     -+	grep "If you want to keep recording it, run \"git am --allow-empty\"." err &&
     ++	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
      +	git am --allow-empty &&
     ++	test_path_is_missing .git/rebase-apply &&
      +	git show empty-commit --format="%s" >expected &&
      +	git show HEAD --format="%s" >actual &&
      +	test_cmp actual expected
     @@ t/t7512-status-help.sh: test_expect_success 'status in an am session: empty patc
       On branch am_empty
       You are in the middle of an am session.
       The current patch is empty.
     -+  (use "git am --allow-empty" to keep recording this empty patch)
     ++  (use "git am --allow-empty" to record this patch as an empty commit)
         (use "git am --skip" to skip this patch)
         (use "git am --abort" to restore the original branch)
       
     @@ wt-status.c: static void show_am_in_progress(struct wt_status *s,
       				_("  (fix conflicts and then run \"git am --continue\")"));
      +		else
      +			status_printf_ln(s, color,
     -+				_("  (use \"git am --allow-empty\" to keep recording this empty patch)"));
     ++				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
       		status_printf_ln(s, color,
       			_("  (use \"git am --skip\" to skip this patch)"));
       		status_printf_ln(s, color,

-- 
gitgitgadget

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

* [PATCH v13 1/3] doc: git-format-patch: describe the option --always
  2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
@ 2021-11-30  9:55                         ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  9:55                         ` [PATCH v13 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                           ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-30  9:55 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v13 2/3] am: support --empty=<option> to handle empty patches
  2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-11-30  9:55                         ` [PATCH v13 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  9:55                         ` 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  9:55                         ` [PATCH v13 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-30  9:55 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  8 ++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..ba17063f621 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,13 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message that lacks a patch. 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v13 3/3] am: support --allow-empty to record specific empty patches
  2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-11-30  9:55                         ` [PATCH v13 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-11-30  9:55                         ` [PATCH v13 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-11-30  9:55                         ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-11-30  9:55 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  6 +++++-
 builtin/am.c             | 29 ++++++++++++++++++++---------
 t/t4150-am.sh            | 24 ++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  3 +++
 5 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index ba17063f621..f2c5e3e37e0 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -199,6 +199,10 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	Record the empty patch as an empty commit with
+	the contents of the e-mail message as its log.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index cc6512275aa..58c7638b9bc 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
 				to_keep = 1;
 				break;
 			case DIE_EMPTY_COMMIT:
-				printf_ln(_("Patch is empty."));
+				printf_ln(_("Patch is empty.\n"
+					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
 				die_user_resolve(state);
 				break;
 			}
@@ -1898,19 +1899,24 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when there is no changes.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
 	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
-		die_user_resolve(state);
+		if (allow_empty)
+			printf_ln(_("No changes - record it as an empty commit."));
+		else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
 	}
 
 	if (unmerged_cache()) {
@@ -2237,7 +2243,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY
 };
 
 struct resume_mode {
@@ -2390,6 +2397,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2498,7 +2508,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 8c8bd4db220..3e60460022f 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1201,4 +1201,28 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	test_cmp actual expected
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..9309becfe03 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
 On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --skip" to skip this patch)
   (use "git am --abort" to restore the original branch)
 
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..d578a0e9192 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
 		if (!s->state.am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
-- 
gitgitgadget

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

* [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
                                           ` (2 preceding siblings ...)
  2021-11-30  9:55                         ` [PATCH v13 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-01  3:37                         ` Aleen via GitGitGadget
  2021-12-01  3:37                           ` [PATCH v14 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                             ` (3 more replies)
  3 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-12-01  3:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

----------------------------------------------------------------------------

Changes since v13:

 1. add an additional description about the 'die' value.

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           | 16 +++++-
 Documentation/git-format-patch.txt |  6 ++-
 builtin/am.c                       | 82 +++++++++++++++++++++++++-----
 t/t4150-am.sh                      | 73 ++++++++++++++++++++++++++
 t/t7512-status-help.sh             |  1 +
 wt-status.c                        |  3 ++
 6 files changed, 166 insertions(+), 15 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v14
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v14
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v13:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  a3e850bab7d ! 2:  b6a04fc12df am: support --empty=<option> to handle empty patches
     @@ Documentation/git-am.txt: OPTIONS
       
      +--empty=(die|drop|keep)::
      +	By default, or when the option is set to 'die', the command
     -+	errors out on an input e-mail message that lacks a patch. When
     -+	this option is set to 'drop', skip such an e-mail message instead.
     ++	errors out on an input e-mail message lacking a patch
     ++	and stops into 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.
      +
 3:  08bd397ae7a ! 3:  cbd822d4340 am: support --allow-empty to record specific empty patches
     @@ Documentation/git-am.txt: default.   You can use `--no-utf8` to override this.
       	Defaults to `raw`.
       
      +--allow-empty::
     -+	Record the empty patch as an empty commit with
     ++	After a patch failure on an input e-mail message lacking a patch,
     ++	the user can still record the empty patch as an empty commit with
      +	the contents of the e-mail message as its log.
      +
       DISCUSSION

-- 
gitgitgadget

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

* [PATCH v14 1/3] doc: git-format-patch: describe the option --always
  2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
@ 2021-12-01  3:37                           ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-01  3:37                           ` [PATCH v14 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-01  3:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v14 2/3] am: support --empty=<option> to handle empty patches
  2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-01  3:37                           ` [PATCH v14 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-01  3:37                           ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-03 22:30                             ` Johannes Schindelin
  2021-12-01  3:37                           ` [PATCH v14 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-01  3:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..cf9cace9678 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, or when the option is set to 'die', the command
+	errors out on an input e-mail message lacking a patch
+	and stops into 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..cc6512275aa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	DIE_EMPTY_COMMIT = 0,  /* output errors */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..8c8bd4db220 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=die empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v14 3/3] am: support --allow-empty to record specific empty patches
  2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-01  3:37                           ` [PATCH v14 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-12-01  3:37                           ` [PATCH v14 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-01  3:37                           ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-02  0:58                             ` Junio C Hamano
  2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-01  3:37 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  7 ++++++-
 builtin/am.c             | 29 ++++++++++++++++++++---------
 t/t4150-am.sh            | 24 ++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  3 +++
 5 files changed, 54 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index cf9cace9678..b6c065b3f7a 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	After a patch failure on an input e-mail message lacking a patch,
+	the user can still record the empty patch as an empty commit with
+	the contents of the e-mail message as its log.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index cc6512275aa..58c7638b9bc 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
 				to_keep = 1;
 				break;
 			case DIE_EMPTY_COMMIT:
-				printf_ln(_("Patch is empty."));
+				printf_ln(_("Patch is empty.\n"
+					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
 				die_user_resolve(state);
 				break;
 			}
@@ -1898,19 +1899,24 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when there is no changes.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
 	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
-		die_user_resolve(state);
+		if (allow_empty)
+			printf_ln(_("No changes - record it as an empty commit."));
+		else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
 	}
 
 	if (unmerged_cache()) {
@@ -2237,7 +2243,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY
 };
 
 struct resume_mode {
@@ -2390,6 +2397,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2498,7 +2508,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 8c8bd4db220..3e60460022f 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1201,4 +1201,28 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	test_cmp actual expected
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..9309becfe03 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
 On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --skip" to skip this patch)
   (use "git am --abort" to restore the original branch)
 
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..d578a0e9192 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
 		if (!s->state.am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
-- 
gitgitgadget

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

* Re: [PATCH v14 3/3] am: support --allow-empty to record specific empty patches
  2021-12-01  3:37                           ` [PATCH v14 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-02  0:58                             ` Junio C Hamano
  2021-12-06  1:35                               ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Junio C Hamano @ 2021-12-02  0:58 UTC (permalink / raw)
  To: ""徐沛文  via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

""徐沛文 (Aleen)" via GitGitGadget"  <gitgitgadget@gmail.com>
writes:

> +test_expect_success 'skip an empty patch in the middle of an am session' '
> +	git checkout empty-commit^ &&
> +	test_must_fail git am empty-commit.patch >err &&
> +	grep "Patch is empty." err &&
> +	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
> +	git am --skip &&
> +	test_path_is_missing .git/rebase-apply &&
> +	git rev-parse empty-commit^ >expected &&
> +	git rev-parse HEAD >actual &&
> +	test_cmp expected actual
> +'
> +
> +test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
> +	git checkout empty-commit^ &&
> +	test_must_fail git am empty-commit.patch >err &&
> +	grep "Patch is empty." err &&
> +	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
> +	git am --allow-empty &&
> +	test_path_is_missing .git/rebase-apply &&
> +	git show empty-commit --format="%s" >expected &&
> +	git show HEAD --format="%s" >actual &&
> +	test_cmp actual expected
> +'

These two are "positive" tests, i.e. the feature does the right
thing when used in the expected situation.

I am worried about the cases where "--allow-empty" is used and
creates a commit that is empty, when it is reasonably expected to
either create a non-empty commit or still fail the same way to give
the user another chance to try.

For example, when "git am -3" fails on such an email message (there
are two failure modes: (a) one results in an unmerged index due to
merge conflicts, (b) the other results in a clean index due to not
finding the blob object recorded as a preimage), what happens when
the user runs "git am --allow-empty", after:

 (1) doing nothing since the failed "git am -3" application,
 (2) resolving the conflicts in working tree, or
 (3) resolving the conflicts in working tree and recording the
     resolution in the index.

There are 6 cases in the above matrix, and "git am --allow-empty"
should not create an empty commit in any one of them.  In 1a and 1b,
"git am --allow-empty" should continue to fail ("git am --skip"
would be a way to ignore and proceed).  3a and 3b should record a
non-empty commit as the result.

Similarly, when an email message with a patch is given to "git am"
and the application fails, and the user runs "git am --allow-empty"
without doing anything in the working tree, what happens?  It should
not happily create an empty commit with only the log message,
without the change, but continue to fail.  If the user resolves and
registers the resolution to the index before "git am --allow-empty",
then the command should create a non-empty commit.

Having positive tests are of course important to avoid regression in
the future, but negative tests, the new and shiny feature does not
misbehave when it shouldn't even kick in, is even more important.

Thanks.

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

* Re: [PATCH v14 2/3] am: support --empty=<option> to handle empty patches
  2021-12-01  3:37                           ` [PATCH v14 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-03 22:30                             ` Johannes Schindelin
  0 siblings, 0 replies; 129+ messages in thread
From: Johannes Schindelin @ 2021-12-03 22:30 UTC (permalink / raw)
  To: 徐沛文 (Aleen) via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Elijah Newren,
	Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

[-- Attachment #1: Type: text/plain, Size: 774 bytes --]

Hi Aleen,

On Wed, 1 Dec 2021, 徐沛文 (Aleen) via GitGitGadget wrote:

> diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
> index 0a4a984dfde..cf9cace9678 100644
> --- a/Documentation/git-am.txt
> +++ b/Documentation/git-am.txt
> @@ -16,6 +16,7 @@ SYNOPSIS
>  	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
>  	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
>  	 [--quoted-cr=<action>]
> +	 [--empty=(die|drop|keep)]

I have to agree with Elijah that "to die" is used differently in Git's
context. This should probably be called "stop" instead. Or "error". But
not "die".

Ciao,
Dscho

P.S.: The enum value should probably have an `_ON_` in it, i.e.
`STOP_ON_EMPTY_COMMIT` or `ERROR_ON_EMPTY_COMMIT`.

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

* Re: [PATCH v14 3/3] am: support --allow-empty to record specific empty patches
  2021-12-02  0:58                             ` Junio C Hamano
@ 2021-12-06  1:35                               ` Aleen 徐沛文
  0 siblings, 0 replies; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-12-06  1:35 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ""徐沛文 (Aleen)" via
	GitGitGadget",
	git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen

Dears Hamano,

    For those 6 cases, it seems `git-am` should only work when the am session
    was stopped in the middle according to an empty patch, but not when the index
    has not changed. Does that mean we should record a new state for the am state?
    If that's right, I will try to patch it via version 15, and try to add additional
    test cases for these 6 situations. I was a little busy those days and sorry for
    the late answer.

Aleen

> These two are "positive" tests, i.e. the feature does the right
> thing when used in the expected situation.
> 
> I am worried about the cases where "--allow-empty" is used and
> creates a commit that is empty, when it is reasonably expected to
> either create a non-empty commit or still fail the same way to give
> the user another chance to try.
> 
> For example, when "git am -3" fails on such an email message (there
> are two failure modes: (a) one results in an unmerged index due to
> merge conflicts, (b) the other results in a clean index due to not
> finding the blob object recorded as a preimage), what happens when
> the user runs "git am --allow-empty", after:
> 
>  (1) doing nothing since the failed "git am -3" application,
>  (2) resolving the conflicts in working tree, or
>  (3) resolving the conflicts in working tree and recording the
>      resolution in the index.
> 
> There are 6 cases in the above matrix, and "git am --allow-empty"
> should not create an empty commit in any one of them.  In 1a and 1b,
> "git am --allow-empty" should continue to fail ("git am --skip"
> would be a way to ignore and proceed).  3a and 3b should record a
> non-empty commit as the result.
> 
> Similarly, when an email message with a patch is given to "git am"
> and the application fails, and the user runs "git am --allow-empty"
> without doing anything in the working tree, what happens?  It should
> not happily create an empty commit with only the log message,
> without the change, but continue to fail.  If the user resolves and
> registers the resolution to the index before "git am --allow-empty",
> then the command should create a non-empty commit.
> 
> Having positive tests are of course important to avoid regression in
> the future, but negative tests, the new and shiny feature does not
> misbehave when it shouldn't even kick in, is even more important.
> 
> Thanks.

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

* [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
                                             ` (2 preceding siblings ...)
  2021-12-01  3:37                           ` [PATCH v14 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-06  9:41                           ` Aleen via GitGitGadget
  2021-12-06  9:41                             ` [PATCH v15 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                               ` (3 more replies)
  3 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-12-06  9:41 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

----------------------------------------------------------------------------

Changes since v13:

 1. add an additional description about the 'die' value.

----------------------------------------------------------------------------

Changes since v14:

 1. reimplement the 'die' value and stop the whole session. (Expected a
    reroll)
 2. the --allow-empty option is a valid resume value only when: (Expected a
    reroll)
    * index has not changed
    * lacking a patch

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           |  16 ++++-
 Documentation/git-format-patch.txt |   6 +-
 builtin/am.c                       | 100 ++++++++++++++++++++++++----
 t/t4150-am.sh                      | 103 +++++++++++++++++++++++++++++
 t/t7512-status-help.sh             |   1 +
 wt-status.c                        |   3 +
 6 files changed, 214 insertions(+), 15 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v15
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v15
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v14:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  b6a04fc12df ! 2:  8ec8e212672 am: support --empty=<option> to handle empty patches
     @@ Documentation/git-am.txt: OPTIONS
       	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
       
      +--empty=(die|drop|keep)::
     -+	By default, or when the option is set to 'die', the command
     -+	errors out on an input e-mail message lacking a patch
     -+	and stops into the middle of the current am session. When this
     -+	option is set to 'drop', skip such an e-mail message instead.
     ++	By default, the command errors out on an input e-mail message
     ++	lacking a patch and stops into the middle of the current am session.
     ++	When this option is set to 'die', the whole session dies with error.
     ++	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.
      +
     @@ builtin/am.c: enum show_patch_type {
       };
       
      +enum empty_action {
     -+	DIE_EMPTY_COMMIT = 0,  /* output errors */
     ++	ERR_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
     ++	DIE_EMPTY_COMMIT,      /* output errors and stop the whole am session */
      +	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
      +	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
      +};
     @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
      +				to_keep = 1;
      +				break;
      +			case DIE_EMPTY_COMMIT:
     ++				am_destroy(state);
     ++				die(_("Patch is empty."));
     ++				break;
     ++			case ERR_EMPTY_COMMIT:
      +				printf_ln(_("Patch is empty."));
      +				die_user_resolve(state);
      +				break;
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
       		  N_("GPG-sign commits"),
       		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
     -+		OPT_CALLBACK_F(0, "empty", &state.empty_type, "{drop,keep,die}",
     ++		OPT_CALLBACK_F(ERR_EMPTY_COMMIT, "empty", &state.empty_type, "{die,drop,keep}",
      +		  N_("how to handle empty patches"),
      +		  PARSE_OPT_NONEG, am_option_parse_empty),
       		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +	test_cmp expected err
      +'
      +
     -+test_expect_success 'a message without a patch is an error (default)' '
     ++test_expect_success 'a message without a patch is an error and stop in the middle of an am session (default)' '
      +	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am empty-commit.patch >err &&
     ++	test_path_is_dir .git/rebase-apply &&
      +	grep "Patch is empty" err
      +'
      +
     -+test_expect_success 'a message without a patch is an error where an explicit "--empty=die" is given' '
     -+	test_when_finished "git am --abort || :" &&
     -+	test_must_fail git am --empty=die empty-commit.patch >err &&
     -+	grep "Patch is empty." err
     ++test_expect_success 'a message without a patch is an error and exit where an explicit "--empty=die" is given' '
     ++	test_must_fail git am --empty=die empty-commit.patch 2>err &&
     ++	test_path_is_missing .git/rebase-apply &&
     ++	grep "fatal: Patch is empty." err
      +'
      +
      +test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
 3:  cbd822d4340 ! 3:  d669406a312 am: support --allow-empty to record specific empty patches
     @@ Commit message
          am: support --allow-empty to record specific empty patches
      
          This option helps to record specific empty patches in the middle
     -    of an am session.
     +    of an am session. However, it is a valid resume value only when:
     +
     +        1. index has not changed
     +        2. lacking a branch
      
          Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
     @@ Documentation/git-am.txt: default.   You can use `--no-utf8` to override this.
      
       ## builtin/am.c ##
      @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
     - 				to_keep = 1;
     + 				die(_("Patch is empty."));
       				break;
     - 			case DIE_EMPTY_COMMIT:
     + 			case ERR_EMPTY_COMMIT:
      -				printf_ln(_("Patch is empty."));
      +				printf_ln(_("Patch is empty.\n"
      +					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
     @@ builtin/am.c: next:
      -static void am_resolve(struct am_state *state)
      +static void am_resolve(struct am_state *state, int allow_empty)
       {
     ++	int index_changed;
     ++
       	validate_resume_state(state);
       
       	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
       
     - 	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
     +-	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
      -		printf_ln(_("No changes - did you forget to use 'git add'?\n"
      -			"If there is nothing left to stage, chances are that something else\n"
      -			"already introduced the same changes; you might want to skip this patch."));
     --		die_user_resolve(state);
     ++	/**
     ++	 * "--allow-empty" is a valid resume value only when:
     ++	 *   1. index has not changed
     ++	 *   2. lacking a patch
     ++	 */
     ++	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
     ++	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {
     ++		printf_ln(_("Invalid resume value."));
     + 		die_user_resolve(state);
     + 	}
     + 
     ++	if (!index_changed) {
      +		if (allow_empty)
      +			printf_ln(_("No changes - record it as an empty commit."));
      +		else {
     @@ builtin/am.c: next:
      +				    "already introduced the same changes; you might want to skip this patch."));
      +			die_user_resolve(state);
      +		}
     - 	}
     - 
     ++	}
     ++
       	if (unmerged_cache()) {
     + 		printf_ln(_("You still have unmerged paths in your index.\n"
     + 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
      @@ builtin/am.c: enum resume_type {
       	RESUME_SKIP,
       	RESUME_ABORT,
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
      +	git show HEAD --format="%s" >actual &&
      +	test_cmp actual expected
      +'
     ++
     ++test_expect_success 'cannot create empty commits when the index is changed' '
     ++	git checkout empty-commit^ &&
     ++	test_must_fail git am empty-commit.patch >err &&
     ++	: >empty-file &&
     ++	git add empty-file &&
     ++	test_must_fail git am --allow-empty >err &&
     ++	grep "Invalid resume value." err
     ++'
     ++
     ++test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
     ++	test_when_finished "git am --abort || :" &&
     ++	git rev-parse HEAD >expected &&
     ++	test_must_fail git am seq.patch &&
     ++	test_must_fail git am --allow-empty >err &&
     ++	grep "Invalid resume value." err &&
     ++	git rev-parse HEAD >actual &&
     ++	test_cmp actual expected
     ++'
     ++
     ++test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
     ++	test_when_finished "git am --abort || :" &&
     ++	git rev-parse HEAD >expected &&
     ++	test_must_fail git am -3 seq.patch &&
     ++	test_must_fail git am --allow-empty >err &&
     ++	grep "Invalid resume value." err &&
     ++	git rev-parse HEAD >actual &&
     ++	test_cmp actual expected
     ++'
      +
       test_done
      

-- 
gitgitgadget

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

* [PATCH v15 1/3] doc: git-format-patch: describe the option --always
  2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
@ 2021-12-06  9:41                             ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-06  9:41                             ` [PATCH v15 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                               ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-06  9:41 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v15 2/3] am: support --empty=<option> to handle empty patches
  2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-06  9:41                             ` [PATCH v15 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-06  9:41                             ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-06  9:41                             ` [PATCH v15 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-06  9:41 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('die', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 ++++++
 builtin/am.c             | 60 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 50 +++++++++++++++++++++++++++++++++
 3 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..4c31c39bf81 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
 	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(die|drop|keep)::
+	By default, the command errors out on an input e-mail message
+	lacking a patch and stops into the middle of the current am session.
+	When this option is set to 'die', the whole session dies with error.
+	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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..43a3b1cf038 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,13 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	ERR_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
+	DIE_EMPTY_COMMIT,      /* output errors and stop the whole am session */
+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +125,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +186,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+				     const char *arg, int unset)
+{
+	int *opt_value = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+
+	if (!strcmp(arg, "die"))
+		*opt_value = DIE_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1275,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1785,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1815,31 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case DIE_EMPTY_COMMIT:
+				am_destroy(state);
+				die(_("Patch is empty."));
+				break;
+			case ERR_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1873,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2404,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(ERR_EMPTY_COMMIT, "empty", &state.empty_type, "{die,drop,keep}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..f2b765644e5 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,48 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error and stop in the middle of an am session (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	test_path_is_dir .git/rebase-apply &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error and exit where an explicit "--empty=die" is given' '
+	test_must_fail git am --empty=die empty-commit.patch 2>err &&
+	test_path_is_missing .git/rebase-apply &&
+	grep "fatal: Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v15 3/3] am: support --allow-empty to record specific empty patches
  2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-06  9:41                             ` [PATCH v15 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-12-06  9:41                             ` [PATCH v15 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-06  9:41                             ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-06  9:41 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session. However, it is a valid resume value only when:

    1. index has not changed
    2. lacking a branch

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  7 +++++-
 builtin/am.c             | 42 ++++++++++++++++++++++++-------
 t/t4150-am.sh            | 53 ++++++++++++++++++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  3 +++
 5 files changed, 96 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 4c31c39bf81..a5223143222 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(die|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	After a patch failure on an input e-mail message lacking a patch,
+	the user can still record the empty patch as an empty commit with
+	the contents of the e-mail message as its log.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index 43a3b1cf038..974924cb83d 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1830,7 +1830,8 @@ static void am_run(struct am_state *state, int resume)
 				die(_("Patch is empty."));
 				break;
 			case ERR_EMPTY_COMMIT:
-				printf_ln(_("Patch is empty."));
+				printf_ln(_("Patch is empty.\n"
+					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
 				die_user_resolve(state);
 				break;
 			}
@@ -1903,21 +1904,39 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when there is no changes.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
+	int index_changed;
+
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
-	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
+	/**
+	 * "--allow-empty" is a valid resume value only when:
+	 *   1. index has not changed
+	 *   2. lacking a patch
+	 */
+	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
+	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {
+		printf_ln(_("Invalid resume value."));
 		die_user_resolve(state);
 	}
 
+	if (!index_changed) {
+		if (allow_empty)
+			printf_ln(_("No changes - record it as an empty commit."));
+		else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
+	}
+
 	if (unmerged_cache()) {
 		printf_ln(_("You still have unmerged paths in your index.\n"
 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
@@ -2242,7 +2261,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY
 };
 
 struct resume_mode {
@@ -2395,6 +2415,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2503,7 +2526,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index f2b765644e5..3f81aa3d6a1 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1202,4 +1202,57 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	test_cmp actual expected
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when the index is changed' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	: >empty-file &&
+	git add empty-file &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am -3 seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..9309becfe03 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
 On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --skip" to skip this patch)
   (use "git am --abort" to restore the original branch)
 
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..d578a0e9192 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
 		if (!s->state.am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
-- 
gitgitgadget

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

* [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
                                               ` (2 preceding siblings ...)
  2021-12-06  9:41                             ` [PATCH v15 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07  5:01                             ` Aleen via GitGitGadget
  2021-12-07  5:01                               ` [PATCH v16 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                                 ` (3 more replies)
  3 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-12-07  5:01 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

----------------------------------------------------------------------------

Changes since v13:

 1. add an additional description about the 'die' value.

----------------------------------------------------------------------------

Changes since v14:

 1. reimplement the 'die' value and stop the whole session. (Expected a
    reroll)
 2. the --allow-empty option is a valid resume value only when: (Expected a
    reroll)
    * index has not changed
    * lacking a patch

----------------------------------------------------------------------------

Changes since v15:

 1. rename "die" to "stop".

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           |  16 ++++-
 Documentation/git-format-patch.txt |   6 +-
 builtin/am.c                       |  90 +++++++++++++++++++++----
 t/t4150-am.sh                      | 102 +++++++++++++++++++++++++++++
 t/t7512-status-help.sh             |   1 +
 wt-status.c                        |   3 +
 6 files changed, 203 insertions(+), 15 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v16
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v16
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v15:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  8ec8e212672 ! 2:  b9e03f2342b am: support --empty=<option> to handle empty patches
     @@ Commit message
          commits that emit no changes, the 'git-am' command should also
          support an option, named as '--empty', to specify how to handle
          those empty patches. In this commit, we have implemented three
     -    valid options ('die', 'drop' and 'keep').
     +    valid options ('stop', 'drop' and 'keep').
      
          Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
     @@ Documentation/git-am.txt: SYNOPSIS
       	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
       	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
       	 [--quoted-cr=<action>]
     -+	 [--empty=(die|drop|keep)]
     ++	 [--empty=(stop|drop|keep)]
       	 [(<mbox> | <Maildir>)...]
       'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
       
     @@ Documentation/git-am.txt: OPTIONS
       --quoted-cr=<action>::
       	This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
       
     -+--empty=(die|drop|keep)::
     -+	By default, the command errors out on an input e-mail message
     -+	lacking a patch and stops into the middle of the current am session.
     -+	When this option is set to 'die', the whole session dies with error.
     -+	When this option is set to 'drop', skip such an e-mail message instead.
     ++--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 into 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.
      +
     @@ builtin/am.c: enum show_patch_type {
       };
       
      +enum empty_action {
     -+	ERR_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
     -+	DIE_EMPTY_COMMIT,      /* output errors and stop the whole am session */
     -+	DROP_EMPTY_COMMIT,     /* skip with a notice message, unless "--quiet" has been passed */
     -+	KEEP_EMPTY_COMMIT      /* keep recording as empty commits */
     ++	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
     ++	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
     ++	KEEP_EMPTY_COMMIT          /* keep recording as empty commits */
      +};
      +
       struct am_state {
     @@ builtin/am.c: static int am_option_parse_quoted_cr(const struct option *opt,
      +
      +	BUG_ON_OPT_NEG(unset);
      +
     -+	if (!strcmp(arg, "die"))
     -+		*opt_value = DIE_EMPTY_COMMIT;
     ++	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"))
     @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
      +			case KEEP_EMPTY_COMMIT:
      +				to_keep = 1;
      +				break;
     -+			case DIE_EMPTY_COMMIT:
     -+				am_destroy(state);
     -+				die(_("Patch is empty."));
     -+				break;
     -+			case ERR_EMPTY_COMMIT:
     ++			case STOP_ON_EMPTY_COMMIT:
      +				printf_ln(_("Patch is empty."));
      +				die_user_resolve(state);
      +				break;
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
       		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
       		  N_("GPG-sign commits"),
       		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
     -+		OPT_CALLBACK_F(ERR_EMPTY_COMMIT, "empty", &state.empty_type, "{die,drop,keep}",
     ++		OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
      +		  N_("how to handle empty patches"),
      +		  PARSE_OPT_NONEG, am_option_parse_empty),
       		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +	test_cmp expected err
      +'
      +
     -+test_expect_success 'a message without a patch is an error and stop in the middle of an am session (default)' '
     ++test_expect_success 'a message without a patch is an error (default)' '
      +	test_when_finished "git am --abort || :" &&
      +	test_must_fail git am empty-commit.patch >err &&
     -+	test_path_is_dir .git/rebase-apply &&
      +	grep "Patch is empty" err
      +'
      +
     -+test_expect_success 'a message without a patch is an error and exit where an explicit "--empty=die" is given' '
     -+	test_must_fail git am --empty=die empty-commit.patch 2>err &&
     -+	test_path_is_missing .git/rebase-apply &&
     -+	grep "fatal: Patch is empty." err
     ++test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
     ++	test_when_finished "git am --abort || :" &&
     ++	test_must_fail git am --empty=stop empty-commit.patch >err &&
     ++	grep "Patch is empty." err
      +'
      +
      +test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
 3:  d669406a312 ! 3:  abcdfa1b375 am: support --allow-empty to record specific empty patches
     @@ Commit message
       ## Documentation/git-am.txt ##
      @@ Documentation/git-am.txt: SYNOPSIS
       	 [--quoted-cr=<action>]
     - 	 [--empty=(die|drop|keep)]
     + 	 [--empty=(stop|drop|keep)]
       	 [(<mbox> | <Maildir>)...]
      -'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
      +'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
     @@ Documentation/git-am.txt: default.   You can use `--no-utf8` to override this.
      
       ## builtin/am.c ##
      @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
     - 				die(_("Patch is empty."));
     + 				to_keep = 1;
       				break;
     - 			case ERR_EMPTY_COMMIT:
     + 			case STOP_ON_EMPTY_COMMIT:
      -				printf_ln(_("Patch is empty."));
      +				printf_ln(_("Patch is empty.\n"
      +					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
     @@ builtin/am.c: next:
        * all the hard work, and we do not have to do any patch application. Just
      - * trust and commit what the user has in the index and working tree.
      + * trust and commit what the user has in the index and working tree. If `allow_empty`
     -+ * is true, commit as an empty commit when there is no changes.
     ++ * is true, commit as an empty commit when index had not changed and lacking a patch.
        */
      -static void am_resolve(struct am_state *state)
      +static void am_resolve(struct am_state *state, int allow_empty)
     @@ builtin/am.c: next:
      -		printf_ln(_("No changes - did you forget to use 'git add'?\n"
      -			"If there is nothing left to stage, chances are that something else\n"
      -			"already introduced the same changes; you might want to skip this patch."));
     -+	/**
     -+	 * "--allow-empty" is a valid resume value only when:
     -+	 *   1. index has not changed
     -+	 *   2. lacking a patch
     -+	 */
      +	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
      +	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {
      +		printf_ln(_("Invalid resume value."));

-- 
gitgitgadget

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

* [PATCH v16 1/3] doc: git-format-patch: describe the option --always
  2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
@ 2021-12-07  5:01                               ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  5:01                               ` [PATCH v16 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                                 ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-07  5:01 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v16 2/3] am: support --empty=<option> to handle empty patches
  2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-07  5:01                               ` [PATCH v16 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07  5:01                               ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  5:01                               ` [PATCH v16 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-07  5:01 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('stop', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..7676bd58ae7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ 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 into 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..f45aa33366f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT          /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case STOP_ON_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..ce8e8e79ace 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=stop empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v16 3/3] am: support --allow-empty to record specific empty patches
  2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-07  5:01                               ` [PATCH v16 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  5:01                               ` [PATCH v16 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07  5:01                               ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-07  5:01 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session. However, it is a valid resume value only when:

    1. index has not changed
    2. lacking a branch

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  7 +++++-
 builtin/am.c             | 37 +++++++++++++++++++++-------
 t/t4150-am.sh            | 53 ++++++++++++++++++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  3 +++
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 7676bd58ae7..8d3aa552a20 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	After a patch failure on an input e-mail message lacking a patch,
+	the user can still record the empty patch as an empty commit with
+	the contents of the e-mail message as its log.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index f45aa33366f..05cbb42dd12 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
 				to_keep = 1;
 				break;
 			case STOP_ON_EMPTY_COMMIT:
-				printf_ln(_("Patch is empty."));
+				printf_ln(_("Patch is empty.\n"
+					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
 				die_user_resolve(state);
 				break;
 			}
@@ -1898,21 +1899,34 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index had not changed and lacking a patch.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
+	int index_changed;
+
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
-	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
+	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
+	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {
+		printf_ln(_("Invalid resume value."));
 		die_user_resolve(state);
 	}
 
+	if (!index_changed) {
+		if (allow_empty)
+			printf_ln(_("No changes - record it as an empty commit."));
+		else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
+	}
+
 	if (unmerged_cache()) {
 		printf_ln(_("You still have unmerged paths in your index.\n"
 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
@@ -2237,7 +2251,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY
 };
 
 struct resume_mode {
@@ -2390,6 +2405,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2498,7 +2516,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index ce8e8e79ace..e154b4201b7 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1201,4 +1201,57 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	test_cmp actual expected
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when the index is changed' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	: >empty-file &&
+	git add empty-file &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am -3 seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..9309becfe03 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
 On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --skip" to skip this patch)
   (use "git am --abort" to restore the original branch)
 
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..d578a0e9192 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
 		if (!s->state.am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
-- 
gitgitgadget

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

* [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
                                                 ` (2 preceding siblings ...)
  2021-12-07  5:01                               ` [PATCH v16 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07  8:31                               ` Aleen via GitGitGadget
  2021-12-07  8:31                                 ` [PATCH v17 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                                   ` (4 more replies)
  3 siblings, 5 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-12-07  8:31 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

----------------------------------------------------------------------------

Changes since v13:

 1. add an additional description about the 'die' value.

----------------------------------------------------------------------------

Changes since v14:

 1. reimplement the 'die' value and stop the whole session. (Expected a
    reroll)
 2. the --allow-empty option is a valid resume value only when: (Expected a
    reroll)
    * index has not changed
    * lacking a patch

----------------------------------------------------------------------------

Changes since v15:

 1. rename "die" to "stop".

----------------------------------------------------------------------------

Changes since v16:

 1. fix typo from "had" to "has" in the comment.

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           |  16 ++++-
 Documentation/git-format-patch.txt |   6 +-
 builtin/am.c                       |  90 +++++++++++++++++++++----
 t/t4150-am.sh                      | 102 +++++++++++++++++++++++++++++
 t/t7512-status-help.sh             |   1 +
 wt-status.c                        |   3 +
 6 files changed, 203 insertions(+), 15 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v17
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v17
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v16:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  b9e03f2342b = 2:  b9e03f2342b am: support --empty=<option> to handle empty patches
 3:  abcdfa1b375 ! 3:  ea2dc088b37 am: support --allow-empty to record specific empty patches
     @@ builtin/am.c: next:
        * all the hard work, and we do not have to do any patch application. Just
      - * trust and commit what the user has in the index and working tree.
      + * trust and commit what the user has in the index and working tree. If `allow_empty`
     -+ * is true, commit as an empty commit when index had not changed and lacking a patch.
     ++ * is true, commit as an empty commit when index has not changed and lacking a patch.
        */
      -static void am_resolve(struct am_state *state)
      +static void am_resolve(struct am_state *state, int allow_empty)

-- 
gitgitgadget

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

* [PATCH v17 1/3] doc: git-format-patch: describe the option --always
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
@ 2021-12-07  8:31                                 ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  8:31                                 ` [PATCH v17 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-07  8:31 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v17 2/3] am: support --empty=<option> to handle empty patches
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-07  8:31                                 ` [PATCH v17 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07  8:31                                 ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07 18:12                                   ` Junio C Hamano
  2021-12-07  8:31                                 ` [PATCH v17 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
                                                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-07  8:31 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('stop', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++++
 builtin/am.c             | 55 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 49 +++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..7676bd58ae7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ 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 into 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..f45aa33366f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT          /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				break;
+			case STOP_ON_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2399,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..ce8e8e79ace 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,47 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=stop empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v17 3/3] am: support --allow-empty to record specific empty patches
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-07  8:31                                 ` [PATCH v17 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-12-07  8:31                                 ` [PATCH v17 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07  8:31                                 ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-07 18:23                                   ` Junio C Hamano
  2021-12-07 18:24                                 ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Junio C Hamano
  2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
  4 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-07  8:31 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session. However, it is a valid resume value only when:

    1. index has not changed
    2. lacking a branch

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  7 +++++-
 builtin/am.c             | 37 +++++++++++++++++++++-------
 t/t4150-am.sh            | 53 ++++++++++++++++++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  3 +++
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 7676bd58ae7..8d3aa552a20 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	After a patch failure on an input e-mail message lacking a patch,
+	the user can still record the empty patch as an empty commit with
+	the contents of the e-mail message as its log.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index f45aa33366f..89a29ccac89 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
 				to_keep = 1;
 				break;
 			case STOP_ON_EMPTY_COMMIT:
-				printf_ln(_("Patch is empty."));
+				printf_ln(_("Patch is empty.\n"
+					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
 				die_user_resolve(state);
 				break;
 			}
@@ -1898,21 +1899,34 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
+	int index_changed;
+
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
-	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
+	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
+	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {
+		printf_ln(_("Invalid resume value."));
 		die_user_resolve(state);
 	}
 
+	if (!index_changed) {
+		if (allow_empty)
+			printf_ln(_("No changes - record it as an empty commit."));
+		else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
+	}
+
 	if (unmerged_cache()) {
 		printf_ln(_("You still have unmerged paths in your index.\n"
 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
@@ -2237,7 +2251,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY
 };
 
 struct resume_mode {
@@ -2390,6 +2405,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2498,7 +2516,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index ce8e8e79ace..e154b4201b7 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1201,4 +1201,57 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	test_cmp actual expected
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%s" >expected &&
+	git show HEAD --format="%s" >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when the index is changed' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	: >empty-file &&
+	git add empty-file &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am -3 seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	grep "Invalid resume value." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..9309becfe03 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
 On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --skip" to skip this patch)
   (use "git am --abort" to restore the original branch)
 
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..d578a0e9192 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
 		if (!s->state.am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
+		else
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
 		status_printf_ln(s, color,
-- 
gitgitgadget

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

* Re: [PATCH v17 2/3] am: support --empty=<option> to handle empty patches
  2021-12-07  8:31                                 ` [PATCH v17 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07 18:12                                   ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-12-07 18:12 UTC (permalink / raw)
  To: ""徐沛文  via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

""徐沛文 (Aleen)" via GitGitGadget"  <gitgitgadget@gmail.com>
writes:


This step look mostly good and well done, except for just a few
things that remain.

> +enum empty_action {
> +	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
> +	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
> +	KEEP_EMPTY_COMMIT          /* keep recording as empty commits */
> +};

It is friendly to future developers to end the last item in enum
with a comma, unless the current last item MUST stay to be the last
one even when they add new ones.  I.e.

	KEEP_EMPTY_COMMIT,          /* keep recording as empty commits */

>  struct am_state {
>  	/* state directory path */
>  	char *dir;
> @@ -118,6 +124,7 @@ struct am_state {
>  	int message_id;
>  	int scissors; /* enum scissors_type */
>  	int quoted_cr; /* enum quoted_cr_action */
> +	int empty_type; /* enum empty_action */

Mental note.  After this series graduates to 'master', at some point
in the future, we should clean these members up to be of their
respective enum types, not "int".

> @@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
>  	while (state->cur <= state->last) {
>  		const char *mail = am_path(state, msgnum(state));
>  		int apply_status;
> +		int to_keep;
>  
>  		reset_ident_date();
>  
> @@ -1792,8 +1814,27 @@ static void am_run(struct am_state *state, int resume)
>  		if (state->interactive && do_interactive(state))
>  			goto next;
>  
> +		to_keep = 0;
> +		if (is_empty_or_missing_file(am_path(state, "patch"))) {
> +			switch (state->empty_type) {
> +			case DROP_EMPTY_COMMIT:
> +				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
> +				goto next;
> +				break;
> +			case KEEP_EMPTY_COMMIT:
> +				to_keep = 1;

This causes the code that produces the "Applying" message jumped
over, so the user will not see anything done for this step.  

I think we want to mimic the above case arm and do something like

				say(state, stdout, _("Creating an empty commit: %.*s"),
					linelen(state->msg), state->msg);

to avoid being mum about what was done in this step.

> +				break;
> +			case STOP_ON_EMPTY_COMMIT:
> +				printf_ln(_("Patch is empty."));
> +				die_user_resolve(state);
> +				break;
> +			}
> +		}
> +
>  		if (run_applypatch_msg_hook(state))
>  			exit(1);
> +		if (to_keep)
> +			goto commit;
>  
>  		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
>  
> @@ -1827,6 +1868,7 @@ static void am_run(struct am_state *state, int resume)
>  			die_user_resolve(state);
>  		}
>  
> +commit:
>  		do_commit(state);
>  
>  next:

> +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
> +	git am --empty=keep empty-commit.patch &&
> +	test_path_is_missing .git/rebase-apply &&
> +	git show empty-commit --format="%s" >expected &&
> +	git show HEAD --format="%s" >actual &&

For the test data prepared by the earlier part of this patch, this
does not make a difference, but by using %B instead of %s, I think
you can catch a future bug that only keeps the subject intact while
munging the body of the message.

Other than these, looking quite good.

Thanks.

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

* Re: [PATCH v17 3/3] am: support --allow-empty to record specific empty patches
  2021-12-07  8:31                                 ` [PATCH v17 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07 18:23                                   ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-12-07 18:23 UTC (permalink / raw)
  To: ""徐沛文  via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

""徐沛文 (Aleen)" via GitGitGadget"  <gitgitgadget@gmail.com>
writes:

> From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
>  <aleen42@vip.qq.com>
>
> This option helps to record specific empty patches in the middle
> of an am session. However, it is a valid resume value only when:
>
>     1. index has not changed
>     2. lacking a branch
>
> Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
> ---
>  Documentation/git-am.txt |  7 +++++-
>  builtin/am.c             | 37 +++++++++++++++++++++-------
>  t/t4150-am.sh            | 53 ++++++++++++++++++++++++++++++++++++++++
>  t/t7512-status-help.sh   |  1 +
>  wt-status.c              |  3 +++
>  5 files changed, 91 insertions(+), 10 deletions(-)
>
> diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
> index 7676bd58ae7..8d3aa552a20 100644
> --- a/Documentation/git-am.txt
> +++ b/Documentation/git-am.txt
> @@ -18,7 +18,7 @@ SYNOPSIS
>  	 [--quoted-cr=<action>]
>  	 [--empty=(stop|drop|keep)]
>  	 [(<mbox> | <Maildir>)...]
> -'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
> +'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
>  
>  DESCRIPTION
>  -----------
> @@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
>  	the e-mail message; if `diff`, show the diff portion only.
>  	Defaults to `raw`.
>  
> +--allow-empty::
> +	After a patch failure on an input e-mail message lacking a patch,
> +	the user can still record the empty patch as an empty commit with
> +	the contents of the e-mail message as its log.

"The user can still do X" is not technically incorrect, but this is
not merely a possiblity but does actively does X, so

	After a patch failure on an input e-mail message lacking a patch,
	create an empty commit with the contents of the e-mail
	message as its log message.

or something.

> diff --git a/builtin/am.c b/builtin/am.c
> index f45aa33366f..89a29ccac89 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1825,7 +1825,8 @@ static void am_run(struct am_state *state, int resume)
>  				to_keep = 1;
>  				break;
>  			case STOP_ON_EMPTY_COMMIT:
> -				printf_ln(_("Patch is empty."));
> +				printf_ln(_("Patch is empty.\n"
> +					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));

It would be better to hide the new part of the message behind
advice_if_enabled(), and advise "--skip" first before giving
"--allow-empty" as another possibility.  It is rather a common
mistake to grab patches with the cover letter and see "am" die on
the cover letter.

If we were to add an advice message there, that is.  I am not sure
if it is necessary, especially since we give them in "git status".

> @@ -1898,21 +1899,34 @@ next:
>  /**
>   * Resume the current am session after patch application failure. The user did
>   * all the hard work, and we do not have to do any patch application. Just
> - * trust and commit what the user has in the index and working tree.
> + * trust and commit what the user has in the index and working tree. If `allow_empty`
> + * is true, commit as an empty commit when index has not changed and lacking a patch.
>   */
> -static void am_resolve(struct am_state *state)
> +static void am_resolve(struct am_state *state, int allow_empty)
>  {
> +	int index_changed;
> +
>  	validate_resume_state(state);
>  
>  	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
>  
> -	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
> -		printf_ln(_("No changes - did you forget to use 'git add'?\n"
> -			"If there is nothing left to stage, chances are that something else\n"
> -			"already introduced the same changes; you might want to skip this patch."));
> +	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
> +	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {

Overlong line.

> +		printf_ln(_("Invalid resume value."));

It is unclear to the user what "resume value" means.  I am guessing
that this is saying that the user gave us the "--allow-empty" option,
but it is an inappropriate option for the situation.  So

	"--allow-empty is not appropriate in this situation"

would be a slight improvement, but it is not clear what makes it
inappropriate, iow, what the situation is, to the users.

Stepping back a bit, if we disect that overlong condition:

	if (allow_empty &&
	    (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {

it seems to be computing something not quite right.  What we want
with "allow" empty is (1) if we have recorded changes to the index,
that is perfectly fine.  We'll create a non-empty commit, and (2) if
we do not have any change in the index because the input did not
have a patch, it is OK to create an empty commit out of the current
index.  But the condition is there only to complain about
"--allow-empty" given when either (1) we have changes, or (2) when
the input had a patch.  These are conditions that we can internally
ignore allow_empty settings and let the existing code handle the
message as before this patch.

>  		die_user_resolve(state);

And I do not see how "when you have resolved with this problem, do one
of these things" message would help in this situation.  Perhaps we
can get rid of the above block.  We still need to compute index_changed,
and make "no changes--we will die" conditional.

> +	if (!index_changed) {
> +		if (allow_empty)
> +			printf_ln(_("No changes - record it as an empty commit."));

A command to the user to "record it as an empty commit" is not what
we want to give.  Perhaps "recorded it as ..." would work better as
a report of what we did for the user.

Also style:

		if (allow_empty) {
			printf_ln(...);
		} else {
                	... original multi-statement block come here ...
		}

That is, when one arm of if/else if/.../else cascade needs {}, put
{} to all of them.

I think there is a logic error here once we remove the way too
aggressive "you may not say --allow-empty when there is a patch"
check we saw earlier.

When we see no changes added to the index and "--allow-empty" given,
we shouldn't be blindly creating an empty commit without checking if
we didn't have any patch.  If the last failure that gave the control
back from "am" to the user was a patch that did not apply, we do not
want to create an empty commit, do we?  So something like

	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
		if (allow_empty &&
		    is_empty_or_missing_file(am_path(state, "patch"))) {
			printf_ln(_("No changes - recorded an empty commit."));
		} else {
			... original block ...

perhaps?

> +		else {
> +			printf_ln(_("No changes - did you forget to use 'git add'?\n"
> +				    "If there is nothing left to stage, chances are that something else\n"
> +				    "already introduced the same changes; you might want to skip this patch."));
> +			die_user_resolve(state);
> +		}
> +	}

> @@ -2237,7 +2251,8 @@ enum resume_type {
>  	RESUME_SKIP,
>  	RESUME_ABORT,
>  	RESUME_QUIT,
> -	RESUME_SHOW_PATCH
> +	RESUME_SHOW_PATCH,
> +	RESUME_ALLOW_EMPTY

Advice on the trailing comma applies here equally.

>  };

> diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
> index 7f2956d77ad..9309becfe03 100755
> --- a/t/t7512-status-help.sh
> +++ b/t/t7512-status-help.sh
> @@ -658,6 +658,7 @@ test_expect_success 'status in an am session: empty patch' '
>  On branch am_empty
>  You are in the middle of an am session.
>  The current patch is empty.
> +  (use "git am --allow-empty" to record this patch as an empty commit)
>    (use "git am --skip" to skip this patch)
>    (use "git am --abort" to restore the original branch)

You do not want to add the new one the first.  "--skip" should come
first, and perhaps this new one, and "--abort" at the end.

The general rule of thumb is to give common ones early before the
less common ones and ones, and give ones with more severe
consequence the last.

> diff --git a/wt-status.c b/wt-status.c
> index 5d215f4e4f1..d578a0e9192 100644
> --- a/wt-status.c
> +++ b/wt-status.c
> @@ -1227,6 +1227,9 @@ static void show_am_in_progress(struct wt_status *s,
>  		if (!s->state.am_empty_patch)
>  			status_printf_ln(s, color,
>  				_("  (fix conflicts and then run \"git am --continue\")"));
> +		else
> +			status_printf_ln(s, color,
> +				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
>  		status_printf_ln(s, color,
>  			_("  (use \"git am --skip\" to skip this patch)"));
>  		status_printf_ln(s, color,

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

* Re: [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
                                                   ` (2 preceding siblings ...)
  2021-12-07  8:31                                 ` [PATCH v17 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-07 18:24                                 ` Junio C Hamano
  2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
  4 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-12-07 18:24 UTC (permalink / raw)
  To: Aleen via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

"Aleen via GitGitGadget" <gitgitgadget@gmail.com> writes:

> Since that git has supported the --always option for the git-format-patch
> command to create a patch with an empty commit message, git-am should
> support applying and committing with empty patches.
> ...
> Changes since v16:
>
>  1. fix typo from "had" to "has" in the comment.

I read the patches in full again and saw some issues that want to be
fixed, but overall it was a pleasant read.

Thanks.

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

* [PATCH v18 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
                                                   ` (3 preceding siblings ...)
  2021-12-07 18:24                                 ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Junio C Hamano
@ 2021-12-08  5:05                                 ` Aleen via GitGitGadget
  2021-12-08  5:05                                   ` [PATCH v18 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                                     ` (3 more replies)
  4 siblings, 4 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-12-08  5:05 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

----------------------------------------------------------------------------

Changes since v13:

 1. add an additional description about the 'die' value.

----------------------------------------------------------------------------

Changes since v14:

 1. reimplement the 'die' value and stop the whole session. (Expected a
    reroll)
 2. the --allow-empty option is a valid resume value only when: (Expected a
    reroll)
    * index has not changed
    * lacking a patch

----------------------------------------------------------------------------

Changes since v15:

 1. rename "die" to "stop".

----------------------------------------------------------------------------

Changes since v16:

 1. fix the typo from "had" to "has" in the comment.

----------------------------------------------------------------------------

Changes since v17:

 1. add trailing comma, show tips of creating an empty commit, and use "%B"
    to construct test cases.
 2. remove the error "Invalid resume value.".
 3. hint "--allow-empty" after "--skip".

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           |  16 ++++-
 Documentation/git-format-patch.txt |   6 +-
 builtin/am.c                       |  96 ++++++++++++++++++++++----
 t/t4150-am.sh                      | 104 +++++++++++++++++++++++++++++
 t/t7512-status-help.sh             |   1 +
 wt-status.c                        |   8 ++-
 6 files changed, 215 insertions(+), 16 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v18
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v18
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v17:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  b9e03f2342b ! 2:  56953d416d9 am: support --empty=<option> to handle empty patches
     @@ builtin/am.c: enum show_patch_type {
      +enum empty_action {
      +	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
      +	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
     -+	KEEP_EMPTY_COMMIT          /* keep recording as empty commits */
     ++	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
      +};
      +
       struct am_state {
     @@ builtin/am.c: static void am_run(struct am_state *state, int resume)
      +				break;
      +			case KEEP_EMPTY_COMMIT:
      +				to_keep = 1;
     ++				say(state, stdout, _("Creating an empty commit: %.*s"),
     ++					linelen(state->msg), state->msg);
      +				break;
      +			case STOP_ON_EMPTY_COMMIT:
      +				printf_ln(_("Patch is empty."));
     @@ t/t4150-am.sh: test_expect_success 'apply binary blob in partial clone' '
      +'
      +
      +test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
     -+	git am --empty=keep empty-commit.patch &&
     ++	git am --empty=keep empty-commit.patch >output &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	git show empty-commit --format="%s" >expected &&
     -+	git show HEAD --format="%s" >actual &&
     -+	test_cmp actual expected
     ++	git show empty-commit --format="%B" >expected &&
     ++	git show HEAD --format="%B" >actual &&
     ++	grep -f actual expected &&
     ++	grep "Creating an empty commit: empty commit" output
      +'
      +
       test_done
 3:  ea2dc088b37 ! 3:  4c3077f9384 am: support --allow-empty to record specific empty patches
     @@ Commit message
          am: support --allow-empty to record specific empty patches
      
          This option helps to record specific empty patches in the middle
     -    of an am session. However, it is a valid resume value only when:
     +    of an am session, which does create empty commits only when:
      
              1. index has not changed
              2. lacking a branch
     @@ Documentation/git-am.txt: default.   You can use `--no-utf8` to override this.
       
      +--allow-empty::
      +	After a patch failure on an input e-mail message lacking a patch,
     -+	the user can still record the empty patch as an empty commit with
     -+	the contents of the e-mail message as its log.
     ++	create an empty commit with the contents of the e-mail message
     ++	as its log message.
      +
       DISCUSSION
       ----------
       
      
       ## builtin/am.c ##
     -@@ builtin/am.c: static void am_run(struct am_state *state, int resume)
     - 				to_keep = 1;
     - 				break;
     - 			case STOP_ON_EMPTY_COMMIT:
     --				printf_ln(_("Patch is empty."));
     -+				printf_ln(_("Patch is empty.\n"
     -+					    "If you want to record it as an empty commit, run \"git am --allow-empty\"."));
     - 				die_user_resolve(state);
     - 				break;
     - 			}
     +@@ builtin/am.c: static void NORETURN die_user_resolve(const struct am_state *state)
     + 
     + 		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
     + 		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
     ++
     ++		if (advice_enabled(ADVICE_AM_WORK_DIR) &&
     ++		    is_empty_or_missing_file(am_path(state, "patch")) &&
     ++		    !repo_index_has_changes(the_repository, NULL, NULL))
     ++			printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
     ++
     + 		printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
     + 	}
     + 
      @@ builtin/am.c: next:
       /**
        * Resume the current am session after patch application failure. The user did
     @@ builtin/am.c: next:
      -			"If there is nothing left to stage, chances are that something else\n"
      -			"already introduced the same changes; you might want to skip this patch."));
      +	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
     -+	if (allow_empty && (index_changed || !is_empty_or_missing_file(am_path(state, "patch")))) {
     -+		printf_ln(_("Invalid resume value."));
     ++	if (allow_empty &&
     ++	    !(!index_changed && is_empty_or_missing_file(am_path(state, "patch"))))
       		die_user_resolve(state);
     - 	}
     - 
     ++
      +	if (!index_changed) {
     -+		if (allow_empty)
     -+			printf_ln(_("No changes - record it as an empty commit."));
     -+		else {
     ++		if (allow_empty) {
     ++			printf_ln(_("No changes - recorded it as an empty commit."));
     ++		} else {
      +			printf_ln(_("No changes - did you forget to use 'git add'?\n"
      +				    "If there is nothing left to stage, chances are that something else\n"
      +				    "already introduced the same changes; you might want to skip this patch."));
      +			die_user_resolve(state);
      +		}
     -+	}
     -+
     + 	}
     + 
       	if (unmerged_cache()) {
     - 		printf_ln(_("You still have unmerged paths in your index.\n"
     - 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
      @@ builtin/am.c: enum resume_type {
       	RESUME_SKIP,
       	RESUME_ABORT,
       	RESUME_QUIT,
      -	RESUME_SHOW_PATCH
      +	RESUME_SHOW_PATCH,
     -+	RESUME_ALLOW_EMPTY
     ++	RESUME_ALLOW_EMPTY,
       };
       
       struct resume_mode {
     @@ builtin/am.c: int cmd_am(int argc, const char **argv, const char *prefix)
      
       ## t/t4150-am.sh ##
      @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mail message that
     - 	test_cmp actual expected
     + 	grep "Creating an empty commit: empty commit" output
       '
       
      +test_expect_success 'skip an empty patch in the middle of an am session' '
      +	git checkout empty-commit^ &&
      +	test_must_fail git am empty-commit.patch >err &&
      +	grep "Patch is empty." err &&
     -+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
     ++	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
      +	git am --skip &&
      +	test_path_is_missing .git/rebase-apply &&
      +	git rev-parse empty-commit^ >expected &&
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
      +	git checkout empty-commit^ &&
      +	test_must_fail git am empty-commit.patch >err &&
      +	grep "Patch is empty." err &&
     -+	grep "If you want to record it as an empty commit, run \"git am --allow-empty\"." err &&
     -+	git am --allow-empty &&
     ++	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
     ++	git am --allow-empty >output &&
     ++	grep "No changes - recorded it as an empty commit." output &&
      +	test_path_is_missing .git/rebase-apply &&
     -+	git show empty-commit --format="%s" >expected &&
     -+	git show HEAD --format="%s" >actual &&
     -+	test_cmp actual expected
     ++	git show empty-commit --format="%B" >expected &&
     ++	git show HEAD --format="%B" >actual &&
     ++	grep -f actual expected
      +'
      +
      +test_expect_success 'cannot create empty commits when the index is changed' '
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
      +	: >empty-file &&
      +	git add empty-file &&
      +	test_must_fail git am --allow-empty >err &&
     -+	grep "Invalid resume value." err
     ++	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err
      +'
      +
      +test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
      +	git rev-parse HEAD >expected &&
      +	test_must_fail git am seq.patch &&
      +	test_must_fail git am --allow-empty >err &&
     -+	grep "Invalid resume value." err &&
     ++	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
      +	git rev-parse HEAD >actual &&
      +	test_cmp actual expected
      +'
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
      +	git rev-parse HEAD >expected &&
      +	test_must_fail git am -3 seq.patch &&
      +	test_must_fail git am --allow-empty >err &&
     -+	grep "Invalid resume value." err &&
     ++	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
      +	git rev-parse HEAD >actual &&
      +	test_cmp actual expected
      +'
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
       test_done
      
       ## t/t7512-status-help.sh ##
     -@@ t/t7512-status-help.sh: test_expect_success 'status in an am session: empty patch' '
     - On branch am_empty
     +@@ t/t7512-status-help.sh: On branch am_empty
       You are in the middle of an am session.
       The current patch is empty.
     -+  (use "git am --allow-empty" to record this patch as an empty commit)
         (use "git am --skip" to skip this patch)
     ++  (use "git am --allow-empty" to record this patch as an empty commit)
         (use "git am --abort" to restore the original branch)
       
     + nothing to commit (use -u to show untracked files)
      
       ## wt-status.c ##
     -@@ wt-status.c: static void show_am_in_progress(struct wt_status *s,
     - 		if (!s->state.am_empty_patch)
     +@@ wt-status.c: static void show_merge_in_progress(struct wt_status *s,
     + static void show_am_in_progress(struct wt_status *s,
     + 				const char *color)
     + {
     ++	int am_empty_patch;
     ++
     + 	status_printf_ln(s, color,
     + 		_("You are in the middle of an am session."));
     + 	if (s->state.am_empty_patch)
     + 		status_printf_ln(s, color,
     + 			_("The current patch is empty."));
     + 	if (s->hints) {
     +-		if (!s->state.am_empty_patch)
     ++		am_empty_patch = s->state.am_empty_patch;
     ++		if (!am_empty_patch)
       			status_printf_ln(s, color,
       				_("  (fix conflicts and then run \"git am --continue\")"));
     -+		else
     -+			status_printf_ln(s, color,
     -+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
       		status_printf_ln(s, color,
       			_("  (use \"git am --skip\" to skip this patch)"));
     ++		if (am_empty_patch)
     ++			status_printf_ln(s, color,
     ++				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
       		status_printf_ln(s, color,
     + 			_("  (use \"git am --abort\" to restore the original branch)"));
     + 	}

-- 
gitgitgadget

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

* [PATCH v18 1/3] doc: git-format-patch: describe the option --always
  2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
@ 2021-12-08  5:05                                   ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-08  5:05                                   ` [PATCH v18 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
                                                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-08  5:05 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v18 2/3] am: support --empty=<option> to handle empty patches
  2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
  2021-12-08  5:05                                   ` [PATCH v18 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-08  5:05                                   ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-08  5:05                                   ` [PATCH v18 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2021-12-09  7:25                                   ` [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-08  5:05 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('stop', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++++
 builtin/am.c             | 57 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 50 +++++++++++++++++++++++++++++++++++
 3 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..7676bd58ae7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ 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 into 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..7a66ad23737 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,29 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				say(state, stdout, _("Creating an empty commit: %.*s"),
+					linelen(state->msg), state->msg);
+				break;
+			case STOP_ON_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1870,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2401,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..f2a7a68eda0 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,48 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=stop empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch >output &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected &&
+	grep "Creating an empty commit: empty commit" output
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v18 3/3] am: support --allow-empty to record specific empty patches
  2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
  2021-12-08  5:05                                   ` [PATCH v18 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-12-08  5:05                                   ` [PATCH v18 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-08  5:05                                   ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-08  6:22                                     ` Junio C Hamano
  2021-12-09  7:25                                   ` [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  3 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-08  5:05 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session, which does create empty commits only when:

    1. index has not changed
    2. lacking a branch

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  7 +++++-
 builtin/am.c             | 39 +++++++++++++++++++++++------
 t/t4150-am.sh            | 54 ++++++++++++++++++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  8 +++++-
 5 files changed, 99 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 7676bd58ae7..09107fb1067 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	After a patch failure on an input e-mail message lacking a patch,
+	create an empty commit with the contents of the e-mail message
+	as its log message.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index 7a66ad23737..d73e415bbb0 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1152,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state)
 
 		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
 		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+
+		if (advice_enabled(ADVICE_AM_WORK_DIR) &&
+		    is_empty_or_missing_file(am_path(state, "patch")) &&
+		    !repo_index_has_changes(the_repository, NULL, NULL))
+			printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+
 		printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
 	}
 
@@ -1900,19 +1906,31 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
+	int index_changed;
+
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
-	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
+	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
+	if (allow_empty &&
+	    !(!index_changed && is_empty_or_missing_file(am_path(state, "patch"))))
 		die_user_resolve(state);
+
+	if (!index_changed) {
+		if (allow_empty) {
+			printf_ln(_("No changes - recorded it as an empty commit."));
+		} else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
 	}
 
 	if (unmerged_cache()) {
@@ -2239,7 +2257,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY,
 };
 
 struct resume_mode {
@@ -2392,6 +2411,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2500,7 +2522,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index f2a7a68eda0..6b23384685b 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1202,4 +1202,58 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	grep "Creating an empty commit: empty commit" output
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty >output &&
+	grep "No changes - recorded it as an empty commit." output &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected
+'
+
+test_expect_success 'cannot create empty commits when the index is changed' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	: >empty-file &&
+	git add empty-file &&
+	test_must_fail git am --allow-empty >err &&
+	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am -3 seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..2f16d5787ed 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -659,6 +659,7 @@ On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
   (use "git am --skip" to skip this patch)
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --abort" to restore the original branch)
 
 nothing to commit (use -u to show untracked files)
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..335e723a71e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1218,17 +1218,23 @@ static void show_merge_in_progress(struct wt_status *s,
 static void show_am_in_progress(struct wt_status *s,
 				const char *color)
 {
+	int am_empty_patch;
+
 	status_printf_ln(s, color,
 		_("You are in the middle of an am session."));
 	if (s->state.am_empty_patch)
 		status_printf_ln(s, color,
 			_("The current patch is empty."));
 	if (s->hints) {
-		if (!s->state.am_empty_patch)
+		am_empty_patch = s->state.am_empty_patch;
+		if (!am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
+		if (am_empty_patch)
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --abort\" to restore the original branch)"));
 	}
-- 
gitgitgadget

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

* Re: [PATCH v18 3/3] am: support --allow-empty to record specific empty patches
  2021-12-08  5:05                                   ` [PATCH v18 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-08  6:22                                     ` Junio C Hamano
  2021-12-08  6:46                                       ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Junio C Hamano @ 2021-12-08  6:22 UTC (permalink / raw)
  To: ""徐沛文  via GitGitGadget
  Cc: git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

""徐沛文 (Aleen)" via GitGitGadget"  <gitgitgadget@gmail.com>
writes:

> @@ -1152,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state)
>  
>  		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
>  		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
> +
> +		if (advice_enabled(ADVICE_AM_WORK_DIR) &&
> +		    is_empty_or_missing_file(am_path(state, "patch")) &&
> +		    !repo_index_has_changes(the_repository, NULL, NULL))
> +			printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
> +

OK, so the idea is when we give back control to the user, if the
reason we stopped is because we did not have a usable patch in the
message and we didn't have any change to the index, we can offer
"--allow-empty" as an extra choice.

I guess that makes sense.

> @@ -1900,19 +1906,31 @@ next:
>  /**
>   * Resume the current am session after patch application failure. The user did
>   * all the hard work, and we do not have to do any patch application. Just
> - * trust and commit what the user has in the index and working tree.
> + * trust and commit what the user has in the index and working tree. If `allow_empty`
> + * is true, commit as an empty commit when index has not changed and lacking a patch.
>   */
> -static void am_resolve(struct am_state *state)
> +static void am_resolve(struct am_state *state, int allow_empty)
>  {
> +	int index_changed;
> +
>  	validate_resume_state(state);
>  
>  	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
>  
> -	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
> -		printf_ln(_("No changes - did you forget to use 'git add'?\n"
> -			"If there is nothing left to stage, chances are that something else\n"
> -			"already introduced the same changes; you might want to skip this patch."));


> +	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
> +	if (allow_empty &&
> +	    !(!index_changed && is_empty_or_missing_file(am_path(state, "patch"))))
>  		die_user_resolve(state);

Why do we need this?  

Intuitively, because "--allow-XYZ" is "we normally die when the
condition XYZ holds, but with the option, we do not die in such a
case", any new condition that leads to "die" that only kicks in
when "--allow-XYZ" is given smell like a BUG.

The condition reads:

    unless we saw an empty patch (i.e. "patch" file is missing or
    empty, and did not result in any change to the index), it is an
    error to give "--allow-empty" to the command.

That somehow does not make any sense to me.  Whether failed patch
application and manual tweaking resulted in the same tree as HEAD,
or a tree different from HEAD, if the user wants to say "I allow
Git to create an empty commit as necessary, instead of stopping",
shouldn't the user be allowed to?  After all, the option is not
"create nothing but an empty commit, it is an error if there is
something to commit."  It merely allows creation of an empty one.

This series has been trying to add code to punish users who give
"--allow-empty" when the command would have worked the same way
without it at least since the last round, and I am not sure where
that wish to stop users comes from.  Please explain.  I am starting
to think I may be missing something and this extra rigidity may be
helping a user (but again, I do not see how).

> +	if (!index_changed) {
> +		if (allow_empty) {

The index_changed variable being false is "the result is an empty
change", and is-empty-or-missing on "patch" file is "the input is an
empty change".  Ideally we would want to create an empty commit only
when both input and result are empty, but in order to prevent mistakes,
we may want to react to the case where the result is empty but the
input is not empty.

Here is where we can use is-empty-or-missing on "patch" and give a
different message (i.e. "even though the patch file is not empty,
the result of your patch application did not change anything"), if
we wanted to.  Or you could choose to reject such an attempt, which
might be simpler.  I.e.

		if (allow_empty &&
		    is_empty_or_missing_file(am_path(state, "patch"))
			"No changes - recorded an empty commit."
		else
			... the original error for no changes ...


Hmm?

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

* Re: [PATCH v18 3/3] am: support --allow-empty to record specific empty patches
  2021-12-08  6:22                                     ` Junio C Hamano
@ 2021-12-08  6:46                                       ` Aleen 徐沛文
  2021-12-08 11:32                                         ` Junio C Hamano
  0 siblings, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-12-08  6:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: ""徐沛文 (Aleen)" via
	GitGitGadget",
	git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen


> > +	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
> > +	if (allow_empty &&
> > +	    !(!index_changed && is_empty_or_missing_file(am_path(state, "patch"))))
> >  		die_user_resolve(state);
> 
> Why do we need this?  
> 
> Intuitively, because "--allow-XYZ" is "we normally die when the
> condition XYZ holds, but with the option, we do not die in such a
> case", any new condition that leads to "die" that only kicks in
> when "--allow-XYZ" is given smell like a BUG.
> 
> The condition reads:
> 
>     unless we saw an empty patch (i.e. "patch" file is missing or
>     empty, and did not result in any change to the index), it is an
>     error to give "--allow-empty" to the command.
> 
> That somehow does not make any sense to me.  Whether failed patch
> application and manual tweaking resulted in the same tree as HEAD,
> or a tree different from HEAD, if the user wants to say "I allow
> Git to create an empty commit as necessary, instead of stopping",
> shouldn't the user be allowed to?  After all, the option is not
> "create nothing but an empty commit, it is an error if there is
> something to commit."  It merely allows creation of an empty one.
> 
> This series has been trying to add code to punish users who give
> "--allow-empty" when the command would have worked the same way
> without it at least since the last round, and I am not sure where
> that wish to stop users comes from.  Please explain.  I am starting
> to think I may be missing something and this extra rigidity may be
> helping a user (but again, I do not see how).

If there is no such code to check, the user can use "--allow-empty" to do the same thing
as "--continue" or "--resolved" after resolving conflicts because they all go into `am_resolve()`.
In my opinion, "--allow-empty" only makes sense to allow the user to create empty commits when:

    1. the result is an empty change (`!index_changed`)
    2. the input is an empty change (`is_empty_or_missing_file(am_path(state, "patch"))`)

> > +	if (!index_changed) {
> > +		if (allow_empty) {
> 
> The index_changed variable being false is "the result is an empty
> change", and is-empty-or-missing on "patch" file is "the input is an
> empty change".  Ideally we would want to create an empty commit only
> when both input and result are empty, but in order to prevent mistakes,
> we may want to react to the case where the result is empty but the
> input is not empty.
> 
> Here is where we can use is-empty-or-missing on "patch" and give a
> different message (i.e. "even though the patch file is not empty,
> the result of your patch application did not change anything"), if
> we wanted to.  Or you could choose to reject such an attempt, which
> might be simpler.  I.e.
> 
> 		if (allow_empty &&
> 		    is_empty_or_missing_file(am_path(state, "patch"))
> 			"No changes - recorded an empty commit."
> 		else
> 			... the original error for no changes ...
> 
> 
> Hmm?

Based on the previous checking, there is no need to check
`is_empty_or_missing_file(am_path(state, "patch"))` here because it is permanently true.

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

* Re: [PATCH v18 3/3] am: support --allow-empty to record specific empty patches
  2021-12-08  6:46                                       ` Aleen 徐沛文
@ 2021-12-08 11:32                                         ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-12-08 11:32 UTC (permalink / raw)
  To: Aleen 徐沛文
  Cc:  徐沛文 (Aleen) via GitGitGadget ,
	git, René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen

Aleen 徐沛文 <pwxu@coremail.cn> writes:

>> This series has been trying to add code to punish users who give
>> "--allow-empty" when the command would have worked the same way
>> without it at least since the last round, and I am not sure where
>> that wish to stop users comes from.  Please explain.  I am starting
>> to think I may be missing something and this extra rigidity may be
>> helping a user (but again, I do not see how).
>
> If there is no such code to check, the user can use
> "--allow-empty" to do the same thing as "--continue" or
> "--resolved" after resolving conflicts because they all go into
> `am_resolve()`.  

Yes, exactly.  That is the point of "allow"; we continue normally,
and even in a narrow situation (i.e. input is empty, and no change
is made to the index) where we normally stop, we allow it to
continue by creating an empty commit.

> In my opinion, "--allow-empty" only makes sense
> to allow the user to create empty commits when:
>
>     1. the result is an empty change (`!index_changed`)
>     2. the input is an empty change (`is_empty_or_missing_file(am_path(state, "patch"))`)

Then you are forcing the user to use it ONLY whe nhe input is empty
and no other time.  Whatever that is, that is not "allow" empty.

>> Here is where we can use is-empty-or-missing on "patch" and give a
>> different message (i.e. "even though the patch file is not empty,
>> the result of your patch application did not change anything"), if
>> we wanted to.  Or you could choose to reject such an attempt, which
>> might be simpler.  I.e.
>> 
>> 		if (allow_empty &&
>> 		    is_empty_or_missing_file(am_path(state, "patch"))
>> 			"No changes - recorded an empty commit."
>> 		else
>> 			... the original error for no changes ...
>> 
>> 
>> Hmm?
>
> Based on the previous checking, there is no need to check
> `is_empty_or_missing_file(am_path(state, "patch"))` here because it is permanently true.

I think you misunderstood.

If we correct the overly strict check that makes "--allow-empty" to
mean "only create empty, anything else is an error", the "is patch
an empty file?" must be checked here.


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

* [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle empty patches
  2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
                                                     ` (2 preceding siblings ...)
  2021-12-08  5:05                                   ` [PATCH v18 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-09  7:25                                   ` Aleen via GitGitGadget
  2021-12-09  7:25                                     ` [PATCH v19 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
                                                       ` (2 more replies)
  3 siblings, 3 replies; 129+ messages in thread
From: Aleen via GitGitGadget @ 2021-12-09  7:25 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

Since that git has supported the --always option for the git-format-patch
command to create a patch with an empty commit message, git-am should
support applying and committing with empty patches.

----------------------------------------------------------------------------

Changes since v1:

 1. add a case when not passing the --always option.
 2. rename the --always option to --allow-empty.

----------------------------------------------------------------------------

Changes since v2:

 1. rename the --allow-empty option to --empty-commit.
 2. introduce three different strategies (die|skip|asis) when trying to
    record empty patches as empty commits.

----------------------------------------------------------------------------

Changes since v3:

 1. generate the missed file for test cases.
 2. grep -f cannot be used under Mac OS.

----------------------------------------------------------------------------

Changes since v4:

 1. rename the --empty-commit option to --empty.
 2. rename three different strategies (die|skip|asis) to die, drop and keep
    correspondingly.

----------------------------------------------------------------------------

Changes since v5:

 1. throw an error when passing --empty option without value.

----------------------------------------------------------------------------

Changes since v6:

 1. add i18n resources.

----------------------------------------------------------------------------

Changes since v7:

 1. update code according to the seen branch.
 2. fix the wrong document of git-am.
 3. sign off commits by a real name.

----------------------------------------------------------------------------

Changes since v8:

 1. update the committer's name with my real name to fix DCO of GGG.

----------------------------------------------------------------------------

Changes since v9:

 1. imitate the signed name format of
    https://lore.kernel.org/git/pull.1143.git.git.1637347813367.gitgitgadget@gmail.com
    .

----------------------------------------------------------------------------

Changes since v11:

 1. introduce an interactive option --allow-empty for git-am to record empty
    patches in the middle of an am session.

----------------------------------------------------------------------------

Changes since v12:

 1. record the empty patch as an empty commit only when there are no
    changes.
 2. fix indentation problems.
 3. simplify "to keep recording" to "to record".
 4. add a test case for skipping empty patches via the --skip option.

----------------------------------------------------------------------------

Changes since v13:

 1. add an additional description about the 'die' value.

----------------------------------------------------------------------------

Changes since v14:

 1. reimplement the 'die' value and stop the whole session. (Expected a
    reroll)
 2. the --allow-empty option is a valid resume value only when: (Expected a
    reroll)
    * index has not changed
    * lacking a patch

----------------------------------------------------------------------------

Changes since v15:

 1. rename "die" to "stop".

----------------------------------------------------------------------------

Changes since v16:

 1. fix the typo from "had" to "has" in the comment.

----------------------------------------------------------------------------

Changes since v17:

 1. add trailing comma, show tips of creating an empty commit, and use "%B"
    to construct test cases.
 2. remove the error "Invalid resume value.".
 3. hint "--allow-empty" after "--skip".

----------------------------------------------------------------------------

Changes since v18:

 1. remove strict checking of "--allow-empty".

徐沛文 (Aleen) (3):
  doc: git-format-patch: describe the option --always
  am: support --empty=<option> to handle empty patches
  am: support --allow-empty to record specific empty patches

 Documentation/git-am.txt           |  16 ++++-
 Documentation/git-format-patch.txt |   6 +-
 builtin/am.c                       |  89 ++++++++++++++++++++----
 t/t4150-am.sh                      | 107 +++++++++++++++++++++++++++++
 t/t7512-status-help.sh             |   1 +
 wt-status.c                        |   8 ++-
 6 files changed, 211 insertions(+), 16 deletions(-)


base-commit: abe6bb3905392d5eb6b01fa6e54d7e784e0522aa
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1076%2Faleen42%2Fnext-v19
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1076/aleen42/next-v19
Pull-Request: https://github.com/gitgitgadget/git/pull/1076

Range-diff vs v18:

 1:  a524ca6adfa = 1:  a524ca6adfa doc: git-format-patch: describe the option --always
 2:  56953d416d9 = 2:  56953d416d9 am: support --empty=<option> to handle empty patches
 3:  4c3077f9384 ! 3:  37875e8d313 am: support --allow-empty to record specific empty patches
     @@ Commit message
          This option helps to record specific empty patches in the middle
          of an am session, which does create empty commits only when:
      
     -        1. index has not changed
     +        1. the index has not changed
              2. lacking a branch
      
     +    When the index has changed, "--allow-empty" will create a non-empty
     +    commit like passing "--continue" or "--resolved".
     +
          Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
      
       ## Documentation/git-am.txt ##
     @@ builtin/am.c: next:
      -static void am_resolve(struct am_state *state)
      +static void am_resolve(struct am_state *state, int allow_empty)
       {
     -+	int index_changed;
     -+
       	validate_resume_state(state);
       
       	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
       
     --	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
     + 	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
      -		printf_ln(_("No changes - did you forget to use 'git add'?\n"
      -			"If there is nothing left to stage, chances are that something else\n"
      -			"already introduced the same changes; you might want to skip this patch."));
     -+	index_changed = repo_index_has_changes(the_repository, NULL, NULL);
     -+	if (allow_empty &&
     -+	    !(!index_changed && is_empty_or_missing_file(am_path(state, "patch"))))
     - 		die_user_resolve(state);
     -+
     -+	if (!index_changed) {
     -+		if (allow_empty) {
     +-		die_user_resolve(state);
     ++		if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
      +			printf_ln(_("No changes - recorded it as an empty commit."));
      +		} else {
      +			printf_ln(_("No changes - did you forget to use 'git add'?\n"
     @@ t/t4150-am.sh: test_expect_success 'record as an empty commit when meeting e-mai
      +	grep -f actual expected
      +'
      +
     -+test_expect_success 'cannot create empty commits when the index is changed' '
     ++test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' '
      +	git checkout empty-commit^ &&
      +	test_must_fail git am empty-commit.patch >err &&
      +	: >empty-file &&
      +	git add empty-file &&
     -+	test_must_fail git am --allow-empty >err &&
     -+	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err
     ++	git am --allow-empty &&
     ++	git show empty-commit --format="%B" >expected &&
     ++	git show HEAD --format="%B" >actual &&
     ++	grep -f actual expected &&
     ++	git diff HEAD^..HEAD --name-only
      +'
      +
      +test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '

-- 
gitgitgadget

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

* [PATCH v19 1/3] doc: git-format-patch: describe the option --always
  2021-12-09  7:25                                   ` [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
@ 2021-12-09  7:25                                     ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-09  9:28                                       ` Bagas Sanjaya
  2021-12-09  7:25                                     ` [PATCH v19 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
  2021-12-09  7:25                                     ` [PATCH v19 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2 siblings, 1 reply; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-09  7:25 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This commit has described how to use '--always' option in the command
'git-format-patch' to include patches for commits that emit no changes.

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-format-patch.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc107c..be797d7a28f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 		   [-n | --numbered | -N | --no-numbered]
 		   [--start-number <n>] [--numbered-files]
 		   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-		   [--ignore-if-in-upstream]
+		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
 	patches being generated, and any patch that matches is
 	ignored.
 
+--always::
+	Include patches for commits that do not introduce any change,
+	which are omitted by default.
+
 --cover-from-description=<mode>::
 	Controls which parts of the cover letter will be automatically
 	populated using the branch's description.
-- 
gitgitgadget


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

* [PATCH v19 2/3] am: support --empty=<option> to handle empty patches
  2021-12-09  7:25                                   ` [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-09  7:25                                     ` [PATCH v19 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-09  7:25                                     ` 徐沛文 (Aleen) via GitGitGadget
  2021-12-09  7:25                                     ` [PATCH v19 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-09  7:25 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

Since that the command 'git-format-patch' can include patches of
commits that emit no changes, the 'git-am' command should also
support an option, named as '--empty', to specify how to handle
those empty patches. In this commit, we have implemented three
valid options ('stop', 'drop' and 'keep').

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  9 +++++++
 builtin/am.c             | 57 ++++++++++++++++++++++++++++++++++++----
 t/t4150-am.sh            | 50 +++++++++++++++++++++++++++++++++++
 3 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984dfde..7676bd58ae7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,6 +16,7 @@ SYNOPSIS
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
 	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
+	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
 
@@ -63,6 +64,14 @@ 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 into 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.
+
 -m::
 --message-id::
 	Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348a..7a66ad23737 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
 	SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+	STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
+	DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
+	KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
 struct am_state {
 	/* state directory path */
 	char *dir;
@@ -118,6 +124,7 @@ struct am_state {
 	int message_id;
 	int scissors; /* enum scissors_type */
 	int quoted_cr; /* enum quoted_cr_action */
+	int empty_type; /* enum empty_action */
 	struct strvec git_apply_opts;
 	const char *resolvemsg;
 	int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
 	return 0;
 }
 
+static int am_option_parse_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 --empty: %s"), arg);
+
+	return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1248,11 +1274,6 @@ static int parse_mail(struct am_state *state, const char *mail)
 		goto finish;
 	}
 
-	if (is_empty_or_missing_file(am_path(state, "patch"))) {
-		printf_ln(_("Patch is empty."));
-		die_user_resolve(state);
-	}
-
 	strbuf_addstr(&msg, "\n\n");
 	strbuf_addbuf(&msg, &mi.log_message);
 	strbuf_stripspace(&msg, 0);
@@ -1763,6 +1784,7 @@ static void am_run(struct am_state *state, int resume)
 	while (state->cur <= state->last) {
 		const char *mail = am_path(state, msgnum(state));
 		int apply_status;
+		int to_keep;
 
 		reset_ident_date();
 
@@ -1792,8 +1814,29 @@ static void am_run(struct am_state *state, int resume)
 		if (state->interactive && do_interactive(state))
 			goto next;
 
+		to_keep = 0;
+		if (is_empty_or_missing_file(am_path(state, "patch"))) {
+			switch (state->empty_type) {
+			case DROP_EMPTY_COMMIT:
+				say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+				goto next;
+				break;
+			case KEEP_EMPTY_COMMIT:
+				to_keep = 1;
+				say(state, stdout, _("Creating an empty commit: %.*s"),
+					linelen(state->msg), state->msg);
+				break;
+			case STOP_ON_EMPTY_COMMIT:
+				printf_ln(_("Patch is empty."));
+				die_user_resolve(state);
+				break;
+			}
+		}
+
 		if (run_applypatch_msg_hook(state))
 			exit(1);
+		if (to_keep)
+			goto commit;
 
 		say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1870,7 @@ static void am_run(struct am_state *state, int resume)
 			die_user_resolve(state);
 		}
 
+commit:
 		do_commit(state);
 
 next:
@@ -2357,6 +2401,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
 		  N_("GPG-sign commits"),
 		  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+		  N_("how to handle empty patches"),
+		  PARSE_OPT_NONEG, am_option_parse_empty),
 		OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
 			N_("(internal use for git-rebase)")),
 		OPT_END()
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 2aaaa0d7ded..f2a7a68eda0 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -196,6 +196,12 @@ test_expect_success setup '
 
 	git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+	git checkout -b empty-commit &&
+	git commit -m "empty commit" --allow-empty &&
+
+	: >empty.patch &&
+	git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
 	# reset time
 	sane_unset test_tick &&
 	test_tick
@@ -1152,4 +1158,48 @@ test_expect_success 'apply binary blob in partial clone' '
 	git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=drop empty.patch 2>actual &&
+	echo "Patch format detection failed." >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+	test_when_finished "git am --abort || :" &&
+	git checkout empty-commit^ &&
+	test_must_fail git am --empty empty-commit.patch 2>err &&
+	echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+	test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+	test_when_finished "git am --abort || :" &&
+	test_must_fail git am --empty=stop empty-commit.patch >err &&
+	grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+	git am --empty=drop empty-commit.patch >output &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual &&
+	grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+	git am --empty=keep empty-commit.patch >output &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected &&
+	grep "Creating an empty commit: empty commit" output
+'
+
 test_done
-- 
gitgitgadget


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

* [PATCH v19 3/3] am: support --allow-empty to record specific empty patches
  2021-12-09  7:25                                   ` [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
  2021-12-09  7:25                                     ` [PATCH v19 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
  2021-12-09  7:25                                     ` [PATCH v19 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-09  7:25                                     ` 徐沛文 (Aleen) via GitGitGadget
  2 siblings, 0 replies; 129+ messages in thread
From: 徐沛文 (Aleen) via GitGitGadget @ 2021-12-09  7:25 UTC (permalink / raw)
  To: git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen, 徐沛文 (Aleen)

From: =?UTF-8?q?=E5=BE=90=E6=B2=9B=E6=96=87=20=28Aleen=29?=
 <aleen42@vip.qq.com>

This option helps to record specific empty patches in the middle
of an am session, which does create empty commits only when:

    1. the index has not changed
    2. lacking a branch

When the index has changed, "--allow-empty" will create a non-empty
commit like passing "--continue" or "--resolved".

Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com>
---
 Documentation/git-am.txt |  7 ++++-
 builtin/am.c             | 32 ++++++++++++++++------
 t/t4150-am.sh            | 57 ++++++++++++++++++++++++++++++++++++++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              |  8 +++++-
 5 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 7676bd58ae7..09107fb1067 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -18,7 +18,7 @@ SYNOPSIS
 	 [--quoted-cr=<action>]
 	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -200,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
+--allow-empty::
+	After a patch failure on an input e-mail message lacking a patch,
+	create an empty commit with the contents of the e-mail message
+	as its log message.
+
 DISCUSSION
 ----------
 
diff --git a/builtin/am.c b/builtin/am.c
index 7a66ad23737..f2e7e533881 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1152,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state)
 
 		printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
 		printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+
+		if (advice_enabled(ADVICE_AM_WORK_DIR) &&
+		    is_empty_or_missing_file(am_path(state, "patch")) &&
+		    !repo_index_has_changes(the_repository, NULL, NULL))
+			printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+
 		printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
 	}
 
@@ -1900,19 +1906,24 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
 	validate_resume_state(state);
 
 	say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
 	if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-		printf_ln(_("No changes - did you forget to use 'git add'?\n"
-			"If there is nothing left to stage, chances are that something else\n"
-			"already introduced the same changes; you might want to skip this patch."));
-		die_user_resolve(state);
+		if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
+			printf_ln(_("No changes - recorded it as an empty commit."));
+		} else {
+			printf_ln(_("No changes - did you forget to use 'git add'?\n"
+				    "If there is nothing left to stage, chances are that something else\n"
+				    "already introduced the same changes; you might want to skip this patch."));
+			die_user_resolve(state);
+		}
 	}
 
 	if (unmerged_cache()) {
@@ -2239,7 +2250,8 @@ enum resume_type {
 	RESUME_SKIP,
 	RESUME_ABORT,
 	RESUME_QUIT,
-	RESUME_SHOW_PATCH
+	RESUME_SHOW_PATCH,
+	RESUME_ALLOW_EMPTY,
 };
 
 struct resume_mode {
@@ -2392,6 +2404,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		  N_("show the patch being applied"),
 		  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
 		  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+		OPT_CMDMODE(0, "allow-empty", &resume.mode,
+			N_("record the empty patch as an empty commit"),
+			RESUME_ALLOW_EMPTY),
 		OPT_BOOL(0, "committer-date-is-author-date",
 			&state.committer_date_is_author_date,
 			N_("lie about committer date")),
@@ -2500,7 +2515,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 		am_run(&state, 1);
 		break;
 	case RESUME_RESOLVED:
-		am_resolve(&state);
+	case RESUME_ALLOW_EMPTY:
+		am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
 		break;
 	case RESUME_SKIP:
 		am_skip(&state);
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index f2a7a68eda0..2db2c5dfe58 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1202,4 +1202,61 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
 	grep "Creating an empty commit: empty commit" output
 '
 
+test_expect_success 'skip an empty patch in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --skip &&
+	test_path_is_missing .git/rebase-apply &&
+	git rev-parse empty-commit^ >expected &&
+	git rev-parse HEAD >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	grep "Patch is empty." err &&
+	grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git am --allow-empty >output &&
+	grep "No changes - recorded it as an empty commit." output &&
+	test_path_is_missing .git/rebase-apply &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected
+'
+
+test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' '
+	git checkout empty-commit^ &&
+	test_must_fail git am empty-commit.patch >err &&
+	: >empty-file &&
+	git add empty-file &&
+	git am --allow-empty &&
+	git show empty-commit --format="%B" >expected &&
+	git show HEAD --format="%B" >actual &&
+	grep -f actual expected &&
+	git diff HEAD^..HEAD --name-only
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+	test_when_finished "git am --abort || :" &&
+	git rev-parse HEAD >expected &&
+	test_must_fail git am -3 seq.patch &&
+	test_must_fail git am --allow-empty >err &&
+	! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+	git rev-parse HEAD >actual &&
+	test_cmp actual expected
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 7f2956d77ad..2f16d5787ed 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -659,6 +659,7 @@ On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
   (use "git am --skip" to skip this patch)
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --abort" to restore the original branch)
 
 nothing to commit (use -u to show untracked files)
diff --git a/wt-status.c b/wt-status.c
index 5d215f4e4f1..335e723a71e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1218,17 +1218,23 @@ static void show_merge_in_progress(struct wt_status *s,
 static void show_am_in_progress(struct wt_status *s,
 				const char *color)
 {
+	int am_empty_patch;
+
 	status_printf_ln(s, color,
 		_("You are in the middle of an am session."));
 	if (s->state.am_empty_patch)
 		status_printf_ln(s, color,
 			_("The current patch is empty."));
 	if (s->hints) {
-		if (!s->state.am_empty_patch)
+		am_empty_patch = s->state.am_empty_patch;
+		if (!am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --skip\" to skip this patch)"));
+		if (am_empty_patch)
+			status_printf_ln(s, color,
+				_("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
 		status_printf_ln(s, color,
 			_("  (use \"git am --abort\" to restore the original branch)"));
 	}
-- 
gitgitgadget

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

* Re: [PATCH v19 1/3] doc: git-format-patch: describe the option --always
  2021-12-09  7:25                                     ` [PATCH v19 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
@ 2021-12-09  9:28                                       ` Bagas Sanjaya
  2021-12-10  1:26                                         ` Aleen 徐沛文
  0 siblings, 1 reply; 129+ messages in thread
From: Bagas Sanjaya @ 2021-12-09  9:28 UTC (permalink / raw)
  To: 徐沛文 (Aleen) via GitGitGadget, git
  Cc: René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen 徐沛文,
	Aleen

On 09/12/21 14.25, 徐沛文 (Aleen) via GitGitGadget wrote:
> +--always::
> +	Include patches for commits that do not introduce any change,
> +	which are omitted by default.
> +

What about this wording below?

diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index be797d7a28..302659cfd5 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -193,8 +193,8 @@ will want to ensure that threading is disabled for `git send-email`.
  	ignored.
  
  --always::
-	Include patches for commits that do not introduce any change,
-	which are omitted by default.
+	Always generate patches, even if there are empty-change commits.
+	Default is to omit such commits.
  
  --cover-from-description=<mode>::
  	Controls which parts of the cover letter will be automatically


-- 
An old man doll... just what I always wanted! - Clara

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

* Re: [PATCH v19 1/3] doc: git-format-patch: describe the option --always
  2021-12-09  9:28                                       ` Bagas Sanjaya
@ 2021-12-10  1:26                                         ` Aleen 徐沛文
  2021-12-10  6:50                                           ` Bagas Sanjaya
  0 siblings, 1 reply; 129+ messages in thread
From: Aleen 徐沛文 @ 2021-12-10  1:26 UTC (permalink / raw)
  To: Bagas Sanjaya
  Cc: 徐沛文 (Aleen) via GitGitGadget, git,
	René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen, Junio C Hamano

> What about this wording below?
> 
> diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
> index be797d7a28..302659cfd5 100644
> --- a/Documentation/git-format-patch.txt
> +++ b/Documentation/git-format-patch.txt
> @@ -193,8 +193,8 @@ will want to ensure that threading is disabled for `git send-email`.
>   	ignored.
>   
>   --always::
> -	Include patches for commits that do not introduce any change,
> -	which are omitted by default.
> +	Always generate patches, even if there are empty-change commits.
> +	Default is to omit such commits.
>   
>   --cover-from-description=<mode>::
>   	Controls which parts of the cover letter will be automatically
> 
> 
> -- 
> An old man doll... just what I always wanted! - Clara

Dears Sanjaya,

    Thanks for your suggestion. I don't see the actual difference between
    the two sentences, and do you want to enhance the word "always"?
    If you do, how about just describing the option as "Always include patches..."?

Aleen

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

* Re: [PATCH v19 1/3] doc: git-format-patch: describe the option --always
  2021-12-10  1:26                                         ` Aleen 徐沛文
@ 2021-12-10  6:50                                           ` Bagas Sanjaya
  2021-12-11  9:22                                             ` Junio C Hamano
  0 siblings, 1 reply; 129+ messages in thread
From: Bagas Sanjaya @ 2021-12-10  6:50 UTC (permalink / raw)
  To: Aleen 徐沛文
  Cc: 徐沛文 (Aleen) via GitGitGadget, git,
	René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen, Junio C Hamano

On 10/12/21 08.26, Aleen 徐沛文 wrote:
> Dears Sanjaya,
> 
>      Thanks for your suggestion. I don't see the actual difference between
>      the two sentences, and do you want to enhance the word "always"?
>      If you do, how about just describing the option as "Always include patches..."?
> 
> Aleen
> 

Semantically speaking, I choose "generate" because git format-patch **generates**
patches from the specified commits.

-- 
An old man doll... just what I always wanted! - Clara

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

* Re: [PATCH v19 1/3] doc: git-format-patch: describe the option --always
  2021-12-10  6:50                                           ` Bagas Sanjaya
@ 2021-12-11  9:22                                             ` Junio C Hamano
  0 siblings, 0 replies; 129+ messages in thread
From: Junio C Hamano @ 2021-12-11  9:22 UTC (permalink / raw)
  To: Bagas Sanjaya
  Cc: Aleen 徐沛文,
	徐沛文 (Aleen) via GitGitGadget, git,
	René Scharfe, Phillip Wood, Johannes Schindelin,
	Elijah Newren, Aleen

Bagas Sanjaya <bagasdotme@gmail.com> writes:

> On 10/12/21 08.26, Aleen 徐沛文 wrote:
>> Dears Sanjaya,
>>      Thanks for your suggestion. I don't see the actual difference
>> between
>>      the two sentences, and do you want to enhance the word "always"?
>>      If you do, how about just describing the option as "Always include patches..."?
>> Aleen
>> 
>
> Semantically speaking, I choose "generate" because git format-patch **generates**
> patches from the specified commits.

But the given commit is empty and does not result in a patch at
all, which is the whole point of having this option, no?  A typical
format-patch output message consists of the message part and the
patch part, and calling an output created for an empty commit a
"patch" leads to confusion, I am afraid.

So, between the two sentences, I do not see much difference either
way.

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

end of thread, other threads:[~2021-12-11  9:22 UTC | newest]

Thread overview: 129+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-12  4:58 [PATCH 0/2] am: support --always option to am empty commits Aleen via GitGitGadget
2021-11-12  4:58 ` [PATCH 1/2] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
2021-11-12  4:58 ` [PATCH 2/2] am: support --always option to am empty commits Aleen via GitGitGadget
2021-11-12  6:17 ` [PATCH 0/2] " René Scharfe
2021-11-12  6:42   ` Aleen
2021-11-12  6:47   ` Junio C Hamano
2021-11-12  7:10     ` Aleen 徐沛文
2021-11-12 15:28     ` René Scharfe
2021-11-12 16:08       ` Junio C Hamano
2021-11-12  6:53 ` [PATCH v2 0/4] am: support --allow-empty " Aleen via GitGitGadget
2021-11-12  6:53   ` [PATCH v2 1/4] doc: git-format-patch: specify the option --always Aleen via GitGitGadget
2021-11-12 22:17     ` Junio C Hamano
2021-11-12  6:53   ` [PATCH v2 2/4] am: support --always option to am empty commits Aleen via GitGitGadget
2021-11-12 22:23     ` Junio C Hamano
2021-11-12  6:53   ` [PATCH v2 3/4] test: am: add the case when not passing the --always option Aleen via GitGitGadget
2021-11-12  6:54   ` [PATCH v2 4/4] chore: am: rename the --always option to --allow-empty Aleen via GitGitGadget
2021-11-15 10:39   ` [PATCH v3 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
2021-11-15 10:39     ` [PATCH v3 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-15 10:39     ` [PATCH v3 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
2021-11-15 11:13       ` Aleen 徐沛文
2021-11-16  5:18     ` [PATCH v4 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
2021-11-16  5:18       ` [PATCH v4 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-16  5:18       ` [PATCH v4 2/2] am: support --empty-commit option to handle empty patches Aleen via GitGitGadget
2021-11-16 10:07         ` Phillip Wood
2021-11-16 10:31           ` Aleen 徐沛文
2021-11-17  8:39           ` Junio C Hamano
2021-11-17  9:33       ` [PATCH v5 0/2] am: support --empty-commit=(die|skip|asis) option to am empty commits Aleen via GitGitGadget
2021-11-17  9:33         ` [PATCH v5 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-17  9:33         ` [PATCH v5 2/2] am: support --empty option to handle empty patches Aleen via GitGitGadget
2021-11-18 10:50         ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) " Aleen via GitGitGadget
2021-11-18 10:50           ` [PATCH v6 1/3] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-18 10:50           ` [PATCH v6 2/3] am: support --empty option to handle empty patches Aleen via GitGitGadget
2021-11-19  0:56             ` Junio C Hamano
2021-11-19 10:33             ` Bagas Sanjaya
2021-11-19 12:10               ` Ævar Arnfjörð Bjarmason
2021-11-19 12:20                 ` Eric Sunshine
2021-11-19 16:49                   ` Junio C Hamano
2021-11-19 16:46               ` Junio C Hamano
2021-11-18 10:50           ` [PATCH v6 3/3] am: throw an error when passing --empty option without value Aleen via GitGitGadget
2021-11-19  1:13             ` Junio C Hamano
2021-11-19  2:11               ` Aleen 徐沛文
2021-11-18 23:47           ` [PATCH v6 0/3] am: support --empty=(die|drop|keep) option to handle empty patches Junio C Hamano
2021-11-19  1:45             ` Aleen 徐沛文
2021-11-19  5:46               ` Junio C Hamano
2021-11-19  7:23                 ` Aleen 徐沛文
2021-11-19  7:25                   ` =?gb18030?B?QWxlZW4=?=
2021-11-19 16:54                   ` Junio C Hamano
2021-11-19 17:14                     ` Aleen 徐沛文
2021-11-19 19:25                       ` Junio C Hamano
2021-11-22 11:57                     ` Johannes Schindelin
2021-11-19  4:16             ` Aleen 徐沛文
2021-11-19  5:04           ` [PATCH v7 0/2] " Aleen via GitGitGadget
2021-11-19  5:04             ` [PATCH v7 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-19  5:04             ` [PATCH v7 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
2021-11-19 22:50               ` Junio C Hamano
2021-11-19 23:07               ` Junio C Hamano
2021-11-22  6:46             ` [PATCH v8 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
2021-11-22  6:46               ` [PATCH v8 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-22  6:46               ` [PATCH v8 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
2021-11-22  7:06                 ` Junio C Hamano
2021-11-22  7:19                   ` Aleen 徐沛文
2021-11-22  7:02               ` [PATCH v9 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
2021-11-22  7:02                 ` [PATCH v9 1/2] doc: git-format-patch: describe the option --always Aleen 徐沛文 via GitGitGadget
2021-11-22  7:02                 ` [PATCH v9 2/2] am: support --empty=<option> to handle empty patches Aleen 徐沛文 via GitGitGadget
2021-11-22  7:04                   ` Aleen 徐沛文
2021-11-22  7:51                 ` [PATCH v10 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
2021-11-22  7:51                   ` [PATCH v10 1/2] doc: git-format-patch: describe the option --always Aleen via GitGitGadget
2021-11-22 12:00                     ` Johannes Schindelin
2021-11-23  1:25                       ` Aleen 徐沛文
2021-11-23 12:30                         ` Johannes Schindelin
2021-11-22  7:51                   ` [PATCH v10 2/2] am: support --empty=<option> to handle empty patches Aleen via GitGitGadget
2021-11-23 15:26                   ` [PATCH v11 0/2] am: support --empty=(die|drop|keep) option " Aleen via GitGitGadget
2021-11-23 15:26                     ` [PATCH v11 1/2] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-11-23 16:12                       ` Johannes Schindelin
2021-11-23 22:02                         ` Junio C Hamano
2021-11-23 15:26                     ` [PATCH v11 2/2] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-11-26 20:14                       ` Elijah Newren
2021-11-29  9:19                         ` Aleen 徐沛文
2021-11-29 10:00                           ` Aleen 徐沛文
2021-11-29 17:10                             ` Elijah Newren
2021-11-30  5:45                               ` [PATCH v12 3/3] am: support --allow-empty to record specific " Aleen 徐沛文
2021-11-29 18:17                         ` [PATCH v11 2/2] am: support --empty=<option> to handle " Junio C Hamano
2021-11-29 18:57                           ` Elijah Newren
2021-11-30  5:37                     ` [PATCH v12 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option " Aleen via GitGitGadget
2021-11-30  5:37                       ` [PATCH v12 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-11-30  5:37                       ` [PATCH v12 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-11-30  5:37                       ` [PATCH v12 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-11-30  7:21                         ` Junio C Hamano
2021-11-30  9:55                       ` [PATCH v13 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
2021-11-30  9:55                         ` [PATCH v13 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-11-30  9:55                         ` [PATCH v13 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-11-30  9:55                         ` [PATCH v13 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-12-01  3:37                         ` [PATCH v14 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
2021-12-01  3:37                           ` [PATCH v14 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-12-01  3:37                           ` [PATCH v14 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-12-03 22:30                             ` Johannes Schindelin
2021-12-01  3:37                           ` [PATCH v14 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-12-02  0:58                             ` Junio C Hamano
2021-12-06  1:35                               ` Aleen 徐沛文
2021-12-06  9:41                           ` [PATCH v15 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
2021-12-06  9:41                             ` [PATCH v15 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-12-06  9:41                             ` [PATCH v15 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-12-06  9:41                             ` [PATCH v15 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-12-07  5:01                             ` [PATCH v16 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
2021-12-07  5:01                               ` [PATCH v16 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-12-07  5:01                               ` [PATCH v16 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-12-07  5:01                               ` [PATCH v16 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-12-07  8:31                               ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
2021-12-07  8:31                                 ` [PATCH v17 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-12-07  8:31                                 ` [PATCH v17 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-12-07 18:12                                   ` Junio C Hamano
2021-12-07  8:31                                 ` [PATCH v17 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-12-07 18:23                                   ` Junio C Hamano
2021-12-07 18:24                                 ` [PATCH v17 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Junio C Hamano
2021-12-08  5:05                                 ` [PATCH v18 " Aleen via GitGitGadget
2021-12-08  5:05                                   ` [PATCH v18 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-12-08  5:05                                   ` [PATCH v18 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-12-08  5:05                                   ` [PATCH v18 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget
2021-12-08  6:22                                     ` Junio C Hamano
2021-12-08  6:46                                       ` Aleen 徐沛文
2021-12-08 11:32                                         ` Junio C Hamano
2021-12-09  7:25                                   ` [PATCH v19 0/3] am: support --empty=(die|drop|keep) option and --allow-empty option to handle " Aleen via GitGitGadget
2021-12-09  7:25                                     ` [PATCH v19 1/3] doc: git-format-patch: describe the option --always 徐沛文 (Aleen) via GitGitGadget
2021-12-09  9:28                                       ` Bagas Sanjaya
2021-12-10  1:26                                         ` Aleen 徐沛文
2021-12-10  6:50                                           ` Bagas Sanjaya
2021-12-11  9:22                                             ` Junio C Hamano
2021-12-09  7:25                                     ` [PATCH v19 2/3] am: support --empty=<option> to handle empty patches 徐沛文 (Aleen) via GitGitGadget
2021-12-09  7:25                                     ` [PATCH v19 3/3] am: support --allow-empty to record specific " 徐沛文 (Aleen) via GitGitGadget

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).