git rebase -i -autosquash конфликт

git вызывает у меня сильную головную боль при использовании --fixup и --autosquash. Я хотел бы привести два примера, один из которых работает отлично, а другой - в беспорядке. (git версия 2.6.2)

Рабочий пример:

Первый коммит:

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

Второй коммит (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

Третий коммит:

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

Четвертый коммит (исправление):

$ 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.

Пример головной боли: (Единственная разница в том, что коммит BUGGY - одна строка)

Первый коммит:

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

Второй коммит (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

Третий коммит:

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

Четвертый коммит (исправление):

$ 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

Почему исправление не применяется чисто?

Почему исправление также содержит "2", которое должно быть не в патче, представленном исправлением, а в патче предыдущего коммита.

3 ответа

Решение

Когда вы делаете --fixup, вы применяете патч не по порядку, поэтому контекст исчез. В первом случае ваши патчи применяются следующим образом:

  1. Вставить 1 на линии 1
  2. Вставить This is\naBUG в строках 2, 3 после 1
  3. Удалить строку a BUGв строке 4 после This is, заменить NOT a BUG
  4. Вставить 2 в строке 2 после 1, до This is

Шаги 2, 3 довольно четкие. Даже если номер строки отличается от ожидаемого на шаге 3, контекст проясняет ситуацию. Во втором случае

  1. Вставить 1 на линии 1
  2. Вставить This is a BUG в строке 2 после 1
  3. Удалить строку This is a BUG, заменить This is NOT a BUG в строке 3 после строки 2
  4. Вставить 2 по строке 2, после 1, до This is a BUG

В этом случае патч № 3 невозможен, потому что This is a BUG не отображается в строке 3, а строка перед ней не 2, Git не предполагает, что строка 2 является правильной в этом случае из-за отсутствия контекста.

Самый простой способ решить эту проблему - изменить порядок ребаз, чтобы они отражали то, что вы на самом деле делаете. Вместо оригинального заказа:

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

переключите последние два элемента, чтобы дать патчу необходимый контекст:

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

В этом случае вам может понадобиться добавить -k флаг вашей командной строки, чтобы сохранить последний коммит, который в основном пуст:

$ 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

Другая альтернатива - это, конечно, исправить конфликт вручную, используя git merge или же git mergetool, следуя инструкциям, когда перебазировка не удалась.

Вы можете сделать ребаз успешным, добавив -s recursive -X theirs или же -s recursive -X ours указать стратегию. Однако из-за конфликта контекста ваше исправление будет сработано в обоих случаях.

Я думаю, что проблема в контексте изменений. Посмотрите на этот коммит:

$ 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

И вы хотите применить этот патч к файлу с содержимым:

1
This is a BUG

Увидеть? Патч не применяется, потому что контекст не совпадает. Таким образом, возникает конфликт, и вы должны исправить это вручную.


Когда у вас разбита строка на две строки, патч выглядит примерно так:

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

И файл:

1
This is
a BUG

Теперь, пока совпадение не является идеальным, по крайней мере, первая немодифицированная строка контекста совпадает, поэтому объединение может продолжаться.

git вызывает у меня сильную головную боль при использовании --fixup и --autosquash.

Остерегайтесь другого случая, когда используете git rebase --autosquash головной боли.

Git 2.20 (Q4 2018) только что исправил ошибку, связанную с автоматическим сквошем: " git rebase -i" неправильно очистил файлы состояния, когда запуск "squash/fixup" был прерван, а затем пользователь вместо этого вручную исправил коммит, который был исправлен.

Смотрите коммит 10d2f35, коммит 2f3eb68 (31 августа 2018 г.) от Johannes Schindelin ( dscho ) (Объединено Юнио С Хамано - gitster - в комитете 87ae8a1, 24 сентября 2018 г.)

rebase -i --autosquash: продемонстрировать проблему, пропуская последний сквош

git commit --squash Команду можно использовать не только для внесения изменений в сообщения коммитов и изменений, но и для записи заметок для предстоящей перезагрузки.

Например, если информация об авторе данного коммита неверна, пользователь может вызвать git commit --allow-empty -m "Fix author" --squash <commit>, чтобы напомнить им, чтобы исправить это во время перебазирования. Когда появится редактор, пользователь просто удалит сообщение коммита, чтобы прервать ребаз на этом этапе, исправить информацию об авторе и продолжить git rebase --skip, (Это реальный пример из перебазирования Git для Windows на v2.19.0-rc1.)

Тем не менее, есть ошибка в git rebase в этом случае сообщение о сквоше не будет забыто. Поэтому он будет повторно использован в следующей цепочке исправлений / сквоша (если есть).

rebase -i: будьте осторожны, чтобы закрутить цепи отладки

Когда интерактивная перебазировка была остановлена ​​в конце цепочки исправлений / сквоша, пользователь мог отредактировать коммит вручную перед продолжением (либо с git rebase --skip или же git rebase --continue, это не имеет значения, какой).

Мы должны быть очень осторожны, чтобы обернуть цепочку исправлений / сквоша и в этом сценарии: в противном случае следующая цепочка исправлений / сквоша будет пытаться подобрать, где была оставлена ​​предыдущая.

Другие вопросы по тегам