Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rebase: allow combining --whitespace=fix with --interactive #1472

Open
dscho opened this issue Jan 27, 2023 · 3 comments
Open

rebase: allow combining --whitespace=fix with --interactive #1472

dscho opened this issue Jan 27, 2023 · 3 comments

Comments

@dscho
Copy link
Member

dscho commented Jan 27, 2023

It is a long time thorn in the side of every user of --whitespace=fix that it does not work in the merge/interactive rebase backend.

There was already an attempt, as a part of a Google Summer of Code project, to address this, but it was considered a "bonus feature" and it's just too complex a task on its own to tack onto any meaningful project.

@dscho
Copy link
Member Author

dscho commented Jan 27, 2023

The complexity of this project stems from the fact that the am rebase backend (which is currently the only one that supports the --whitespace=fix option) merely passes this option through to git apply, which works very differently from the merge backend.

In the merge backend, we ultimately have to follow the code path of what we do for --ignore-whitespace, i.e. we

A viable approach to add --whitespace=fix support here would be to add a new XDF_* flag that allows to transmogrify the lines ("records" in xdiff speak) in a fashion similar to XDF_IGNORE_WHITESPACE_CHANGE, but via a callback. This callback would then be used to fix the pre-image as well as the post-image. And that callback would need to call ws_fix_copy() much in the same way as apply.c does.

One particularly tricky aspect about trying to do --whitespace=fix in cherry-picks (which rebase essentially executes in a loop) is that in contrast to the apply command, xmerge works on two diffs. Essentially, cherry-picking a commit C boils down to combining the diffs C^..HEAD and C^..C. And only the post-image lines (without context lines!) of C^..C need to be transformed via ws_fix_copy()!

That is very different from what apply has to do: it needs to be able to match pre-image lines the may, or may not, violate the white-space rules, against post-image lines that again may, or may not, violate the white-space rules, and therefore apply needs to process both pre-image and post-image via ws_fix_copy(), even if only the fixed + lines will make it into the final result.

However, we still face a bit of a problem in case C^..C has a context line that has white-space issues and that has been fixed in HEAD: xmerge would mark those as conflicts, even if there should not be any conflict. The same issue applies to pre-image lines in C^..C which had white-space issues that were fixed in C^..HEAD.

So essentially, before we generate conflicts for overlapping line ranges, we have to be careful to treat white-space fixes in C^..HEAD as non-conflicting changes and quietly appending the white-space fixes via xdl_append_merge()/xdl_cleanup_merge() instead. We can do this by calling xdl_do_diff() on the respective xscr1 line range while applying ws_fix_copy() to the pre-image.

Not exactly trivial, right?

So now that I scared the heck out of most, for those readers who made it this far, here is an alternative to consider: apply ws_fix_copy() in xdl_recmatch() and in xdl_hash_record_with_whitespace() for both generated diffs, much like --ignore-whitespace does. And then also when copying the non-conflicting changes (here and here). For large files, it might be too expensive. But then, it might be okay, and much simpler to implement?

After this is implemented, we still have a little bit more to do to actually finish this ticket up: we have to cope with WS_BLANK_AT_EOF. But that will be substantially easier than supporting the rest of --whitespace=fix in the merge rebase backend...

@phil-blain
Copy link

Hi Dscho, nice write-up :) I had a feeling that when I wrote this:

I recently learned that 'git rebase --whitespace=fix' exists, which is also
great but since it uses the apply backend, it can't be used with --update-refs.
I understand this, and the fact that adding '--whitespace=fix' to the merge
backend would be a big task; this is not what this email is about.

" a big ask" was an understatement. With what you outline above, I understand more what it entails.

A few questions:

  • this would not be specific to --interactive (as you write in the description), but more to the merge backend in general. Why not title the ticket "rebase: allow using --whitespace=fix with the merge backend" then?
  • should this be only --whitespace=fix, or should other --whitespace=* values also be implemented ? I agree fix is arguably the most useful...

Also, I thought of this:

And that callback would need to call ws_fix_copy() much in the same way as apply.c does.

ws_fix_copy needs a whitespace_rule, so the callback will also need to call ws.c::whitespace_rule to get the relevant rule for the path.

@dscho
Copy link
Member Author

dscho commented Jan 30, 2023

  • Why not title the ticket "rebase: allow using --whitespace=fix with the merge backend" then?

I tried to be mindful of the motivation. And honestly, to me it sounds much more enticing to teach the interactive rebase to fix white-space issues than to teach the merge rebase backend (which most Git users do not even know exists and is used by default if git rebase <commit> is called).

  • should this be only --whitespace=fix, or should other --whitespace=* values also be implemented ? I agree fix is arguably the most useful...

Indeed. And I don't want to make the big ask of supporting git rebase -i --whitespace=fix even bigger. The --whitespace=* ask can easily be separated because, quite frankly, it is a separate concern.

And that callback would need to call ws_fix_copy() much in the same way as apply.c does.

ws_fix_copy needs a whitespace_rule, so the callback will also need to call ws.c::whitespace_rule to get the relevant rule for the path.

What the callback will actually need is a pointer to a cb_data that contains the whitespace rules, among other things. It's not only those rules, after all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants