threads / patch / 51475

patchrebase -i: support --ignore-whitespace

Subject: [GSoC][PATCH 0/1] rebase -i: support --ignore-whitespace

## tl;dr

32 messages between Jul 12, 2019 and Aug 2, 2019. Diffs are folded; open one to read it.

replies: 31people: 5as markdown or json

Rohit Ashiwal· Jul 12, 2019, 18:50 UTC · lore
[cc'ing Phillip Wood, Martin Ågren and Dscho; they might be interested]

There are two backends available for rebasing, viz, the am and the interactive. Naturally, there shall be some features that are implemented in one but not in the other. One such flag is --ignore-whitespace which indicated merge mechanism to treat lines with only whitespace changes as unchanged.

NB: am's --ignore-space-change (also aliased --ignore-whitespace; see git-apply's
description) not only share the same name with diff's --ignore-space-change and
merge's -Xignore-space-change, but the similarity in naming appears to have been
intentional with am's --ignore-space-change being designed to have the same
functionality (see e.g. the commit messages for f008cef ("Merge branch
'jc/apply-ignore-whitespace'", 2014-06-04) and 4e5dd04 ("merge-recursive: options
to ignore whitespace changes", 2010-08-26)).

For the most part, these options do provide the same behaviour. However, there are some edge cases where both apply's --ignore-space-change and merge's -Xignore-space-change fall short of optimal behaviour, and that too in different ways. Fixing these differences in edge cases is left for future work.

Rohit Ashiwal (1):
  rebase -i: add --ignore-whitespace flag
 Documentation/git-rebase.txt            |  9 +++-
 builtin/rebase.c                        | 24 +++++++--
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
 5 files changed, 95 insertions(+), 6 deletions(-)
 create mode 100755 t/t3431-rebase-options-compatibility.sh
-- 
2.21.0
Rohit Ashiwal· Jul 12, 2019, 18:50 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH 1/1] rebase -i: add --ignore-whitespace flag

There are two backends available for rebasing, viz, the am and the interactive. Naturally, there shall be some features that are implemented in one but not in the other. One such flag is --ignore-whitespace which indicates merge mechanism to treat lines with only whitespace changes as unchanged. Wire the interactive rebase to also understand the --ignore-whitespace flag by translating it to -Xignore-space-change.

Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 Documentation/git-rebase.txt            |  9 +++-
 builtin/rebase.c                        | 24 +++++++--
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
 5 files changed, 95 insertions(+), 6 deletions(-)
 create mode 100755 t/t3431-rebase-options-compatibility.sh
Show changes to 5 files +95 −6

Documentation/git-rebase.txt, builtin/rebase.c, sequencer.h, t/t3422-rebase-incompatible-options.sh, t/t3431-rebase-options-compatibility.sh

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5e4e92764..eda52ed82 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -371,8 +371,13 @@ If either <upstream> or --root is given on the command line, then the
 default is `--no-fork-point`, otherwise the default is `--fork-point`.
 
 --ignore-whitespace::
+	This flag is either passed to the 'git apply' program
+	(see linkgit:git-apply[1]), or to 'git merge' program
+	(see linkgit:git-merge[1]) as `-Xignore-space-change`,
+	depending on which backend is selected by other options.
+
 --whitespace=<option>::
-	These flag are passed to the 'git apply' program
+	This flag is passed to the 'git apply' program
 	(see linkgit:git-apply[1]) that applies the patch.
 +
 See also INCOMPATIBLE OPTIONS below.
@@ -520,7 +525,6 @@ The following options:
  * --committer-date-is-author-date
  * --ignore-date
  * --whitespace
- * --ignore-whitespace
  * -C
 
 are incompatible with the following options:
@@ -543,6 +547,7 @@ In addition, the following pairs of options are incompatible:
  * --preserve-merges and --interactive
  * --preserve-merges and --signoff
  * --preserve-merges and --rebase-merges
+ * --rebase-merges and --ignore-whitespace
  * --rebase-merges and --strategy
  * --rebase-merges and --strategy-option
 
diff --git a/builtin/rebase.c b/builtin/rebase.c
index db6ca9bd7..c55957c31 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,7 @@ struct rebase_options {
 	int allow_rerere_autoupdate;
 	int keep_empty;
 	int autosquash;
+	int ignore_whitespace;
 	char *gpg_sign_opt;
 	int autostash;
 	char *cmd;
@@ -376,6 +377,17 @@ static int run_rebase_interactive(struct rebase_options *opts,
 	flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
 	flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
 
+	if (opts->ignore_whitespace) {
+		struct strbuf buf = STRBUF_INIT;
+
+		if (opts->strategy_opts)
+			strbuf_addstr(&buf, opts->strategy_opts);
+
+		strbuf_addstr(&buf, " --ignore-space-change");
+		free(opts->strategy_opts);
+		opts->strategy_opts = strbuf_detach(&buf, NULL);
+	}
+
 	switch (command) {
 	case ACTION_NONE: {
 		if (!opts->onto && !opts->upstream)
@@ -489,6 +501,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
 			N_("GPG-sign commits"),
 			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
+			 N_("ignore changes in whitespace")),
 		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
 			   N_("rebase strategy")),
 		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
@@ -954,6 +968,8 @@ static int run_am(struct rebase_options *opts)
 	am.git_cmd = 1;
 	argv_array_push(&am.args, "am");
 
+	if (opts->ignore_whitespace)
+		argv_array_push(&am.args, "--ignore-whitespace");
 	if (opts->action && !strcmp("continue", opts->action)) {
 		argv_array_push(&am.args, "--resolved");
 		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
@@ -1401,9 +1417,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
 		OPT_BOOL(0, "signoff", &options.signoff,
 			 N_("add a Signed-off-by: line to each commit")),
-		OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
-				  NULL, N_("passed to 'git am'"),
-				  PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
 				  &options.git_am_opts, NULL,
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
@@ -1411,6 +1424,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
 				  N_("passed to 'git apply'"), 0),
+		OPT_BOOL(0, "ignore-whitespace", &options.ignore_whitespace,
+			 N_("ignore changes in whitespace")),
 		OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
 				  N_("action"), N_("passed to 'git apply'"), 0),
 		OPT_BIT('f', "force-rebase", &options.flags,
@@ -1821,6 +1836,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	}
 
 	if (options.rebase_merges) {
+		if (options.ignore_whitespace)
+			die(_("cannot combine '--rebase-merges' with "
+			      "'--ignore-whitespace'"));
 		if (strategy_options.nr)
 			die(_("cannot combine '--rebase-merges' with "
 			      "'--strategy-option'"));
diff --git a/sequencer.h b/sequencer.h
index 0c494b83d..303047a13 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -43,6 +43,7 @@ struct replay_opts {
 	int verbose;
 	int quiet;
 	int reschedule_failed_exec;
+	int ignore_whitespace;
 
 	int mainline;
 
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index a5868ea15..4342f79ee 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -61,7 +61,6 @@ test_rebase_am_only () {
 }
 
 test_rebase_am_only --whitespace=fix
-test_rebase_am_only --ignore-whitespace
 test_rebase_am_only --committer-date-is-author-date
 test_rebase_am_only -C4
 
diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
new file mode 100755
index 000000000..f38ae6f5f
--- /dev/null
+++ b/t/t3431-rebase-options-compatibility.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Rohit Ashiwal
+#
+
+test_description='tests to ensure compatibility between am and interactive backends'
+
+. ./test-lib.sh
+
+# This is a special case in which both am and interactive backends
+# provide the same outputs. It was done intentionally because
+# --ignore-whitespace both the backends fall short of optimal
+# behaviour.
+test_expect_success 'setup' '
+	git checkout -b topic &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	Qline 2
+	line 3
+	EOF
+	git add file &&
+	git commit -m "add file" &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	git commit -am "update file" &&
+	git tag side &&
+
+	git checkout --orphan master &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	        line 2
+	line 3
+	EOF
+	git add file &&
+	git commit -m "add file" &&
+	git tag main
+'
+
+test_expect_success '--ignore-whitespace works with am backend' '
+	cat >expect <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	test_must_fail git rebase main side &&
+	git rebase --abort &&
+	git rebase --ignore-whitespace main side &&
+	test_cmp expect file
+'
+
+test_expect_success '--ignore-whitespace works with interactive backend' '
+	cat >expect <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	test_must_fail git rebase --merge main side &&
+	git rebase --abort &&
+	git rebase --merge --ignore-whitespace main side &&
+	test_cmp expect file
+'
+
+test_done
-- 
2.21.0
Junio C Hamano· Jul 15, 2019, 17:57 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH 1/1] rebase -i: add --ignore-whitespace flag

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
Show 8 quoted lines
> +	if (opts->ignore_whitespace) {
> +		struct strbuf buf = STRBUF_INIT;
> +
> +		if (opts->strategy_opts)
> +			strbuf_addstr(&buf, opts->strategy_opts);
> +
> +		strbuf_addstr(&buf, " --ignore-space-change");
> +		free(opts->strategy_opts);
Is this call to free() safe?
Show 10 quoted lines
> +		opts->strategy_opts = strbuf_detach(&buf, NULL);
> +	}
> +
> @@ -1821,6 +1836,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	}
>  
>  	if (options.rebase_merges) {
> +		if (options.ignore_whitespace)
> +			die(_("cannot combine '--rebase-merges' with "
> +			      "'--ignore-whitespace'"));
Hmph, this is unfortunate.  The patch is not making things worse, though.
Rohit Ashiwal· Jul 15, 2019, 22:00 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH 1/1] rebase -i: add --ignore-whitespace flag

Hi Junio
On Mon, 15 Jul 2019 10:57:18 -0700 Junio C Hamano <gitster@pobox.com> wrote:
Show 13 quoted lines
> 
> Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
> 
> > +	if (opts->ignore_whitespace) {
> > +		struct strbuf buf = STRBUF_INIT;
> > +
> > +		if (opts->strategy_opts)
> > +			strbuf_addstr(&buf, opts->strategy_opts);
> > +
> > +		strbuf_addstr(&buf, " --ignore-space-change");
> > +		free(opts->strategy_opts);
> 
> Is this call to free() safe?

Yes, as far as I can tell. Since up till now, `strategy_opts` is either "NULL" or xstrdup()'d string. There is no double free involved.

Show 12 quoted lines
> > +		opts->strategy_opts = strbuf_detach(&buf, NULL);
> > +	}
> > +
> > @@ -1821,6 +1836,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> >  	}
> >
> >  	if (options.rebase_merges) {
> > +		if (options.ignore_whitespace)
> > +			die(_("cannot combine '--rebase-merges' with "
> > +			      "'--ignore-whitespace'"));
> 
> Hmph, this is unfortunate.  The patch is not making things worse, though.

Actually, there is no need for --rebase-merges and --ignore-whitespace to be "incompatible". My mentors also agree on this one. Making them work in harmony will be handled in another patch series.

Best Rohit

Junio C Hamano· Jul 15, 2019, 22:08 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH 1/1] rebase -i: add --ignore-whitespace flag

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
Show 19 quoted lines
> Hi Junio
>
> On Mon, 15 Jul 2019 10:57:18 -0700 Junio C Hamano <gitster@pobox.com> wrote:
>> 
>> Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
>> 
>> > +	if (opts->ignore_whitespace) {
>> > +		struct strbuf buf = STRBUF_INIT;
>> > +
>> > +		if (opts->strategy_opts)
>> > +			strbuf_addstr(&buf, opts->strategy_opts);
>> > +
>> > +		strbuf_addstr(&buf, " --ignore-space-change");
>> > +		free(opts->strategy_opts);
>> 
>> Is this call to free() safe?
>
> Yes, as far as I can tell. Since up till now, `strategy_opts` is
> either "NULL" or xstrdup()'d string. There is no double free involved.

Doesn't it also come from handling OPT_STRING() via parse_options(), which gives a pointer from argv[], freeing something that is not allocated in the first place?

Rohit Ashiwal· Jul 15, 2019, 22:42 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH 1/1] rebase -i: add --ignore-whitespace flag

Hi Junio
On Mon, 15 Jul 2019 15:08:30 -0700 Junio C Hamano <gitster@pobox.com> wrote:
>
> Doesn't it also come from handling OPT_STRING() via parse_options(),
> which gives a pointer from argv[], freeing something that is not
> allocated in the first place?
Ah! The path from cmd_rebase__interactive! A simple workaround could be:
Show changes to builtin/rebase.c +2 −0
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c55957c31f..afe376c3fe 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -525,6 +525,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, NULL, options,
                        builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);

+       opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
+
        if (!is_null_oid(&squash_onto))
                opts.squash_onto = &squash_onto;
Rohit Ashiwal· Jul 12, 2019, 18:53 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH 0/2] rebase -i: support --committer-date-is-author-date

rebase am already has this flag to "lie" about the committer date, i.e., by changing it to the author date. Let's add the same for interactive machinery. This will get us a step closer to the ultimate aim of achieving consistency between sequencer commands.

