git rebase -i -autosquash conflict
Asked Answered
N

3

6

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.

Newkirk answered 3/11, 2015 at 13:26 Comment(3)
Is <<<<<<< HEAD This is a BUG really split over two lines by any chance: <<<<<<< HEAD and This is a BUG?Kailakaile
Yes, it is. Sorry for the inconvenience and thanks for editing :)Newkirk
OK. I think I know what the problem is. Let me see if using a different merge methodology will fix it.Kailakaile
K
4

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.

Kailakaile answered 3/11, 2015 at 15:50 Comment(3)
Delete line This is a BUG, replace with This is NOT a BUG on line 4 after line 2 Should be --> Delete line This is a BUG, replace with This is NOT a BUG on line 3 after line 2 ??Newkirk
Had a little trouble formatting my comment :P - ThanksNewkirk
Although "line 2" should really be 2Newkirk
T
3

I think that the problem is the context of the changes. Look at this commit:

$ git show c3d3db7
diff --git a/test.file b/test.file
index 7a103db..8c8e69a 100644
--- a/test.file
+++ b/test.file
@@ -1,3 +1,3 @@
 1
 2
-This is a BUG
+This is NOT a BUG

And you want to apply this patch to the file with contents:

1
This is a BUG

See? The patch does not apply, because the context does not match. So a conflict arises and you have to fix it manually.


When you have the bugger line split in two, the patch is something like:

diff --git a/test.file b/test.file
--- a/test.file
+++ b/test.file
@@ -1,3 +1,3 @@
 1
 2
 This is
-a BUG
+NOT a BUG

And the file is:

1
This is
a BUG

Now, while the match is not perfect, at least the first unmodified line of the context matches, so the merge may continue.

Tittle answered 3/11, 2015 at 14:45 Comment(0)
L
3

git is giving me a major headache when using --fixup and --autosquash.

Beware there is another case, when using git rebase --autosquash, of headache.

Git 2.20 (Q4 2018) just fixed a bug related to autosquash: "git rebase -i" did not clear the state files correctly when a run of "squash/fixup" is aborted and then the user manually amended the commit instead, which has been corrected.

See commit 10d2f35, commit 2f3eb68 (31 Aug 2018) by Johannes Schindelin (dscho). (Merged by Junio C Hamano -- gitster -- in commit 87ae8a1, 24 Sep 2018)

rebase -i --autosquash: demonstrate a problem skipping the last squash

The git commit --squash command can be used not only to amend commit messages and changes, but also to record notes for an upcoming rebase.

For example, when the author information of a given commit is incorrect, a user might call git commit --allow-empty -m "Fix author" --squash <commit>, to remind them to fix that during the rebase. When the editor would pop up, the user would simply delete the commit message to abort the rebase at this stage, fix the author information, and continue with git rebase --skip. (This is a real-world example from the rebase of Git for Windows onto v2.19.0-rc1.)

However, there is a bug in git rebase that will cause the squash message not to be forgotten in this case. It will therefore be reused in the next fixup/squash chain (if any).

rebase -i: be careful to wrap up fixup/squash chains

When an interactive rebase was stopped at the end of a fixup/squash chain, the user might have edited the commit manually before continuing (with either git rebase --skip or git rebase --continue, it does not really matter which).

We need to be very careful to wrap up the fixup/squash chain also in this scenario: otherwise the next fixup/squash chain would try to pick up where the previous one was left.


Before Git 2.27 (Q2 2020), "rebase -i" segfaulted when rearranging a sequence that has a fix-up that applies another fix-up (which may or may not be a fix-up of yet another step).

See commit 02471e7 (09 May 2020) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit a2a0942, 14 May 2020)

rebase --autosquash: fix a potential segfault

Reported-by: Paul Ganssle
Helped-by: Jeff King
Signed-off-by: Johannes Schindelin

When rearranging the todo list so that the fixups/squashes are reordered just after the commits they intend to fix up, we use two arrays to maintain that list: next and tail.

The idea is that next[i], if set to a non-negative value, contains the index of the item that should be rearranged just after the ith item.

To avoid having to walk the entire next chain when appending another fixup/squash, we also store the end of the next chain in tail[i].

The logic we currently use to update these array items is based on the assumption that given a fixup/squash item at index i, we just found the index i2 indicating the first item in that fixup chain.

However, as reported by Paul Ganssle, that need not be true: the special form fixup! <commit-hash> is allowed to point to another fixup commit in the middle of the fixup chain.

Example:

* 0192a To fixup
* 02f12 fixup! To fixup
* 03763 fixup! To fixup
* 04ecb fixup! 02f12

Note how the fourth commit targets the second commit, which is already a fixup that targets the first commit.

Previously, we would update next and tail under our assumption that every fixup! commit would find the start of the fixup!/squash! chain.
This would lead to a segmentation fault because we would actually end up with a next[i] pointing to a fixup! but the corresponding tail[i] pointing nowhere, which would the lead to a segmentation fault.

Let's fix this by inserting, rather than appending, the item.

In other words, if we make a given line successor of another line, we do not simply forget any previously set successor of the latter, but make it a successor of the former.

In the above example, at the point when we insert 04ecb just after 02f12, 03763 would already be recorded as a successor of 04ecb, and we now "squeeze in" 04ecb.

To complete the idea, we now no longer assume that next[i] pointing to a line means that last[i] points to a line, too.
Instead, we extend the concept of last to cover also partial fixup!/squash! chains, i.e. chains starting in the middle of a larger such chain.

In the above example, after processing all lines, last[0] (corresponding to 0192a) would point to 03763, which indeed is the end of the overall fixup! chain, and last[1] (corresponding to 02f12) would point to 04ecb (which is the last fixup! targeting 02f12, but it has 03763 as successor, i.e. it is not the end of overall fixup! chain).

Lochia answered 25/9, 2018 at 18:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.