Hi Phillip, sorry to be _so_ late in the game. (And sorry for sending this to you twice, I managed to skip all the Cc:s due to the Reply-To: header the first time round.) On Wed, 27 May 2020, Phillip Wood wrote: > From: Rohit Ashiwal <rohit.ashiwal265@xxxxxxxxx> > > Rebase is implemented with two different backends - 'apply' and 'merge' > each of which support a different set of options. In particuar the apply > backend supports a number of options implemented by 'git am' that are > not available to the merge backend. As part of an on going effort to As a non-native speaker, I am thrown off when reading "available to" instead of the grammatically correct (I believe) "available in". Likewise, "on going" instead of "ongoing" just disrupts my workflow. Maybe these can be fixed? > remove the apply backend this patch adds support for the > --ignore-whitespace option to the merge backend. This option treats > lines with only whitespace changes as unchanged and is implemented in > the merge backend by translating it to -Xignore-space-change. > > Signed-off-by: Rohit Ashiwal <rohit.ashiwal265@xxxxxxxxx> > Signed-off-by: Phillip Wood <phillip.wood@xxxxxxxxxxxxx> > --- > Documentation/git-rebase.txt | 19 +++++- > builtin/rebase.c | 19 ++++-- > t/t3422-rebase-incompatible-options.sh | 1 - > t/t3436-rebase-more-options.sh | 86 ++++++++++++++++++++++++++ > 4 files changed, 118 insertions(+), 7 deletions(-) > create mode 100755 t/t3436-rebase-more-options.sh > > diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt > index f7a6033607..b003784f01 100644 > --- a/Documentation/git-rebase.txt > +++ b/Documentation/git-rebase.txt > @@ -422,8 +422,23 @@ your branch contains commits which were dropped, this option can be used > with `--keep-base` in order to drop those commits from your branch. > > --ignore-whitespace:: > + Ignore whitespace differences when trying to reconcile > +differences. Currently, each backend implements an approximation of > +this behavior: > ++ > +apply backend: When applying a patch, ignore changes in whitespace in > +context lines. Unfortunately, this means that if the "old" lines being > +replaced by the patch differ only in whitespace from the existing > +file, you will get a merge conflict instead of a successful patch > +application. > ++ > +merge backend: Treat lines with only whitespace changes as unchanged > +when merging. Unfortunately, this means that any patch hunks that were > +intended to modify whitespace and nothing else will be dropped, even > +if the other side had no changes that conflicted. > + > --whitespace=<option>:: > - These flags 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. > Implies --apply. > + > @@ -572,7 +587,6 @@ The following options: > * --apply > * --committer-date-is-author-date > * --ignore-date > - * --ignore-whitespace > * --whitespace > * -C > > @@ -598,6 +612,7 @@ In addition, the following pairs of options are incompatible: > * --preserve-merges and --signoff > * --preserve-merges and --rebase-merges > * --preserve-merges and --empty= > + * --preserve-merges and --ignore-whitespace > * --keep-base and --onto > * --keep-base and --root > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index 27a07d4e78..5d8e117276 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -86,6 +86,7 @@ struct rebase_options { > int signoff; > int allow_rerere_autoupdate; > int autosquash; > + int ignore_whitespace; > char *gpg_sign_opt; > int autostash; > char *cmd; > @@ -108,6 +109,7 @@ struct rebase_options { > > static struct replay_opts get_replay_opts(const struct rebase_options *opts) > { > + struct strbuf strategy_buf = STRBUF_INIT; > struct replay_opts replay = REPLAY_OPTS_INIT; > > replay.action = REPLAY_INTERACTIVE_REBASE; > @@ -126,14 +128,20 @@ 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->strategy_opts) > - parse_strategy_opts(&replay, opts->strategy_opts); > + strbuf_addstr(&strategy_buf, opts->strategy_opts); > + if (opts->ignore_whitespace) > + strbuf_addstr(&strategy_buf, " --ignore-space-change"); > + if (strategy_buf.len) > + parse_strategy_opts(&replay, strategy_buf.buf); Quite honestly, this is very, very ugly. I would have expected this at a way earlier layer, namely in `cmd__rebase()`. Something along these lines: -- snip -- diff --git a/builtin/rebase.c b/builtin/rebase.c index 37ba76ac3d26..748e08aee2f2 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1289,6 +1289,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct strbuf revisions = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; struct object_id merge_base; + int ignore_whitespace = 0; enum action action = ACTION_NONE; const char *gpg_sign = NULL; struct string_list exec = STRING_LIST_INIT_NODUP; @@ -1318,9 +1319,8 @@ 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_BOOL(0, "ignore-whitespace", &ignore_whitespace, + N_("passed to 'git am'")), OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date", &options.git_am_opts, NULL, N_("passed to 'git am'"), PARSE_OPT_NOARG), @@ -1682,6 +1682,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) imply_merge(&options, "--rebase-merges"); } + if (ignore_whitespace) { + if (options.type == REBASE_APPLY) + argv_array_push(&options.git_am_opts, + "--ignore-whitespace"); + else + string_list_append(&stragey_options, + "--ignore-space-change"); + } + if (strategy_options.nr) { int i; -- snap -- > > if (opts->squash_onto) { > oidcpy(&replay.squash_onto, opts->squash_onto); > replay.have_squash_onto = 1; > } > > + strbuf_release(&strategy_buf); > return replay; > } > > @@ -539,6 +547,8 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) > argc = parse_options(argc, argv, prefix, options, > builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0); > > + opts.strategy_opts = xstrdup_or_null(opts.strategy_opts); I am not sure what this is about: `opts.strategy_opts` is of type `char *`, i.e. it is supposed to be already allocated. Not that `cmd_rebase__interactive()` matters _all_ that much anymore, of course: it is only used by the --preserve-merges backend, which will hopefully be retired soon. > + > if (!is_null_oid(&squash_onto)) > opts.squash_onto = &squash_onto; > > @@ -991,6 +1001,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); > @@ -1495,16 +1507,15 @@ 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), > 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"), > 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, > diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh > index 50e7960702..55ca46786d 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/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh > new file mode 100755 > index 0000000000..fb5e747e86 > --- /dev/null > +++ b/t/t3436-rebase-more-options.sh > @@ -0,0 +1,86 @@ > +#!/bin/sh > +# > +# Copyright (c) 2019 Rohit Ashiwal > +# > + > +test_description='tests to ensure compatibility between am and interactive backends' > + > +. ./test-lib.sh > + > +. "$TEST_DIRECTORY"/lib-rebase.sh > + > +# This is a special case in which both am and interactive backends > +# provide the same output. It was done intentionally because > +# 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 && > + sed -e "s/^|//" >file <<-\EOF && > + |line 1 > + | line 2 > + |line 3 > + EOF > + git add file && > + git commit -m "add file" && > + git tag main > +' The file contents are repeated in an awfully repetitive manner. That not only makes things a bit hard to read, it also makes it all too easy to slip in bugs by mistake. How about something like this instead? test_commit file && test_write_lines line1 Qline2 line3 >templ && q_to_tab <templ >file.t && git commit -m tab file.t && sed "s/Q/new /" <templ >file.t && git commit -m new file.t && git tag side && git checkout file -- && sed "s/Q/ /" <templ >file.t && git commit -m spaces file.t ... and then... > + > +test_expect_success '--ignore-whitespace works with apply backend' ' > + cat >expect <<-\EOF && > + line 1 > + new line 2 > + line 3 > + EOF sed "s/Q/new /" <templ >expect > + test_must_fail git rebase --apply main side && > + git rebase --abort && > + git rebase --apply --ignore-whitespace main side && > + test_cmp expect file Personally, I prefer to read the contents of `expect` directly before the `test_cmp expect file.t` > +' > + > +test_expect_success '--ignore-whitespace works with merge backend' ' > + cat >expect <<-\EOF && > + line 1 > + new line 2 > + line 3 > + EOF Isn't this totally identical to the `expect` constructed earlier? And in any case, isn't this identical to `git show main:file.t`, which is what we _actually_ expect: for the file contents to be identical to the tagged `main`? I.e. git diff --exit-code main > + test_must_fail git rebase --merge main side && > + git rebase --abort && > + git rebase --merge --ignore-whitespace main side && > + test_cmp expect file > +' > + > +test_expect_success '--ignore-whitespace is remembered when continuing' ' > + cat >expect <<-\EOF && > + line 1 > + new line 2 > + line 3 > + EOF > + ( > + set_fake_editor && > + FAKE_LINES="break 1" git rebase -i --ignore-whitespace main side > + ) && > + git rebase --continue && > + test_cmp expect file It is a bit funny to see these two invocations _specifically_ pulled out from the subshell, that's not how we do things in other test scripts: instead, we run all the Git commands _inside_ the subshell, and all the verifications after the subshell. I believe that with my suggestions, this test script will be a ton easier to read and to maintain. At least it will be a lot DRYer. Ciao, Dscho > +' > + > +# This must be the last test in this file > +test_expect_success '$EDITOR and friends are unchanged' ' > + test_editor_unchanged > +' > + > +test_done > -- > 2.26.2 > >