2

I am trying to rebase a branch with another branch with few commits. Although commits includes merge commits i am able to rebase it successfully with below command git rebase -i -r -m <base_branch>

However when i try to modify some of the commit messages using 'reword', i see merge conflicts.

I tried git rebase -i -r -m <base_branch> command and changed few commits to reword instead of pick, getting merge conflicts after this.

Command used:
git rebase -i -r -m <base_branch>

I expect successful rebase , but there are conflicts.

smilyface
  • 5,021
  • 8
  • 41
  • 57

1 Answers1

2

This is probably because the original merge had a conflict as well. (I can't prove that this is the case without having access to the repository and its commits.)

What you're seeing is a side effect of the fact that sometimes, git rebase can avoid some or all copies, and when it can and does avoid all copies, it ends up not doing anything at all. Doing nothing means no merge commits, but "copying" a merge that had a merge conflict before means that you see the same merge conflict again.

You can work around this problem with git rerere (and the rerere-train script, perhaps: see Smarter rebase avoiding redundant work?).

Long

Remember that git rebase works by copying commits. A simple interactive rebase starts by listing all the commits to copy, using pick commands for each such commit. This simple kind of rebase discards merges, though. Adding --rebase-merges (or -r) tells it: keep the merges, along with the original arrangement of commits. But there's a flaw in this whole concept: while it's possible to copy a non-merge commit, it's not possible to copy a merge commit.

What git rebase -r (and its older, poorer git rebase -p cousin) do instead, because they can't copy merges, is to re-perform merges when necessary. That is, after copying some number of old commits to new ones, with new and different hash IDs, they get to the point where it is time to "copy" a merge, and instead, they just run git merge directly.

For instance, given:

                    C--D
                   /    \
               A--B      G--H   <-- source (HEAD)
              /    \    /
             /      E--F
            /
...--o--o--*--o--o   <-- target

and the command-line request git rebase -r target will generate a series of pick, label, reset, and merge commands to copy A, B, C, D, E, and F; then merge the copies ofDandF; then copyGandH`, so as to get:

                    C--D
                   /    \
               A--B      G--H   <-- [abandoned]
              /    \    /
             /      E--F
            /
...--o--o--*--o--o   <-- target
                  \
                   \      C'-D'
                    \    /    \
                     A'-B'     G'-H'   <-- source (HEAD)
                         \    /
                          E'-F'

where commit A' is a copy of A, B' is a copy of B, and so on.

But, with or without the -r option, consider a rebase operation that starts with:

...--I--J   <-- target
         \
          K--L   <-- source (HEAD)

You now ask Git, via git rebase, to copy commit K to a new-and-improved K that is exactly like K except that it comes after J, rather than coming after J. Then you tell Git to copy L so that instead of coming after K, it comes after the new copy of K.

Git realizes that the existing copy of K that comes after J already comes right after J. So the "copy" it needs to make is already there: git rebase just re-uses K directly. Now it must copy L to come after K, but look: L already comes right after K, so Git just re-uses L directly. The result is that nothing changes. (If, for some reason—and there are some reasons—you really want Git to copy K anyway, you can use --force-rebase or --no-ff or -f, all of which do the same thing.)

If you run this same rebase, being sure to use interactive mode, and use reword on K, now Git really does have to copy K to a new K' that is like K but has a different commit message. As a result, Git now really does have to copy L so that it comes after the new K' with the new message. The result is:

          K'-L'  <-- source (HEAD)
         /
...--I--J   <-- target
         \
          K--L   [abandoned]

For a simple linear case like this one, the rebase always goes smoothly, but when you add -r to the mix, to force Git to "copy" (i.e., re-perform) a merge, if the original merge had conflicts, so will the new merge.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 1
    Thanks for the quick reponse. But one doubt i still have is how does making 'pick' to 'reword' creates a merge conflict. I don't see merge conflict when i simply pick all the commits during rebase, including merge commits. – shubham shandilya May 23 '19 at 05:39
  • I'm not sure how else to explain it than I already did: `pick` will, if it can, *re-use the original commit*. If that happens, Git can get away without re-merging. Doing a `reword` and changing the wording, Git *cannot* re-use the original commit, and must re-merge. – torek May 23 '19 at 05:54