1

The situation

I am trying to reapply my git filters after having identified and resolved unexpected, and undesirable behavior in the previous iteration of the script. I have had to do this once before, and at no point did I get a merge conflict notice. However, now I am and can't figure out how to deal with it.

What I'm doing

I'm running $git rebase -i --root --preserve-merges. I am aware of the caveat involving merges and rebase, and it does not apply to this situation, since I am not reordering any commits.

I then switch pick to edit

Once editing, the process consists of running the smudge filter on each file, then applying the cleaning filter. It's very straightforward process.

The problem.

git rebase --continue executes a merge. The next commit's files will differ considerably since the files being filtered are LaTeX files, and I'm breaking up paragraphs into multiple lines.

This is expected, and necessary, for merge commits, but it's doing this for those commits with only one parent.

Question

What can I do to prevent this behavior?

What I've tried.

So far, what I've tried has been to play with -X theirs, -X ours, and -s ours. This has not worked. Reading the manual, it says that in the case of binary files, when -X ours is specified it simply disregards the changes in the other "branch" (previous commit in this case), so I set *.* binary in .git/info/attributes. In case I got it backwards, I also tried this with theirs. However, none of these attempts have produced the desired behavior.

Minimal working example

I've been testing this with a simple three commit repo where the first commit is "hello", the second is "hello\nwrld" and the third is "hello\nwrld\n." where \n is actually a new line in the file.

I then pick the first (root) and third (last) commits and edit the middle one. The change made is "hello" to "hello\nworld" (fixing a typo in world). I then commit and continue.

So far these have produced the following outcomes:

  • A warning about an empty commit
  • deletion of the last commit without warning
  • the last commit inherits the "fix"

All of these are undesirable.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
Nero gris
  • 562
  • 7
  • 15
  • `git rebase -i --preserve-merges` actually *re-performs* the merges. Git needs to do them over again. You *must* provide the correct merge result; there is no way around this. – torek Sep 07 '18 at 21:43
  • @torek I think you misunderstand. I'm not talking about a merge point. I'm referring to a point in the middle of two commits---only one parent. Let me read over this and clarify that. Its like in my minimalist example with only three commits in one branch. – Nero gris Sep 07 '18 at 21:46
  • Might have read through this too hastily. Each `pick` (or `edit` which is just a pick plus a stop-for-editing) is a cherry-pick, and each cherry-pick is also a merge. But you'll still have to supply the correct merge result if Git is not able to do so. – torek Sep 07 '18 at 21:59
  • @torek Indeed, that is my understanding of how it works. What I want Git to do is disregard all changes made in recently altered commit in favor of keeping the next commit to be amended the same as what is already recorded. When I'm merging one branch into another and I want to disregard changes I use `-X theirs` which in the case of a rebase is revered to `-X ours`, but this doesn't have the expected result. – Nero gris Sep 07 '18 at 22:13
  • I'll have to experiment a bit with your MWE, but on re-reading your question, I now suspect the problem is that each cherry-pick is using as its merge base a commit that's too different from the ones you're rebuilding post-filtering. When using `git merge`, there's a "renormalize" extended option (`-X renormalize`) that handles these cases better. It is not listed directly as a cherry-pick option, but might work anyway (none of the -X options are listed directly). – torek Sep 08 '18 at 17:20
  • @torek Ah! That is a huge help! Looking up renormalize in the `git-merge` documentation shows that it is meant specificly for cases like mine and points to further reading in `gitattributes`. I'll have to look at those closely. Yes! It looks like I need `git config merge.renormalize true` set. I'll need to try that. – Nero gris Sep 08 '18 at 19:27
  • Okay, that didn't work. Or if it didn't, it didn't work well enough. – Nero gris Sep 08 '18 at 19:37
  • However, `git rebase -i --root -X renormalize` did work. I would have thought the merge.renormalize setting would have taken care of that, but I guess not. Not what I was looking for, but it solves the functional problem. – Nero gris Sep 08 '18 at 19:54
  • Hm, that should be impossible—Git always builds a commit from the index, and the index copy of any file should always result from running through the "clean" filter. Could be worth setting some debug tracing and watching each step of the rebase. This would be easier in older Git where `git rebase -i` was purely shell script... – torek Sep 08 '18 at 20:26
  • Are you referring to the comment I deleted? Where I said that the result was the smudged version? If so, I was mistaken. What happened was the rebase had "ripped" the branch off from master so there was no common history, but all the commit names were the same. So when I went to verify everything worked as expected with "git show master:file.tex" it showed the pre-rebased commit, which was not the behavior I had expected. I need to rebase that then, then. . .I'm not sure. Do something. . . – Nero gris Sep 08 '18 at 20:32
  • Yes, that was referring to a comment that seems to be gone now. – torek Sep 08 '18 at 20:36

1 Answers1

0

If I understand, you are trying to rebase a series of commits that involve text files (LaTeX), applying some filters to each commit during the rebase.
However, due to the nature of the changes (breaking paragraphs into multiple lines), this is causing conflicts during the rebase process, even on commits with a single parent.

You want to avoid these conflicts by having Git automatically choose one version of the file over another (either "ours" or "theirs"), but the standard conflict resolution strategies (-X ours, -X theirs) are not working as expected.

The option git rebase -i --root -X renormalize was able to solve your functional problem, and it involves:

  • -X: That is an option for specifying a strategy-option when using git rebase. The option after -X tells git what to do when it encounters a conflict.

  • renormalize: That is a merge strategy option. That option tells Git to treat files as text and normalize them (convert line endings to LF) before the merge.

So, in your case, it works because it is normalizing line endings before each merge operation, which is effectively preventing the conflicts that are caused by breaking up paragraphs into multiple lines.

Just to clarify, rebase internally does a series of cherry-picks (which are essentially merges), and therefore merge strategy options are applicable.

Remember, however, that -X options are strategy options for the recursive merge strategy, and will not affect the behavior of rebase itself, but only how conflicts are resolved during the application of each patch.

That should still be true since 2018, and the ORT ("Ostensibly Recursive's Twin") new default merge strategy.


When you are using the git rebase command, you are not strictly performing a merge operation. While it is true that rebase is implemented in terms of cherry-pick operations, which in turn are sort of like merges, the rebase command has its own behavior and does not necessarily respect all merge settings.

In contrast, when you specify -X renormalize directly in the command line for git rebase, you are explicitly telling the rebase command to use the renormalize option for its internal merge operations. That is why git rebase -i --root -X renormalize worked in your case, while setting merge.renormalize did not have the desired effect.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250