NB: To reduce merge conflicts on the reviewer's part, I've based this patch
series on my previous patch series[1].
[1]: https://public-inbox.org/git/20190712185015.20585-1-rohit.ashiwal265@gmail.com/
Rohit Ashiwal (2):
  sequencer: add NULL checks under read_author_script
  rebase -i: support --committer-date-is-author-date
 Documentation/git-rebase.txt            |  7 ++-
 builtin/rebase.c                        | 23 +++++++--
 sequencer.c                             | 62 +++++++++++++++++++++++--
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 15 ++++++
 6 files changed, 97 insertions(+), 12 deletions(-)
-- 
2.21.0
Rohit Ashiwal· Jul 18, 2019, 19:03 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v2 0/2] rebase -i: support --committer-date-is-author-date

Another revision to keep this thread alive.
Rohit Ashiwal (2):
  sequencer: add NULL checks under read_author_script
  rebase -i: support --committer-date-is-author-date
 Documentation/git-rebase.txt            |  7 ++-
 builtin/rebase.c                        | 23 +++++++--
 sequencer.c                             | 62 +++++++++++++++++++++++--
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 21 +++++++++
 6 files changed, 103 insertions(+), 12 deletions(-)
Range-diff:
1:  6d2a5a382d ! 1:  a1bb91fe43 rebase -i: add --ignore-whitespace flag
    @@ -85,6 +85,15 @@
      		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
      			   N_("rebase strategy")),
      		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
    +@@
    + 	argc = parse_options(argc, argv, NULL, options,
    + 			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
    + 
    ++	opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
    ++
    + 	if (!is_null_oid(&squash_onto))
    + 		opts.squash_onto = &squash_onto;
    + 
     @@
      	am.git_cmd = 1;
      	argv_array_push(&am.args, "am");
2:  3e6a67410c = 2:  597d282fa6 sequencer: add NULL checks under read_author_script
3:  3d5f2e2960 ! 3:  33cfc0f890 rebase -i: support --committer-date-is-author-date
    @@ -262,14 +262,20 @@
      	test_cmp expect file
      '
      
    -+test_expect_success '--committer-date-is-author-date works with interactive backend' '
    ++test_expect_success '--committer-date-is-author-date works with am backend' '
    It is implied that --force-rebase will change the committer date.
    We can add a test (somewhere else) that will check so, but that will
    be moot IMO.
     +	git rebase -f HEAD^ &&
    ++	git rebase --committer-date-is-author-date HEAD^ &&
     +	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
     +	sed -ne "/^author /s/.*> //p" head >authortime &&
     +	sed -ne "/^committer /s/.*> //p" head >committertime &&
    -+	! test_cmp at ct &&
    ++	test_cmp authortime committertime
    ++'
    ++
    ++test_expect_success '--committer-date-is-author-date works with interactive backend' '
    ++	git rebase -f HEAD^ &&
     +	git rebase -i --committer-date-is-author-date HEAD^ &&
     +	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
    ++	sed -ne "/^author /s/.*> //p" head >authortime &&
     +	sed -ne "/^committer /s/.*> //p" head >committertime &&
     +	test_cmp authortime committertime
     +'
-- 
2.21.0
Rohit Ashiwal· Jul 18, 2019, 19:03 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v2 1/2] sequencer: add NULL checks under read_author_script

read_author_script reads name, email and author date from the author script. However, it does not check if the arguments are NULL. Adding NULL checks will allow us to selectively get the required value, for example:

    char *date;
    if (read_author_script(_path_, NULL, NULL, &date, _int_))
	    die(_("failed to read author date"));
    /* needs to be free()'d */
    return date;
Add NULL checks for better control over the information retrieved.
Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 sequencer.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
Show changes to sequencer.c +13 −3
diff --git a/sequencer.c b/sequencer.c
index f88a97fb10..a2d7b0925e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -821,9 +821,19 @@ int read_author_script(const char *path, char **name, char **email, char **date,
 		error(_("missing 'GIT_AUTHOR_DATE'"));
 	if (date_i < 0 || email_i < 0 || date_i < 0 || err)
 		goto finish;
-	*name = kv.items[name_i].util;
-	*email = kv.items[email_i].util;
-	*date = kv.items[date_i].util;
+
+	if (name)
+		*name = kv.items[name_i].util;
+	else
+		free(kv.items[name_i].util);
+	if (email)
+		*email = kv.items[email_i].util;
+	else
+		free(kv.items[email_i].util);
+	if (date)
+		*date = kv.items[date_i].util;
+	else
+		free(kv.items[date_i].util);
 	retval = 0;
 finish:
 	string_list_clear(&kv, !!retval);
-- 
2.21.0
Rohit Ashiwal· Jul 18, 2019, 19:03 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v2 2/2] rebase -i: support --committer-date-is-author-date

rebase am already has this flag to "lie" about the committer date by changing it to the author date. Let's add the same for interactive machinery.

Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 Documentation/git-rebase.txt            |  7 ++--
 builtin/rebase.c                        | 23 ++++++++++---
 sequencer.c                             | 46 ++++++++++++++++++++++++-
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 21 +++++++++++
 6 files changed, 90 insertions(+), 9 deletions(-)
Show changes to 6 files +90 −9

Documentation/git-rebase.txt, builtin/rebase.c, sequencer.c, sequencer.h, t/t3422-rebase-incompatible-options.sh, t/t3431-rebase-options-compatibility.sh

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index eda52ed824..ddd111de69 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -383,8 +383,12 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
 See also INCOMPATIBLE OPTIONS below.
 
 --committer-date-is-author-date::
+	Instead of recording the time the rebased commits are
+	created as the committer date, reuse the author date
+	as the committer date. This implies --force-rebase.
+
 --ignore-date::
-	These flags are passed to 'git am' to easily change the dates
+	This flag is passed to 'git am' to change the author date
 	of the rebased commits (see linkgit:git-am[1]).
 +
 See also INCOMPATIBLE OPTIONS below.
@@ -522,7 +526,6 @@ INCOMPATIBLE OPTIONS
 
 The following options:
 
- * --committer-date-is-author-date
  * --ignore-date
  * --whitespace
  * -C
diff --git a/builtin/rebase.c b/builtin/rebase.c
index afe376c3fe..c317fbe53c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -82,6 +82,7 @@ struct rebase_options {
 	int ignore_whitespace;
 	char *gpg_sign_opt;
 	int autostash;
+	int committer_date_is_author_date;
 	char *cmd;
 	int allow_empty_message;
 	int rebase_merges, rebase_cousins;
@@ -113,6 +114,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 	replay.allow_empty_message = opts->allow_empty_message;
 	replay.verbose = opts->flags & REBASE_VERBOSE;
 	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
+	replay.committer_date_is_author_date =
+					opts->committer_date_is_author_date;
 	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
 	replay.strategy = opts->strategy;
 	if (opts->strategy_opts)
@@ -467,6 +470,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 		OPT_BOOL(0, "autosquash", &opts.autosquash,
 			 N_("move commits that begin with squash!/fixup!")),
 		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
+		OPT_BOOL(0, "committer-date-is-author-date",
+			 &opts.committer_date_is_author_date,
+			 N_("make committer date match author date")),
 		OPT_BIT('v', "verbose", &opts.flags,
 			N_("display a diffstat of what changed upstream"),
 			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
@@ -534,6 +540,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 		warning(_("--[no-]rebase-cousins has no effect without "
 			  "--rebase-merges"));
 
+	if (opts.committer_date_is_author_date)
+		opts.flags |= REBASE_FORCE;
+
 	return !!run_rebase_interactive(&opts, command);
 }
 
@@ -972,6 +981,8 @@ static int run_am(struct rebase_options *opts)
 
 	if (opts->ignore_whitespace)
 		argv_array_push(&am.args, "--ignore-whitespace");
+	if (opts->committer_date_is_author_date)
+		argv_array_push(&opts->git_am_opts, "--committer-date-is-author-date");
 	if (opts->action && !strcmp("continue", opts->action)) {
 		argv_array_push(&am.args, "--resolved");
 		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
@@ -1419,9 +1430,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
 		OPT_BOOL(0, "signoff", &options.signoff,
 			 N_("add a Signed-off-by: line to each commit")),
-		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
-				  &options.git_am_opts, NULL,
-				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
+		OPT_BOOL(0, "committer-date-is-author-date",
+			 &options.committer_date_is_author_date,
+			 N_("make committer date match author date")),
 		OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
@@ -1688,10 +1699,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		    state_dir_base, cmd_live_rebase, buf.buf);
 	}
 
