Comment by xg15

2 days ago

I use interactive rebases frequently, but even then, I think conflicts happening during a rebase are often somewhat cryptic.

I think I know what happens in principle (each step of a rebase does a merge of the commit from the list with the last rebased commit) but it's still often hard to understand which side of the conflict represents what, and what would be the desired outcome for that step in the history. So I tend to abort the rebase when I get a conflict and see if I can plan it differently so I can get it through without conflicts.

What I found good to know is that only actions that modify the actual changes in the history - reordering, deleting or editing commits - can cause conflicts. The other actions - reword, fixup or squash - only modify how the changes are grouped into commits and cannot cause conflicts.

So I usually do an interactive rebase in several passes: First a rebase that only reorders commits (or rarely does deletes or edits) and where I deal with the resulting conflicts, then a second pass for fixup or squash operations that can then use the reordered history from the first pass.

Rewords are both harmless and don't require any particular ordering, so can be done in any pass.

> but it's still often hard to understand which side of the conflict represents what, and what would be the desired outcome for that step in the history.

Are you using diff3/zdiff3? I ask because you seem to be describing exactly the problem it solves, or at least the way I try to sell people on it.

Basically in addition to 'current' & 'incoming from the rebase' hunks you get 'parent commit of incoming from the rebase' – which allows you to see 'what was I trying to change', i.e. how does the intent of it apply to the different thing that's now on master (whatever I'm reading onto).

  • Thanks for the info! I didn't try that so far, but it sounds like it could really make that easier. Will give it a try!

In case of many commits with also many complex merge conflicts, I often find it easier to just:

1. Abort rebase.

2. Squash all my commits into a single commit.

3. Rebase on target branch again.

I use it often as well, but if I'm rebasing more than a handful of commits it becomes very annoying and confusing if there's a conflict. For example, I often run into the situation where I'll get a conflict, I resolve that conflict on the first rebase step, but then the same conflict keeps popping up. Where did the resolution I just did disappear to? Why do I have to keep redoing the resolution?

  • Yep. I think that's a "normal" feature of how conflict resolution during rebases work: To perform your rebase, git merges each of the original commits into your new history, in turn. If one if those commits has a change that causes a conflict, the subsequent commits will likely have the same change and cause that same conflict again. And because your resolution is only for one commit, it will be "forgotten" in the next merge.

    I think "git rerere" was built as a sort of bandaid for this. It doesn't really fix the problem but it at least makes it less annoying. (Haven't used it yet though)

    https://git-scm.com/book/en/v2/Git-Tools-Rerere