[PATCH] format-patch: allow a non-integral version numbers

Subsystems: documentation, the rest

STALE1968d

30 messages, 5 authors, 2021-03-24 · open the first message on its own page

[PATCH] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-02-25 16:18:36

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but if we can provide `format-patch` with
non-integer versions numbers of patches, this may help us to send patches
such as "v1.1" versions sometimes.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
     * format-patch previously only integer version number -v<n>, now trying
       to provide a non-integer version.
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/885

 Documentation/git-format-patch.txt |  6 +++---
 builtin/log.c                      | 20 ++++++++++----------
 log-tree.c                         |  4 ++--
 revision.h                         |  2 +-
 t/t4014-format-patch.sh            |  8 ++++----
 5 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..0cc39dbf573c 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -215,12 +215,12 @@ populated with placeholder text.
 
 -v <n>::
 --reroll-count=<n>::
-	Mark the series as the <n>-th iteration of the topic. The
+	Mark the series as the specified version of the topic. The
 	output filenames have `v<n>` prepended to them, and the
 	subject prefix ("PATCH" by default, but configurable via the
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
-	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
-	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	`--reroll-count 4.4` may produce `v4.4-0001-add-makefile.patch`
+	file that has "Subject: [PATCH v4.4 1/20] Add makefile" in it.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..72af140b812e 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,13 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
+static const char *diff_title(struct strbuf *sb, const char *reroll_count,
 		       const char *generic, const char *rerolled)
 {
-	if (reroll_count <= 0)
+	if (!reroll_count)
 		strbuf_addstr(sb, generic);
 	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
+		strbuf_addf(sb, rerolled, "last version");
 	return sb->buf;
 }
 
@@ -1717,7 +1717,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,8 +1751,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
-			    N_("mark the series as Nth re-roll")),
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
+			    N_("mark the series as specified version re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
 		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
@@ -1862,9 +1862,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
@@ -2080,7 +2080,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
 		rev.idiff_title = diff_title(&idiff_title, reroll_count,
 					     _("Interdiff:"),
-					     _("Interdiff against v%d:"));
+					     _("Interdiff against %s:"));
 	}
 
 	if (creation_factor < 0)
@@ -2099,7 +2099,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.creation_factor = creation_factor;
 		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
 					     _("Range-diff:"),
-					     _("Range-diff against v%d:"));
+					     _("Range-diff against %s:"));
 	}
 
 	if (!signature) {
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..b911e08f0810 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -372,10 +372,10 @@ test_expect_success 'filename limit applies only to basename' '
 
 test_expect_success 'reroll count' '
 	rm -fr patches &&
-	git format-patch -o patches --cover-letter --reroll-count 4 main..side >list &&
-	! grep -v "^patches/v4-000[0-3]-" list &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
 	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
-	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
 '
 
 test_expect_success 'reroll count (-v)' '
@@ -2252,7 +2252,7 @@ test_expect_success 'interdiff: cover-letter' '
 
 test_expect_success 'interdiff: reroll-count' '
 	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
-	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+	test_i18ngrep "^Interdiff ..* last version:$" v2-0000-cover-letter.patch
 '
 
 test_expect_success 'interdiff: solo-patch' '
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-02-25 17:58:37

On Thu, Feb 25, 2021 at 11:19 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but if we can provide `format-patch` with
non-integer versions numbers of patches, this may help us to send patches
such as "v1.1" versions sometimes.
On the Git project itself, fractional or non-numeric re-roll "numbers"
are not necessarily encouraged[1], so this feature may not be
particularly useful here, though perhaps some other project might
benefit from it(?). Usually, you would want to justify why the change
is desirable. Denton did give a bit of justification in his
proposal[2] for this feature, so perhaps update this commit message by
copying some of what he wrote as justification.

[1]: I think I've only seen Denton send fractional re-rolls; other
people sometimes send a periodic "fixup!" patch, but both approaches
place extra burden on the project maintainer than merely re-rolling
the entire series with a new integer re-roll count.

[2]: https://github.com/gitgitgadget/git/issues/882
quoted hunk
Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
@@ -215,12 +215,12 @@ populated with placeholder text.
 -v <n>::
 --reroll-count=<n>::
-       Mark the series as the <n>-th iteration of the topic. The
+       Mark the series as the specified version of the topic. The
        output filenames have `v<n>` prepended to them, and the
        subject prefix ("PATCH" by default, but configurable via the
        `--subject-prefix` option) has ` v<n>` appended to it.  E.g.
-       `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
-       file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+       `--reroll-count 4.4` may produce `v4.4-0001-add-makefile.patch`
+       file that has "Subject: [PATCH v4.4 1/20] Add makefile" in it.
I'm not sure we want to encourage the use of fractional re-roll counts
by using it in an example like this. It would probably be better to
leave the example as-is. If you really want people to know that
fractional re-roll counts are supported, perhaps add separate sentence
saying that they are.
quoted hunk
diff --git a/builtin/log.c b/builtin/log.c
@@ -1662,13 +1662,13 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
-static const char *diff_title(struct strbuf *sb, int reroll_count,
+static const char *diff_title(struct strbuf *sb, const char *reroll_count,
                       const char *generic, const char *rerolled)
 {
-       if (reroll_count <= 0)
+       if (!reroll_count)
                strbuf_addstr(sb, generic);
        else /* RFC may be v0, so allow -v1 to diff against v0 */
-               strbuf_addf(sb, rerolled, reroll_count - 1);
+               strbuf_addf(sb, rerolled, "last version");
        return sb->buf;
 }
There are a couple problems here (at least). First, the string "last
version" should be localizable, `_("last version")`. Second, in
Denton's proposal[2], he suggested using the string "last version"
_only_ if the re-roll count is not an integer. What you have here
applies "last version" unconditionally when -v is used so that the
outcome is _always_ "Range-diff since last version". If that's what
you intend to do, there's no reason to do any sort of interpolation
using the template "Range-diff since %". What Denton had in mind was
this (using pseudo-code):

    if re-roll count not specified:
        message = "Range-diff"
    else if re-roll count is integer:
        message = "Range-diff since v%d", re-roll
    else:
        message = "Range-diff since v%s", re-roll

However, there isn't a good reason to favor "Range-diff since last
version" over the simpler generic message "Range-diff". So, the above
should be collapsed to:

    if re-roll count is specified and integer:
        message = "Range-diff since v%d", re-roll
    else:
        message = "Range-diff"
quoted hunk
@@ -2080,7 +2080,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
-                                            _("Interdiff against v%d:"));
+                                            _("Interdiff against %s:"));
@@ -2099,7 +2099,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
-                                            _("Range-diff against v%d:"));
+                                            _("Range-diff against %s:"));
If you follow my recommendation above using the simplified
conditional, then you don't need to drop the "v" since you won't be
saying "last version".

Re: [PATCH] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-02-27 07:01:13

Eric Sunshine [off-list ref] 于2021年2月26日周五 上午1:57写道:
On Thu, Feb 25, 2021 at 11:19 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but if we can provide `format-patch` with
non-integer versions numbers of patches, this may help us to send patches
such as "v1.1" versions sometimes.
On the Git project itself, fractional or non-numeric re-roll "numbers"
are not necessarily encouraged[1], so this feature may not be
particularly useful here, though perhaps some other project might
benefit from it(?). Usually, you would want to justify why the change
is desirable. Denton did give a bit of justification in his
proposal[2] for this feature, so perhaps update this commit message by
copying some of what he wrote as justification.
OK, I will remember it.
[1]: I think I've only seen Denton send fractional re-rolls; other
people sometimes send a periodic "fixup!" patch, but both approaches
place extra burden on the project maintainer than merely re-rolling
the entire series with a new integer re-roll count.

[2]: https://github.com/gitgitgadget/git/issues/882
quoted
Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
@@ -215,12 +215,12 @@ populated with placeholder text.
 -v <n>::
 --reroll-count=<n>::
-       Mark the series as the <n>-th iteration of the topic. The
+       Mark the series as the specified version of the topic. The
        output filenames have `v<n>` prepended to them, and the
        subject prefix ("PATCH" by default, but configurable via the
        `--subject-prefix` option) has ` v<n>` appended to it.  E.g.
-       `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
-       file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+       `--reroll-count 4.4` may produce `v4.4-0001-add-makefile.patch`
+       file that has "Subject: [PATCH v4.4 1/20] Add makefile" in it.
I'm not sure we want to encourage the use of fractional re-roll counts
by using it in an example like this. It would probably be better to
leave the example as-is. If you really want people to know that
fractional re-roll counts are supported, perhaps add separate sentence
saying that they are.
Yes, but the original description `<n>-th iteration` may imply that the version
number is an integer. Is there any good way to solve it?
quoted
diff --git a/builtin/log.c b/builtin/log.c
@@ -1662,13 +1662,13 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
-static const char *diff_title(struct strbuf *sb, int reroll_count,
+static const char *diff_title(struct strbuf *sb, const char *reroll_count,
                       const char *generic, const char *rerolled)
 {
-       if (reroll_count <= 0)
+       if (!reroll_count)
                strbuf_addstr(sb, generic);
        else /* RFC may be v0, so allow -v1 to diff against v0 */
-               strbuf_addf(sb, rerolled, reroll_count - 1);
+               strbuf_addf(sb, rerolled, "last version");
        return sb->buf;
 }
There are a couple problems here (at least). First, the string "last
version" should be localizable, `_("last version")`. Second, in
Denton's proposal[2], he suggested using the string "last version"
_only_ if the re-roll count is not an integer. What you have here
applies "last version" unconditionally when -v is used so that the
outcome is _always_ "Range-diff since last version". If that's what
you intend to do, there's no reason to do any sort of interpolation
using the template "Range-diff since %". What Denton had in mind was
this (using pseudo-code):

    if re-roll count not specified:
        message = "Range-diff"
    else if re-roll count is integer:
        message = "Range-diff since v%d", re-roll
    else:
        message = "Range-diff since v%s", re-roll

However, there isn't a good reason to favor "Range-diff since last
version" over the simpler generic message "Range-diff". So, the above
should be collapsed to:interpolation

    if re-roll count is specified and integer:
        message = "Range-diff since v%d", re-roll
    else:
        message = "Range-diff"
You mean using "Range-diff since %" may not be as
 good as" Range-diff" without sorting. I agree with you.
quoted
@@ -2080,7 +2080,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
-                                            _("Interdiff against v%d:"));
+                                            _("Interdiff against %s:"));
@@ -2099,7 +2099,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
-                                            _("Range-diff against v%d:"));
+                                            _("Range-diff against %s:"));
If you follow my recommendation above using the simplified
conditional, then you don't need to drop the "v" since you won't be
saying "last version".
Your suggestion is very good and easy to implement, but I may have
made some changes in Junio suggestion later, that is, I used the
 `previous_count` method to provide it to `diff_title()`. I will explain
my thoughts and problems in my reply to Junio. You'll see it later.
Thank you for your help.

--
ZheNing Hu

[PATCH v2] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-01 08:41:42

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
laveled as a non-integral versions like `v1.1`,so teach format-patch
allow a non-integral versions may be helpful to send those patches.