+	if (options.committer_date_is_author_date)
+		options.flags |= REBASE_FORCE;
+
 	for (i = 0; i < options.git_am_opts.argc; i++) {
 		const char *option = options.git_am_opts.argv[i], *p;
-		if (!strcmp(option, "--committer-date-is-author-date") ||
-		    !strcmp(option, "--ignore-date") ||
+		if (!strcmp(option, "--ignore-date") ||
 		    !strcmp(option, "--whitespace=fix") ||
 		    !strcmp(option, "--whitespace=strip"))
 			options.flags |= REBASE_FORCE;
diff --git a/sequencer.c b/sequencer.c
index a2d7b0925e..a65f01a422 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -147,6 +147,7 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
  * command-line.
  */
 static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
+static GIT_PATH_FUNC(rebase_path_cdate_is_adate, "rebase-merge/cdate_is_adate")
 static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
 static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
 static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
@@ -876,6 +877,18 @@ static char *get_author(const char *message)
 	return NULL;
 }
 
+/* Returns a "date" string that needs to be free()'d by the caller */
+static char *read_author_date_or_die(void)
+{
+	char *date;
+
+	if (read_author_script(rebase_path_author_script(),
+			       NULL, NULL, &date, 0))
+		die(_("failed to read author date"));
+
+	return date;
+}
+
 /* Read author-script and return an ident line (author <email> timestamp) */
 static const char *read_author_ident(struct strbuf *buf)
 {
@@ -986,10 +999,17 @@ static int run_git_commit(struct repository *r,
 
 		if (res <= 0)
 			res = error_errno(_("could not read '%s'"), defmsg);
-		else
+		else {
+			if (opts->committer_date_is_author_date) {
+				char *date = read_author_date_or_die();
+				setenv("GIT_COMMITTER_DATE", date, 1);
+				free(date);
+			}
+
 			res = commit_tree(msg.buf, msg.len, cache_tree_oid,
 					  NULL, &root_commit, author,
 					  opts->gpg_sign);
+		}
 
 		strbuf_release(&msg);
 		strbuf_release(&script);
@@ -1019,6 +1039,11 @@ static int run_git_commit(struct repository *r,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
+	if (opts->committer_date_is_author_date) {
+		char *date = read_author_date_or_die();
+		argv_array_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", date);
+		free(date);
+	}
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	else if (!(flags & EDIT_MSG))
@@ -1467,6 +1492,12 @@ static int try_to_commit(struct repository *r,
 
 	reset_ident_date();
 
+	if (opts->committer_date_is_author_date) {
+		char *date = read_author_date_or_die();
+		setenv("GIT_COMMITTER_DATE", date, 1);
+		free(date);
+	}
+
 	if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
 				 oid, author, opts->gpg_sign, extra)) {
 		res = error(_("failed to write commit object"));
@@ -2538,6 +2569,11 @@ static int read_populate_opts(struct replay_opts *opts)
 			opts->signoff = 1;
 		}
 
+		if (file_exists(rebase_path_cdate_is_adate())) {
+			opts->allow_ff = 0;
+			opts->committer_date_is_author_date = 1;
+		}
+
 		if (file_exists(rebase_path_reschedule_failed_exec()))
 			opts->reschedule_failed_exec = 1;
 
@@ -2620,6 +2656,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
 		write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
 	if (opts->signoff)
 		write_file(rebase_path_signoff(), "--signoff\n");
+	if (opts->committer_date_is_author_date)
+		write_file(rebase_path_cdate_is_adate(), "%s", "");
 	if (opts->reschedule_failed_exec)
 		write_file(rebase_path_reschedule_failed_exec(), "%s", "");
 
@@ -3437,6 +3475,12 @@ static int do_merge(struct repository *r,
 		argv_array_push(&cmd.args, git_path_merge_msg(r));
 		if (opts->gpg_sign)
 			argv_array_push(&cmd.args, opts->gpg_sign);
+		if (opts->committer_date_is_author_date) {
+			char *date = read_author_date_or_die();
+			argv_array_pushf(&cmd.env_array,
+					 "GIT_COMMITTER_DATE=%s", date);
+			free(date);
+		}
 
 		/* Add the tips to be merged */
 		for (j = to_merge; j; j = j->next)
diff --git a/sequencer.h b/sequencer.h
index 303047a133..0cfe184fc2 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -44,6 +44,7 @@ struct replay_opts {
 	int quiet;
 	int reschedule_failed_exec;
 	int ignore_whitespace;
+	int committer_date_is_author_date;
 
 	int mainline;
 
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4342f79eea..7402f7e3da 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -61,7 +61,6 @@ test_rebase_am_only () {
 }
 
 test_rebase_am_only --whitespace=fix
-test_rebase_am_only --committer-date-is-author-date
 test_rebase_am_only -C4
 
 test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
index f38ae6f5fc..c3f7f6d5d0 100755
--- a/t/t3431-rebase-options-compatibility.sh
+++ b/t/t3431-rebase-options-compatibility.sh
@@ -7,6 +7,9 @@ test_description='tests to ensure compatibility between am and interactive backe
 
 . ./test-lib.sh
 
+GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
+export GIT_AUTHOR_DATE
+
 # This is a special case in which both am and interactive backends
 # provide the same outputs. It was done intentionally because
 # --ignore-whitespace both the backends fall short of optimal
@@ -63,4 +66,22 @@ test_expect_success '--ignore-whitespace works with interactive backend' '
 	test_cmp expect file
 '
 
+test_expect_success '--committer-date-is-author-date works with am backend' '
+	git rebase -f HEAD^ &&
+	git rebase --committer-date-is-author-date HEAD^ &&
+	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
+	sed -ne "/^author /s/.*> //p" head >authortime &&
+	sed -ne "/^committer /s/.*> //p" head >committertime &&
+	test_cmp authortime committertime
+'
+
+test_expect_success '--committer-date-is-author-date works with interactive backend' '
+	git rebase -f HEAD^ &&
+	git rebase -i --committer-date-is-author-date HEAD^ &&
+	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
+	sed -ne "/^author /s/.*> //p" head >authortime &&
+	sed -ne "/^committer /s/.*> //p" head >committertime &&
+	test_cmp authortime committertime
+'
+
 test_done
-- 
2.21.0
Junio C Hamano· Jul 19, 2019, 22:36 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 2/2] rebase -i: support --committer-date-is-author-date

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
Show 15 quoted lines
> @@ -1688,10 +1699,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  		    state_dir_base, cmd_live_rebase, buf.buf);
>  	}
>  
> +	if (options.committer_date_is_author_date)
> +		options.flags |= REBASE_FORCE;
> +
>  	for (i = 0; i < options.git_am_opts.argc; i++) {
>  		const char *option = options.git_am_opts.argv[i], *p;
> -		if (!strcmp(option, "--committer-date-is-author-date") ||
> -		    !strcmp(option, "--ignore-date") ||
> +		if (!strcmp(option, "--ignore-date") ||
>  		    !strcmp(option, "--whitespace=fix") ||
>  		    !strcmp(option, "--whitespace=strip"))
>  			options.flags |= REBASE_FORCE;

This is needed here, because am-opts no longer has the committer-date-is-author-date passed through with the parse_options() call in cmd_rebase(), which makes sense.

Show 15 quoted lines
> diff --git a/sequencer.c b/sequencer.c
> index a2d7b0925e..a65f01a422 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -986,10 +999,17 @@ static int run_git_commit(struct repository *r,
>  
>  		if (res <= 0)
>  			res = error_errno(_("could not read '%s'"), defmsg);
> -		else
> +		else {
> +			if (opts->committer_date_is_author_date) {
> +				char *date = read_author_date_or_die();
> +				setenv("GIT_COMMITTER_DATE", date, 1);
> +				free(date);
> +			}

Hmph, are we sure that author-script is always available at this point so that a call to read_author_date_or_die() is safe? There are three callers to the run_git_commit() function and I am not sure if codepaths that reach all of them prepared the input to the read_author_script() helper.

Show 22 quoted lines
> @@ -1019,6 +1039,11 @@ static int run_git_commit(struct repository *r,
>  		argv_array_push(&cmd.args, "--amend");
>  	if (opts->gpg_sign)
>  		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
> +	if (opts->committer_date_is_author_date) {
> +		char *date = read_author_date_or_die();
> +		argv_array_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", date);
> +		free(date);
> +	}
>  	if (defmsg)
>  		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
>  	else if (!(flags & EDIT_MSG))
> @@ -1467,6 +1492,12 @@ static int try_to_commit(struct repository *r,
>  
>  	reset_ident_date();
>  
> +	if (opts->committer_date_is_author_date) {
> +		char *date = read_author_date_or_die();
> +		setenv("GIT_COMMITTER_DATE", date, 1);
> +		free(date);
> +	}
> +

In the same function, we seem to be grabbing the author ident by calling get_author(message), where the message is an in-core copy of a commit object, which suggests me that we may not necessarily be working with the on-disk information read_author_date_or_die() is prepared to deal with. Are we sure we have the needed information on disk so that read_author_date_or_die() will read the correct information from the disk?

Show 20 quoted lines
> @@ -2538,6 +2569,11 @@ static int read_populate_opts(struct replay_opts *opts)
>  			opts->signoff = 1;
>  		}
>  
> +		if (file_exists(rebase_path_cdate_is_adate())) {
> +			opts->allow_ff = 0;
> +			opts->committer_date_is_author_date = 1;
> +		}
> +
>  		if (file_exists(rebase_path_reschedule_failed_exec()))
>  			opts->reschedule_failed_exec = 1;
>  
> @@ -2620,6 +2656,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
>  		write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
>  	if (opts->signoff)
>  		write_file(rebase_path_signoff(), "--signoff\n");
> +	if (opts->committer_date_is_author_date)
> +		write_file(rebase_path_cdate_is_adate(), "%s", "");
>  	if (opts->reschedule_failed_exec)
>  		write_file(rebase_path_reschedule_failed_exec(), "%s", "");

These two are propagating the option across rebase invocations, and they should be correct (as correct as how correctly other options are propagated ;-)).

Show 9 quoted lines
> @@ -3437,6 +3475,12 @@ static int do_merge(struct repository *r,
>  		argv_array_push(&cmd.args, git_path_merge_msg(r));
>  		if (opts->gpg_sign)
>  			argv_array_push(&cmd.args, opts->gpg_sign);
> +		if (opts->committer_date_is_author_date) {
> +			char *date = read_author_date_or_die();
> +			argv_array_pushf(&cmd.env_array,
> +					 "GIT_COMMITTER_DATE=%s", date);
> +			free(date);

This codepath does have read_env_script() which reads from the author script an earlier call to write_author_script() would have left on disk (at least when do_merge() is called with 'commit' argument, anyway), so we should be able to read from it (or the existing code is already buggy---and this patch just makes it slightly worse by depending on a wrong assumption even more). OK.

Rohit Ashiwal· Aug 2, 2019, 20:57 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH v2 2/2] rebase -i: support --committer-date-is-author-date

Hi Junio
On Fri, 19 Jul 2019 15:36:15 -0700 Junio C Hamano <gitster@pobox.com> wrote:
Show 7 quoted lines
>
> [...]
> Hmph, are we sure that author-script is always available at this
> point so that a call to read_author_date_or_die() is safe?  There
> are three callers to the run_git_commit() function and I am not sure
> if codepaths that reach all of them prepared the input to the
> read_author_script() helper.

Functions do_pick_commit() and do_merge() always write author_script before calling run_git_commit(), so, we are sure to find it on disk. Furthermore, commit_staged_changes() only calls run_git_commit() when rebase is stopped (by merge conflicts or 'edit' commands), so we are sure that the previous invocation of git must have saved author_script. In all other cases, we fail as we should.

Show 9 quoted lines
> [...]
>
> In the same function, we seem to be grabbing the author ident by
> calling get_author(message), where the message is an in-core copy of
> a commit object, which suggests me that we may not necessarily be
> working with the on-disk information read_author_date_or_die() is
> prepared to deal with.  Are we sure we have the needed information
> on disk so that read_author_date_or_die() will read the correct
> information from the disk?

Yes, in that case, we can re-parse the author date and re-set the env variable while we are in this if branch. I believe that the information stored on the disk is same as the information retrieved through get_author(), please confirm or disprove this.

> [...]
With all this knowledge, I'll polish the patch and re-send it.

Thanks Rohit

Phillip Wood· Jul 20, 2019, 14:56 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 2/2] rebase -i: support --committer-date-is-author-date

Hi Rohit

It's good to see this patch reducing the differences between the rebase backends.

On 18/07/2019 20:03, Rohit Ashiwal wrote:
Show 68 quoted lines
> rebase am already has this flag to "lie" about the committer date
> by changing it to the author date. Let's add the same for
> interactive machinery.
> 
> Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
> ---
>   Documentation/git-rebase.txt            |  7 ++--
>   builtin/rebase.c                        | 23 ++++++++++---
>   sequencer.c                             | 46 ++++++++++++++++++++++++-
>   sequencer.h                             |  1 +
>   t/t3422-rebase-incompatible-options.sh  |  1 -
>   t/t3431-rebase-options-compatibility.sh | 21 +++++++++++
>   6 files changed, 90 insertions(+), 9 deletions(-)
> 
> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index eda52ed824..ddd111de69 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -383,8 +383,12 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
>   See also INCOMPATIBLE OPTIONS below.
>   
>   --committer-date-is-author-date::
> +	Instead of recording the time the rebased commits are
> +	created as the committer date, reuse the author date
> +	as the committer date. This implies --force-rebase.
> +
>   --ignore-date::
> -	These flags are passed to 'git am' to easily change the dates
> +	This flag is passed to 'git am' to change the author date
>   	of the rebased commits (see linkgit:git-am[1]).
>   +
>   See also INCOMPATIBLE OPTIONS below.
> @@ -522,7 +526,6 @@ INCOMPATIBLE OPTIONS
>   
>   The following options:
>   
> - * --committer-date-is-author-date
>    * --ignore-date
>    * --whitespace
>    * -C
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index afe376c3fe..c317fbe53c 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -82,6 +82,7 @@ struct rebase_options {
>   	int ignore_whitespace;
>   	char *gpg_sign_opt;
>   	int autostash;
> +	int committer_date_is_author_date;
>   	char *cmd;
>   	int allow_empty_message;
>   	int rebase_merges, rebase_cousins;
> @@ -113,6 +114,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
>   	replay.allow_empty_message = opts->allow_empty_message;
>   	replay.verbose = opts->flags & REBASE_VERBOSE;
>   	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
> +	replay.committer_date_is_author_date =
> +					opts->committer_date_is_author_date;
>   	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
>   	replay.strategy = opts->strategy;
>   	if (opts->strategy_opts)
> @@ -467,6 +470,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
>   		OPT_BOOL(0, "autosquash", &opts.autosquash,
>   			 N_("move commits that begin with squash!/fixup!")),
>   		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
> +		OPT_BOOL(0, "committer-date-is-author-date",
> +			 &opts.committer_date_is_author_date,
> +			 N_("make committer date match author date")),

I guess it's good to do this for completeness but does rebase--preserver-merges.sh support --committer-date-is-author-date? It is the only caller of rebase--interactive I think so would be the only user of this code.

Show 74 quoted lines
>   		OPT_BIT('v', "verbose", &opts.flags,
>   			N_("display a diffstat of what changed upstream"),
>   			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
> @@ -534,6 +540,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
>   		warning(_("--[no-]rebase-cousins has no effect without "
>   			  "--rebase-merges"));
>   
> +	if (opts.committer_date_is_author_date)
> +		opts.flags |= REBASE_FORCE;
> +
>   	return !!run_rebase_interactive(&opts, command);
>   }
>   
> @@ -972,6 +981,8 @@ static int run_am(struct rebase_options *opts)
>   
>   	if (opts->ignore_whitespace)
>   		argv_array_push(&am.args, "--ignore-whitespace");
> +	if (opts->committer_date_is_author_date)
> +		argv_array_push(&opts->git_am_opts, "--committer-date-is-author-date");
>   	if (opts->action && !strcmp("continue", opts->action)) {
>   		argv_array_push(&am.args, "--resolved");
>   		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
> @@ -1419,9 +1430,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
>   		OPT_BOOL(0, "signoff", &options.signoff,
>   			 N_("add a Signed-off-by: line to each commit")),
> -		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
> -				  &options.git_am_opts, NULL,
> -				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
> +		OPT_BOOL(0, "committer-date-is-author-date",
> +			 &options.committer_date_is_author_date,
> +			 N_("make committer date match author date")),
>   		OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
>   				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
>   		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
> @@ -1688,10 +1699,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   		    state_dir_base, cmd_live_rebase, buf.buf);
>   	}
>   
> +	if (options.committer_date_is_author_date)
> +		options.flags |= REBASE_FORCE;
> +
>   	for (i = 0; i < options.git_am_opts.argc; i++) {
>   		const char *option = options.git_am_opts.argv[i], *p;
> -		if (!strcmp(option, "--committer-date-is-author-date") ||
> -		    !strcmp(option, "--ignore-date") ||
> +		if (!strcmp(option, "--ignore-date") ||
>   		    !strcmp(option, "--whitespace=fix") ||
>   		    !strcmp(option, "--whitespace=strip"))
>   			options.flags |= REBASE_FORCE;
> diff --git a/sequencer.c b/sequencer.c
> index a2d7b0925e..a65f01a422 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -147,6 +147,7 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
>    * command-line.
>    */
>   static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
> +static GIT_PATH_FUNC(rebase_path_cdate_is_adate, "rebase-merge/cdate_is_adate")
>   static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
>   static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
>   static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
> @@ -876,6 +877,18 @@ static char *get_author(const char *message)
>   	return NULL;
>   }
>   
> +/* Returns a "date" string that needs to be free()'d by the caller */
> +static char *read_author_date_or_die(void)
> +{
> +	char *date;
> +
> +	if (read_author_script(rebase_path_author_script(),
> +			       NULL, NULL, &date, 0))
> +		die(_("failed to read author date"));

Can we have this return an error please - we try quite hard in the sequencer not to die in library code.

Show 35 quoted lines
> +
> +	return date;
> +}
> +
>   /* Read author-script and return an ident line (author <email> timestamp) */
>   static const char *read_author_ident(struct strbuf *buf)
>   {
> @@ -986,10 +999,17 @@ static int run_git_commit(struct repository *r,
>   
>   		if (res <= 0)
>   			res = error_errno(_("could not read '%s'"), defmsg);
> -		else
> +		else {
> +			if (opts->committer_date_is_author_date) {
> +				char *date = read_author_date_or_die();
> +				setenv("GIT_COMMITTER_DATE", date, 1);
> +				free(date);
> +			}
> +
>   			res = commit_tree(msg.buf, msg.len, cache_tree_oid,
>   					  NULL, &root_commit, author,
>   					  opts->gpg_sign);
> +		}
>   
>   		strbuf_release(&msg);
>   		strbuf_release(&script);
> @@ -1019,6 +1039,11 @@ static int run_git_commit(struct repository *r,
>   		argv_array_push(&cmd.args, "--amend");
>   	if (opts->gpg_sign)
>   		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
> +	if (opts->committer_date_is_author_date) {
> +		char *date = read_author_date_or_die();
> +		argv_array_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", date);
> +		free(date);
> +	}

It's a shame to be doing this twice is slightly different ways in the same function (and again in try_to_commit() but I don't think that can be avoided as not all callers of run_git_commit() go through try_to_commit()). As I think the child inherits the current environment modified by cmd.env_array we could just call setenv() at the top of the function. It would be worth looking to see if it would be simpler to do the setenv() call in the loop that picks the commits, then we would avoid having to do it in do_merge() and try_to_commit() separately.

Show 22 quoted lines
>   	if (defmsg)
>   		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
>   	else if (!(flags & EDIT_MSG))
> @@ -1467,6 +1492,12 @@ static int try_to_commit(struct repository *r,
>   
>   	reset_ident_date();
>   
> +	if (opts->committer_date_is_author_date) {
> +		char *date = read_author_date_or_die();
> +		setenv("GIT_COMMITTER_DATE", date, 1);
> +		free(date);
> +	}
> +
>   	if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
>   				 oid, author, opts->gpg_sign, extra)) {
>   		res = error(_("failed to write commit object"));
> @@ -2538,6 +2569,11 @@ static int read_populate_opts(struct replay_opts *opts)
>   			opts->signoff = 1;
>   		}
>   
> +		if (file_exists(rebase_path_cdate_is_adate())) {
> +			opts->allow_ff = 0;

This is safe as we don't save the state of allow_ff for rebases so it wont be overridden later. It would be an idea to add to the checks in the assert() at the beginning of pick_commits() no we have another option that implies --force-rebase.

Best Wishes
Phillip
Show 90 quoted lines
> +			opts->committer_date_is_author_date = 1;
> +		}
> +
>   		if (file_exists(rebase_path_reschedule_failed_exec()))
>   			opts->reschedule_failed_exec = 1;
>   
> @@ -2620,6 +2656,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
>   		write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
>   	if (opts->signoff)
>   		write_file(rebase_path_signoff(), "--signoff\n");
> +	if (opts->committer_date_is_author_date)
> +		write_file(rebase_path_cdate_is_adate(), "%s", "");
>   	if (opts->reschedule_failed_exec)
>   		write_file(rebase_path_reschedule_failed_exec(), "%s", "");
>   
> @@ -3437,6 +3475,12 @@ static int do_merge(struct repository *r,
>   		argv_array_push(&cmd.args, git_path_merge_msg(r));
>   		if (opts->gpg_sign)
>   			argv_array_push(&cmd.args, opts->gpg_sign);
> +		if (opts->committer_date_is_author_date) {
> +			char *date = read_author_date_or_die();
> +			argv_array_pushf(&cmd.env_array,
> +					 "GIT_COMMITTER_DATE=%s", date);
> +			free(date);
> +		}
>   
>   		/* Add the tips to be merged */
>   		for (j = to_merge; j; j = j->next)
> diff --git a/sequencer.h b/sequencer.h
> index 303047a133..0cfe184fc2 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -44,6 +44,7 @@ struct replay_opts {
>   	int quiet;
>   	int reschedule_failed_exec;
>   	int ignore_whitespace;
> +	int committer_date_is_author_date;
>   
>   	int mainline;
>   
> diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
> index 4342f79eea..7402f7e3da 100755
> --- a/t/t3422-rebase-incompatible-options.sh
> +++ b/t/t3422-rebase-incompatible-options.sh
> @@ -61,7 +61,6 @@ test_rebase_am_only () {
>   }
>   
>   test_rebase_am_only --whitespace=fix
> -test_rebase_am_only --committer-date-is-author-date
>   test_rebase_am_only -C4
>   
>   test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
> diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
> index f38ae6f5fc..c3f7f6d5d0 100755
> --- a/t/t3431-rebase-options-compatibility.sh
> +++ b/t/t3431-rebase-options-compatibility.sh
> @@ -7,6 +7,9 @@ test_description='tests to ensure compatibility between am and interactive backe
>   
>   . ./test-lib.sh
>   
> +GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
> +export GIT_AUTHOR_DATE
> +
>   # This is a special case in which both am and interactive backends
>   # provide the same outputs. It was done intentionally because
>   # --ignore-whitespace both the backends fall short of optimal
> @@ -63,4 +66,22 @@ test_expect_success '--ignore-whitespace works with interactive backend' '
>   	test_cmp expect file
>   '
>   
> +test_expect_success '--committer-date-is-author-date works with am backend' '
> +	git rebase -f HEAD^ &&
> +	git rebase --committer-date-is-author-date HEAD^ &&
> +	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
> +	sed -ne "/^author /s/.*> //p" head >authortime &&
> +	sed -ne "/^committer /s/.*> //p" head >committertime &&
> +	test_cmp authortime committertime
> +'
> +
> +test_expect_success '--committer-date-is-author-date works with interactive backend' '
> +	git rebase -f HEAD^ &&
> +	git rebase -i --committer-date-is-author-date HEAD^ &&
> +	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
> +	sed -ne "/^author /s/.*> //p" head >authortime &&
> +	sed -ne "/^committer /s/.*> //p" head >committertime &&
> +	test_cmp authortime committertime
> +'
> +
>   test_done
> 
Rohit Ashiwal· Jul 23, 2019, 19:57 UTC · re: Phillip Wood · lore

Re: [GSoC][PATCH v2 2/2] rebase -i: support --committer-date-is-author-date

Hi Phillip
On Sat, 20 Jul 2019 15:56:50 +0100 Phillip Wood <phillip.wood123@gmail.com> wrote:
Show 15 quoted lines
> 
> [...]
> 
> > @@ -467,6 +470,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
> >   		OPT_BOOL(0, "autosquash", &opts.autosquash,
> >   			 N_("move commits that begin with squash!/fixup!")),
> >   		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
> > +		OPT_BOOL(0, "committer-date-is-author-date",
> > +			 &opts.committer_date_is_author_date,
> > +			 N_("make committer date match author date")),
> 
> I guess it's good to do this for completeness but does
> rebase--preserver-merges.sh support --committer-date-is-author-date? It
> is the only caller of rebase--interactive I think so would be the only
> user of this code.

Oh! Yes, I did it for the completeness. Let's add the flag while we still have that _rebase--interactive_ command hanging out with us.

Show 8 quoted lines
> [...]
> 
> > +	if (read_author_script(rebase_path_author_script(),
> > +			       NULL, NULL, &date, 0))
> > +		die(_("failed to read author date"));
> 
> Can we have this return an error please - we try quite hard in the
> sequencer not to die in library code.

Yes, we can through an error and continue, but then the user will see the unchanged author date which is against his / her will but it will not crash the program at least.

Show 16 quoted lines
> [...]
> 
> > +	if (opts->committer_date_is_author_date) {
> > +		char *date = read_author_date_or_die();
> > +		argv_array_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", date);
> > +		free(date);
> > +	}
> 
> It's a shame to be doing this twice is slightly different ways in the
> same function (and again in try_to_commit() but I don't think that can
> be avoided as not all callers of run_git_commit() go through
> try_to_commit()). As I think the child inherits the current environment
> modified by cmd.env_array we could just call setenv() at the top of the
> function. It would be worth looking to see if it would be simpler to do
> the setenv() call in the loop that picks the commits, then we would
> avoid having to do it in do_merge() and try_to_commit() separately.

Ok, I'll have to change the code according to what Junio suggested. Let's see how this area will look after that.

Show 9 quoted lines
> [...]
> 
> > +		if (file_exists(rebase_path_cdate_is_adate())) {
> > +			opts->allow_ff = 0;
> 
> This is safe as we don't save the state of allow_ff for rebases so it
> wont be overridden later. It would be an idea to add to the checks in
> the assert() at the beginning of pick_commits() no we have another
> option that implies --force-rebase.
Are you suggesting to modify this assert() call (in pick_commits())?
    if (opts->allow_ff)
        assert(!(opts->signoff || opts->no_commit ||
                opts->record_origin || opts->edit));

Thanks Rohit

Phillip Wood· Jul 24, 2019, 13:33 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 2/2] rebase -i: support --committer-date-is-author-date

Hi Rohit
On 23/07/2019 20:57, Rohit Ashiwal wrote:
Show 34 quoted lines
> Hi Phillip
> 
> On Sat, 20 Jul 2019 15:56:50 +0100 Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>> [...]
>>
>>> @@ -467,6 +470,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
>>>    		OPT_BOOL(0, "autosquash", &opts.autosquash,
>>>    			 N_("move commits that begin with squash!/fixup!")),
>>>    		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
>>> +		OPT_BOOL(0, "committer-date-is-author-date",
>>> +			 &opts.committer_date_is_author_date,
>>> +			 N_("make committer date match author date")),
>>
>> I guess it's good to do this for completeness but does
>> rebase--preserver-merges.sh support --committer-date-is-author-date? It
>> is the only caller of rebase--interactive I think so would be the only
>> user of this code.
> 
> Oh! Yes, I did it for the completeness. Let's add the flag while we
> still have that _rebase--interactive_ command hanging out with us.
> 
>> [...]
>>
>>> +	if (read_author_script(rebase_path_author_script(),
>>> +			       NULL, NULL, &date, 0))
>>> +		die(_("failed to read author date"));
>>
>> Can we have this return an error please - we try quite hard in the
>> sequencer not to die in library code.
> 
> Yes, we can through an error and continue, but then the user will
> see the unchanged author date which is against his / her will but
> it will not crash the program at least.

I dont think it should continue, the error should be propagated up to cmd_rebase() (In your patch I think one of the context lines in run_git_commit() shows this happening)

Show 36 quoted lines
> 
>> [...]
>>
>>> +	if (opts->committer_date_is_author_date) {
>>> +		char *date = read_author_date_or_die();
>>> +		argv_array_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", date);
>>> +		free(date);
>>> +	}
>>
>> It's a shame to be doing this twice is slightly different ways in the
>> same function (and again in try_to_commit() but I don't think that can
>> be avoided as not all callers of run_git_commit() go through
>> try_to_commit()). As I think the child inherits the current environment
>> modified by cmd.env_array we could just call setenv() at the top of the
>> function. It would be worth looking to see if it would be simpler to do
>> the setenv() call in the loop that picks the commits, then we would
>> avoid having to do it in do_merge() and try_to_commit() separately.
> 
> Ok, I'll have to change the code according to what Junio suggested.
> Let's see how this area will look after that.
> 
>> [...]
>>
>>> +		if (file_exists(rebase_path_cdate_is_adate())) {
>>> +			opts->allow_ff = 0;
>>
>> This is safe as we don't save the state of allow_ff for rebases so it
>> wont be overridden later. It would be an idea to add to the checks in
>> the assert() at the beginning of pick_commits() no we have another
>> option that implies --force-rebase.
> 
> Are you suggesting to modify this assert() call (in pick_commits())?
> 
>      if (opts->allow_ff)
>          assert(!(opts->signoff || opts->no_commit ||
>                  opts->record_origin || opts->edit));
Yes I think it should check for opts->committer_date_is_author_date here
Best Wishes
Phillip
> Thanks
> Rohit
> 
Junio C Hamano· Jul 19, 2019, 21:26 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 0/2] rebase -i: support --committer-date-is-author-date

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
> Another revision to keep this thread alive.
And on top of what commit have these two been built on?
Junio C Hamano· Jul 19, 2019, 21:47 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH v2 0/2] rebase -i: support --committer-date-is-author-date

Junio C Hamano <gitster@pobox.com> writes:
Show 5 quoted lines
> Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
>
>> Another revision to keep this thread alive.
>
> And on top of what commit have these two been built on?

I had trouble figuring out where to base the other one, on top of which these two apply. Why are these split into separate topics?

Rohit Ashiwal· Jul 12, 2019, 18:53 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH 1/2] sequencer: add NULL checks under read_author_script

read_author_script reads name, email and author date from the author script. However, it does not check if the arguments are NULL. Adding NULL checks will allow us to selectively get the required value, for example:

    char *date;
    if (read_author_script(_path_, NULL, NULL, &date, _int_))
	    die(_("failed to read author date"));
    /* needs to be free()'d */
    return date;
Add NULL checks for better control over the information retrieved.
Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 sequencer.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
Show changes to sequencer.c +13 −3
diff --git a/sequencer.c b/sequencer.c
index f88a97fb1..a2d7b0925 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -821,9 +821,19 @@ int read_author_script(const char *path, char **name, char **email, char **date,
 		error(_("missing 'GIT_AUTHOR_DATE'"));
 	if (date_i < 0 || email_i < 0 || date_i < 0 || err)
 		goto finish;
-	*name = kv.items[name_i].util;
-	*email = kv.items[email_i].util;
-	*date = kv.items[date_i].util;
+
+	if (name)
+		*name = kv.items[name_i].util;
+	else
+		free(kv.items[name_i].util);
+	if (email)
+		*email = kv.items[email_i].util;
+	else
+		free(kv.items[email_i].util);
+	if (date)
+		*date = kv.items[date_i].util;
+	else
+		free(kv.items[date_i].util);
 	retval = 0;
 finish:
 	string_list_clear(&kv, !!retval);
-- 
2.21.0
Junio C Hamano· Jul 15, 2019, 18:04 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH 1/2] sequencer: add NULL checks under read_author_script

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
Show 13 quoted lines
> +	if (name)
> +		*name = kv.items[name_i].util;
> +	else
> +		free(kv.items[name_i].util);
> +	if (email)
> +		*email = kv.items[email_i].util;
> +	else
> +		free(kv.items[email_i].util);
> +	if (date)
> +		*date = kv.items[date_i].util;
> +	else
> +		free(kv.items[date_i].util);
>  	retval = 0;

Some of the .util field may have been freed and others may have taken possession by the caller at this point.

>  finish:
>  	string_list_clear(&kv, !!retval);

And we tell string_list_clear() not to free the .util field, so kv.items[].util won't leak, which is good.

Rohit Ashiwal· Jul 12, 2019, 18:53 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH 2/2] rebase -i: support --committer-date-is-author-date

rebase am already has this flag to "lie" about the committer date by changing it to the author date. Let's add the same for interactive machinery.

Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 Documentation/git-rebase.txt            |  7 ++--
 builtin/rebase.c                        | 23 ++++++++++---
 sequencer.c                             | 46 ++++++++++++++++++++++++-
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 15 ++++++++
 6 files changed, 84 insertions(+), 9 deletions(-)
Show changes to 6 files +84 −9

Documentation/git-rebase.txt, builtin/rebase.c, sequencer.c, sequencer.h, t/t3422-rebase-incompatible-options.sh, t/t3431-rebase-options-compatibility.sh

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index eda52ed82..ddd111de6 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -383,8 +383,12 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
 See also INCOMPATIBLE OPTIONS below.
 
 --committer-date-is-author-date::
+	Instead of recording the time the rebased commits are
+	created as the committer date, reuse the author date
+	as the committer date. This implies --force-rebase.
+
 --ignore-date::
-	These flags are passed to 'git am' to easily change the dates
+	This flag is passed to 'git am' to change the author date
 	of the rebased commits (see linkgit:git-am[1]).
 +
 See also INCOMPATIBLE OPTIONS below.
@@ -522,7 +526,6 @@ INCOMPATIBLE OPTIONS
 
 The following options:
 
- * --committer-date-is-author-date
  * --ignore-date
  * --whitespace
  * -C
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c55957c31..82a3f7743 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -82,6 +82,7 @@ struct rebase_options {
 	int ignore_whitespace;
 	char *gpg_sign_opt;
 	int autostash;
+	int committer_date_is_author_date;
 	char *cmd;
 	int allow_empty_message;
 	int rebase_merges, rebase_cousins;
@@ -113,6 +114,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 	replay.allow_empty_message = opts->allow_empty_message;
 	replay.verbose = opts->flags & REBASE_VERBOSE;
 	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
+	replay.committer_date_is_author_date =
+					opts->committer_date_is_author_date;
 	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
 	replay.strategy = opts->strategy;
 	if (opts->strategy_opts)
@@ -467,6 +470,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 		OPT_BOOL(0, "autosquash", &opts.autosquash,
 			 N_("move commits that begin with squash!/fixup!")),
 		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
+		OPT_BOOL(0, "committer-date-is-author-date",
+			 &opts.committer_date_is_author_date,
+			 N_("make committer date match author date")),
 		OPT_BIT('v', "verbose", &opts.flags,
 			N_("display a diffstat of what changed upstream"),
 			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
@@ -532,6 +538,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 		warning(_("--[no-]rebase-cousins has no effect without "
 			  "--rebase-merges"));
 
+	if (opts.committer_date_is_author_date)
+		opts.flags |= REBASE_FORCE;
+
 	return !!run_rebase_interactive(&opts, command);
 }
 
@@ -970,6 +979,8 @@ static int run_am(struct rebase_options *opts)
 
 	if (opts->ignore_whitespace)
 		argv_array_push(&am.args, "--ignore-whitespace");
+	if (opts->committer_date_is_author_date)
+		argv_array_push(&opts->git_am_opts, "--committer-date-is-author-date");
 	if (opts->action && !strcmp("continue", opts->action)) {
 		argv_array_push(&am.args, "--resolved");
 		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
@@ -1417,9 +1428,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
 		OPT_BOOL(0, "signoff", &options.signoff,
 			 N_("add a Signed-off-by: line to each commit")),
-		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
-				  &options.git_am_opts, NULL,
-				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
+		OPT_BOOL(0, "committer-date-is-author-date",
+			 &options.committer_date_is_author_date,
+			 N_("make committer date match author date")),
 		OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
@@ -1686,10 +1697,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		    state_dir_base, cmd_live_rebase, buf.buf);
 	}
 
+	if (options.committer_date_is_author_date)
+		options.flags |= REBASE_FORCE;
+
 	for (i = 0; i < options.git_am_opts.argc; i++) {
 		const char *option = options.git_am_opts.argv[i], *p;
-		if (!strcmp(option, "--committer-date-is-author-date") ||
-		    !strcmp(option, "--ignore-date") ||
+		if (!strcmp(option, "--ignore-date") ||
 		    !strcmp(option, "--whitespace=fix") ||
 		    !strcmp(option, "--whitespace=strip"))
 			options.flags |= REBASE_FORCE;
diff --git a/sequencer.c b/sequencer.c
index a2d7b0925..a65f01a42 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -147,6 +147,7 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
  * command-line.
  */
 static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
+static GIT_PATH_FUNC(rebase_path_cdate_is_adate, "rebase-merge/cdate_is_adate")
 static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
 static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
 static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
@@ -876,6 +877,18 @@ static char *get_author(const char *message)
 	return NULL;
 }
 
+/* Returns a "date" string that needs to be free()'d by the caller */
+static char *read_author_date_or_die(void)
+{
+	char *date;
+
+	if (read_author_script(rebase_path_author_script(),
+			       NULL, NULL, &date, 0))
+		die(_("failed to read author date"));
+
+	return date;
+}
+
 /* Read author-script and return an ident line (author <email> timestamp) */
 static const char *read_author_ident(struct strbuf *buf)
 {
@@ -986,10 +999,17 @@ static int run_git_commit(struct repository *r,
 
 		if (res <= 0)
 			res = error_errno(_("could not read '%s'"), defmsg);
-		else
+		else {
+			if (opts->committer_date_is_author_date) {
+				char *date = read_author_date_or_die();
+				setenv("GIT_COMMITTER_DATE", date, 1);
+				free(date);
+			}
+
 			res = commit_tree(msg.buf, msg.len, cache_tree_oid,
 					  NULL, &root_commit, author,
 					  opts->gpg_sign);
+		}
 
 		strbuf_release(&msg);
 		strbuf_release(&script);
@@ -1019,6 +1039,11 @@ static int run_git_commit(struct repository *r,
 		argv_array_push(&cmd.args, "--amend");
 	if (opts->gpg_sign)
 		argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
+	if (opts->committer_date_is_author_date) {
+		char *date = read_author_date_or_die();
+		argv_array_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", date);
+		free(date);
+	}
 	if (defmsg)
 		argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 	else if (!(flags & EDIT_MSG))
@@ -1467,6 +1492,12 @@ static int try_to_commit(struct repository *r,
 
 	reset_ident_date();
 
+	if (opts->committer_date_is_author_date) {
+		char *date = read_author_date_or_die();
+		setenv("GIT_COMMITTER_DATE", date, 1);
+		free(date);
+	}
+
 	if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
 				 oid, author, opts->gpg_sign, extra)) {
 		res = error(_("failed to write commit object"));
@@ -2538,6 +2569,11 @@ static int read_populate_opts(struct replay_opts *opts)
 			opts->signoff = 1;
 		}
 
+		if (file_exists(rebase_path_cdate_is_adate())) {
+			opts->allow_ff = 0;
+			opts->committer_date_is_author_date = 1;
+		}
+
 		if (file_exists(rebase_path_reschedule_failed_exec()))
 			opts->reschedule_failed_exec = 1;
 
@@ -2620,6 +2656,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
 		write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
 	if (opts->signoff)
 		write_file(rebase_path_signoff(), "--signoff\n");
+	if (opts->committer_date_is_author_date)
+		write_file(rebase_path_cdate_is_adate(), "%s", "");
 	if (opts->reschedule_failed_exec)
 		write_file(rebase_path_reschedule_failed_exec(), "%s", "");
 
@@ -3437,6 +3475,12 @@ static int do_merge(struct repository *r,
 		argv_array_push(&cmd.args, git_path_merge_msg(r));
 		if (opts->gpg_sign)
 			argv_array_push(&cmd.args, opts->gpg_sign);
+		if (opts->committer_date_is_author_date) {
+			char *date = read_author_date_or_die();
+			argv_array_pushf(&cmd.env_array,
+					 "GIT_COMMITTER_DATE=%s", date);
+			free(date);
+		}
 
 		/* Add the tips to be merged */
 		for (j = to_merge; j; j = j->next)
diff --git a/sequencer.h b/sequencer.h
index 303047a13..0cfe184fc 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -44,6 +44,7 @@ struct replay_opts {
 	int quiet;
 	int reschedule_failed_exec;
 	int ignore_whitespace;
+	int committer_date_is_author_date;
 
 	int mainline;
 
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4342f79ee..7402f7e3d 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -61,7 +61,6 @@ test_rebase_am_only () {
 }
 
 test_rebase_am_only --whitespace=fix
-test_rebase_am_only --committer-date-is-author-date
 test_rebase_am_only -C4
 
 test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
index f38ae6f5f..a06d55dfc 100755
--- a/t/t3431-rebase-options-compatibility.sh
+++ b/t/t3431-rebase-options-compatibility.sh
@@ -7,6 +7,9 @@ test_description='tests to ensure compatibility between am and interactive backe
 
 . ./test-lib.sh
 
+GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
+export GIT_AUTHOR_DATE
+
 # This is a special case in which both am and interactive backends
 # provide the same outputs. It was done intentionally because
 # --ignore-whitespace both the backends fall short of optimal
@@ -63,4 +66,16 @@ test_expect_success '--ignore-whitespace works with interactive backend' '
 	test_cmp expect file
 '
 
+test_expect_success '--committer-date-is-author-date works with interactive backend' '
+	git rebase -f HEAD^ &&
+	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
+	sed -ne "/^author /s/.*> //p" head >authortime &&
+	sed -ne "/^committer /s/.*> //p" head >committertime &&
+	! test_cmp at ct &&
+	git rebase -i --committer-date-is-author-date HEAD^ &&
+	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
+	sed -ne "/^committer /s/.*> //p" head >committertime &&
+	test_cmp authortime committertime
+'
+
 test_done
-- 
2.21.0
Rohit Ashiwal· Jul 14, 2019, 11:31 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH 2/2] rebase -i: support --committer-date-is-author-date

On Sat, 13 Jul 2019 00:23:57 +0530 Rohit Ashiwal <rohit.ashiwal265@gmail.com> wrote:
Show 14 quoted lines
>
> rebase am already has this flag to "lie" about the committer date
> by changing it to the author date. Let's add the same for
> interactive machinery.
>
> Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
> [...]
>
> +test_expect_success '--committer-date-is-author-date works with interactive backend' '
> +	git rebase -f HEAD^ &&
> +	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
> +	sed -ne "/^author /s/.*> //p" head >authortime &&
> +	sed -ne "/^committer /s/.*> //p" head >committertime &&
> +	! test_cmp at ct &&
                   ^~~^~~~~
Slight mistake in changing names, I'll change it after a full review,
please ignore till then.
Show 5 quoted lines
> +	git rebase -i --committer-date-is-author-date HEAD^ &&
> +	git cat-file commit HEAD | sed -e "/^\$/q" >head &&
> +	sed -ne "/^committer /s/.*> //p" head >committertime &&
> +	test_cmp authortime committertime
> +'

We should also include a test for am backend since the flag is not directly passed to the am after the application of this patch.

> +
>  test_done
> --
> 2.21.0

Best Rohit

Rohit Ashiwal· Jul 18, 2019, 18:55 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v2 0/1] rebase -i: support --ignore-whitespace

Another revision to keep the thread alive.
Rohit Ashiwal (1):
  rebase -i: add --ignore-whitespace flag
 Documentation/git-rebase.txt            |  9 +++-
 builtin/rebase.c                        | 26 ++++++++--
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
 5 files changed, 97 insertions(+), 6 deletions(-)
 create mode 100755 t/t3431-rebase-options-compatibility.sh
Range-diff:
1:  7dda0de288 ! 1:  a1bb91fe43 rebase -i: add --ignore-whitespace flag
    @@ -85,6 +85,15 @@
      		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
      			   N_("rebase strategy")),
      		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
    +@@
    + 	argc = parse_options(argc, argv, NULL, options,
    + 			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
    + 
    ++	opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
    ++
    + 	if (!is_null_oid(&squash_onto))
    + 		opts.squash_onto = &squash_onto;
    + 
     @@
      	am.git_cmd = 1;
      	argv_array_push(&am.args, "am");
-- 
2.21.0
Rohit Ashiwal· Jul 18, 2019, 18:55 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

There are two backends available for rebasing, viz, the am and the interactive. Naturally, there shall be some features that are implemented in one but not in the other. One such flag is --ignore-whitespace which indicates merge mechanism to treat lines with only whitespace changes as unchanged. Wire the interactive rebase to also understand the --ignore-whitespace flag by translating it to -Xignore-space-change.

Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 Documentation/git-rebase.txt            |  9 +++-
 builtin/rebase.c                        | 26 ++++++++--
 sequencer.h                             |  1 +
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
 5 files changed, 97 insertions(+), 6 deletions(-)
 create mode 100755 t/t3431-rebase-options-compatibility.sh
Show changes to 5 files +97 −6

Documentation/git-rebase.txt, builtin/rebase.c, sequencer.h, t/t3422-rebase-incompatible-options.sh, t/t3431-rebase-options-compatibility.sh

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5e4e927647..eda52ed824 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -371,8 +371,13 @@ If either <upstream> or --root is given on the command line, then the
 default is `--no-fork-point`, otherwise the default is `--fork-point`.
 
 --ignore-whitespace::
+	This flag is either passed to the 'git apply' program
+	(see linkgit:git-apply[1]), or to 'git merge' program
+	(see linkgit:git-merge[1]) as `-Xignore-space-change`,
+	depending on which backend is selected by other options.
+
 --whitespace=<option>::
-	These flag are passed to the 'git apply' program
+	This flag is passed to the 'git apply' program
 	(see linkgit:git-apply[1]) that applies the patch.
 +
 See also INCOMPATIBLE OPTIONS below.
@@ -520,7 +525,6 @@ The following options:
  * --committer-date-is-author-date
  * --ignore-date
  * --whitespace
- * --ignore-whitespace
  * -C
 
 are incompatible with the following options:
@@ -543,6 +547,7 @@ In addition, the following pairs of options are incompatible:
  * --preserve-merges and --interactive
  * --preserve-merges and --signoff
  * --preserve-merges and --rebase-merges
+ * --rebase-merges and --ignore-whitespace
  * --rebase-merges and --strategy
  * --rebase-merges and --strategy-option
 
diff --git a/builtin/rebase.c b/builtin/rebase.c
index db6ca9bd7d..afe376c3fe 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,7 @@ struct rebase_options {
 	int allow_rerere_autoupdate;
 	int keep_empty;
 	int autosquash;
+	int ignore_whitespace;
 	char *gpg_sign_opt;
 	int autostash;
 	char *cmd;
@@ -376,6 +377,17 @@ static int run_rebase_interactive(struct rebase_options *opts,
 	flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
 	flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
 
+	if (opts->ignore_whitespace) {
+		struct strbuf buf = STRBUF_INIT;
+
+		if (opts->strategy_opts)
+			strbuf_addstr(&buf, opts->strategy_opts);
+
+		strbuf_addstr(&buf, " --ignore-space-change");
+		free(opts->strategy_opts);
+		opts->strategy_opts = strbuf_detach(&buf, NULL);
+	}
+
 	switch (command) {
 	case ACTION_NONE: {
 		if (!opts->onto && !opts->upstream)
@@ -489,6 +501,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
 			N_("GPG-sign commits"),
 			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
+			 N_("ignore changes in whitespace")),
 		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
 			   N_("rebase strategy")),
 		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
@@ -511,6 +525,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, NULL, options,
 			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
 
+	opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
+
 	if (!is_null_oid(&squash_onto))
 		opts.squash_onto = &squash_onto;
 
@@ -954,6 +970,8 @@ static int run_am(struct rebase_options *opts)
 	am.git_cmd = 1;
 	argv_array_push(&am.args, "am");
 
+	if (opts->ignore_whitespace)
+		argv_array_push(&am.args, "--ignore-whitespace");
 	if (opts->action && !strcmp("continue", opts->action)) {
 		argv_array_push(&am.args, "--resolved");
 		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
@@ -1401,9 +1419,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
 		OPT_BOOL(0, "signoff", &options.signoff,
 			 N_("add a Signed-off-by: line to each commit")),
-		OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
-				  NULL, N_("passed to 'git am'"),
-				  PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
 				  &options.git_am_opts, NULL,
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
@@ -1411,6 +1426,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
 				  N_("passed to 'git apply'"), 0),
+		OPT_BOOL(0, "ignore-whitespace", &options.ignore_whitespace,
+			 N_("ignore changes in whitespace")),
 		OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
 				  N_("action"), N_("passed to 'git apply'"), 0),
 		OPT_BIT('f', "force-rebase", &options.flags,
@@ -1821,6 +1838,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	}
 
 	if (options.rebase_merges) {
+		if (options.ignore_whitespace)
+			die(_("cannot combine '--rebase-merges' with "
+			      "'--ignore-whitespace'"));
 		if (strategy_options.nr)
 			die(_("cannot combine '--rebase-merges' with "
 			      "'--strategy-option'"));
diff --git a/sequencer.h b/sequencer.h
index 0c494b83d4..303047a133 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -43,6 +43,7 @@ struct replay_opts {
 	int verbose;
 	int quiet;
 	int reschedule_failed_exec;
+	int ignore_whitespace;
 
 	int mainline;
 
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index a5868ea152..4342f79eea 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -61,7 +61,6 @@ test_rebase_am_only () {
 }
 
 test_rebase_am_only --whitespace=fix
-test_rebase_am_only --ignore-whitespace
 test_rebase_am_only --committer-date-is-author-date
 test_rebase_am_only -C4
 
diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
new file mode 100755
index 0000000000..f38ae6f5fc
--- /dev/null
+++ b/t/t3431-rebase-options-compatibility.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Rohit Ashiwal
+#
+
+test_description='tests to ensure compatibility between am and interactive backends'
+
+. ./test-lib.sh
+
+# This is a special case in which both am and interactive backends
+# provide the same outputs. It was done intentionally because
+# --ignore-whitespace both the backends fall short of optimal
+# behaviour.
+test_expect_success 'setup' '
+	git checkout -b topic &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	Qline 2
+	line 3
+	EOF
+	git add file &&
+	git commit -m "add file" &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	git commit -am "update file" &&
+	git tag side &&
+
+	git checkout --orphan master &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	        line 2
+	line 3
+	EOF
+	git add file &&
+	git commit -m "add file" &&
+	git tag main
+'
+
+test_expect_success '--ignore-whitespace works with am backend' '
+	cat >expect <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	test_must_fail git rebase main side &&
+	git rebase --abort &&
+	git rebase --ignore-whitespace main side &&
+	test_cmp expect file
+'
+
+test_expect_success '--ignore-whitespace works with interactive backend' '
+	cat >expect <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	test_must_fail git rebase --merge main side &&
+	git rebase --abort &&
+	git rebase --merge --ignore-whitespace main side &&
+	test_cmp expect file
+'
+
+test_done
-- 
2.21.0
Junio C Hamano· Jul 19, 2019, 21:33 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
>  t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
Isn't 3431 already taken?
Show 9 quoted lines
>  5 files changed, 97 insertions(+), 6 deletions(-)
>  create mode 100755 t/t3431-rebase-options-compatibility.sh
> ...
> +	git checkout --orphan master &&
> +	q_to_tab >file <<-EOF &&
> +	line 1
> +	        line 2
> +	line 3
> +	EOF

This will trigger "indent-with-space". Instead of using q-to-tab, perhaps something like this would be more appropriate (not limited to this piece, but also other ones in this script that actually do use Q to visualize a tab)?

	sed -e "s/^|//" >file <<-EOF &&
	|line 1
	|        line 2
	|line 3
	EOF
Rohit Ashiwal· Jul 23, 2019, 19:59 UTC · re: Junio C Hamano · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

Hi Junio
On Fri, 19 Jul 2019 14:33:49 -0700 Junio C Hamano <gitster@pobox.com> wrote:
> 
> >  t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
> 
> Isn't 3431 already taken?
It is not in git/git[1].
Show 21 quoted lines
> >  5 files changed, 97 insertions(+), 6 deletions(-)
> >  create mode 100755 t/t3431-rebase-options-compatibility.sh
> > ...
> > +	git checkout --orphan master &&
> > +	q_to_tab >file <<-EOF &&
> > +	line 1
> > +	        line 2
> > +	line 3
> > +	EOF
> 
> This will trigger "indent-with-space".  Instead of using q-to-tab,
> perhaps something like this would be more appropriate (not limited
> to this piece, but also other ones in this script that actually do
> use Q to visualize a tab)?
> 
> 	sed -e "s/^|//" >file <<-EOF &&
> 	|line 1
> 	|        line 2
> 	|line 3
> 	EOF
> 
Oh! My mistake, I should have used cat instead.

Thanks Rohit

[1]: https://github.com/git/git/tree/master/t
Junio C Hamano· Jul 23, 2019, 20:57 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

Rohit Ashiwal <rohit.ashiwal265@gmail.com> writes:
Show 9 quoted lines
> Hi Junio
>
> On Fri, 19 Jul 2019 14:33:49 -0700 Junio C Hamano <gitster@pobox.com> wrote:
>> 
>> >  t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
>> 
>> Isn't 3431 already taken?
>
> It is not in git/git[1].

That's a very selfish attitude ;-) Pay attention to what others are doing while you are working on this topic (hint: do not limit your check to 'master', but make sure your changes also work well when merged to 'next' and 'pu'---make a trial merge or two and run test suite, for example).

Show 20 quoted lines
>> > +	git checkout --orphan master &&
>> > +	q_to_tab >file <<-EOF &&
>> > +	line 1
>> > +	        line 2
>> > +	line 3
>> > +	EOF
>> 
>> This will trigger "indent-with-space".  Instead of using q-to-tab,
>> perhaps something like this would be more appropriate (not limited
>> to this piece, but also other ones in this script that actually do
>> use Q to visualize a tab)?
>> 
>> 	sed -e "s/^|//" >file <<-EOF &&
>> 	|line 1
>> 	|        line 2
>> 	|line 3
>> 	EOF
>> 
>
> Oh! My mistake, I should have used cat instead.

I am not sure how you would avoid indent-with-space with "cat". With the use of "sed" like I showed above to spell the left margin explicitly out, we can---I am not saying it's the only way, but I do not think of a clean way to do the same with "cat".

Phillip Wood· Jul 22, 2019, 10:00 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

Hi Rohit,

It's good to see another patch reducing the differences between the rebase back ends.

On 18/07/2019 19:55, Rohit Ashiwal wrote:
Show 80 quoted lines
> There are two backends available for rebasing, viz, the am and the
> interactive. Naturally, there shall be some features that are
> implemented in one but not in the other. One such flag is
> --ignore-whitespace which indicates merge mechanism to treat lines
> with only whitespace changes as unchanged. Wire the interactive
> rebase to also understand the --ignore-whitespace flag by
> translating it to -Xignore-space-change.
> 
> Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
> ---
>  Documentation/git-rebase.txt            |  9 +++-
>  builtin/rebase.c                        | 26 ++++++++--
>  sequencer.h                             |  1 +
>  t/t3422-rebase-incompatible-options.sh  |  1 -
>  t/t3431-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
>  5 files changed, 97 insertions(+), 6 deletions(-)
>  create mode 100755 t/t3431-rebase-options-compatibility.sh
> 
> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index 5e4e927647..eda52ed824 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -371,8 +371,13 @@ If either <upstream> or --root is given on the command line, then the
>  default is `--no-fork-point`, otherwise the default is `--fork-point`.
>  
>  --ignore-whitespace::
> +	This flag is either passed to the 'git apply' program
> +	(see linkgit:git-apply[1]), or to 'git merge' program
> +	(see linkgit:git-merge[1]) as `-Xignore-space-change`,
> +	depending on which backend is selected by other options.
> +
>  --whitespace=<option>::
> -	These flag are passed to the 'git apply' program
> +	This flag is passed to the 'git apply' program
>  	(see linkgit:git-apply[1]) that applies the patch.
>  +
>  See also INCOMPATIBLE OPTIONS below.
> @@ -520,7 +525,6 @@ The following options:
>   * --committer-date-is-author-date
>   * --ignore-date
>   * --whitespace
> - * --ignore-whitespace
>   * -C
>  
>  are incompatible with the following options:
> @@ -543,6 +547,7 @@ In addition, the following pairs of options are incompatible:
>   * --preserve-merges and --interactive
>   * --preserve-merges and --signoff
>   * --preserve-merges and --rebase-merges
> + * --rebase-merges and --ignore-whitespace
>   * --rebase-merges and --strategy
>   * --rebase-merges and --strategy-option
>  
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index db6ca9bd7d..afe376c3fe 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -79,6 +79,7 @@ struct rebase_options {
>  	int allow_rerere_autoupdate;
>  	int keep_empty;
>  	int autosquash;
> +	int ignore_whitespace;
>  	char *gpg_sign_opt;
>  	int autostash;
>  	char *cmd;
> @@ -376,6 +377,17 @@ static int run_rebase_interactive(struct rebase_options *opts,
>  	flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
>  	flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
>  
> +	if (opts->ignore_whitespace) {
> +		struct strbuf buf = STRBUF_INIT;
> +
> +		if (opts->strategy_opts)
> +			strbuf_addstr(&buf, opts->strategy_opts);
> +
> +		strbuf_addstr(&buf, " --ignore-space-change");
> +		free(opts->strategy_opts);
> +		opts->strategy_opts = strbuf_detach(&buf, NULL);
> +	}
> +
I think this would fit better in get_replay_opts()
Show 9 quoted lines
>  	switch (command) {
>  	case ACTION_NONE: {
>  		if (!opts->onto && !opts->upstream)
> @@ -489,6 +501,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
>  		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
>  			N_("GPG-sign commits"),
>  			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> +		OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
> +			 N_("ignore changes in whitespace")),

As with the other patch is this actually going to be used by rebase--preserve-merges.sh?

Show 47 quoted lines
>  		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
>  			   N_("rebase strategy")),
>  		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
> @@ -511,6 +525,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
>  	argc = parse_options(argc, argv, NULL, options,
>  			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
>  
> +	opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
> +
>  	if (!is_null_oid(&squash_onto))
>  		opts.squash_onto = &squash_onto;
>  
> @@ -954,6 +970,8 @@ static int run_am(struct rebase_options *opts)
>  	am.git_cmd = 1;
>  	argv_array_push(&am.args, "am");
>  
> +	if (opts->ignore_whitespace)
> +		argv_array_push(&am.args, "--ignore-whitespace");
>  	if (opts->action && !strcmp("continue", opts->action)) {
>  		argv_array_push(&am.args, "--resolved");
>  		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
> @@ -1401,9 +1419,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
>  		OPT_BOOL(0, "signoff", &options.signoff,
>  			 N_("add a Signed-off-by: line to each commit")),
> -		OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
> -				  NULL, N_("passed to 'git am'"),
> -				  PARSE_OPT_NOARG),
>  		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
>  				  &options.git_am_opts, NULL,
>  				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
> @@ -1411,6 +1426,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
>  		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
>  				  N_("passed to 'git apply'"), 0),
> +		OPT_BOOL(0, "ignore-whitespace", &options.ignore_whitespace,
> +			 N_("ignore changes in whitespace")),
>  		OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
>  				  N_("action"), N_("passed to 'git apply'"), 0),
>  		OPT_BIT('f', "force-rebase", &options.flags,
> @@ -1821,6 +1838,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>  	}
>  
>  	if (options.rebase_merges) {
> +		if (options.ignore_whitespace)
> +			die(_("cannot combine '--rebase-merges' with "
> +			      "'--ignore-whitespace'"));

I was going to ask why we cannot just pass -Xignore-space-change when making the merge but the lines below seem to show we don't support merge options yet.

Show 12 quoted lines
>  		if (strategy_options.nr)
>  			die(_("cannot combine '--rebase-merges' with "
>  			      "'--strategy-option'"));
> diff --git a/sequencer.h b/sequencer.h
> index 0c494b83d4..303047a133 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -43,6 +43,7 @@ struct replay_opts {
>  	int verbose;
>  	int quiet;
>  	int reschedule_failed_exec;
> +	int ignore_whitespace;

Is this new field used anywhere - we add -Xignore-space-change to replay_opts.xopts so why do we need this as well?

Best Wishes
Phillip
Show 88 quoted lines
>  
>  	int mainline;
>  
> diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
> index a5868ea152..4342f79eea 100755
> --- a/t/t3422-rebase-incompatible-options.sh
> +++ b/t/t3422-rebase-incompatible-options.sh
> @@ -61,7 +61,6 @@ test_rebase_am_only () {
>  }
>  
>  test_rebase_am_only --whitespace=fix
> -test_rebase_am_only --ignore-whitespace
>  test_rebase_am_only --committer-date-is-author-date
>  test_rebase_am_only -C4
>  
> diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
> new file mode 100755
> index 0000000000..f38ae6f5fc
> --- /dev/null
> +++ b/t/t3431-rebase-options-compatibility.sh
> @@ -0,0 +1,66 @@
> +#!/bin/sh
> +#
> +# Copyright (c) 2019 Rohit Ashiwal
> +#
> +
> +test_description='tests to ensure compatibility between am and interactive backends'
> +
> +. ./test-lib.sh
> +
> +# This is a special case in which both am and interactive backends
> +# provide the same outputs. It was done intentionally because
> +# --ignore-whitespace both the backends fall short of optimal
> +# behaviour.
> +test_expect_success 'setup' '
> +	git checkout -b topic &&
> +	q_to_tab >file <<-EOF &&
> +	line 1
> +	Qline 2
> +	line 3
> +	EOF
> +	git add file &&
> +	git commit -m "add file" &&
> +	q_to_tab >file <<-EOF &&
> +	line 1
> +	new line 2
> +	line 3
> +	EOF
> +	git commit -am "update file" &&
> +	git tag side &&
> +
> +	git checkout --orphan master &&
> +	q_to_tab >file <<-EOF &&
> +	line 1
> +	        line 2
> +	line 3
> +	EOF
> +	git add file &&
> +	git commit -m "add file" &&
> +	git tag main
> +'
> +
> +test_expect_success '--ignore-whitespace works with am backend' '
> +	cat >expect <<-EOF &&
> +	line 1
> +	new line 2
> +	line 3
> +	EOF
> +	test_must_fail git rebase main side &&
> +	git rebase --abort &&
> +	git rebase --ignore-whitespace main side &&
> +	test_cmp expect file
> +'
> +
> +test_expect_success '--ignore-whitespace works with interactive backend' '
> +	cat >expect <<-EOF &&
> +	line 1
> +	new line 2
> +	line 3
> +	EOF
> +	test_must_fail git rebase --merge main side &&
> +	git rebase --abort &&
> +	git rebase --merge --ignore-whitespace main side &&
> +	test_cmp expect file
> +'
> +
> +test_done
> 
Rohit Ashiwal· Jul 23, 2019, 19:58 UTC · re: Phillip Wood · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

Hi Phillip
On Mon, 22 Jul 2019 11:00:40 +0100 Phillip Wood <phillip.wood123@gmail.com> wrote:
Show 16 quoted lines
> 
> [...]
> >
> > +	if (opts->ignore_whitespace) {
> > +		struct strbuf buf = STRBUF_INIT;
> > +
> > +		if (opts->strategy_opts)
> > +			strbuf_addstr(&buf, opts->strategy_opts);
> > +
> > +		strbuf_addstr(&buf, " --ignore-space-change");
> > +		free(opts->strategy_opts);
> > +		opts->strategy_opts = strbuf_detach(&buf, NULL);
> > +	}
> > +
> 
> I think this would fit better in get_replay_opts()
Agreed, I'll move this to get_replay_opts().
Show 11 quoted lines
> [...]
> 
> > @@ -489,6 +501,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
> >  		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
> >  			N_("GPG-sign commits"),
> >  			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> > +		OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
> > +			 N_("ignore changes in whitespace")),
> 
> As with the other patch is this actually going to be used by
> rebase--preserve-merges.sh?

I added this just for the completness. Is there any discussion on dropping rebase--interactive as command and may be lib'fying it while deprecating rebase--preserve-merges?

Show 10 quoted lines
> [...]
> 
> > @@ -43,6 +43,7 @@ struct replay_opts {
> >  	int verbose;
> >  	int quiet;
> >  	int reschedule_failed_exec;
> > +	int ignore_whitespace;
> 
> Is this new field used anywhere - we add -Xignore-space-change to
> replay_opts.xopts so why do we need this as well?

Ah! I just realised cmd_rebase__interactive use rebase_options and not replay_options. I too think this field is not required.

Thanks Rohit

Elijah Newren· Jul 23, 2019, 21:01 UTC · re: Rohit Ashiwal · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

On Tue, Jul 23, 2019 at 1:01 PM Rohit Ashiwal <rohit.ashiwal265@gmail.com> wrote:

Show 16 quoted lines
> On Mon, 22 Jul 2019 11:00:40 +0100 Phillip Wood <phillip.wood123@gmail.com> wrote:
> > [...]
> >
> > > @@ -489,6 +501,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
> > >             { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
> > >                     N_("GPG-sign commits"),
> > >                     PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> > > +           OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
> > > +                    N_("ignore changes in whitespace")),
> >
> > As with the other patch is this actually going to be used by
> > rebase--preserve-merges.sh?
>
> I added this just for the completness. Is there any discussion on
> dropping rebase--interactive as command and may be lib'fying it while
> deprecating rebase--preserve-merges?
preserve-merges is already deprecated, see
https://public-inbox.org/git/pull.158.git.gitgitgadget@gmail.com/ and
the output of:
   git grep deprecated -- '*rebase*'

It's also instructive to take a look at https://public-inbox.org/git/xmqqk1rrm8ic.fsf@gitster-ct.c.googlers.com/, which talks about how git-rebase--preserve-merges.sh came to be; from reading that, it looks like the whole point of making rebase--preserve-merges.sh a separate script was to avoid the effort needed to libify it. As such, you probably just want to avoid breaking it or even changing it at all until it can be deleted. Anything that only the preserve-rebases backend uses (such as direct invocations of rebase--interactive, according to what Phillip said elsewhere in this thread), are probably better left alone as much as possible, with us instead documenting that preserve-merges lacks any new capabilities you are otherwise adding to rebase.

Not sure if that answers all your questions, though.
Johannes Schindelin· Jul 24, 2019, 11:14 UTC · re: Elijah Newren · lore

Re: [GSoC][PATCH v2 1/1] rebase -i: add --ignore-whitespace flag

Hi Elijah & Rohit,
On Tue, 23 Jul 2019, Elijah Newren wrote:
Show 36 quoted lines
> On Tue, Jul 23, 2019 at 1:01 PM Rohit Ashiwal
> <rohit.ashiwal265@gmail.com> wrote:
> > On Mon, 22 Jul 2019 11:00:40 +0100 Phillip Wood <phillip.wood123@gmail.com> wrote:
> > > [...]
> > >
> > > > @@ -489,6 +501,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
> > > >             { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
> > > >                     N_("GPG-sign commits"),
> > > >                     PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> > > > +           OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
> > > > +                    N_("ignore changes in whitespace")),
> > >
> > > As with the other patch is this actually going to be used by
> > > rebase--preserve-merges.sh?
> >
> > I added this just for the completness. Is there any discussion on
> > dropping rebase--interactive as command and may be lib'fying it while
> > deprecating rebase--preserve-merges?
>
> preserve-merges is already deprecated, see
> https://public-inbox.org/git/pull.158.git.gitgitgadget@gmail.com/ and
> the output of:
>    git grep deprecated -- '*rebase*'
>
> It's also instructive to take a look at
> https://public-inbox.org/git/xmqqk1rrm8ic.fsf@gitster-ct.c.googlers.com/,
> which talks about how git-rebase--preserve-merges.sh came to be; from
> reading that, it looks like the whole point of making
> rebase--preserve-merges.sh a separate script was to avoid the effort
> needed to libify it.  As such, you probably just want to avoid
> breaking it or even changing it at all until it can be deleted.
> Anything that only the preserve-rebases backend uses (such as direct
> invocations of rebase--interactive, according to what Phillip said
> elsewhere in this thread), are probably better left alone as much as
> possible, with us instead documenting that preserve-merges lacks any
> new capabilities you are otherwise adding to rebase.

Indeed, libifying or even as much as enhancing it is a lot of love lost at this stage.

> Not sure if that answers all your questions, though.

I am sure that Rohit has a lot more questions that are not answered by this email (maybe even "What is the meaning of life?"), but one particular (not quite asked) question that I would like to answer here is: what does it take to actually drop `--preserve-merges`?

To answer that question, I prepared the `drop-rebase-p` branch at https://github.com/dscho/git/commits/drop-rebase-p, essentially dropping `git-rebase--preserve--merges.sh` and addressing the fall-out.

The biggest issue seems to be `--rebase-merges`' lack of support for merge strategies other than the default, recursive merge one. To address that, I implemented this patch: https://github.com/dscho/git/commit/afa39b4fbb3ef5a53ccb9ae1f4376be87f570110

There are a couple other patches that need to broken out into their own patch series, I just did not get to that yet.

Ciao, Dscho

Rohit Ashiwal· Jul 24, 2019, 21:18 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v3 0/1] rebase -i: add --ignore-whitespace flag

This revision removes --ignore-whitespace from rebase--interactive since its only caller preserve-merges is now deprecated. Also rename t3431 to t3433.

Rohit Ashiwal (1):
  rebase -i: add --ignore-whitespace flag
 Documentation/git-rebase.txt            | 10 +++-
 builtin/rebase.c                        | 26 ++++++++--
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3433-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
 4 files changed, 96 insertions(+), 7 deletions(-)
 create mode 100755 t/t3433-rebase-options-compatibility.sh
Range-diff:
1:  a1bb91fe43 ! 1:  eef6f6fa25 rebase -i: add --ignore-whitespace flag
    @@ -42,6 +42,7 @@
       * --preserve-merges and --interactive
       * --preserve-merges and --signoff
       * --preserve-merges and --rebase-merges
    ++ * --preserve-merges and --ignore-whitespace
     + * --rebase-merges and --ignore-whitespace
       * --rebase-merges and --strategy
       * --rebase-merges and --strategy-option
    @@ -59,9 +60,19 @@
      	int autostash;
      	char *cmd;
     @@
    - 	flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
    - 	flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
    + 		.git_format_patch_opt = STRBUF_INIT	\
    + 	}
    + 
    +-static struct replay_opts get_replay_opts(const struct rebase_options *opts)
    ++static struct replay_opts get_replay_opts(struct rebase_options *opts)
    + {
    + 	struct replay_opts replay = REPLAY_OPTS_INIT;
      
    +@@
    + 	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
    + 	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
    + 	replay.strategy = opts->strategy;
    ++
     +	if (opts->ignore_whitespace) {
     +		struct strbuf buf = STRBUF_INIT;
     +
    @@ -72,19 +83,9 @@
     +		free(opts->strategy_opts);
     +		opts->strategy_opts = strbuf_detach(&buf, NULL);
     +	}
    -+
    - 	switch (command) {
    - 	case ACTION_NONE: {
    - 		if (!opts->onto && !opts->upstream)
    -@@
    - 		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
    - 			N_("GPG-sign commits"),
    - 			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
    -+		OPT_BOOL(0, "ignore-whitespace", &opts.ignore_whitespace,
    -+			 N_("ignore changes in whitespace")),
    - 		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
    - 			   N_("rebase strategy")),
    - 		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
    + 	if (opts->strategy_opts)
    + 		parse_strategy_opts(&replay, opts->strategy_opts);
    + 
     @@
      	argc = parse_options(argc, argv, NULL, options,
      			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
    @@ -133,18 +134,6 @@
      			die(_("cannot combine '--rebase-merges' with "
      			      "'--strategy-option'"));
     
    - diff --git a/sequencer.h b/sequencer.h
    - --- a/sequencer.h
    - +++ b/sequencer.h
    -@@
    - 	int verbose;
    - 	int quiet;
    - 	int reschedule_failed_exec;
    -+	int ignore_whitespace;
    - 
    - 	int mainline;
    - 
    -
      diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
      --- a/t/t3422-rebase-incompatible-options.sh
      +++ b/t/t3422-rebase-incompatible-options.sh
    @@ -157,10 +146,10 @@
      test_rebase_am_only -C4
      
     
    - diff --git a/t/t3431-rebase-options-compatibility.sh b/t/t3431-rebase-options-compatibility.sh
    + diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
      new file mode 100755
      --- /dev/null
    - +++ b/t/t3431-rebase-options-compatibility.sh
    + +++ b/t/t3433-rebase-options-compatibility.sh
     @@
     +#!/bin/sh
     +#
    @@ -184,7 +173,7 @@
     +	EOF
     +	git add file &&
     +	git commit -m "add file" &&
    -+	q_to_tab >file <<-EOF &&
    ++	cat >file <<-EOF &&
     +	line 1
     +	new line 2
     +	line 3
    @@ -193,7 +182,7 @@
     +	git tag side &&
     +
     +	git checkout --orphan master &&
    -+	q_to_tab >file <<-EOF &&
    ++	cat >file <<-EOF &&
     +	line 1
     +	        line 2
     +	line 3
-- 
2.21.0
Rohit Ashiwal· Jul 24, 2019, 21:18 UTC · re: Rohit Ashiwal · lore

[GSoC][PATCH v3 1/1] rebase -i: add --ignore-whitespace flag

There are two backends available for rebasing, viz, the am and the interactive. Naturally, there shall be some features that are implemented in one but not in the other. One such flag is --ignore-whitespace which indicates merge mechanism to treat lines with only whitespace changes as unchanged. Wire the interactive rebase to also understand the --ignore-whitespace flag by translating it to -Xignore-space-change.

Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@gmail.com>
---
 Documentation/git-rebase.txt            | 10 +++-
 builtin/rebase.c                        | 26 ++++++++--
 t/t3422-rebase-incompatible-options.sh  |  1 -
 t/t3433-rebase-options-compatibility.sh | 66 +++++++++++++++++++++++++
 4 files changed, 96 insertions(+), 7 deletions(-)
 create mode 100755 t/t3433-rebase-options-compatibility.sh
Show changes to 4 files +96 −7

Documentation/git-rebase.txt, builtin/rebase.c, t/t3422-rebase-incompatible-options.sh, t/t3433-rebase-options-compatibility.sh

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5e4e927647..85404fea52 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -371,8 +371,13 @@ If either <upstream> or --root is given on the command line, then the
 default is `--no-fork-point`, otherwise the default is `--fork-point`.
 
 --ignore-whitespace::
+	This flag is either passed to the 'git apply' program
+	(see linkgit:git-apply[1]), or to 'git merge' program
+	(see linkgit:git-merge[1]) as `-Xignore-space-change`,
+	depending on which backend is selected by other options.
+
 --whitespace=<option>::
-	These flag are passed to the 'git apply' program
+	This flag is passed to the 'git apply' program
 	(see linkgit:git-apply[1]) that applies the patch.
 +
 See also INCOMPATIBLE OPTIONS below.
@@ -520,7 +525,6 @@ The following options:
  * --committer-date-is-author-date
  * --ignore-date
  * --whitespace
- * --ignore-whitespace
  * -C
 
 are incompatible with the following options:
@@ -543,6 +547,8 @@ In addition, the following pairs of options are incompatible:
  * --preserve-merges and --interactive
  * --preserve-merges and --signoff
  * --preserve-merges and --rebase-merges
+ * --preserve-merges and --ignore-whitespace
+ * --rebase-merges and --ignore-whitespace
  * --rebase-merges and --strategy
  * --rebase-merges and --strategy-option
 
diff --git a/builtin/rebase.c b/builtin/rebase.c
index db6ca9bd7d..3c195ddc73 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,7 @@ struct rebase_options {
 	int allow_rerere_autoupdate;
 	int keep_empty;
 	int autosquash;
+	int ignore_whitespace;
 	char *gpg_sign_opt;
 	int autostash;
 	char *cmd;
@@ -97,7 +98,7 @@ struct rebase_options {
 		.git_format_patch_opt = STRBUF_INIT	\
 	}
 
-static struct replay_opts get_replay_opts(const struct rebase_options *opts)
+static struct replay_opts get_replay_opts(struct rebase_options *opts)
 {
 	struct replay_opts replay = REPLAY_OPTS_INIT;
 
@@ -114,6 +115,17 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
 	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
 	replay.strategy = opts->strategy;
+
+	if (opts->ignore_whitespace) {
+		struct strbuf buf = STRBUF_INIT;
+
+		if (opts->strategy_opts)
+			strbuf_addstr(&buf, opts->strategy_opts);
+
+		strbuf_addstr(&buf, " --ignore-space-change");
+		free(opts->strategy_opts);
+		opts->strategy_opts = strbuf_detach(&buf, NULL);
+	}
 	if (opts->strategy_opts)
 		parse_strategy_opts(&replay, opts->strategy_opts);
 
@@ -511,6 +523,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, NULL, options,
 			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
 
+	opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
+
 	if (!is_null_oid(&squash_onto))
 		opts.squash_onto = &squash_onto;
 
@@ -954,6 +968,8 @@ static int run_am(struct rebase_options *opts)
 	am.git_cmd = 1;
 	argv_array_push(&am.args, "am");
 
+	if (opts->ignore_whitespace)
+		argv_array_push(&am.args, "--ignore-whitespace");
 	if (opts->action && !strcmp("continue", opts->action)) {
 		argv_array_push(&am.args, "--resolved");
 		argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
@@ -1401,9 +1417,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
 		OPT_BOOL(0, "signoff", &options.signoff,
 			 N_("add a Signed-off-by: line to each commit")),
-		OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
-				  NULL, N_("passed to 'git am'"),
-				  PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
 				  &options.git_am_opts, NULL,
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
@@ -1411,6 +1424,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				  N_("passed to 'git am'"), PARSE_OPT_NOARG),
 		OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
 				  N_("passed to 'git apply'"), 0),
+		OPT_BOOL(0, "ignore-whitespace", &options.ignore_whitespace,
+			 N_("ignore changes in whitespace")),
 		OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
 				  N_("action"), N_("passed to 'git apply'"), 0),
 		OPT_BIT('f', "force-rebase", &options.flags,
@@ -1821,6 +1836,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	}
 
 	if (options.rebase_merges) {
+		if (options.ignore_whitespace)
+			die(_("cannot combine '--rebase-merges' with "
+			      "'--ignore-whitespace'"));
 		if (strategy_options.nr)
 			die(_("cannot combine '--rebase-merges' with "
 			      "'--strategy-option'"));
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index a5868ea152..4342f79eea 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -61,7 +61,6 @@ test_rebase_am_only () {
 }
 
 test_rebase_am_only --whitespace=fix
-test_rebase_am_only --ignore-whitespace
 test_rebase_am_only --committer-date-is-author-date
 test_rebase_am_only -C4
 
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
new file mode 100755
index 0000000000..24bb8f3dd0
--- /dev/null
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Rohit Ashiwal
+#
+
+test_description='tests to ensure compatibility between am and interactive backends'
+
+. ./test-lib.sh
+
+# This is a special case in which both am and interactive backends
+# provide the same outputs. It was done intentionally because
+# --ignore-whitespace both the backends fall short of optimal
+# behaviour.
+test_expect_success 'setup' '
+	git checkout -b topic &&
+	q_to_tab >file <<-EOF &&
+	line 1
+	Qline 2
+	line 3
+	EOF
+	git add file &&
+	git commit -m "add file" &&
+	cat >file <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	git commit -am "update file" &&
+	git tag side &&
+
+	git checkout --orphan master &&
+	cat >file <<-EOF &&
+	line 1
+	        line 2
+	line 3
+	EOF
+	git add file &&
+	git commit -m "add file" &&
+	git tag main
+'
+
+test_expect_success '--ignore-whitespace works with am backend' '
+	cat >expect <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	test_must_fail git rebase main side &&
+	git rebase --abort &&
+	git rebase --ignore-whitespace main side &&
+	test_cmp expect file
+'
+
+test_expect_success '--ignore-whitespace works with interactive backend' '
+	cat >expect <<-EOF &&
+	line 1
+	new line 2
+	line 3
+	EOF
+	test_must_fail git rebase --merge main side &&
+	git rebase --abort &&
+	git rebase --merge --ignore-whitespace main side &&
+	test_cmp expect file
+'
+
+test_done
-- 
2.21.0

← back to recent threads