Как отмечено в комментариях под вопросом, это потому, что GitHub предлагает объединить коммитов для парня, который не видит проблем, но для вас GitHub предлагает rebase коммиты. Иногда слияния являются легкими, а перебазировки - нет.
Вот тривиальный пример серии коммитов, которую можно легко объединить, но не перебазировать. Сначала нарисуем фактический график коммитов:
...--G--H------L <-- master
\
I--J--K <-- branch
В коммите H
файл makeconfict
выглядит следующим образом:
This is line 1
This is line 2
This is line 3
В коммите L
мы изменили строку 2 на This is line two
. Поэтому H
-vs- L
изменяет строку 2 файла makeconflict
(и не вносит никаких других изменений).
Между тем, в коммите I
мы удаляем строку 2 файла makeconflict
. В коммите J
мы вносим полезное изменение в некоторый полезный файл, не касаясь файла makeconflict
. В коммите K
мы помещаем строку 2 файла makeconflict
обратно.
Если мы попытаемся merge
зафиксировать K
(конец ветви branch
) в master
, Git сравнит H
с L
и увидит, что makeconflict
изменила строку 2. Он сравнивает H
с K
: здесь говорится, что с H
мы должны изменить строку 2 файла makeconflict
, чтобы сохранить изменения, сделанные в master
. Затем он сравнивает H
с K
, чтобы увидеть, что мы сделали. Поскольку мы помещаем строку 2 обратно в K
, единственное изменение Git, которое необходимо объединить с изменением makeconflict
, - это изменение в важном файле. Таким образом, слияние проходит гладко, изменяя важный файл, и мы получаем:
...--G--H------L--M <-- master (HEAD)
\ /
I--J--K <-- branch
Если мы попытаемся сделать rebase , Git необходимо скопировать коммит I
в новый коммит I'
, следующий за L
:
I' [in progress, not actually committed yet]
/
...--G--H------L <-- master
\
I--J--K <-- branch
Попытка скопировать I
в I'
- разница от H
до I
- говорит, что мы должны удалить строку 2 файла makeconflict
, тогда как разница между H
и L
- это то, что мы должны изменить строку 2 файла makeconflict
. Эти два разных изменения конфликтуют друг с другом. Мы должны выбрать, какое изменение сделать: заменить 2
на two
или полностью удалить строку?
Если мы полностью удалим строку - что требует ручного вмешательства, а GitHub не сделает этого через веб-интерфейс - мы можем продолжить копирование J
в J'
и K
в K'
:
I'-J'-K' <-- branch (HEAD)
/
...--G--H------L <-- master
\
I--J--K [abandoned]
В этом процессе мы вернули строку 2 файла makeconflict
обратно в исходное состояние и все еще в оставленном сейчас коммите K
. Таким образом, мы получаем один конфликт слияния (копирование I
поверх L
для создания I'
), и, в сущности, в итоге мы отменяем изменения, сделанные в L
. Но как только мы закончим, у нас есть серия коммитов, которые просто добавляются непосредственно к master
, так что мы можем продвинуть имя master
для коммита K'
, используя git checkout master; git merge --ff-only branch
:
...--G--H------L--I'-J'-K' <-- branch, master (HEAD)
\
I--J--K [abandoned]
Или, конечно, при обработке конфликта слияния на I'
мы можем вместо этого отказаться от наших изменений с I
и сохранить изменения L
. В конце мы просто пропускаем копирование I
полностью. Когда мы копируем commit K
для создания K'
, мы обычно получаем другой конфликт слияния, хотя иногда Git понимает, что коммит K
также не требуется. Если мы правильно обработаем конфликт, мы также сбросим K
:
J' <-- branch (HEAD)
/
...--G--H------L <-- master
\
I--J--K [abandoned]
и снова мы можем теперь перемотать вперед master
, чтобы указать, чтобы скопировать J'
.
Этот конкретный пример, конечно, довольно искусственный, но такие вещи действительно случаются в реальной жизни.