Since the original `format-patch` logic, if we specify a version `-v<n>`
and commbine with `--interdiff` or `--rangediff`, the patch will output
"Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
not meet the requirements of our fractional version numbers, so provide
`format patch` a new option `--previous-count=<n>`, the patch can output
user-specified previous version number. If the user use a integral version
number `-v<n>`, ensure that the output in the patch is still `v<n-1>`.
(let `--previous-count` become invalid.)

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v1:

 1:  285e085cd546 ! 1:  800094cbf53b format-patch: allow a non-integral version numbers
     @@ Commit message
          format-patch: allow a non-integral version numbers
      
          Usually we can only use `format-patch -v<n>` to generate integral
     -    version numbers patches, but if we can provide `format-patch` with
     -    non-integer versions numbers of patches, this may help us to send patches
     -    such as "v1.1" versions sometimes.
     +    version numbers patches, but sometimes a same fixup should be
     +    laveled as a non-integral versions like `v1.1`,so teach format-patch
     +    allow a non-integral versions may be helpful to send those patches.
     +
     +    Since the original `format-patch` logic, if we specify a version `-v<n>`
     +    and commbine with `--interdiff` or `--rangediff`, the patch will output
     +    "Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
     +    not meet the requirements of our fractional version numbers, so provide
     +    `format patch` a new option `--previous-count=<n>`, the patch can output
     +    user-specified previous version number. If the user use a integral version
     +    number `-v<n>`, ensure that the output in the patch is still `v<n-1>`.
     +    (let `--previous-count` become invalid.)
      
          Signed-off-by: ZheNing Hu [off-list ref]
      
       ## Documentation/git-format-patch.txt ##
     +@@ Documentation/git-format-patch.txt: SYNOPSIS
     + 		   [--cover-from-description=<mode>]
     + 		   [--rfc] [--subject-prefix=<subject prefix>]
     + 		   [(--reroll-count|-v) <n>]
     ++		   [--previous-count=<n>]
     + 		   [--to=<email>] [--cc=<email>]
     + 		   [--[no-]cover-letter] [--quiet]
     + 		   [--[no-]encode-email-headers]
      @@ Documentation/git-format-patch.txt: populated with placeholder text.
     - 
     - -v <n>::
     - --reroll-count=<n>::
     --	Mark the series as the <n>-th iteration of the topic. The
     -+	Mark the series as the specified version of the topic. The
     - 	output filenames have `v<n>` prepended to them, and the
     - 	subject prefix ("PATCH" by default, but configurable via the
       	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
     --	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
     --	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
     -+	`--reroll-count 4.4` may produce `v4.4-0001-add-makefile.patch`
     -+	file that has "Subject: [PATCH v4.4 1/20] Add makefile" in it.
     + 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
     + 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
     ++	now can support non-integrated version number like `-v1.1`.
     ++
     ++--previous-count=<n>::
     ++	Under the premise that we have used `--reroll-count=<n>`,
     ++	we can use `--previous-count=<n>` to specify the previous
     ++	version number. E.g. When we use the `--range-diff` or
     ++	`--interdiff` option and combine with `-v2.3 --previous-count=2.2`,
     ++	"Interdiff against v2.2:" or "Range-diff against v2.2:"
     ++	will be output in the patch.
       
       --to=<email>::
       	Add a `To:` header to the email headers. This is in addition
     @@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
       }
       
      -static const char *diff_title(struct strbuf *sb, int reroll_count,
     -+static const char *diff_title(struct strbuf *sb, const char *reroll_count,
     - 		       const char *generic, const char *rerolled)
     +-		       const char *generic, const char *rerolled)
     ++static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
     ++			const char*previous_count, const char *generic, const char *rerolled)
       {
      -	if (reroll_count <= 0)
     -+	if (!reroll_count)
     ++	if (!reroll_count || (!reroll_count_is_integer && !previous_count))
       		strbuf_addstr(sb, generic);
     - 	else /* RFC may be v0, so allow -v1 to diff against v0 */
     +-	else /* RFC may be v0, so allow -v1 to diff against v0 */
      -		strbuf_addf(sb, rerolled, reroll_count - 1);
     -+		strbuf_addf(sb, rerolled, "last version");
     ++	else if (reroll_count_is_integer)/* RFC may be v0, so allow -v1 to diff against v0 */
     ++		strbuf_addf(sb, rerolled, atoi(reroll_count) - 1);
     ++	else if (previous_count)
     ++		strbuf_addf(sb, rerolled, previous_count);
       	return sb->buf;
       }
       
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
       	int use_patch_format = 0;
       	int quiet = 0;
      -	int reroll_count = -1;
     ++	int reroll_count_is_integer = 0;
      +	const char *reroll_count = NULL;
     ++	const char *previous_count = NULL;
       	char *cover_from_description_arg = NULL;
       	char *branch_name = NULL;
       	char *base_commit = NULL;
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
      -			    N_("mark the series as Nth re-roll")),
      +		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
      +			    N_("mark the series as specified version re-roll")),
     ++		OPT_STRING(0, "previous-count", &previous_count, N_("previous-count"),
     ++			    N_("specified as the last version while we use --reroll-count")),
       		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
       			    N_("max length of output filename")),
       		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 
       	if (cover_from_description_arg)
       		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
     - 
     +-
      -	if (0 < reroll_count) {
     ++	if (previous_count && !reroll_count)
     ++		usage(_("previous-count can only used when reroll-count is used"));
      +	if (reroll_count) {
       		struct strbuf sprefix = STRBUF_INIT;
      -		strbuf_addf(&sprefix, "%s v%d",
     ++		char ch;
     ++		size_t i = 0 , reroll_count_len = strlen(reroll_count);
     ++
     ++		for (; i != reroll_count_len; i++) {
     ++			ch = reroll_count[i];
     ++			if(!isdigit(ch))
     ++				break;
     ++		}
     ++		reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
      +		strbuf_addf(&sprefix, "%s v%s",
       			    rev.subject_prefix, reroll_count);
       		rev.reroll_count = reroll_count;
       		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
       		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
       		rev.idiff_title = diff_title(&idiff_title, reroll_count,
     - 					     _("Interdiff:"),
     +-					     _("Interdiff:"),
      -					     _("Interdiff against v%d:"));
     -+					     _("Interdiff against %s:"));
     ++			reroll_count_is_integer, previous_count, _("Interdiff:"),
     ++				reroll_count_is_integer ? _("Interdiff against v%d:") :
     ++					_("Interdiff against v%s:"));
       	}
       
       	if (creation_factor < 0)
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 		rev.rdiff2 = rdiff2.buf;
       		rev.creation_factor = creation_factor;
       		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
     - 					     _("Range-diff:"),
     +-					     _("Range-diff:"),
      -					     _("Range-diff against v%d:"));
     -+					     _("Range-diff against %s:"));
     ++			reroll_count_is_integer, previous_count, _("Range-diff:"),
     ++				reroll_count_is_integer ? _("Range-diff against v%d:") :
     ++					_("Range-diff against v%s:"));
       	}
       
       	if (!signature) {
     @@ revision.h: struct rev_info {
       	struct ident_split from_ident;
       	struct string_list *ref_message_ids;
      
     + ## t/t3206-range-diff.sh ##
     +@@ t/t3206-range-diff.sh: test_expect_success 'format-patch --range-diff as commentary' '
     + 	grep "> 1: .* new message" 0001-*
     + '
     + 
     ++test_expect_success 'format-patch --range-diff reroll-count with a non-integer and previous-count ' '
     ++	git format-patch --range-diff=HEAD~1 -v2.9 --previous-count=2.8 HEAD~1 >actual &&
     ++	test_when_finished "rm v2.9-0001-*" &&
     ++	test_line_count = 1 actual &&
     ++	test_i18ngrep "^Range-diff ..* v2.8:$" v2.9-0001-* &&
     ++	grep "> 1: .* new message" v2.9-0001-*
     ++'
     ++
     ++test_expect_success 'format-patch --range-diff reroll-count with a integer previous-count' '
     ++	git format-patch --range-diff=HEAD~1 -v2 --previous-count=1.8 HEAD~1 >actual &&
     ++	test_when_finished "rm v2-0001-*" &&
     ++	test_line_count = 1 actual &&
     ++	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
     ++	grep "> 1: .* new message" v2-0001-*
     ++'
     ++
     + test_expect_success 'range-diff overrides diff.noprefix internally' '
     + 	git -c diff.noprefix=true range-diff HEAD^...
     + '
     +
       ## t/t4014-format-patch.sh ##
     -@@ t/t4014-format-patch.sh: test_expect_success 'filename limit applies only to basename' '
     +@@ t/t4014-format-patch.sh: test_expect_success 'reroll count' '
     + 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
     + '
       
     - test_expect_success 'reroll count' '
     - 	rm -fr patches &&
     --	git format-patch -o patches --cover-letter --reroll-count 4 main..side >list &&
     --	! grep -v "^patches/v4-000[0-3]-" list &&
     ++test_expect_success 'reroll count with a non-integer' '
     ++	rm -fr patches &&
      +	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
      +	! grep -v "^patches/v4.4-000[0-3]-" list &&
     - 	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     --	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
     ++	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
      +	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
     - '
     - 
     ++'
     ++
       test_expect_success 'reroll count (-v)' '
     -@@ t/t4014-format-patch.sh: test_expect_success 'interdiff: cover-letter' '
     + 	rm -fr patches &&
     + 	git format-patch -o patches --cover-letter -v4 main..side >list &&
     +@@ t/t4014-format-patch.sh: test_expect_success 'reroll count (-v)' '
     + 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
     + '
       
     - test_expect_success 'interdiff: reroll-count' '
     - 	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
     --	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
     -+	test_i18ngrep "^Interdiff ..* last version:$" v2-0000-cover-letter.patch
     ++test_expect_success 'reroll count (-v) with a non-integer' '
     ++	rm -fr patches &&
     ++	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
     ++	! grep -v "^patches/v4.4-000[0-3]-" list &&
     ++	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     ++	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
     ++'
     ++
     + check_threading () {
     + 	expect="$1" &&
     + 	shift &&
     +@@ t/t4014-format-patch.sh: test_expect_success 'interdiff: reroll-count' '
     + 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
       '
       
     ++test_expect_success 'interdiff: reroll-count with a non-integer' '
     ++	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
     ++	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
     ++'
     ++
     ++test_expect_success 'interdiff: reroll-count with a non-integer and previous-count ' '
     ++	git format-patch --cover-letter --interdiff=boop~2 -v2.2 --previous-count=2.1 -1 boop &&
     ++	test_i18ngrep "^Interdiff ..* v2.1:$" v2.2-0000-cover-letter.patch
     ++'
     ++
     ++test_expect_success 'interdiff: reroll-count with a integer and previous-count ' '
     ++	git format-patch --cover-letter --interdiff=boop~2 -v2 --previous-count=1.5 -1 boop &&
     ++	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
     ++'
     ++test_expect_success 'interdiff: previous-count without reroll-count ' '
     ++	test_must_fail git format-patch --cover-letter --interdiff=boop~2 --previous-count=1.5 -1 boop
     ++'
       test_expect_success 'interdiff: solo-patch' '
     + 	cat >expect <<-\EOF &&
     + 	  +fleep


 Documentation/git-format-patch.txt | 10 +++++++
 builtin/log.c                      | 48 ++++++++++++++++++++----------
 log-tree.c                         |  4 +--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 ++++++++++
 t/t4014-format-patch.sh            | 33 ++++++++++++++++++++
 6 files changed, 95 insertions(+), 18 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..f10fa6527f5f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -22,6 +22,7 @@ SYNOPSIS
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
+		   [--previous-count=<n>]
 		   [--to=<email>] [--cc=<email>]
 		   [--[no-]cover-letter] [--quiet]
 		   [--[no-]encode-email-headers]
@@ -221,6 +222,15 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	now can support non-integrated version number like `-v1.1`.
+
+--previous-count=<n>::
+	Under the premise that we have used `--reroll-count=<n>`,
+	we can use `--previous-count=<n>` to specify the previous
+	version number. E.g. When we use the `--range-diff` or
+	`--interdiff` option and combine with `-v2.3 --previous-count=2.2`,
+	"Interdiff against v2.2:" or "Range-diff against v2.2:"
+	will be output in the patch.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..95c95eab5393 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,15 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
+			const char*previous_count, const char *generic, const char *rerolled)
 {
-	if (reroll_count <= 0)
+	if (!reroll_count || (!reroll_count_is_integer && !previous_count))
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
+	else if (reroll_count_is_integer)/* RFC may be v0, so allow -v1 to diff against v0 */
+		strbuf_addf(sb, rerolled, atoi(reroll_count) - 1);
+	else if (previous_count)
+		strbuf_addf(sb, rerolled, previous_count);
 	return sb->buf;
 }
 
@@ -1717,7 +1719,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	int reroll_count_is_integer = 0;
+	const char *reroll_count = NULL;
+	const char *previous_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,8 +1755,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
-			    N_("mark the series as Nth re-roll")),
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
+			    N_("mark the series as specified version re-roll")),
+		OPT_STRING(0, "previous-count", &previous_count, N_("previous-count"),
+			    N_("specified as the last version while we use --reroll-count")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
 		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
@@ -1861,10 +1867,20 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
-
-	if (0 < reroll_count) {
+	if (previous_count && !reroll_count)
+		usage(_("previous-count can only used when reroll-count is used"));
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+		char ch;
+		size_t i = 0 , reroll_count_len = strlen(reroll_count);
+
+		for (; i != reroll_count_len; i++) {
+			ch = reroll_count[i];
+			if(!isdigit(ch))
+				break;
+		}
+		reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
@@ -2079,8 +2095,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
 		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
 		rev.idiff_title = diff_title(&idiff_title, reroll_count,
-					     _("Interdiff:"),
-					     _("Interdiff against v%d:"));
+			reroll_count_is_integer, previous_count, _("Interdiff:"),
+				reroll_count_is_integer ? _("Interdiff against v%d:") :
+					_("Interdiff against v%s:"));
 	}
 
 	if (creation_factor < 0)
@@ -2098,8 +2115,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.rdiff2 = rdiff2.buf;
 		rev.creation_factor = creation_factor;
 		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
-					     _("Range-diff:"),
-					     _("Range-diff against v%d:"));
+			reroll_count_is_integer, previous_count, _("Range-diff:"),
+				reroll_count_is_integer ? _("Range-diff against v%d:") :
+					_("Range-diff against v%s:"));
 	}
 
 	if (!signature) {
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..ffb91f27f75f 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer and previous-count ' '
+	git format-patch --range-diff=HEAD~1 -v2.9 --previous-count=2.8 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v2.8:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer previous-count' '
+	git format-patch --range-diff=HEAD~1 -v2 --previous-count=1.8 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..1b3c1dc273aa 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,23 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a non-integer and previous-count ' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 --previous-count=2.1 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v2.1:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer and previous-count ' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 --previous-count=1.5 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+test_expect_success 'interdiff: previous-count without reroll-count ' '
+	test_must_fail git format-patch --cover-letter --interdiff=boop~2 --previous-count=1.5 -1 boop
+'
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v2] format-patch: allow a non-integral version numbers

From: Junio C Hamano <hidden>
Date: 2021-03-03 06:42:29

"ZheNing Hu via GitGitGadget" [off-list ref] writes:
quoted hunk
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..95c95eab5393 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,15 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
+			const char*previous_count, const char *generic, const char *rerolled)
Avoid overly long lines here, but quite honestly, I find that this
interface is way too ugly to live.

Can we do all the computation around previous count in the caller,
so that this function only takes reroll_count and previous_count
that are both "const char *", and then the body will just be:

	if (!reroll_count)
		strbuf_addstr(sb, generic);
	else if (previous_count)
		strbuf_addf(sb, rerolled, previous_count);
	return sb->buf;

That way, the callers do not have to prepare two different rerolled
template and switch between them based on "is_integer".

In other words, they need to care "is_integer" already, so making
them responsible for preparing "previous_count" always usable by
this function would be a reasonable way to partition the tasks
between this callee and the caller.

That way, this function do not even need to know about "is_integer"
bit.
+	if (previous_count && !reroll_count)
+		usage(_("previous-count can only used when reroll-count is used"));
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+		char ch;
+		size_t i = 0 , reroll_count_len = strlen(reroll_count);
+
+		for (; i != reroll_count_len; i++) {
+			ch = reroll_count[i];
+			if(!isdigit(ch))
+				break;
+		}
+		reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
Do not reinvent integer parsing.  In our codebase, it is far more
common (and it is less error prone) to do something like this:

	char *endp;

	count = strtoul(reroll_count_string, &endp, 10);
	if (*endp) {
		/* followed by non-digit: not an integer */
		is_integer = 0;
	} else {
        	is_integer = 1;
                if (0 < count)
			previous_count_string = xstrfmt("%d", count - 1);
	}

And then, you can move the "if previous is there and count is not
specified" check after this block, to make sure that a non-integer
reroll count is always accompanied by a previous count, for example.
quoted hunk
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
@@ -2079,8 +2095,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
 		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
 		rev.idiff_title = diff_title(&idiff_title, reroll_count,
-					     _("Interdiff:"),
-					     _("Interdiff against v%d:"));
+			reroll_count_is_integer, previous_count, _("Interdiff:"),
+				reroll_count_is_integer ? _("Interdiff against v%d:") :
+					_("Interdiff against v%s:"));
I've touched the calling convention into diff_title() function
earlier.
quoted hunk
@@ -2098,8 +2115,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.rdiff2 = rdiff2.buf;
 		rev.creation_factor = creation_factor;
 		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
-					     _("Range-diff:"),
-					     _("Range-diff against v%d:"));
+			reroll_count_is_integer, previous_count, _("Range-diff:"),
+				reroll_count_is_integer ? _("Range-diff against v%d:") :
+					_("Range-diff against v%s:"));
Ditto.

Re: [PATCH v2] format-patch: allow a non-integral version numbers

From: Denton Liu <hidden>
Date: 2021-03-04 00:22:58

Hi ZheNing,

Thanks for picking up the issue. It's good to see that the GGG issue
tracker is helpful.

On Mon, Mar 01, 2021 at 08:40:29AM +0000, ZheNing Hu via GitGitGadget wrote:
From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
laveled as a non-integral versions like `v1.1`,so teach format-patch
Some typos:

s/laveled/labeled/; s/1.1`,/& /; s/format-patch/& to/
allow a non-integral versions may be helpful to send those patches.

Since the original `format-patch` logic, if we specify a version `-v<n>`
and commbine with `--interdiff` or `--rangediff`, the patch will output
"Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
not meet the requirements of our fractional version numbers, so provide
`format patch` a new option `--previous-count=<n>`, the patch can output
user-specified previous version number. If the user use a integral version
number `-v<n>`, ensure that the output in the patch is still `v<n-1>`.
(let `--previous-count` become invalid.)
Hmm, others may disagree but I don't really like the idea of
`--previous-count`. It may be useful for populating "Range-diff vs <n>"
instead of just "Range-diff" but I don't think it's worth the cost of
maintaining this option.

Signed-off-by: ZheNing Hu <redacted>
---
[...]
quoted hunk
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..f10fa6527f5f 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -22,6 +22,7 @@ SYNOPSIS
 		   [--cover-from-description=<mode>]
 		   [--rfc] [--subject-prefix=<subject prefix>]
 		   [(--reroll-count|-v) <n>]
+		   [--previous-count=<n>]
 		   [--to=<email>] [--cc=<email>]
 		   [--[no-]cover-letter] [--quiet]
 		   [--[no-]encode-email-headers]
@@ -221,6 +222,15 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	now can support non-integrated version number like `-v1.1`.
+
+--previous-count=<n>::
+	Under the premise that we have used `--reroll-count=<n>`,
+	we can use `--previous-count=<n>` to specify the previous
+	version number. E.g. When we use the `--range-diff` or
+	`--interdiff` option and combine with `-v2.3 --previous-count=2.2`,
+	"Interdiff against v2.2:" or "Range-diff against v2.2:"
+	will be output in the patch.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..95c95eab5393 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,15 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
+			const char*previous_count, const char *generic, const char *rerolled)
 {
-	if (reroll_count <= 0)
+	if (!reroll_count || (!reroll_count_is_integer && !previous_count))
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
+	else if (reroll_count_is_integer)/* RFC may be v0, so allow -v1 to diff against v0 */
+		strbuf_addf(sb, rerolled, atoi(reroll_count) - 1);
+	else if (previous_count)
+		strbuf_addf(sb, rerolled, previous_count);
 	return sb->buf;
 }
 I would just remove this hunk entirely and keep the existing logic...
quoted hunk
@@ -1717,7 +1719,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	int reroll_count_is_integer = 0;
+	const char *reroll_count = NULL;
+	const char *previous_count = NULL;
...then over here, we can do something like

	const char *reroll_count = NULL;
	int reroll_count_int = -1;

and then...
quoted hunk
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,8 +1755,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
-			    N_("mark the series as Nth re-roll")),
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
+			    N_("mark the series as specified version re-roll")),
+		OPT_STRING(0, "previous-count", &previous_count, N_("previous-count"),
+			    N_("specified as the last version while we use --reroll-count")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
 		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
@@ -1861,10 +1867,20 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
-
-	if (0 < reroll_count) {
+	if (previous_count && !reroll_count)
+		usage(_("previous-count can only used when reroll-count is used"));
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+		char ch;
+		size_t i = 0 , reroll_count_len = strlen(reroll_count);
+
+		for (; i != reroll_count_len; i++) {
+			ch = reroll_count[i];
+			if(!isdigit(ch))
+				break;
+		}
+		reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
...over here we can use Junio's integer parsing example and assign
reroll_count_int only if reroll_count can be parsed into an integer.

Thanks,
Denton

[PATCH v3] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-04 12:13:52

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral versions like `v1.1`, so teach `format-patch`
to allow a non-integral versions may be helpful to send those patches.

Since the original `format-patch` logic, if we specify a version `-v<n>`
and commbine with `--interdiff` or `--rangediff`, the patch will output
"Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
not meet the requirements of our fractional version numbers, so if the
user use a integral version number `-v<n>`, ensure that the output in
the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
"Range-diff".

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v2:

 1:  800094cbf53b ! 1:  d4f38b78c464 format-patch: allow a non-integral version numbers
     @@ Commit message
      
          Usually we can only use `format-patch -v<n>` to generate integral
          version numbers patches, but sometimes a same fixup should be
     -    laveled as a non-integral versions like `v1.1`,so teach format-patch
     -    allow a non-integral versions may be helpful to send those patches.
     +    labeled as a non-integral versions like `v1.1`, so teach `format-patch`
     +    to allow a non-integral versions may be helpful to send those patches.
      
          Since the original `format-patch` logic, if we specify a version `-v<n>`
          and commbine with `--interdiff` or `--rangediff`, the patch will output
          "Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
     -    not meet the requirements of our fractional version numbers, so provide
     -    `format patch` a new option `--previous-count=<n>`, the patch can output
     -    user-specified previous version number. If the user use a integral version
     -    number `-v<n>`, ensure that the output in the patch is still `v<n-1>`.
     -    (let `--previous-count` become invalid.)
     +    not meet the requirements of our fractional version numbers, so if the
     +    user use a integral version number `-v<n>`, ensure that the output in
     +    the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
     +    "Range-diff".
      
          Signed-off-by: ZheNing Hu [off-list ref]
      
       ## Documentation/git-format-patch.txt ##
     -@@ Documentation/git-format-patch.txt: SYNOPSIS
     - 		   [--cover-from-description=<mode>]
     - 		   [--rfc] [--subject-prefix=<subject prefix>]
     - 		   [(--reroll-count|-v) <n>]
     -+		   [--previous-count=<n>]
     - 		   [--to=<email>] [--cc=<email>]
     - 		   [--[no-]cover-letter] [--quiet]
     - 		   [--[no-]encode-email-headers]
      @@ Documentation/git-format-patch.txt: populated with placeholder text.
       	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
       	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
       	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
      +	now can support non-integrated version number like `-v1.1`.
     -+
     -+--previous-count=<n>::
     -+	Under the premise that we have used `--reroll-count=<n>`,
     -+	we can use `--previous-count=<n>` to specify the previous
     -+	version number. E.g. When we use the `--range-diff` or
     -+	`--interdiff` option and combine with `-v2.3 --previous-count=2.2`,
     -+	"Interdiff against v2.2:" or "Range-diff against v2.2:"
     -+	will be output in the patch.
       
       --to=<email>::
       	Add a `To:` header to the email headers. This is in addition
     @@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
       
      -static const char *diff_title(struct strbuf *sb, int reroll_count,
      -		       const char *generic, const char *rerolled)
     -+static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
     -+			const char*previous_count, const char *generic, const char *rerolled)
     ++static const char *diff_title(struct strbuf *sb,
     ++			const char *reroll_count_string,
     ++			const char*previous_count_string,
     ++			const char *generic, const char *rerolled)
       {
      -	if (reroll_count <= 0)
     -+	if (!reroll_count || (!reroll_count_is_integer && !previous_count))
     ++	if (!reroll_count_string || !previous_count_string)
       		strbuf_addstr(sb, generic);
      -	else /* RFC may be v0, so allow -v1 to diff against v0 */
      -		strbuf_addf(sb, rerolled, reroll_count - 1);
     -+	else if (reroll_count_is_integer)/* RFC may be v0, so allow -v1 to diff against v0 */
     -+		strbuf_addf(sb, rerolled, atoi(reroll_count) - 1);
     -+	else if (previous_count)
     -+		strbuf_addf(sb, rerolled, previous_count);
     ++	else if (previous_count_string)
     ++		strbuf_addf(sb, rerolled, previous_count_string);
       	return sb->buf;
       }
       
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     - 	struct strbuf buf = STRBUF_INIT;
       	int use_patch_format = 0;
       	int quiet = 0;
     --	int reroll_count = -1;
     -+	int reroll_count_is_integer = 0;
     -+	const char *reroll_count = NULL;
     -+	const char *previous_count = NULL;
     + 	int reroll_count = -1;
     ++	const char *reroll_count_string = NULL;
     ++	const char *previous_count_string = NULL;
       	char *cover_from_description_arg = NULL;
       	char *branch_name = NULL;
       	char *base_commit = NULL;
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
       			    N_("start numbering patches at <n> instead of 1")),
      -		OPT_INTEGER('v', "reroll-count", &reroll_count,
      -			    N_("mark the series as Nth re-roll")),
     -+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
     ++		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
      +			    N_("mark the series as specified version re-roll")),
     -+		OPT_STRING(0, "previous-count", &previous_count, N_("previous-count"),
     -+			    N_("specified as the last version while we use --reroll-count")),
       		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
       			    N_("max length of output filename")),
       		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     - 
       	if (cover_from_description_arg)
       		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
     --
     + 
      -	if (0 < reroll_count) {
     -+	if (previous_count && !reroll_count)
     -+		usage(_("previous-count can only used when reroll-count is used"));
     -+	if (reroll_count) {
     ++	if (reroll_count_string) {
       		struct strbuf sprefix = STRBUF_INIT;
      -		strbuf_addf(&sprefix, "%s v%d",
     -+		char ch;
     -+		size_t i = 0 , reroll_count_len = strlen(reroll_count);
     +-			    rev.subject_prefix, reroll_count);
     +-		rev.reroll_count = reroll_count;
     ++		char *endp;
      +
     -+		for (; i != reroll_count_len; i++) {
     -+			ch = reroll_count[i];
     -+			if(!isdigit(ch))
     -+				break;
     ++		reroll_count = strtoul(reroll_count_string, &endp, 10);
     ++		if (!*endp && 0 < reroll_count) {
     ++			previous_count_string = xstrfmt("%d", reroll_count - 1);
      +		}
     -+		reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
      +		strbuf_addf(&sprefix, "%s v%s",
     - 			    rev.subject_prefix, reroll_count);
     - 		rev.reroll_count = reroll_count;
     ++			    rev.subject_prefix, reroll_count_string);
     ++		rev.reroll_count = reroll_count_string;
       		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
     + 	}
     + 
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 			die(_("--interdiff requires --cover-letter or single patch"));
       		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
       		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
     - 		rev.idiff_title = diff_title(&idiff_title, reroll_count,
     +-		rev.idiff_title = diff_title(&idiff_title, reroll_count,
      -					     _("Interdiff:"),
      -					     _("Interdiff against v%d:"));
     -+			reroll_count_is_integer, previous_count, _("Interdiff:"),
     -+				reroll_count_is_integer ? _("Interdiff against v%d:") :
     ++		rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
     ++					previous_count_string,
     ++					_("Interdiff:"),
      +					_("Interdiff against v%s:"));
       	}
       
       	if (creation_factor < 0)
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 		rev.rdiff1 = rdiff1.buf;
       		rev.rdiff2 = rdiff2.buf;
       		rev.creation_factor = creation_factor;
     - 		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
     +-		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
      -					     _("Range-diff:"),
      -					     _("Range-diff against v%d:"));
     -+			reroll_count_is_integer, previous_count, _("Range-diff:"),
     -+				reroll_count_is_integer ? _("Range-diff against v%d:") :
     ++		rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
     ++					previous_count_string,
     ++					_("Range-diff:"),
      +					_("Range-diff against v%s:"));
       	}
       
     @@ t/t3206-range-diff.sh: test_expect_success 'format-patch --range-diff as comment
       	grep "> 1: .* new message" 0001-*
       '
       
     -+test_expect_success 'format-patch --range-diff reroll-count with a non-integer and previous-count ' '
     -+	git format-patch --range-diff=HEAD~1 -v2.9 --previous-count=2.8 HEAD~1 >actual &&
     ++test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
     ++	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
      +	test_when_finished "rm v2.9-0001-*" &&
      +	test_line_count = 1 actual &&
     -+	test_i18ngrep "^Range-diff ..* v2.8:$" v2.9-0001-* &&
     ++	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
      +	grep "> 1: .* new message" v2.9-0001-*
      +'
      +
     -+test_expect_success 'format-patch --range-diff reroll-count with a integer previous-count' '
     -+	git format-patch --range-diff=HEAD~1 -v2 --previous-count=1.8 HEAD~1 >actual &&
     ++test_expect_success 'format-patch --range-diff reroll-count with a integer' '
     ++	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
      +	test_when_finished "rm v2-0001-*" &&
      +	test_line_count = 1 actual &&
      +	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
     @@ t/t4014-format-patch.sh: test_expect_success 'interdiff: reroll-count' '
      +	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
      +'
      +
     -+test_expect_success 'interdiff: reroll-count with a non-integer and previous-count ' '
     -+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 --previous-count=2.1 -1 boop &&
     -+	test_i18ngrep "^Interdiff ..* v2.1:$" v2.2-0000-cover-letter.patch
     -+'
     -+
     -+test_expect_success 'interdiff: reroll-count with a integer and previous-count ' '
     -+	git format-patch --cover-letter --interdiff=boop~2 -v2 --previous-count=1.5 -1 boop &&
     ++test_expect_success 'interdiff: reroll-count with a integer' '
     ++	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
      +	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
      +'
     -+test_expect_success 'interdiff: previous-count without reroll-count ' '
     -+	test_must_fail git format-patch --cover-letter --interdiff=boop~2 --previous-count=1.5 -1 boop
     -+'
     ++
       test_expect_success 'interdiff: solo-patch' '
       	cat >expect <<-\EOF &&
       	  +fleep


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 46 +++++++++++++++++++-----------
 log-tree.c                         |  4 +--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 +++++++++++
 t/t4014-format-patch.sh            | 26 +++++++++++++++++
 6 files changed, 75 insertions(+), 20 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..8af0d2923118 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	now can support non-integrated version number like `-v1.1`.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..d135c30620b6 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,15 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			const char *reroll_count_string,
+			const char*previous_count_string,
+			const char *generic, const char *rerolled)
 {
-	if (reroll_count <= 0)
+	if (!reroll_count_string || !previous_count_string)
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
+	else if (previous_count_string)
+		strbuf_addf(sb, rerolled, previous_count_string);
 	return sb->buf;
 }
 
@@ -1718,6 +1720,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	int use_patch_format = 0;
 	int quiet = 0;
 	int reroll_count = -1;
+	const char *reroll_count_string = NULL;
+	const char *previous_count_string = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,8 +1755,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
-			    N_("mark the series as Nth re-roll")),
+		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
+			    N_("mark the series as specified version re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
 		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
@@ -1862,11 +1866,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count_string) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
-			    rev.subject_prefix, reroll_count);
-		rev.reroll_count = reroll_count;
+		char *endp;
+
+		reroll_count = strtoul(reroll_count_string, &endp, 10);
+		if (!*endp && 0 < reroll_count) {
+			previous_count_string = xstrfmt("%d", reroll_count - 1);
+		}
+		strbuf_addf(&sprefix, "%s v%s",
+			    rev.subject_prefix, reroll_count_string);
+		rev.reroll_count = reroll_count_string;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
 	}
 
@@ -2078,9 +2088,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			die(_("--interdiff requires --cover-letter or single patch"));
 		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
 		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
-		rev.idiff_title = diff_title(&idiff_title, reroll_count,
-					     _("Interdiff:"),
-					     _("Interdiff against v%d:"));
+		rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
+					previous_count_string,
+					_("Interdiff:"),
+					_("Interdiff against v%s:"));
 	}
 
 	if (creation_factor < 0)
@@ -2097,9 +2108,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.rdiff1 = rdiff1.buf;
 		rev.rdiff2 = rdiff2.buf;
 		rev.creation_factor = creation_factor;
-		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
-					     _("Range-diff:"),
-					     _("Range-diff against v%d:"));
+		rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
+					previous_count_string,
+					_("Range-diff:"),
+					_("Range-diff against v%s:"));
 	}
 
 	if (!signature) {
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v3] format-patch: allow a non-integral version numbers

From: Denton Liu <hidden>
Date: 2021-03-04 12:50:26

Hi ZheNing,

On Thu, Mar 04, 2021 at 12:12:06PM +0000, ZheNing Hu via GitGitGadget wrote:
From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral versions like `v1.1`, so teach `format-patch`
s/versions/version/
to allow a non-integral versions may be helpful to send those patches.
s/may be/which &/
quoted hunk
Since the original `format-patch` logic, if we specify a version `-v<n>`
and commbine with `--interdiff` or `--rangediff`, the patch will output
"Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
not meet the requirements of our fractional version numbers, so if the
user use a integral version number `-v<n>`, ensure that the output in
the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
"Range-diff".

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v2:

 1:  800094cbf53b ! 1:  d4f38b78c464 format-patch: allow a non-integral version numbers
     @@ Commit message
      
          Usually we can only use `format-patch -v<n>` to generate integral
          version numbers patches, but sometimes a same fixup should be
     -    laveled as a non-integral versions like `v1.1`,so teach format-patch
     -    allow a non-integral versions may be helpful to send those patches.
     +    labeled as a non-integral versions like `v1.1`, so teach `format-patch`
     +    to allow a non-integral versions may be helpful to send those patches.
      
          Since the original `format-patch` logic, if we specify a version `-v<n>`
          and commbine with `--interdiff` or `--rangediff`, the patch will output
          "Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
     -    not meet the requirements of our fractional version numbers, so provide
     -    `format patch` a new option `--previous-count=<n>`, the patch can output
     -    user-specified previous version number. If the user use a integral version
     -    number `-v<n>`, ensure that the output in the patch is still `v<n-1>`.
     -    (let `--previous-count` become invalid.)
     +    not meet the requirements of our fractional version numbers, so if the
     +    user use a integral version number `-v<n>`, ensure that the output in
     +    the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
     +    "Range-diff".
      
          Signed-off-by: ZheNing Hu [off-list ref]
      
       ## Documentation/git-format-patch.txt ##
     -@@ Documentation/git-format-patch.txt: SYNOPSIS
     - 		   [--cover-from-description=<mode>]
     - 		   [--rfc] [--subject-prefix=<subject prefix>]
     - 		   [(--reroll-count|-v) <n>]
     -+		   [--previous-count=<n>]
     - 		   [--to=<email>] [--cc=<email>]
     - 		   [--[no-]cover-letter] [--quiet]
     - 		   [--[no-]encode-email-headers]
      @@ Documentation/git-format-patch.txt: populated with placeholder text.
       	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
       	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
       	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
      +	now can support non-integrated version number like `-v1.1`.
     -+
     -+--previous-count=<n>::
     -+	Under the premise that we have used `--reroll-count=<n>`,
     -+	we can use `--previous-count=<n>` to specify the previous
     -+	version number. E.g. When we use the `--range-diff` or
     -+	`--interdiff` option and combine with `-v2.3 --previous-count=2.2`,
     -+	"Interdiff against v2.2:" or "Range-diff against v2.2:"
     -+	will be output in the patch.
       
       --to=<email>::
       	Add a `To:` header to the email headers. This is in addition
     @@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
       
      -static const char *diff_title(struct strbuf *sb, int reroll_count,
      -		       const char *generic, const char *rerolled)
     -+static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
     -+			const char*previous_count, const char *generic, const char *rerolled)
     ++static const char *diff_title(struct strbuf *sb,
     ++			const char *reroll_count_string,
     ++			const char*previous_count_string,
     ++			const char *generic, const char *rerolled)
       {
      -	if (reroll_count <= 0)
     -+	if (!reroll_count || (!reroll_count_is_integer && !previous_count))
     ++	if (!reroll_count_string || !previous_count_string)
       		strbuf_addstr(sb, generic);
      -	else /* RFC may be v0, so allow -v1 to diff against v0 */
      -		strbuf_addf(sb, rerolled, reroll_count - 1);
     -+	else if (reroll_count_is_integer)/* RFC may be v0, so allow -v1 to diff against v0 */
     -+		strbuf_addf(sb, rerolled, atoi(reroll_count) - 1);
     -+	else if (previous_count)
     -+		strbuf_addf(sb, rerolled, previous_count);
     ++	else if (previous_count_string)
     ++		strbuf_addf(sb, rerolled, previous_count_string);
       	return sb->buf;
       }
       
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     - 	struct strbuf buf = STRBUF_INIT;
       	int use_patch_format = 0;
       	int quiet = 0;
     --	int reroll_count = -1;
     -+	int reroll_count_is_integer = 0;
     -+	const char *reroll_count = NULL;
     -+	const char *previous_count = NULL;
     + 	int reroll_count = -1;
     ++	const char *reroll_count_string = NULL;
     ++	const char *previous_count_string = NULL;
       	char *cover_from_description_arg = NULL;
       	char *branch_name = NULL;
       	char *base_commit = NULL;
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
       			    N_("start numbering patches at <n> instead of 1")),
      -		OPT_INTEGER('v', "reroll-count", &reroll_count,
      -			    N_("mark the series as Nth re-roll")),
     -+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
     ++		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
      +			    N_("mark the series as specified version re-roll")),
     -+		OPT_STRING(0, "previous-count", &previous_count, N_("previous-count"),
     -+			    N_("specified as the last version while we use --reroll-count")),
       		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
       			    N_("max length of output filename")),
       		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     - 
       	if (cover_from_description_arg)
       		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
     --
     + 
      -	if (0 < reroll_count) {
     -+	if (previous_count && !reroll_count)
     -+		usage(_("previous-count can only used when reroll-count is used"));
     -+	if (reroll_count) {
     ++	if (reroll_count_string) {
       		struct strbuf sprefix = STRBUF_INIT;
      -		strbuf_addf(&sprefix, "%s v%d",
     -+		char ch;
     -+		size_t i = 0 , reroll_count_len = strlen(reroll_count);
     +-			    rev.subject_prefix, reroll_count);
     +-		rev.reroll_count = reroll_count;
     ++		char *endp;
      +
     -+		for (; i != reroll_count_len; i++) {
     -+			ch = reroll_count[i];
     -+			if(!isdigit(ch))
     -+				break;
     ++		reroll_count = strtoul(reroll_count_string, &endp, 10);
     ++		if (!*endp && 0 < reroll_count) {
     ++			previous_count_string = xstrfmt("%d", reroll_count - 1);
      +		}
     -+		reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
      +		strbuf_addf(&sprefix, "%s v%s",
     - 			    rev.subject_prefix, reroll_count);
     - 		rev.reroll_count = reroll_count;
     ++			    rev.subject_prefix, reroll_count_string);
     ++		rev.reroll_count = reroll_count_string;
       		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
     + 	}
     + 
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 			die(_("--interdiff requires --cover-letter or single patch"));
       		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
       		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
     - 		rev.idiff_title = diff_title(&idiff_title, reroll_count,
     +-		rev.idiff_title = diff_title(&idiff_title, reroll_count,
      -					     _("Interdiff:"),
      -					     _("Interdiff against v%d:"));
     -+			reroll_count_is_integer, previous_count, _("Interdiff:"),
     -+				reroll_count_is_integer ? _("Interdiff against v%d:") :
     ++		rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
     ++					previous_count_string,
     ++					_("Interdiff:"),
      +					_("Interdiff against v%s:"));
       	}
       
       	if (creation_factor < 0)
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 		rev.rdiff1 = rdiff1.buf;
       		rev.rdiff2 = rdiff2.buf;
       		rev.creation_factor = creation_factor;
     - 		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
     +-		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
      -					     _("Range-diff:"),
      -					     _("Range-diff against v%d:"));
     -+			reroll_count_is_integer, previous_count, _("Range-diff:"),
     -+				reroll_count_is_integer ? _("Range-diff against v%d:") :
     ++		rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
     ++					previous_count_string,
     ++					_("Range-diff:"),
      +					_("Range-diff against v%s:"));
       	}
       
     @@ t/t3206-range-diff.sh: test_expect_success 'format-patch --range-diff as comment
       	grep "> 1: .* new message" 0001-*
       '
       
     -+test_expect_success 'format-patch --range-diff reroll-count with a non-integer and previous-count ' '
     -+	git format-patch --range-diff=HEAD~1 -v2.9 --previous-count=2.8 HEAD~1 >actual &&
     ++test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
     ++	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
      +	test_when_finished "rm v2.9-0001-*" &&
      +	test_line_count = 1 actual &&
     -+	test_i18ngrep "^Range-diff ..* v2.8:$" v2.9-0001-* &&
     ++	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
      +	grep "> 1: .* new message" v2.9-0001-*
      +'
      +
     -+test_expect_success 'format-patch --range-diff reroll-count with a integer previous-count' '
     -+	git format-patch --range-diff=HEAD~1 -v2 --previous-count=1.8 HEAD~1 >actual &&
     ++test_expect_success 'format-patch --range-diff reroll-count with a integer' '
     ++	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
      +	test_when_finished "rm v2-0001-*" &&
      +	test_line_count = 1 actual &&
      +	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
     @@ t/t4014-format-patch.sh: test_expect_success 'interdiff: reroll-count' '
      +	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
      +'
      +
     -+test_expect_success 'interdiff: reroll-count with a non-integer and previous-count ' '
     -+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 --previous-count=2.1 -1 boop &&
     -+	test_i18ngrep "^Interdiff ..* v2.1:$" v2.2-0000-cover-letter.patch
     -+'
     -+
     -+test_expect_success 'interdiff: reroll-count with a integer and previous-count ' '
     -+	git format-patch --cover-letter --interdiff=boop~2 -v2 --previous-count=1.5 -1 boop &&
     ++test_expect_success 'interdiff: reroll-count with a integer' '
     ++	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
      +	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
      +'
     -+test_expect_success 'interdiff: previous-count without reroll-count ' '
     -+	test_must_fail git format-patch --cover-letter --interdiff=boop~2 --previous-count=1.5 -1 boop
     -+'
     ++
       test_expect_success 'interdiff: solo-patch' '
       	cat >expect <<-\EOF &&
       	  +fleep


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 46 +++++++++++++++++++-----------
 log-tree.c                         |  4 +--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 +++++++++++
 t/t4014-format-patch.sh            | 26 +++++++++++++++++
 6 files changed, 75 insertions(+), 20 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..8af0d2923118 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	now can support non-integrated version number like `-v1.1`.
Perhaps something like:

	+
	`<n>` can be any string, such as `-v1.1`.  In the case where it
	is a non-integral value, the "Range-diff" and "Interdiff"
	headers will not include the previous version.
quoted hunk
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..d135c30620b6 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,15 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			const char *reroll_count_string,
+			const char*previous_count_string,
+			const char *generic, const char *rerolled)
 {
-	if (reroll_count <= 0)
+	if (!reroll_count_string || !previous_count_string)
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
+	else if (previous_count_string)
+		strbuf_addf(sb, rerolled, previous_count_string);
 	return sb->buf;
 }
I don't think it's necessary to do this at all. We can just leave
`reroll_count < 0` here.
quoted hunk
@@ -1718,6 +1720,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	int use_patch_format = 0;
 	int quiet = 0;
 	int reroll_count = -1;
+	const char *reroll_count_string = NULL;
+	const char *previous_count_string = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,8 +1755,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
-			    N_("mark the series as Nth re-roll")),
+		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
+			    N_("mark the series as specified version re-roll")),
Others may disagree but I'm okay with leaving this as "Nth re-roll".
It's just a synopsis. More information can be found in the docs.
quoted hunk
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
 		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
@@ -1862,11 +1866,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count_string) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
-			    rev.subject_prefix, reroll_count);
-		rev.reroll_count = reroll_count;
+		char *endp;
+
+		reroll_count = strtoul(reroll_count_string, &endp, 10);
+		if (!*endp && 0 < reroll_count) {
This 0 < reroll_count check is unnecessary; it was initialised to -1 and
it hasn't changed since here.

Also, we can take advantage of the strtol_i() function, which can
perform bounds checking and error checking for us. This allows us to
assign reroll_count only when a valid integer is found so we can
eliminate previous_count_string().

Something like:

	strtol_i(reroll_count_string, 10, &reroll_count);
quoted hunk
+			previous_count_string = xstrfmt("%d", reroll_count - 1);
+		}
+		strbuf_addf(&sprefix, "%s v%s",
+			    rev.subject_prefix, reroll_count_string);
+		rev.reroll_count = reroll_count_string;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
 	}
 
@@ -2078,9 +2088,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			die(_("--interdiff requires --cover-letter or single patch"));
 		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
 		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
-		rev.idiff_title = diff_title(&idiff_title, reroll_count,
-					     _("Interdiff:"),
-					     _("Interdiff against v%d:"));
+		rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
+					previous_count_string,
+					_("Interdiff:"),
+					_("Interdiff against v%s:"));
 	}
 
 	if (creation_factor < 0)
@@ -2097,9 +2108,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		rev.rdiff1 = rdiff1.buf;
 		rev.rdiff2 = rdiff2.buf;
 		rev.creation_factor = creation_factor;
-		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
-					     _("Range-diff:"),
-					     _("Range-diff against v%d:"));
+		rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
+					previous_count_string,
+					_("Range-diff:"),
+					_("Range-diff against v%s:"));
 	}
 
 	if (!signature) {
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v3] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-05 04:56:31

Denton Liu [off-list ref] 于2021年3月4日周四 下午8:49写道:
Hi ZheNing,

On Thu, Mar 04, 2021 at 12:12:06PM +0000, ZheNing Hu via GitGitGadget wrote:
quoted
From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral versions like `v1.1`, so teach `format-patch`
s/versions/version/
quoted
to allow a non-integral versions may be helpful to send those patches.
s/may be/which &/
quoted
Since the original `format-patch` logic, if we specify a version `-v<n>`
and commbine with `--interdiff` or `--rangediff`, the patch will output
"Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
not meet the requirements of our fractional version numbers, so if the
user use a integral version number `-v<n>`, ensure that the output in
the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
"Range-diff".

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers

    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?

    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v2:

 1:  800094cbf53b ! 1:  d4f38b78c464 format-patch: allow a non-integral version numbers
     @@ Commit message

          Usually we can only use `format-patch -v<n>` to generate integral
          version numbers patches, but sometimes a same fixup should be
     -    laveled as a non-integral versions like `v1.1`,so teach format-patch
     -    allow a non-integral versions may be helpful to send those patches.
     +    labeled as a non-integral versions like `v1.1`, so teach `format-patch`
     +    to allow a non-integral versions may be helpful to send those patches.

          Since the original `format-patch` logic, if we specify a version `-v<n>`
          and commbine with `--interdiff` or `--rangediff`, the patch will output
          "Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
     -    not meet the requirements of our fractional version numbers, so provide
     -    `format patch` a new option `--previous-count=<n>`, the patch can output
     -    user-specified previous version number. If the user use a integral version
     -    number `-v<n>`, ensure that the output in the patch is still `v<n-1>`.
     -    (let `--previous-count` become invalid.)
     +    not meet the requirements of our fractional version numbers, so if the
     +    user use a integral version number `-v<n>`, ensure that the output in
     +    the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
     +    "Range-diff".

          Signed-off-by: ZheNing Hu [off-list ref]

       ## Documentation/git-format-patch.txt ##
     -@@ Documentation/git-format-patch.txt: SYNOPSIS
     -                   [--cover-from-description=<mode>]
     -                   [--rfc] [--subject-prefix=<subject prefix>]
     -                   [(--reroll-count|-v) <n>]
     -+                  [--previous-count=<n>]
     -                   [--to=<email>] [--cc=<email>]
     -                   [--[no-]cover-letter] [--quiet]
     -                   [--[no-]encode-email-headers]
      @@ Documentation/git-format-patch.txt: populated with placeholder text.
              `--subject-prefix` option) has ` v<n>` appended to it.  E.g.
              `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
              file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
      +       now can support non-integrated version number like `-v1.1`.
     -+
     -+--previous-count=<n>::
     -+       Under the premise that we have used `--reroll-count=<n>`,
     -+       we can use `--previous-count=<n>` to specify the previous
     -+       version number. E.g. When we use the `--range-diff` or
     -+       `--interdiff` option and combine with `-v2.3 --previous-count=2.2`,
     -+       "Interdiff against v2.2:" or "Range-diff against v2.2:"
     -+       will be output in the patch.

       --to=<email>::
              Add a `To:` header to the email headers. This is in addition
     @@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)

      -static const char *diff_title(struct strbuf *sb, int reroll_count,
      -                      const char *generic, const char *rerolled)
     -+static const char *diff_title(struct strbuf *sb, const char *reroll_count, int reroll_count_is_integer,
     -+                       const char*previous_count, const char *generic, const char *rerolled)
     ++static const char *diff_title(struct strbuf *sb,
     ++                       const char *reroll_count_string,
     ++                       const char*previous_count_string,
     ++                       const char *generic, const char *rerolled)
       {
      -       if (reroll_count <= 0)
     -+       if (!reroll_count || (!reroll_count_is_integer && !previous_count))
     ++       if (!reroll_count_string || !previous_count_string)
                      strbuf_addstr(sb, generic);
      -       else /* RFC may be v0, so allow -v1 to diff against v0 */
      -               strbuf_addf(sb, rerolled, reroll_count - 1);
     -+       else if (reroll_count_is_integer)/* RFC may be v0, so allow -v1 to diff against v0 */
     -+               strbuf_addf(sb, rerolled, atoi(reroll_count) - 1);
     -+       else if (previous_count)
     -+               strbuf_addf(sb, rerolled, previous_count);
     ++       else if (previous_count_string)
     ++               strbuf_addf(sb, rerolled, previous_count_string);
              return sb->buf;
       }

      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     -        struct strbuf buf = STRBUF_INIT;
              int use_patch_format = 0;
              int quiet = 0;
     --       int reroll_count = -1;
     -+       int reroll_count_is_integer = 0;
     -+       const char *reroll_count = NULL;
     -+       const char *previous_count = NULL;
     +        int reroll_count = -1;
     ++       const char *reroll_count_string = NULL;
     ++       const char *previous_count_string = NULL;
              char *cover_from_description_arg = NULL;
              char *branch_name = NULL;
              char *base_commit = NULL;
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
                                  N_("start numbering patches at <n> instead of 1")),
      -               OPT_INTEGER('v', "reroll-count", &reroll_count,
      -                           N_("mark the series as Nth re-roll")),
     -+               OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
     ++               OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
      +                           N_("mark the series as specified version re-roll")),
     -+               OPT_STRING(0, "previous-count", &previous_count, N_("previous-count"),
     -+                           N_("specified as the last version while we use --reroll-count")),
                      OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
                                  N_("max length of output filename")),
                      OPT_CALLBACK_F(0, "rfc", &rev, NULL,
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     -
              if (cover_from_description_arg)
                      cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
     --
     +
      -       if (0 < reroll_count) {
     -+       if (previous_count && !reroll_count)
     -+               usage(_("previous-count can only used when reroll-count is used"));
     -+       if (reroll_count) {
     ++       if (reroll_count_string) {
                      struct strbuf sprefix = STRBUF_INIT;
      -               strbuf_addf(&sprefix, "%s v%d",
     -+               char ch;
     -+               size_t i = 0 , reroll_count_len = strlen(reroll_count);
     +-                           rev.subject_prefix, reroll_count);
     +-               rev.reroll_count = reroll_count;
     ++               char *endp;
      +
     -+               for (; i != reroll_count_len; i++) {
     -+                       ch = reroll_count[i];
     -+                       if(!isdigit(ch))
     -+                               break;
     ++               reroll_count = strtoul(reroll_count_string, &endp, 10);
     ++               if (!*endp && 0 < reroll_count) {
     ++                       previous_count_string = xstrfmt("%d", reroll_count - 1);
      +               }
     -+               reroll_count_is_integer = i == reroll_count_len ? 1 : 0;
      +               strbuf_addf(&sprefix, "%s v%s",
     -                            rev.subject_prefix, reroll_count);
     -                rev.reroll_count = reroll_count;
     ++                           rev.subject_prefix, reroll_count_string);
     ++               rev.reroll_count = reroll_count_string;
                      rev.subject_prefix = strbuf_detach(&sprefix, NULL);
     +        }
     +
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     +                        die(_("--interdiff requires --cover-letter or single patch"));
                      rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
                      rev.idiff_oid2 = get_commit_tree_oid(list[0]);
     -                rev.idiff_title = diff_title(&idiff_title, reroll_count,
     +-               rev.idiff_title = diff_title(&idiff_title, reroll_count,
      -                                            _("Interdiff:"),
      -                                            _("Interdiff against v%d:"));
     -+                       reroll_count_is_integer, previous_count, _("Interdiff:"),
     -+                               reroll_count_is_integer ? _("Interdiff against v%d:") :
     ++               rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
     ++                                       previous_count_string,
     ++                                       _("Interdiff:"),
      +                                       _("Interdiff against v%s:"));
              }

              if (creation_factor < 0)
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     +                rev.rdiff1 = rdiff1.buf;
                      rev.rdiff2 = rdiff2.buf;
                      rev.creation_factor = creation_factor;
     -                rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
     +-               rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
      -                                            _("Range-diff:"),
      -                                            _("Range-diff against v%d:"));
     -+                       reroll_count_is_integer, previous_count, _("Range-diff:"),
     -+                               reroll_count_is_integer ? _("Range-diff against v%d:") :
     ++               rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
     ++                                       previous_count_string,
     ++                                       _("Range-diff:"),
      +                                       _("Range-diff against v%s:"));
              }

     @@ t/t3206-range-diff.sh: test_expect_success 'format-patch --range-diff as comment
              grep "> 1: .* new message" 0001-*
       '

     -+test_expect_success 'format-patch --range-diff reroll-count with a non-integer and previous-count ' '
     -+       git format-patch --range-diff=HEAD~1 -v2.9 --previous-count=2.8 HEAD~1 >actual &&
     ++test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
     ++       git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
      +       test_when_finished "rm v2.9-0001-*" &&
      +       test_line_count = 1 actual &&
     -+       test_i18ngrep "^Range-diff ..* v2.8:$" v2.9-0001-* &&
     ++       test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
      +       grep "> 1: .* new message" v2.9-0001-*
      +'
      +
     -+test_expect_success 'format-patch --range-diff reroll-count with a integer previous-count' '
     -+       git format-patch --range-diff=HEAD~1 -v2 --previous-count=1.8 HEAD~1 >actual &&
     ++test_expect_success 'format-patch --range-diff reroll-count with a integer' '
     ++       git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
      +       test_when_finished "rm v2-0001-*" &&
      +       test_line_count = 1 actual &&
      +       test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
     @@ t/t4014-format-patch.sh: test_expect_success 'interdiff: reroll-count' '
      +       test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
      +'
      +
     -+test_expect_success 'interdiff: reroll-count with a non-integer and previous-count ' '
     -+       git format-patch --cover-letter --interdiff=boop~2 -v2.2 --previous-count=2.1 -1 boop &&
     -+       test_i18ngrep "^Interdiff ..* v2.1:$" v2.2-0000-cover-letter.patch
     -+'
     -+
     -+test_expect_success 'interdiff: reroll-count with a integer and previous-count ' '
     -+       git format-patch --cover-letter --interdiff=boop~2 -v2 --previous-count=1.5 -1 boop &&
     ++test_expect_success 'interdiff: reroll-count with a integer' '
     ++       git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
      +       test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
      +'
     -+test_expect_success 'interdiff: previous-count without reroll-count ' '
     -+       test_must_fail git format-patch --cover-letter --interdiff=boop~2 --previous-count=1.5 -1 boop
     -+'
     ++
       test_expect_success 'interdiff: solo-patch' '
              cat >expect <<-\EOF &&
                +fleep


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 46 +++++++++++++++++++-----------
 log-tree.c                         |  4 +--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 +++++++++++
 t/t4014-format-patch.sh            | 26 +++++++++++++++++
 6 files changed, 75 insertions(+), 20 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..8af0d2923118 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
      `--subject-prefix` option) has ` v<n>` appended to it.  E.g.
      `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
      file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+     now can support non-integrated version number like `-v1.1`.
Perhaps something like:

        +
        `<n>` can be any string, such as `-v1.1`.  In the case where it
        is a non-integral value, the "Range-diff" and "Interdiff"
        headers will not include the previous version.
quoted
 --to=<email>::
      Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..d135c30620b6 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,15 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
      oidclr(&bases->base_commit);
 }

-static const char *diff_title(struct strbuf *sb, int reroll_count,
-                    const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+                     const char *reroll_count_string,
+                     const char*previous_count_string,
+                     const char *generic, const char *rerolled)
 {
-     if (reroll_count <= 0)
+     if (!reroll_count_string || !previous_count_string)
              strbuf_addstr(sb, generic);
-     else /* RFC may be v0, so allow -v1 to diff against v0 */
-             strbuf_addf(sb, rerolled, reroll_count - 1);
+     else if (previous_count_string)
+             strbuf_addf(sb, rerolled, previous_count_string);
      return sb->buf;
 }
I don't think it's necessary to do this at all. We can just leave
`reroll_count < 0` here.
Truly.
quoted
@@ -1718,6 +1720,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
      int use_patch_format = 0;
      int quiet = 0;
      int reroll_count = -1;
+     const char *reroll_count_string = NULL;
+     const char *previous_count_string = NULL;
      char *cover_from_description_arg = NULL;
      char *branch_name = NULL;
      char *base_commit = NULL;
@@ -1751,8 +1755,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                          N_("use <sfx> instead of '.patch'")),
              OPT_INTEGER(0, "start-number", &start_number,
                          N_("start numbering patches at <n> instead of 1")),
-             OPT_INTEGER('v', "reroll-count", &reroll_count,
-                         N_("mark the series as Nth re-roll")),
+             OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
+                         N_("mark the series as specified version re-roll")),
Others may disagree but I'm okay with leaving this as "Nth re-roll".
It's just a synopsis. More information can be found in the docs.
Does Nth feel that reroll_count must be an integer? If you think it is
possible, I can restore it to the original state.
quoted
              OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
                          N_("max length of output filename")),
              OPT_CALLBACK_F(0, "rfc", &rev, NULL,
@@ -1862,11 +1866,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
      if (cover_from_description_arg)
              cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);

-     if (0 < reroll_count) {
+     if (reroll_count_string) {
              struct strbuf sprefix = STRBUF_INIT;
-             strbuf_addf(&sprefix, "%s v%d",
-                         rev.subject_prefix, reroll_count);
-             rev.reroll_count = reroll_count;
+             char *endp;
+
+             reroll_count = strtoul(reroll_count_string, &endp, 10);
+             if (!*endp && 0 < reroll_count) {
This 0 < reroll_count check is unnecessary; it was initialised to -1 and
it hasn't changed since here.

Also, we can take advantage of the strtol_i() function, which can
perform bounds checking and error checking for us. This allows us to
assign reroll_count only when a valid integer is found so we can
eliminate previous_count_string().

Something like:

        strtol_i(reroll_count_string, 10, &reroll_count);
My code does make the program a lot more complicated,
and your advice is good.
quoted
+                     previous_count_string = xstrfmt("%d", reroll_count - 1);
+             }
+             strbuf_addf(&sprefix, "%s v%s",
+                         rev.subject_prefix, reroll_count_string);
+             rev.reroll_count = reroll_count_string;
              rev.subject_prefix = strbuf_detach(&sprefix, NULL);
      }
@@ -2078,9 +2088,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                      die(_("--interdiff requires --cover-letter or single patch"));
              rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
              rev.idiff_oid2 = get_commit_tree_oid(list[0]);
-             rev.idiff_title = diff_title(&idiff_title, reroll_count,
-                                          _("Interdiff:"),
-                                          _("Interdiff against v%d:"));
+             rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
+                                     previous_count_string,
+                                     _("Interdiff:"),
+                                     _("Interdiff against v%s:"));
      }

      if (creation_factor < 0)
@@ -2097,9 +2108,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
              rev.rdiff1 = rdiff1.buf;
              rev.rdiff2 = rdiff2.buf;
              rev.creation_factor = creation_factor;
-             rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
-                                          _("Range-diff:"),
-                                          _("Range-diff against v%d:"));
+             rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
+                                     previous_count_string,
+                                     _("Range-diff:"),
+                                     _("Range-diff against v%s:"));
      }

      if (!signature) {
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
      int start_len = filename->len;
      int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);

-     if (0 < info->reroll_count)
-             strbuf_addf(filename, "v%d-", info->reroll_count);
+     if (info->reroll_count)
+             strbuf_addf(filename, "v%s-", info->reroll_count);
      strbuf_addf(filename, "%04d-%s", nr, subject);

      if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
      const char      *mime_boundary;
      const char      *patch_suffix;
      int             numbered_files;
-     int             reroll_count;
+     const char      *reroll_count;
      char            *message_id;
      struct ident_split from_ident;
      struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
      grep "> 1: .* new message" 0001-*
 '

+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+     git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+     test_when_finished "rm v2.9-0001-*" &&
+     test_line_count = 1 actual &&
+     test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+     grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+     git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+     test_when_finished "rm v2-0001-*" &&
+     test_line_count = 1 actual &&
+     test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+     grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
      git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
      ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '

+test_expect_success 'reroll count with a non-integer' '
+     rm -fr patches &&
+     git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+     ! grep -v "^patches/v4.4-000[0-3]-" list &&
+     sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+     ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
      rm -fr patches &&
      git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
      ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '

+test_expect_success 'reroll count (-v) with a non-integer' '
+     rm -fr patches &&
+     git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+     ! grep -v "^patches/v4.4-000[0-3]-" list &&
+     sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+     ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
      expect="$1" &&
      shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
      test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '

+test_expect_success 'interdiff: reroll-count with a non-integer' '
+     git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+     test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+     git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+     test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
      cat >expect <<-\EOF &&
        +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
--
gitgitgadget

[PATCH v4] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-05 07:10:16

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v3:

 1:  d4f38b78c464 ! 1:  cb1c0267e16b format-patch: allow a non-integral version numbers
     @@ Commit message
      
          Usually we can only use `format-patch -v<n>` to generate integral
          version numbers patches, but sometimes a same fixup should be
     -    labeled as a non-integral versions like `v1.1`, so teach `format-patch`
     -    to allow a non-integral versions may be helpful to send those patches.
     +    labeled as a non-integral version like `v1.1`, so teach `format-patch`
     +    to allow a non-integral version which may be helpful to send those
     +    patches.
      
     -    Since the original `format-patch` logic, if we specify a version `-v<n>`
     -    and commbine with `--interdiff` or `--rangediff`, the patch will output
     -    "Interdiff again v<n-1>:" or "Range-diff again v<n-1>:`, but this does
     -    not meet the requirements of our fractional version numbers, so if the
     -    user use a integral version number `-v<n>`, ensure that the output in
     -    the patch is still `v<n-1>`; otherwise, only output "Interdiff" or
     -    "Range-diff".
     +    `<n>` can be any string, such as `-v1.1`.  In the case where it
     +    is a non-integral value, the "Range-diff" and "Interdiff"
     +    headers will not include the previous version.
      
          Signed-off-by: ZheNing Hu [off-list ref]
      
     @@ Documentation/git-format-patch.txt: populated with placeholder text.
       	Add a `To:` header to the email headers. This is in addition
      
       ## builtin/log.c ##
     -@@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
     - 	oidclr(&bases->base_commit);
     - }
     - 
     --static const char *diff_title(struct strbuf *sb, int reroll_count,
     --		       const char *generic, const char *rerolled)
     -+static const char *diff_title(struct strbuf *sb,
     -+			const char *reroll_count_string,
     -+			const char*previous_count_string,
     -+			const char *generic, const char *rerolled)
     - {
     --	if (reroll_count <= 0)
     -+	if (!reroll_count_string || !previous_count_string)
     - 		strbuf_addstr(sb, generic);
     --	else /* RFC may be v0, so allow -v1 to diff against v0 */
     --		strbuf_addf(sb, rerolled, reroll_count - 1);
     -+	else if (previous_count_string)
     -+		strbuf_addf(sb, rerolled, previous_count_string);
     - 	return sb->buf;
     - }
     - 
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
       	int use_patch_format = 0;
       	int quiet = 0;
       	int reroll_count = -1;
      +	const char *reroll_count_string = NULL;
     -+	const char *previous_count_string = NULL;
       	char *cover_from_description_arg = NULL;
       	char *branch_name = NULL;
       	char *base_commit = NULL;
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
       		OPT_INTEGER(0, "start-number", &start_number,
       			    N_("start numbering patches at <n> instead of 1")),
      -		OPT_INTEGER('v', "reroll-count", &reroll_count,
     --			    N_("mark the series as Nth re-roll")),
      +		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
     -+			    N_("mark the series as specified version re-roll")),
     + 			    N_("mark the series as Nth re-roll")),
       		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
       			    N_("max length of output filename")),
     - 		OPT_CALLBACK_F(0, "rfc", &rev, NULL,
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
       	if (cover_from_description_arg)
       		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
      -		strbuf_addf(&sprefix, "%s v%d",
      -			    rev.subject_prefix, reroll_count);
      -		rev.reroll_count = reroll_count;
     -+		char *endp;
      +
     -+		reroll_count = strtoul(reroll_count_string, &endp, 10);
     -+		if (!*endp && 0 < reroll_count) {
     -+			previous_count_string = xstrfmt("%d", reroll_count - 1);
     -+		}
     ++		strtol_i(reroll_count_string, 10, &reroll_count);
      +		strbuf_addf(&sprefix, "%s v%s",
      +			    rev.subject_prefix, reroll_count_string);
      +		rev.reroll_count = reroll_count_string;
       		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
       	}
       
     -@@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     - 			die(_("--interdiff requires --cover-letter or single patch"));
     - 		rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
     - 		rev.idiff_oid2 = get_commit_tree_oid(list[0]);
     --		rev.idiff_title = diff_title(&idiff_title, reroll_count,
     --					     _("Interdiff:"),
     --					     _("Interdiff against v%d:"));
     -+		rev.idiff_title = diff_title(&idiff_title, reroll_count_string,
     -+					previous_count_string,
     -+					_("Interdiff:"),
     -+					_("Interdiff against v%s:"));
     - 	}
     - 
     - 	if (creation_factor < 0)
     -@@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     - 		rev.rdiff1 = rdiff1.buf;
     - 		rev.rdiff2 = rdiff2.buf;
     - 		rev.creation_factor = creation_factor;
     --		rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
     --					     _("Range-diff:"),
     --					     _("Range-diff against v%d:"));
     -+		rev.rdiff_title = diff_title(&rdiff_title, reroll_count_string,
     -+					previous_count_string,
     -+					_("Range-diff:"),
     -+					_("Range-diff against v%s:"));
     - 	}
     - 
     - 	if (!signature) {
      
       ## log-tree.c ##
      @@ log-tree.c: void fmt_output_subject(struct strbuf *filename,


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 13 ++++++++-----
 log-tree.c                         |  4 ++--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 ++++++++++++++++
 t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++
 6 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..8af0d2923118 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	now can support non-integrated version number like `-v1.1`.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..03be9675fc5e 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1718,6 +1718,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	int use_patch_format = 0;
 	int quiet = 0;
 	int reroll_count = -1;
+	const char *reroll_count_string = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1752,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,11 +1863,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count_string) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
-			    rev.subject_prefix, reroll_count);
-		rev.reroll_count = reroll_count;
+
+		strtol_i(reroll_count_string, 10, &reroll_count);
+		strbuf_addf(&sprefix, "%s v%s",
+			    rev.subject_prefix, reroll_count_string);
+		rev.reroll_count = reroll_count_string;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
 	}
 
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v4] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-15 23:42:31

