gitconflictfixup

git rebase -i -autosquash conflict


git is giving me a major headache when using --fixup and --autosquash. I would like to give two examples, one working perfectly fine and the other being a mess. (git version 2.6.2)

Working example:

First commit:

$ git init
$ printf '1\n' > test.file
$ git add test.file
$ git commit -m 'Insert 1 --> first line'  
$ cat test.file
1

Second commit (BUG):

$ printf 'This is\na BUG\n' >> test.file
$ git commit -am 'Added line 2 and 3 with BUG'
$ cat test.file
1  
This is  
a BUG

Third commit:

$ sed -i '2i 2' test.file
$ git commit -am 'Insert 2 --> second line'
$ cat test.file
1  
2  
This is  
a BUG

Fourth commit (fixup):

$ sed -i 's/a BUG/NOT a BUG/' test.file
$ git add test.file
$ git log --oneline
b021696 Insert 2 --> second line  
2e18b8d Added line 2 and 3 with BUG  
d7b60a1 Insert 1 --> first line  
$ git commit --fixup HEAD~
$ cat test.file
1  
2  
This is  
NOT a BUG

Rebase:

$ git log --oneline
fe99989 fixup! Added line 2 and 3 with BUG  
b021696 Insert 2 --> second line  
2e18b8d Added line 2 and 3 with BUG  
d7b60a1 Insert 1 --> first line

$ git rebase -i --autosquash HEAD~3

[detached HEAD 6660b0e] Added line 2 and 3 with BUG
Date: Tue Nov 3 13:28:07 2015 +0100
1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/master.

Headache example: (Only difference is the BUGGY commit is a single line)

First commit:

$ git init
$ printf '1\n' > test.file
$ git add test.file
$ git commit -m 'Insert 1 --> first line'  
$ cat test.file
1

Second commit (BUG):

$ printf 'This is a BUG\n' >> test.file
$ git commit -am 'Added line 2 with BUG'
$ cat test.file
1
This is a BUG

Third commit:

$ sed -i '2i 2' test.file
$ git commit -am 'Insert 2 --> second line'
$ cat test.file
1  
2  
This is a BUG

Fourth commit (fixup):

$ sed -i 's/a BUG/NOT a BUG/' test.file
$ git add test.file
$ git log --oneline
2b83fe7 Insert 2 --> second line  
62cdd05 Added line 2 with BUG  
0ee3343 Insert 1 --> first line
$ git commit --fixup HEAD~
$ cat test.file
1  
2  
This is NOT a BUG

Rebase:

$ git log --oneline
c3d3db7 fixup! Added line 2 with BUG  
2b83fe7 Insert 2 --> second line  
62cdd05 Added line 2 with BUG  
0ee3343 Insert 1 --> first line
$ git rebase -i --autosquash HEAD~3
error: could not apply c3d3db7... fixup! Added line 2 with BUG

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Could not apply c3d3db78440e48c1bb637f78e0767520db65ea1e... fixup! Added line 2 with BUG

$ git status
interactive rebase in progress; onto 0ee3343
Last commands done (2 commands done):
   pick 62cdd05 Added line 2 with BUG
   fixup c3d3db7 fixup! Added line 2 with BUG
Next command to do (1 remaining command):
   pick 2b83fe7 Insert 2 --> second line
  (use "git rebase --edit-todo" to view and edit)
You are currently rebasing branch 'master' on '0ee3343'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   test.file

no changes added to commit (use "git add" and/or "git commit -a")  

$ cat test.file
1 
<<<<<<< HEAD
This is a BUG
======= 
2 
This is NOT a BUG
>>>>>>> c3d3db7... fixup! Added line 2 with BUG

Why does the fixup not apply cleanly?

Why does the fixup also contain "2" which should not be in the patch introduced by the fixup but in the patch of the former commit.


Solution

  • When you do a --fixup, you are applying a patch out of order, so the context has disappeared. In the first case, your patches are applied as follows:

    1. Insert 1 on line 1
    2. Insert This is\naBUG on lines 2, 3 after 1
    3. Delete line a BUG, on line 4, after This is, replace with NOT a BUG
    4. Insert 2 on line 2 after 1, before This is

    Steps 2, 3 are pretty clear-cut. Even though the line number is different than expected in step 3, the context makes it clear. In the second case,

    1. Insert 1 on line 1
    2. Insert This is a BUG on line 2 after 1
    3. Delete line This is a BUG, replace with This is NOT a BUG on line 3 after line 2
    4. Insert 2 on line 2, after 1, before This is a BUG

    In this case, patch #3 is impossible because This is a BUG does not appear on line 3 and the line before it is not 2. Git does not assume that line 2 is the correct one in this case because of the missing context.

    The easiest way to fix this problem is to rearrange the order of the rebase to reflect what you are actually doing. Instead of the original order:

    pick 5ef0459 Added line 2 with BUG
    fixup ed5cd81 fixup! Added line 2 with BUG
    pick 20e104e Insert 2 --> second line
    

    switch the last two elements to give the patch the context it needs:

    pick 5ef0459 Added line 2 with BUG
    pick 20e104e Insert 2 --> second line
    fixup ed5cd81 fixup! Added line 2 with BUG
    

    In this case, you may need to add the -k flag to your command line to preserve the last commit, which is basically empty:

    $ git rebase -i -k --autosquash HEAD~3
     Date: Tue Nov 3 10:45:40 2015 -0500
     1 file changed, 2 insertions(+), 1 deletion(-)
    Successfully rebased and updated refs/heads/master.
    $ cat test
    1
    2
    This is NOT a BUG
    

    The other alternative is of course to fix the conflict manually using git merge or git mergetool, following the prompts when the rebase fails.

    You can make the rebase "succeed" by adding -s recursive -X theirs or -s recursive -X ours to specify the strategy. However, because of the context conflict, your fixup will get clobbered in both of those cases.