On Fri, Mar 5, 2021 at 2:10 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted hunk
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
        `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
        file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+       now can support non-integrated version number like `-v1.1`.
Let's drop "now" from the beginning of the sentence since it is only
meaningful for people who have read this documentation previously, but
not for people newly learning about the option. Perhaps just say:

    `<n>` may be a fractional number.
quoted hunk
diff --git a/builtin/log.c b/builtin/log.c
@@ -1862,11 +1863,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
-       if (0 < reroll_count) {
+       if (reroll_count_string) {
                struct strbuf sprefix = STRBUF_INIT;
+
+               strtol_i(reroll_count_string, 10, &reroll_count);
+               strbuf_addf(&sprefix, "%s v%s",
+                           rev.subject_prefix, reroll_count_string);
+               rev.reroll_count = reroll_count_string;
This code can be confusing to readers since it appears to ignore the
result of strtol_i(), and it's difficult for the reader to understand
the difference between `reroll_count` and `reroll_count_string` and
why you need both variables. I was going to suggest that you write an
/* in-code comment */ here explaining why you don't care if the reroll
count parsed correctly as a number. However, now that I'm examining
the code again, I think it would be clearer if you move the strtol_i()
call into the diff_title() function since -- following your changes --
that function is the only code which cares whether `reroll_count` can
be parsed as a number (the rest of the code, after your change, just
uses it as a string).

Re: [PATCH v4] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-16 05:49:28

Eric Sunshine [off-list ref] 于2021年3月16日周二 上午7:41写道:
On Fri, Mar 5, 2021 at 2:10 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
        `--reroll-count=4` may produce `v4-0001-add-makefile.patch`
        file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+       now can support non-integrated version number like `-v1.1`.
Let's drop "now" from the beginning of the sentence since it is only
meaningful for people who have read this documentation previously, but
not for people newly learning about the option. Perhaps just say:

    `<n>` may be a fractional number.
I agree with you.
quoted
diff --git a/builtin/log.c b/builtin/log.c
@@ -1862,11 +1863,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
-       if (0 < reroll_count) {
+       if (reroll_count_string) {
                struct strbuf sprefix = STRBUF_INIT;
+
+               strtol_i(reroll_count_string, 10, &reroll_count);
+               strbuf_addf(&sprefix, "%s v%s",
+                           rev.subject_prefix, reroll_count_string);
+               rev.reroll_count = reroll_count_string;
This code can be confusing to readers since it appears to ignore the
result of strtol_i(), and it's difficult for the reader to understand
the difference between `reroll_count` and `reroll_count_string` and
why you need both variables. I was going to suggest that you write an
/* in-code comment */ here explaining why you don't care if the reroll
count parsed correctly as a number. However, now that I'm examining
the code again, I think it would be clearer if you move the strtol_i()
call into the diff_title() function since -- following your changes --
that function is the only code which cares whether `reroll_count` can
be parsed as a number (the rest of the code, after your change, just
uses it as a string).
Well, The reason `strtol_i` does not check the return value is that
`strtol_i` will
only modify the value of `reroll_count` if the `reroll_count_string`
we provide is
 an integer string, so if `reroll_count_string` is not an integer string, then
`reroll_count` will remain -1, and then `diff_title` will only execute

        if (reroll_count <= 0)
                strbuf_addstr(sb, generic);

So don't need check strtol_i return value. But what you said to put
`strtol_i` in
`diff_title` is indeed a good idea. Of course, we need to modify the declaration
 and parameters of `diff_title`.

Re: [PATCH v4] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-16 06:16:09

On Tue, Mar 16, 2021 at 1:48 AM ZheNing Hu [off-list ref] wrote:
Eric Sunshine [off-list ref] 于2021年3月16日周二 上午7:41写道:
quoted
quoted
+               strtol_i(reroll_count_string, 10, &reroll_count);
This code can be confusing to readers since it appears to ignore the
result of strtol_i() [...]
[...] I think it would be clearer if you move the strtol_i()
call into the diff_title() function since -- following your changes --
that function is the only code which cares whether `reroll_count` can
be parsed as a number (the rest of the code, after your change, just
uses it as a string).
Well, The reason `strtol_i` does not check the return value is that
`strtol_i` will
only modify the value of `reroll_count` if the `reroll_count_string`
we provide is
 an integer string, so if `reroll_count_string` is not an integer string, then
`reroll_count` will remain -1, and then `diff_title` will only execute

        if (reroll_count <= 0)
                strbuf_addstr(sb, generic);

So don't need check strtol_i return value.
Yes, I understand the reason, but it is not easy for someone new to
this code to figure it out without looking at diff_title() and its
callers. The place where the return value of strtol_i() is ignored is
very far removed from the place where the value of `reroll_count` is
consumed in diff_title(), so it's not obvious to the reader how this
is all supposed to work. If you move strtol_i() into diff_title(),
then the parsing and the consuming of that value happen in the same
place, making it easier to understand.
But what you said to put
`strtol_i` in
`diff_title` is indeed a good idea. Of course, we need to modify the declaration
 and parameters of `diff_title`.
Yes, it is a very minor modification to diff_title(), changing
`reroll_count` from int to string.

Moving the parsing into diff_title() also allows you to use the
simpler name `reroll_count` for the string variable in cmd_fmt_patch()
rather than the longer `reroll_count_string`; just change
`reroll_count` from int to string.

[PATCH v5] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-16 08:26:23

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v4:

 1:  cb1c0267e16b ! 1:  3c4e828dbf3f format-patch: allow a non-integral version numbers
     @@ Documentation/git-format-patch.txt: populated with placeholder text.
       	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
       	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
       	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
     -+	now can support non-integrated version number like `-v1.1`.
     ++	 `<n>` may be a fractional number.
       
       --to=<email>::
       	Add a `To:` header to the email headers. This is in addition
      
       ## builtin/log.c ##
     +@@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
     + 	oidclr(&bases->base_commit);
     + }
     + 
     +-static const char *diff_title(struct strbuf *sb, int reroll_count,
     +-		       const char *generic, const char *rerolled)
     ++static const char *diff_title(struct strbuf *sb,
     ++			      const char *reroll_count,
     ++			      const char *generic,
     ++			      const char *rerolled)
     + {
     +-	if (reroll_count <= 0)
     ++	int reroll_count_int = -1;
     ++
     ++	if (reroll_count)
     ++		strtol_i(reroll_count, 10, &reroll_count_int);
     ++	if (reroll_count_int <= 0)
     + 		strbuf_addstr(sb, generic);
     + 	else /* RFC may be v0, so allow -v1 to diff against v0 */
     +-		strbuf_addf(sb, rerolled, reroll_count - 1);
     ++		strbuf_addf(sb, rerolled, reroll_count_int - 1);
     + 	return sb->buf;
     + }
     + 
      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
     + 	struct strbuf buf = STRBUF_INIT;
       	int use_patch_format = 0;
       	int quiet = 0;
     - 	int reroll_count = -1;
     -+	const char *reroll_count_string = NULL;
     +-	int reroll_count = -1;
     ++	const char *reroll_count = NULL;
       	char *cover_from_description_arg = NULL;
       	char *branch_name = NULL;
       	char *base_commit = NULL;
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
       		OPT_INTEGER(0, "start-number", &start_number,
       			    N_("start numbering patches at <n> instead of 1")),
      -		OPT_INTEGER('v', "reroll-count", &reroll_count,
     -+		OPT_STRING('v', "reroll-count", &reroll_count_string, N_("reroll-count"),
     ++		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
       			    N_("mark the series as Nth re-roll")),
       		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
       			    N_("max length of output filename")),
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
       		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
       
      -	if (0 < reroll_count) {
     -+	if (reroll_count_string) {
     ++	if (reroll_count) {
       		struct strbuf sprefix = STRBUF_INIT;
      -		strbuf_addf(&sprefix, "%s v%d",
     --			    rev.subject_prefix, reroll_count);
     --		rev.reroll_count = reroll_count;
      +
     -+		strtol_i(reroll_count_string, 10, &reroll_count);
      +		strbuf_addf(&sprefix, "%s v%s",
     -+			    rev.subject_prefix, reroll_count_string);
     -+		rev.reroll_count = reroll_count_string;
     + 			    rev.subject_prefix, reroll_count);
     + 		rev.reroll_count = reroll_count;
       		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
     - 	}
     - 
      
       ## log-tree.c ##
      @@ log-tree.c: void fmt_output_subject(struct strbuf *filename,


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 23 +++++++++++++++--------
 log-tree.c                         |  4 ++--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 ++++++++++++++++
 t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++
 6 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..82abef1a6d92 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	 `<n>` may be a fractional number.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..6fe0ec8327ec 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			      const char *reroll_count,
+			      const char *generic,
+			      const char *rerolled)
 {
-	if (reroll_count <= 0)
+	int reroll_count_int = -1;
+
+	if (reroll_count)
+		strtol_i(reroll_count, 10, &reroll_count_int);
+	if (reroll_count_int <= 0)
 		strbuf_addstr(sb, generic);
 	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
+		strbuf_addf(sb, rerolled, reroll_count_int - 1);
 	return sb->buf;
 }
 
@@ -1717,7 +1723,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,9 +1868,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v5] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-16 23:37:28

On Tue, Mar 16, 2021 at 4:25 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted hunk
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/builtin/log.c b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
+static const char *diff_title(struct strbuf *sb,
+                             const char *reroll_count,
+                             const char *generic,
+                             const char *rerolled)
 {
+       int reroll_count_int = -1;
+
+       if (reroll_count)
+               strtol_i(reroll_count, 10, &reroll_count_int);
+       if (reroll_count_int <= 0)
                strbuf_addstr(sb, generic);
        else /* RFC may be v0, so allow -v1 to diff against v0 */
+               strbuf_addf(sb, rerolled, reroll_count_int - 1);
        return sb->buf;
 }
Thanks. The logic of this version is much easier to understand now
that the number parsing has been moved into diff_title().

It may still be a bit confusing for someone reading this code to
understand why you don't check the return value of strtol_i().
Therefore, it might be a good idea to add an /* in-code comment */
explaining why you don't check whether the parse succeeded or failed.
However, if we rewrite the code like this:

    int v;
    if (reroll_count && !strtol_i(reroll_count, 10, &v))
        strbuf_addf(sb, rerolled, v - 1);
    else
        strbuf_addstr(sb, generic);
    return sb->buf;

then the logic becomes obvious, and we don't even need a comment.
(Notice that I also shortened the variable name since the code is just
as clear with a short name as with a long spelled out name such as
"reroll_count_int".)

Re: [PATCH v5] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-17 02:06:35

Eric Sunshine [off-list ref] 于2021年3月17日周三 上午7:36写道:
On Tue, Mar 16, 2021 at 4:25 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/builtin/log.c b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
+static const char *diff_title(struct strbuf *sb,
+                             const char *reroll_count,
+                             const char *generic,
+                             const char *rerolled)
 {
+       int reroll_count_int = -1;
+
+       if (reroll_count)
+               strtol_i(reroll_count, 10, &reroll_count_int);
+       if (reroll_count_int <= 0)
                strbuf_addstr(sb, generic);
        else /* RFC may be v0, so allow -v1 to diff against v0 */
+               strbuf_addf(sb, rerolled, reroll_count_int - 1);
        return sb->buf;
 }
Thanks. The logic of this version is much easier to understand now
that the number parsing has been moved into diff_title().

It may still be a bit confusing for someone reading this code to
understand why you don't check the return value of strtol_i().
Therefore, it might be a good idea to add an /* in-code comment */
explaining why you don't check whether the parse succeeded or failed.
However, if we rewrite the code like this:

    int v;
    if (reroll_count && !strtol_i(reroll_count, 10, &v))
        strbuf_addf(sb, rerolled, v - 1);
    else
        strbuf_addstr(sb, generic);
    return sb->buf;

then the logic becomes obvious, and we don't even need a comment.
(Notice that I also shortened the variable name since the code is just
as clear with a short name as with a long spelled out name such as
"reroll_count_int".)
Yes, It is better to handle the return value of `strtol_i` in an if judgment and
use `v` instead of `reroll_count_int`.

Thanks.

[PATCH v6] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-18 06:01:24

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v5:

 1:  3c4e828dbf3f ! 1:  d5f5e3f073de format-patch: allow a non-integral version numbers
     @@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
      +			      const char *rerolled)
       {
      -	if (reroll_count <= 0)
     -+	int reroll_count_int = -1;
     ++	int v;
      +
     -+	if (reroll_count)
     -+		strtol_i(reroll_count, 10, &reroll_count_int);
     -+	if (reroll_count_int <= 0)
     ++	/* RFC may be v0, so allow -v1 to diff against v0 */
     ++	if (reroll_count && !strtol_i(reroll_count, 10, &v))
     ++		strbuf_addf(sb, rerolled, v - 1);
     ++	else
       		strbuf_addstr(sb, generic);
     - 	else /* RFC may be v0, so allow -v1 to diff against v0 */
     +-	else /* RFC may be v0, so allow -v1 to diff against v0 */
      -		strbuf_addf(sb, rerolled, reroll_count - 1);
     -+		strbuf_addf(sb, rerolled, reroll_count_int - 1);
       	return sb->buf;
       }
       


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 24 +++++++++++++++---------
 log-tree.c                         |  4 ++--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 ++++++++++++++++
 t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++
 6 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..82abef1a6d92 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	 `<n>` may be a fractional number.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..2e2fdbe8ae9f 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,18 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			      const char *reroll_count,
+			      const char *generic,
+			      const char *rerolled)
 {
-	if (reroll_count <= 0)
+	int v;
+
+	/* RFC may be v0, so allow -v1 to diff against v0 */
+	if (reroll_count && !strtol_i(reroll_count, 10, &v))
+		strbuf_addf(sb, rerolled, v - 1);
+	else
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
 	return sb->buf;
 }
 
@@ -1717,7 +1722,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1756,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,9 +1867,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v6] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-19 06:01:15

On Thu, Mar 18, 2021 at 2:00 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted hunk
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/builtin/log.c b/builtin/log.c
@@ -1662,13 +1662,18 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
+static const char *diff_title(struct strbuf *sb,
+                             const char *reroll_count,
+                             const char *generic,
+                             const char *rerolled)
 {
-       if (reroll_count <= 0)
+       int v;
+
+       /* RFC may be v0, so allow -v1 to diff against v0 */
+       if (reroll_count && !strtol_i(reroll_count, 10, &v))
+               strbuf_addf(sb, rerolled, v - 1);
+       else
                strbuf_addstr(sb, generic);
-       else /* RFC may be v0, so allow -v1 to diff against v0 */
-               strbuf_addf(sb, rerolled, reroll_count - 1);
        return sb->buf;
 }
The comment about RFC and v0 doesn't really make sense anymore. Its
original purpose was to explain why the `if` condition (which goes
away with this patch) was `<=0` rather than `<=1`. It might make sense
to keep the comment if the code is written like this:

    if (reroll_count &&
            !strtol_i(reroll_count, 10, &v) &&
            reroll_count >= 1)
        strbuf_addf(sb, rerolled, v - 1);
    else
        ...

However, I'm not sure it's worth re-rolling just to make this change.

Thanks.

Re: [PATCH v6] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-19 07:26:40

Eric Sunshine [off-list ref] 于2021年3月19日周五 下午2:00写道:
On Thu, Mar 18, 2021 at 2:00 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
diff --git a/builtin/log.c b/builtin/log.c
@@ -1662,13 +1662,18 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
+static const char *diff_title(struct strbuf *sb,
+                             const char *reroll_count,
+                             const char *generic,
+                             const char *rerolled)
 {
-       if (reroll_count <= 0)
+       int v;
+
+       /* RFC may be v0, so allow -v1 to diff against v0 */
+       if (reroll_count && !strtol_i(reroll_count, 10, &v))
+               strbuf_addf(sb, rerolled, v - 1);
+       else
                strbuf_addstr(sb, generic);
-       else /* RFC may be v0, so allow -v1 to diff against v0 */
-               strbuf_addf(sb, rerolled, reroll_count - 1);
        return sb->buf;
 }
The comment about RFC and v0 doesn't really make sense anymore. Its
original purpose was to explain why the `if` condition (which goes
away with this patch) was `<=0` rather than `<=1`. It might make sense
to keep the comment if the code is written like this:

    if (reroll_count &&
            !strtol_i(reroll_count, 10, &v) &&
            reroll_count >= 1)
        strbuf_addf(sb, rerolled, v - 1);
    else
        ...

However, I'm not sure it's worth re-rolling just to make this change.
Well, after testing, I think it is still necessary to add "v-1 >=0" to
the judgment
condition. Because if we use `-v0`, "Range-diff against v-1:" will be output in
the patch.
Thanks.
Thanks.

[PATCH v7] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-19 11:22:14

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a same fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as `-v1.1`.  In the case where it
is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v7
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v6:

 1:  d5f5e3f073de ! 1:  95cfe75ee7da format-patch: allow a non-integral version numbers
     @@ builtin/log.c: static void print_bases(struct base_tree_info *bases, FILE *file)
      +	int v;
      +
      +	/* RFC may be v0, so allow -v1 to diff against v0 */
     -+	if (reroll_count && !strtol_i(reroll_count, 10, &v))
     ++	if (reroll_count && !strtol_i(reroll_count, 10, &v) &&
     ++	    v >= 1)
      +		strbuf_addf(sb, rerolled, v - 1);
      +	else
       		strbuf_addstr(sb, generic);


 Documentation/git-format-patch.txt |  1 +
 builtin/log.c                      | 25 ++++++++++++++++---------
 log-tree.c                         |  4 ++--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 16 ++++++++++++++++
 t/t4014-format-patch.sh            | 26 ++++++++++++++++++++++++++
 6 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..82abef1a6d92 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,7 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	 `<n>` may be a fractional number.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..af853f111464 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			      const char *reroll_count,
+			      const char *generic,
+			      const char *rerolled)
 {
-	if (reroll_count <= 0)
+	int v;
+
+	/* RFC may be v0, so allow -v1 to diff against v0 */
+	if (reroll_count && !strtol_i(reroll_count, 10, &v) &&
+	    v >= 1)
+		strbuf_addf(sb, rerolled, v - 1);
+	else
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
 	return sb->buf;
 }
 
@@ -1717,7 +1723,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,9 +1868,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..dc419c087e07 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,22 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..59dff38065ab 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,14 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +394,14 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a non-integer' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2271,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

[PATCH v8] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-20 14:57:06

From: ZheNing Hu <redacted>

Usually we can only use `format-patch -v<n>` to generate integral
version numbers patches, but sometimes a small fixup should be
labeled as a non-integral version like `v1.1`, so teach `format-patch`
to allow a non-integral version which may be helpful to send those
patches.

`<n>` can be any string, such as '3.1' or '4rev2'. In the case
where it is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    There is a small question: in the case of --reroll-count=<n>, "n" is an
    integer, we output "n-1" in the patch instead of "m" specified by
    --previous-count=<m>,Should we switch the priority of these two: let "m"
    output?
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v8
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v8
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v7:

 1:  95cfe75ee7da ! 1:  89458d384fd0 format-patch: allow a non-integral version numbers
     @@ Commit message
          format-patch: allow a non-integral version numbers
      
          Usually we can only use `format-patch -v<n>` to generate integral
     -    version numbers patches, but sometimes a same fixup should be
     +    version numbers patches, but sometimes a small fixup should be
          labeled as a non-integral version like `v1.1`, so teach `format-patch`
          to allow a non-integral version which may be helpful to send those
          patches.
      
     -    `<n>` can be any string, such as `-v1.1`.  In the case where it
     -    is a non-integral value, the "Range-diff" and "Interdiff"
     +    `<n>` can be any string, such as '3.1' or '4rev2'. In the case
     +    where it is a non-integral value, the "Range-diff" and "Interdiff"
          headers will not include the previous version.
      
          Signed-off-by: ZheNing Hu [off-list ref]
     @@ Documentation/git-format-patch.txt: populated with placeholder text.
       	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
       	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
       	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
     -+	 `<n>` may be a fractional number.
     ++	 `<n>` may be a non-integer number.  E.g. `--reroll-count=4.4`
     ++	may produce `v4.4-0001-add-makefile.patch` file that has
     ++	"Subject: [PATCH v4.4 1/20] Add makefile" in it.
     ++	`--reroll-count=4rev2` may produce `v4rev2-0001-add-makefile.patch`
     ++	file that has "Subject: [PATCH v4rev2 1/20] Add makefile" in it.
       
       --to=<email>::
       	Add a `To:` header to the email headers. This is in addition
     @@ t/t3206-range-diff.sh: test_expect_success 'format-patch --range-diff as comment
      +	grep "> 1: .* new message" v2-0001-*
      +'
      +
     ++test_expect_success 'format-patch --range-diff with v0' '
     ++	git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
     ++	test_when_finished "rm v0-0001-*" &&
     ++	test_line_count = 1 actual &&
     ++	test_i18ngrep "^Range-diff:$" v0-0001-* &&
     ++	grep "> 1: .* new message" v0-0001-*
     ++'
       test_expect_success 'range-diff overrides diff.noprefix internally' '
       	git -c diff.noprefix=true range-diff HEAD^...
       '
     @@ t/t4014-format-patch.sh: test_expect_success 'reroll count' '
       	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
       '
       
     -+test_expect_success 'reroll count with a non-integer' '
     ++test_expect_success 'reroll count with a fractional number' '
      +	rm -fr patches &&
      +	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
      +	! grep -v "^patches/v4.4-000[0-3]-" list &&
      +	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
      +	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
      +'
     ++
     ++test_expect_success 'reroll count with a non number' '
     ++	rm -fr patches &&
     ++	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
     ++	! grep -v "^patches/v4rev2-000[0-3]-" list &&
     ++	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     ++	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
     ++'
      +
       test_expect_success 'reroll count (-v)' '
       	rm -fr patches &&
     @@ t/t4014-format-patch.sh: test_expect_success 'reroll count (-v)' '
       	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
       '
       
     -+test_expect_success 'reroll count (-v) with a non-integer' '
     ++test_expect_success 'reroll count (-v) with a fractional number' '
      +	rm -fr patches &&
      +	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
      +	! grep -v "^patches/v4.4-000[0-3]-" list &&
      +	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
      +	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
      +'
     ++
     ++test_expect_success 'reroll (-v) count with a non number' '
     ++	rm -fr patches &&
     ++	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
     ++	! grep -v "^patches/v4rev2-000[0-3]-" list &&
     ++	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     ++	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
     ++'
      +
       check_threading () {
       	expect="$1" &&


 Documentation/git-format-patch.txt |  5 ++++
 builtin/log.c                      | 25 +++++++++++-------
 log-tree.c                         |  4 +--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 23 ++++++++++++++++
 t/t4014-format-patch.sh            | 42 ++++++++++++++++++++++++++++++
 6 files changed, 89 insertions(+), 12 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..e2c29a856451 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,11 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	 `<n>` may be a non-integer number.  E.g. `--reroll-count=4.4`
+	may produce `v4.4-0001-add-makefile.patch` file that has
+	"Subject: [PATCH v4.4 1/20] Add makefile" in it.
+	`--reroll-count=4rev2` may produce `v4rev2-0001-add-makefile.patch`
+	file that has "Subject: [PATCH v4rev2 1/20] Add makefile" in it.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..af853f111464 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			      const char *reroll_count,
+			      const char *generic,
+			      const char *rerolled)
 {
-	if (reroll_count <= 0)
+	int v;
+
+	/* RFC may be v0, so allow -v1 to diff against v0 */
+	if (reroll_count && !strtol_i(reroll_count, 10, &v) &&
+	    v >= 1)
+		strbuf_addf(sb, rerolled, v - 1);
+	else
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
 	return sb->buf;
 }
 
@@ -1717,7 +1723,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,9 +1868,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..5f2e08ebcaab 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count)
+		strbuf_addf(filename, "v%s-", info->reroll_count);
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..a501949575fa 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,29 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
+test_expect_success 'format-patch --range-diff with v0' '
+	git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
+	test_when_finished "rm v0-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v0-0001-* &&
+	grep "> 1: .* new message" v0-0001-*
+'
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..457312793d7e 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,22 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a fractional number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll count with a non number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
+	! grep -v "^patches/v4rev2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +402,22 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a fractional number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a non number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
+	! grep -v "^patches/v4rev2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2287,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v8] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-21 04:07:05

On Sat, Mar 20, 2021 at 10:56 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted hunk
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
@@ -221,6 +221,11 @@ populated with placeholder text.
+        `<n>` may be a non-integer number.  E.g. `--reroll-count=4.4`
+       may produce `v4.4-0001-add-makefile.patch` file that has
+       "Subject: [PATCH v4.4 1/20] Add makefile" in it.
+       `--reroll-count=4rev2` may produce `v4rev2-0001-add-makefile.patch`
+       file that has "Subject: [PATCH v4rev2 1/20] Add makefile" in it.
This new example raises the question about what happens if the
argument to --reroll-count contains characters which don't belong in
pathnames. For instance, what happens if `--reroll-count=1/2` is
specified? Most likely, it will fail trying to write the
"v1/2-whatever.patch" file to a nonexistent directory named "v1".
quoted hunk
diff --git a/log-tree.c b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
+       if (info->reroll_count)
+               strbuf_addf(filename, "v%s-", info->reroll_count);
        strbuf_addf(filename, "%04d-%s", nr, subject);
To protect against that problem, you may need to call
format_sanitized_subject() manually after formatting "v%s-". (I'm just
looking at this code for the first time, so I could be hopelessly
wrong. There may be a better way to fix it.)

Re: [PATCH v8] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-21 07:23:19

Eric Sunshine [off-list ref] 于2021年3月21日周日 下午12:05写道:
On Sat, Mar 20, 2021 at 10:56 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
@@ -221,6 +221,11 @@ populated with placeholder text.
+        `<n>` may be a non-integer number.  E.g. `--reroll-count=4.4`
+       may produce `v4.4-0001-add-makefile.patch` file that has
+       "Subject: [PATCH v4.4 1/20] Add makefile" in it.
+       `--reroll-count=4rev2` may produce `v4rev2-0001-add-makefile.patch`
+       file that has "Subject: [PATCH v4rev2 1/20] Add makefile" in it.
This new example raises the question about what happens if the
argument to --reroll-count contains characters which don't belong in
pathnames. For instance, what happens if `--reroll-count=1/2` is
specified? Most likely, it will fail trying to write the
"v1/2-whatever.patch" file to a nonexistent directory named "v1".
quoted
diff --git a/log-tree.c b/log-tree.c
@@ -369,8 +369,8 @@ void fmt_output_subject(struct strbuf *filename,
+       if (info->reroll_count)
+               strbuf_addf(filename, "v%s-", info->reroll_count);
        strbuf_addf(filename, "%04d-%s", nr, subject);
To protect against that problem, you may need to call
format_sanitized_subject() manually after formatting "v%s-". (I'm just
looking at this code for the first time, so I could be hopelessly
wrong. There may be a better way to fix it.)
Hi, Eric,
This is a kind of "injection" problem,
thank you for your discovery and solution method.

[PATCH v9] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-21 09:01:57

From: ZheNing Hu <redacted>

The `-v<n>` option of `format-patch` can give nothing but an
integral iteration number to patches in a series.  Some people,
however, prefer to mark a new iteration with only a small fixup
with a non integral iteration number (e.g. an "oops, that was
wrong" fix-up patch for v4 iteration may be labeled as "v4.1").

Allow `format-patch` to take such a non-integral iteration
number.

`<n>` can be any string, such as '3.1' or '4rev2'. In the case
where it is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v9
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v9
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v8:

 1:  89458d384fd0 ! 1:  97eaae4a8d9e format-patch: allow a non-integral version numbers
     @@ Metadata
       ## Commit message ##
          format-patch: allow a non-integral version numbers
      
     -    Usually we can only use `format-patch -v<n>` to generate integral
     -    version numbers patches, but sometimes a small fixup should be
     -    labeled as a non-integral version like `v1.1`, so teach `format-patch`
     -    to allow a non-integral version which may be helpful to send those
     -    patches.
     +    The `-v<n>` option of `format-patch` can give nothing but an
     +    integral iteration number to patches in a series.  Some people,
     +    however, prefer to mark a new iteration with only a small fixup
     +    with a non integral iteration number (e.g. an "oops, that was
     +    wrong" fix-up patch for v4 iteration may be labeled as "v4.1").
     +
     +    Allow `format-patch` to take such a non-integral iteration
     +    number.
      
          `<n>` can be any string, such as '3.1' or '4rev2'. In the case
          where it is a non-integral value, the "Range-diff" and "Interdiff"
     @@ Documentation/git-format-patch.txt: populated with placeholder text.
       	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
       	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
       	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
     -+	 `<n>` may be a non-integer number.  E.g. `--reroll-count=4.4`
     -+	may produce `v4.4-0001-add-makefile.patch` file that has
     -+	"Subject: [PATCH v4.4 1/20] Add makefile" in it.
     -+	`--reroll-count=4rev2` may produce `v4rev2-0001-add-makefile.patch`
     -+	file that has "Subject: [PATCH v4rev2 1/20] Add makefile" in it.
     ++	`<n>` does not have to be an integer (e.g. "--reroll-count=4.4",
     ++	or "--reroll-count=4rev2" are allowed), but the downside of
     ++	using such a reroll-count is that the range-diff/interdiff
     ++	with the previous version does not state exactly which
     ++	version the new interation is compared against.
       
       --to=<email>::
       	Add a `To:` header to the email headers. This is in addition
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
      
       ## log-tree.c ##
      @@ log-tree.c: void fmt_output_subject(struct strbuf *filename,
     + 	int nr = info->nr;
       	int start_len = filename->len;
       	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
     ++	struct strbuf temp = STRBUF_INIT;
       
      -	if (0 < info->reroll_count)
      -		strbuf_addf(filename, "v%d-", info->reroll_count);
     -+	if (info->reroll_count)
     -+		strbuf_addf(filename, "v%s-", info->reroll_count);
     ++	if (info->reroll_count) {
     ++		strbuf_addf(&temp, "v%s", info->reroll_count);
     ++		format_sanitized_subject(filename, temp.buf, temp.len);
     ++		strbuf_addstr(filename, "-");
     ++		strbuf_release(&temp);
     ++	}
       	strbuf_addf(filename, "%04d-%s", nr, subject);
       
       	if (max_len < filename->len)
     @@ t/t4014-format-patch.sh: test_expect_success 'reroll count (-v)' '
      +
      +test_expect_success 'reroll (-v) count with a non number' '
      +	rm -fr patches &&
     -+	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
     ++	git format-patch -o patches --cover-letter -v4rev2 main..side >list &&
      +	! grep -v "^patches/v4rev2-000[0-3]-" list &&
      +	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
      +	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
      +'
     ++
     ++test_expect_success 'reroll (-v) count with a "injection (1)"' '
     ++	rm -fr patches &&
     ++	git format-patch -o patches --cover-letter -v4..././../1/.2//  main..side >list &&
     ++	! grep -v "^patches/v4.-.-.-1-.2-000[0-3]-" list &&
     ++	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     ++	! grep -v "^Subject: \[PATCH v4..././../1/.2// [0-3]/3\] " subjects
     ++'
     ++
     ++test_expect_success 'reroll (-v) count with a "injection (2)"' '
     ++	rm -fr patches &&
     ++	git format-patch -o patches --cover-letter -v4-----//1//--.--  main..side >list &&
     ++	! grep -v "^patches/v4-1-000[0-3]-" list &&
     ++	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     ++	! grep -v "^Subject: \[PATCH v4-----//1//--.-- [0-3]/3\] " subjects
     ++'
      +
       check_threading () {
       	expect="$1" &&


 Documentation/git-format-patch.txt |  5 +++
 builtin/log.c                      | 25 ++++++++-----
 log-tree.c                         |  9 +++--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 23 ++++++++++++
 t/t4014-format-patch.sh            | 58 ++++++++++++++++++++++++++++++
 6 files changed, 110 insertions(+), 12 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..741c9f8b2b88 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,11 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	`<n>` does not have to be an integer (e.g. "--reroll-count=4.4",
+	or "--reroll-count=4rev2" are allowed), but the downside of
+	using such a reroll-count is that the range-diff/interdiff
+	with the previous version does not state exactly which
+	version the new interation is compared against.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..af853f111464 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			      const char *reroll_count,
+			      const char *generic,
+			      const char *rerolled)
 {
-	if (reroll_count <= 0)
+	int v;
+
+	/* RFC may be v0, so allow -v1 to diff against v0 */
+	if (reroll_count && !strtol_i(reroll_count, 10, &v) &&
+	    v >= 1)
+		strbuf_addf(sb, rerolled, v - 1);
+	else
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
 	return sb->buf;
 }
 
@@ -1717,7 +1723,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,9 +1868,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..ebdeb29f92a2 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -368,9 +368,14 @@ void fmt_output_subject(struct strbuf *filename,
 	int nr = info->nr;
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
+	struct strbuf temp = STRBUF_INIT;
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count) {
+		strbuf_addf(&temp, "v%s", info->reroll_count);
+		format_sanitized_subject(filename, temp.buf, temp.len);
+		strbuf_addstr(filename, "-");
+		strbuf_release(&temp);
+	}
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..a501949575fa 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,29 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
+test_expect_success 'format-patch --range-diff with v0' '
+	git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
+	test_when_finished "rm v0-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v0-0001-* &&
+	grep "> 1: .* new message" v0-0001-*
+'
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..d59a06248dbd 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -378,6 +378,22 @@ test_expect_success 'reroll count' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count with a fractional number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll count with a non number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
+	! grep -v "^patches/v4rev2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
+
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
 	git format-patch -o patches --cover-letter -v4 main..side >list &&
@@ -386,6 +402,38 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a fractional number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a non number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4rev2 main..side >list &&
+	! grep -v "^patches/v4rev2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a "injection (1)"' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4..././../1/.2//  main..side >list &&
+	! grep -v "^patches/v4.-.-.-1-.2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4..././../1/.2// [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a "injection (2)"' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4-----//1//--.--  main..side >list &&
+	! grep -v "^patches/v4-1-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4-----//1//--.-- [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2303,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v9] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-23 05:32:16

On Sun, Mar 21, 2021 at 5:00 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
[...]
Allow `format-patch` to take such a non-integral iteration
number.
[...]
Signed-off-by: ZheNing Hu <redacted>
Just a few nits below; nothing very important (except perhaps the
final comment about the potential for people to get confused while
reading the tests). Junio already has this marked as ready to merge to
"next", so these nits may not be worth a re-roll.
quoted hunk
diff --git a/log-tree.c b/log-tree.c
@@ -368,9 +368,14 @@ void fmt_output_subject(struct strbuf *filename,
        int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
+       struct strbuf temp = STRBUF_INIT;

+       if (info->reroll_count) {
+               strbuf_addf(&temp, "v%s", info->reroll_count);
+               format_sanitized_subject(filename, temp.buf, temp.len);
+               strbuf_addstr(filename, "-");
+               strbuf_release(&temp);
+       }
The new `temp` strbuf is use only inside the conditional, so it
could/should have been declared in that block rather than in the outer
block:

    if (info->reroll_count) {
        struct strbuf temp = STRBUF_INIT;

        strbuf_addf(&temp, "v%s", info->reroll_count);
        ...
    }
quoted hunk
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
@@ -378,6 +378,22 @@ test_expect_success 'reroll count' '
+test_expect_success 'reroll count with a fractional number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+       ! grep -v "^patches/v4.4-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll count with a non number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
+       ! grep -v "^patches/v4rev2-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
The above two tests...
quoted hunk
@@ -386,6 +402,38 @@ test_expect_success 'reroll count (-v)' '
+test_expect_success 'reroll count (-v) with a fractional number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+       ! grep -v "^patches/v4.4-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a non number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4rev2 main..side >list &&
+       ! grep -v "^patches/v4rev2-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
... are repeated here with the only difference being `--reroll-count`
versus `-v`. Since other tests have already established that
`--reroll-count` and `-v` are identical, it's not really necessary to
do that work again with these duplicate tests.
+test_expect_success 'reroll (-v) count with a "injection (1)"' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4..././../1/.2//  main..side >list &&
+       ! grep -v "^patches/v4.-.-.-1-.2-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4..././../1/.2// [0-3]/3\] " subjects
+'
A couple comments:

The test title might be easier for other people to understand if it
says "non-pathname character" or "non filename character" rather than
"injection".

Note that the `grep -v` is casting a wider net than it seems at first
glance. The `.` matches any character, not just a period ".". To
tighten the matching and make `.` match just a ".", you can use `grep
-vF`.
+test_expect_success 'reroll (-v) count with a "injection (2)"' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4-----//1//--.--  main..side >list &&
+       ! grep -v "^patches/v4-1-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4-----//1//--.-- [0-3]/3\] " subjects
+'
Presumably the coverage of format_sanitized_subject() is already being
tested elsewhere, so it's not clear that this second "injection" test
adds any value over the first test. Moreover, this second test can
confuse readers into thinking that it is testing something that the
first test didn't cover, but that isn't the case (as far as I can
tell).

Re: [PATCH v9] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-23 08:55:21

Eric Sunshine [off-list ref] 于2021年3月23日周二 下午1:31写道:
On Sun, Mar 21, 2021 at 5:00 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
[...]
Allow `format-patch` to take such a non-integral iteration
number.
[...]
Signed-off-by: ZheNing Hu <redacted>
Just a few nits below; nothing very important (except perhaps the
final comment about the potential for people to get confused while
reading the tests). Junio already has this marked as ready to merge to
"next", so these nits may not be worth a re-roll.
Thanks, Eric, these suggestions are worth considering.
quoted
diff --git a/log-tree.c b/log-tree.c
@@ -368,9 +368,14 @@ void fmt_output_subject(struct strbuf *filename,
        int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
+       struct strbuf temp = STRBUF_INIT;

+       if (info->reroll_count) {
+               strbuf_addf(&temp, "v%s", info->reroll_count);
+               format_sanitized_subject(filename, temp.buf, temp.len);
+               strbuf_addstr(filename, "-");
+               strbuf_release(&temp);
+       }
The new `temp` strbuf is use only inside the conditional, so it
could/should have been declared in that block rather than in the outer
block:
Sometimes I always forget this: which variables need to be placed locally,
It seems that I have to be more careful.
    if (info->reroll_count) {
        struct strbuf temp = STRBUF_INIT;

        strbuf_addf(&temp, "v%s", info->reroll_count);
        ...
    }
quoted
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
@@ -378,6 +378,22 @@ test_expect_success 'reroll count' '
+test_expect_success 'reroll count with a fractional number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
+       ! grep -v "^patches/v4.4-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll count with a non number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
+       ! grep -v "^patches/v4rev2-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
The above two tests...
quoted
@@ -386,6 +402,38 @@ test_expect_success 'reroll count (-v)' '
+test_expect_success 'reroll count (-v) with a fractional number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+       ! grep -v "^patches/v4.4-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a non number' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4rev2 main..side >list &&
+       ! grep -v "^patches/v4rev2-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
... are repeated here with the only difference being `--reroll-count`
versus `-v`. Since other tests have already established that
`--reroll-count` and `-v` are identical, it's not really necessary to
do that work again with these duplicate tests.
Makes sense.
quoted
+test_expect_success 'reroll (-v) count with a "injection (1)"' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4..././../1/.2//  main..side >list &&
+       ! grep -v "^patches/v4.-.-.-1-.2-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4..././../1/.2// [0-3]/3\] " subjects
+'
A couple comments:

The test title might be easier for other people to understand if it
says "non-pathname character" or "non filename character" rather than
"injection".

Note that the `grep -v` is casting a wider net than it seems at first
glance. The `.` matches any character, not just a period ".". To
tighten the matching and make `.` match just a ".", you can use `grep
-vF`.
Yes, `grep -vF` is very important for the correctness of the test.
quoted
+test_expect_success 'reroll (-v) count with a "injection (2)"' '
+       rm -fr patches &&
+       git format-patch -o patches --cover-letter -v4-----//1//--.--  main..side >list &&
+       ! grep -v "^patches/v4-1-000[0-3]-" list &&
+       sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+       ! grep -v "^Subject: \[PATCH v4-----//1//--.-- [0-3]/3\] " subjects
+'
Presumably the coverage of format_sanitized_subject() is already being
tested elsewhere, so it's not clear that this second "injection" test
adds any value over the first test. Moreover, this second test can
confuse readers into thinking that it is testing something that the
first test didn't cover, but that isn't the case (as far as I can
tell).
The second test I just want to test character `-`, but now I think it can
merge to first test.

Thanks.

Re: [PATCH v9] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-23 09:17:51

quoted
Note that the `grep -v` is casting a wider net than it seems at first
glance. The `.` matches any character, not just a period ".". To
tighten the matching and make `.` match just a ".", you can use `grep
-vF`.
Yes, `grep -vF` is very important for the correctness of the test.
Correct it, I still have to use `grep -v`, but I need to change the
'.' to `\.` .

[PATCH v10] format-patch: allow a non-integral version numbers

From: ZheNing Hu via GitGitGadget <hidden>
Date: 2021-03-23 11:13:09

From: ZheNing Hu <redacted>

The `-v<n>` option of `format-patch` can give nothing but an
integral iteration number to patches in a series.  Some people,
however, prefer to mark a new iteration with only a small fixup
with a non integral iteration number (e.g. an "oops, that was
wrong" fix-up patch for v4 iteration may be labeled as "v4.1").

Allow `format-patch` to take such a non-integral iteration
number.

`<n>` can be any string, such as '3.1' or '4rev2'. In the case
where it is a non-integral value, the "Range-diff" and "Interdiff"
headers will not include the previous version.

Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    
    A rollback was performed again under Eric's suggestion.
    
    this want to fix #882 Thanks.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-885%2Fadlternative%2Fformat_patch_non_intergral-v10
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-885/adlternative/format_patch_non_intergral-v10
Pull-Request: https://github.com/gitgitgadget/git/pull/885

Range-diff vs v9:

 1:  97eaae4a8d9e ! 1:  53c359f364fa format-patch: allow a non-integral version numbers
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
      
       ## log-tree.c ##
      @@ log-tree.c: void fmt_output_subject(struct strbuf *filename,
     - 	int nr = info->nr;
       	int start_len = filename->len;
       	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
     -+	struct strbuf temp = STRBUF_INIT;
       
      -	if (0 < info->reroll_count)
      -		strbuf_addf(filename, "v%d-", info->reroll_count);
      +	if (info->reroll_count) {
     ++		struct strbuf temp = STRBUF_INIT;
     ++
      +		strbuf_addf(&temp, "v%s", info->reroll_count);
      +		format_sanitized_subject(filename, temp.buf, temp.len);
      +		strbuf_addstr(filename, "-");
     @@ t/t3206-range-diff.sh: test_expect_success 'format-patch --range-diff as comment
      +	test_i18ngrep "^Range-diff:$" v0-0001-* &&
      +	grep "> 1: .* new message" v0-0001-*
      +'
     ++
       test_expect_success 'range-diff overrides diff.noprefix internally' '
       	git -c diff.noprefix=true range-diff HEAD^...
       '
      
       ## t/t4014-format-patch.sh ##
     -@@ t/t4014-format-patch.sh: test_expect_success 'reroll count' '
     - 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
     - '
     - 
     -+test_expect_success 'reroll count with a fractional number' '
     -+	rm -fr patches &&
     -+	git format-patch -o patches --cover-letter --reroll-count 4.4 main..side >list &&
     -+	! grep -v "^patches/v4.4-000[0-3]-" list &&
     -+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     -+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
     -+'
     -+
     -+test_expect_success 'reroll count with a non number' '
     -+	rm -fr patches &&
     -+	git format-patch -o patches --cover-letter --reroll-count 4rev2 main..side >list &&
     -+	! grep -v "^patches/v4rev2-000[0-3]-" list &&
     -+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     -+	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
     -+'
     -+
     - test_expect_success 'reroll count (-v)' '
     - 	rm -fr patches &&
     - 	git format-patch -o patches --cover-letter -v4 main..side >list &&
      @@ t/t4014-format-patch.sh: test_expect_success 'reroll count (-v)' '
       	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
       '
     @@ t/t4014-format-patch.sh: test_expect_success 'reroll count (-v)' '
      +	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
      +'
      +
     -+test_expect_success 'reroll (-v) count with a "injection (1)"' '
     -+	rm -fr patches &&
     -+	git format-patch -o patches --cover-letter -v4..././../1/.2//  main..side >list &&
     -+	! grep -v "^patches/v4.-.-.-1-.2-000[0-3]-" list &&
     -+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     -+	! grep -v "^Subject: \[PATCH v4..././../1/.2// [0-3]/3\] " subjects
     -+'
     -+
     -+test_expect_success 'reroll (-v) count with a "injection (2)"' '
     ++test_expect_success 'reroll (-v) count with a non-pathname character' '
      +	rm -fr patches &&
     -+	git format-patch -o patches --cover-letter -v4-----//1//--.--  main..side >list &&
     -+	! grep -v "^patches/v4-1-000[0-3]-" list &&
     ++	git format-patch -o patches --cover-letter -v4---..././../--1/.2//  main..side >list &&
     ++	! grep -v "patches/v4-\.-\.-\.-1-\.2-000[0-3]-" list &&
      +	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
     -+	! grep -v "^Subject: \[PATCH v4-----//1//--.-- [0-3]/3\] " subjects
     ++	! grep -v "^Subject: \[PATCH v4---\.\.\./\./\.\./--1/\.2// [0-3]/3\] " subjects
      +'
      +
       check_threading () {


 Documentation/git-format-patch.txt |  5 +++++
 builtin/log.c                      | 25 ++++++++++++++--------
 log-tree.c                         | 10 +++++++--
 revision.h                         |  2 +-
 t/t3206-range-diff.sh              | 24 +++++++++++++++++++++
 t/t4014-format-patch.sh            | 34 ++++++++++++++++++++++++++++++
 6 files changed, 88 insertions(+), 12 deletions(-)
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 3e49bf221087..741c9f8b2b88 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -221,6 +221,11 @@ populated with placeholder text.
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
 	file that has "Subject: [PATCH v4 1/20] Add makefile" in it.
+	`<n>` does not have to be an integer (e.g. "--reroll-count=4.4",
+	or "--reroll-count=4rev2" are allowed), but the downside of
+	using such a reroll-count is that the range-diff/interdiff
+	with the previous version does not state exactly which
+	version the new interation is compared against.
 
 --to=<email>::
 	Add a `To:` header to the email headers. This is in addition
diff --git a/builtin/log.c b/builtin/log.c
index f67b67d80ed1..af853f111464 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1662,13 +1662,19 @@ static void print_bases(struct base_tree_info *bases, FILE *file)
 	oidclr(&bases->base_commit);
 }
 
-static const char *diff_title(struct strbuf *sb, int reroll_count,
-		       const char *generic, const char *rerolled)
+static const char *diff_title(struct strbuf *sb,
+			      const char *reroll_count,
+			      const char *generic,
+			      const char *rerolled)
 {
-	if (reroll_count <= 0)
+	int v;
+
+	/* RFC may be v0, so allow -v1 to diff against v0 */
+	if (reroll_count && !strtol_i(reroll_count, 10, &v) &&
+	    v >= 1)
+		strbuf_addf(sb, rerolled, v - 1);
+	else
 		strbuf_addstr(sb, generic);
-	else /* RFC may be v0, so allow -v1 to diff against v0 */
-		strbuf_addf(sb, rerolled, reroll_count - 1);
 	return sb->buf;
 }
 
@@ -1717,7 +1723,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf buf = STRBUF_INIT;
 	int use_patch_format = 0;
 	int quiet = 0;
-	int reroll_count = -1;
+	const char *reroll_count = NULL;
 	char *cover_from_description_arg = NULL;
 	char *branch_name = NULL;
 	char *base_commit = NULL;
@@ -1751,7 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 			    N_("use <sfx> instead of '.patch'")),
 		OPT_INTEGER(0, "start-number", &start_number,
 			    N_("start numbering patches at <n> instead of 1")),
-		OPT_INTEGER('v', "reroll-count", &reroll_count,
+		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
 		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
 			    N_("max length of output filename")),
@@ -1862,9 +1868,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (cover_from_description_arg)
 		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (0 < reroll_count) {
+	if (reroll_count) {
 		struct strbuf sprefix = STRBUF_INIT;
-		strbuf_addf(&sprefix, "%s v%d",
+
+		strbuf_addf(&sprefix, "%s v%s",
 			    rev.subject_prefix, reroll_count);
 		rev.reroll_count = reroll_count;
 		rev.subject_prefix = strbuf_detach(&sprefix, NULL);
diff --git a/log-tree.c b/log-tree.c
index 4531cebfab38..f3178a66a95b 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -369,8 +369,14 @@ void fmt_output_subject(struct strbuf *filename,
 	int start_len = filename->len;
 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
 
-	if (0 < info->reroll_count)
-		strbuf_addf(filename, "v%d-", info->reroll_count);
+	if (info->reroll_count) {
+		struct strbuf temp = STRBUF_INIT;
+
+		strbuf_addf(&temp, "v%s", info->reroll_count);
+		format_sanitized_subject(filename, temp.buf, temp.len);
+		strbuf_addstr(filename, "-");
+		strbuf_release(&temp);
+	}
 	strbuf_addf(filename, "%04d-%s", nr, subject);
 
 	if (max_len < filename->len)
diff --git a/revision.h b/revision.h
index e6be3c845e66..097d08354c61 100644
--- a/revision.h
+++ b/revision.h
@@ -235,7 +235,7 @@ struct rev_info {
 	const char	*mime_boundary;
 	const char	*patch_suffix;
 	int		numbered_files;
-	int		reroll_count;
+	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
 	struct string_list *ref_message_ids;
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 1b26c4c2ef91..e30bc48a2901 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -521,6 +521,30 @@ test_expect_success 'format-patch --range-diff as commentary' '
 	grep "> 1: .* new message" 0001-*
 '
 
+test_expect_success 'format-patch --range-diff reroll-count with a non-integer' '
+	git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual &&
+	test_when_finished "rm v2.9-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v2.9-0001-* &&
+	grep "> 1: .* new message" v2.9-0001-*
+'
+
+test_expect_success 'format-patch --range-diff reroll-count with a integer' '
+	git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual &&
+	test_when_finished "rm v2-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff ..* v1:$" v2-0001-* &&
+	grep "> 1: .* new message" v2-0001-*
+'
+
+test_expect_success 'format-patch --range-diff with v0' '
+	git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual &&
+	test_when_finished "rm v0-0001-*" &&
+	test_line_count = 1 actual &&
+	test_i18ngrep "^Range-diff:$" v0-0001-* &&
+	grep "> 1: .* new message" v0-0001-*
+'
+
 test_expect_success 'range-diff overrides diff.noprefix internally' '
 	git -c diff.noprefix=true range-diff HEAD^...
 '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 66630c8413d5..ed0cc8b5e177 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -386,6 +386,30 @@ test_expect_success 'reroll count (-v)' '
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
 '
 
+test_expect_success 'reroll count (-v) with a fractional number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4.4 main..side >list &&
+	! grep -v "^patches/v4.4-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4.4 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a non number' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4rev2 main..side >list &&
+	! grep -v "^patches/v4rev2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4rev2 [0-3]/3\] " subjects
+'
+
+test_expect_success 'reroll (-v) count with a non-pathname character' '
+	rm -fr patches &&
+	git format-patch -o patches --cover-letter -v4---..././../--1/.2//  main..side >list &&
+	! grep -v "patches/v4-\.-\.-\.-1-\.2-000[0-3]-" list &&
+	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
+	! grep -v "^Subject: \[PATCH v4---\.\.\./\./\.\./--1/\.2// [0-3]/3\] " subjects
+'
+
 check_threading () {
 	expect="$1" &&
 	shift &&
@@ -2255,6 +2279,16 @@ test_expect_success 'interdiff: reroll-count' '
 	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
 '
 
+test_expect_success 'interdiff: reroll-count with a non-integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2.2 -1 boop &&
+	test_i18ngrep "^Interdiff:$" v2.2-0000-cover-letter.patch
+'
+
+test_expect_success 'interdiff: reroll-count with a integer' '
+	git format-patch --cover-letter --interdiff=boop~2 -v2 -1 boop &&
+	test_i18ngrep "^Interdiff ..* v1:$" v2-0000-cover-letter.patch
+'
+
 test_expect_success 'interdiff: solo-patch' '
 	cat >expect <<-\EOF &&
 	  +fleep
base-commit: 966e671106b2fd38301e7c344c754fd118d0bb07
-- 
gitgitgadget

Re: [PATCH v10] format-patch: allow a non-integral version numbers

From: Eric Sunshine <hidden>
Date: 2021-03-24 03:59:51

On Tue, Mar 23, 2021 at 7:12 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
[...]
Allow `format-patch` to take such a non-integral iteration
number.
[...]
Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    A rollback was performed again under Eric's suggestion.
Thanks. I think this version addresses my previous review comments.

I did not find anything to comment about in this version.

Re: [PATCH v10] format-patch: allow a non-integral version numbers

From: ZheNing Hu <hidden>
Date: 2021-03-24 04:44:32

Eric Sunshine [off-list ref] 于2021年3月24日周三 上午11:59写道:
On Tue, Mar 23, 2021 at 7:12 AM ZheNing Hu via GitGitGadget
[off-list ref] wrote:
quoted
[...]
Allow `format-patch` to take such a non-integral iteration
number.
[...]
Signed-off-by: ZheNing Hu <redacted>
---
    [GSOC] format-patch: allow a non-integral version numbers
    A rollback was performed again under Eric's suggestion.
Thanks. I think this version addresses my previous review comments.

I did not find anything to comment about in this version.
Eric, Thanks for all your comments and guidance! :)

--
ZheNing Hu
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help