Перейти к содержанию

Разница между git merge и rebase

Работая с git в качестве системы контроля версий (VCS), можно следовать любой из стратегий ветвления, но в конечном итоге может понадобиться интегрировать изменения одной из функциональных веток в основную ветку. В этом руководстве рассмотрим два разных способа интеграции изменений из одной ветки в другую.

Git rebase

Проще говоря, git rebase берет всю функциональную ветку и перемещает ее в конец основной ветки. Он создает новые коммиты для каждого коммита в исходной функциональной ветке.

Давайте создадим новый репозиторий и функциональную ветку в репозитории, чтобы понять, как работает rebase:

git clone <your_repository_here>
git branch testBranch1
git branch testBranch2

Создадим новый файл в функциональной ветке testBranch1 и зафиксируем изменения:

git add .
git commit -m "<Commit_Message_Here>"
git push --set-upstream origin testBranch1
git log

Выполнение этих команд выведет следующее:

Теперь давайте попробуем перебазировать эту ветку на основную ветку:

git rebase main

Это приведет к следующему сообщению:

Так как в основной ветке нет коммитов, как видно из вышеизложенного, ожидать каких-либо изменений не стоит. Теперь объединим функциональную ветку с основной веткой:

git checkout main
git merge testBranch1
git push
git log

Эти команды выведут следующее:

Идентификаторы коммитов из функциональной ветки не изменяются при слиянии с основной веткой. Это похоже на то, что происходит при быстром слиянии.

Поскольку мы уже объединили testBranch1 с основной веткой, в testBranch2 отсутствуют коммиты, из которых она была вырезана.

Давайте посмотрим, как происходит перебазирование и слияние testBranch2.

Давайте создадим новый файл в функциональной ветке testBranch2 и зафиксируем изменения:

git checkout testBranch2
git add .
git commit -m "<Commit_Message_Here>"
git push --set-upstream origin testBranch2
git log

После выполнения этих команд увидим:

Теперь попробуем перебазировать эту ветку на основную ветку:

git rebase main

Получим сообщение, отличное от предыдущего случая:

Поскольку в основной ветке есть некоторые коммиты, функциональная ветка была перебазирована на нее. Теперь объединим featureBranch2 с основной веткой. Мы должны ожидать, что идентификаторы коммитов будут разными для featureBranch2 до и после перебазирования:

Эти команды выведут следующее:

Идентификаторы коммитов отличаются, как и ожидалось, и если посмотрим на график лога git, то увидим, что репозиторий имеет линейную историю:

git log --graph --oneline

Приведенная выше команда показывает графическую структуру, отображающую информацию о коммите в одной строке:

Git merge

Git merge возьмет две ветки, которые объединяем, найдет общий базовый коммит, а затем воспроизведет последовательность коммитов из двух веток в базовом коммите, чтобы объединить ветки.

Давайте создадим новый репозиторий и пару функциональных веток, чтобы понять, как работает слияние.

Склонируйте репозиторий на свой локальный компьютер и создайте новую функциональную ветку:

git clone <your_repository_here>
git branch testBranch1
git branch testBranch2

Давайте создадим новый файл в функциональной ветке testBranch1 и зафиксируем изменения:

git add .
git commit -m "<Commit_Message_Here>"
git push --set-upstream origin testBranch1
git log

Выполнение этих команд выведет следующее:

Теперь давайте объединим эту функциональную ветку с основной веткой с помощью команды merge:

git checkout main
git merge testBranch1
git push
git log

Эти команды выведут следующее:

Можно заметить, что последние идентификаторы коммитов такие же, как и на предыдущем изображении, но указатель HEAD указывает на основную ветку.

Выше было простое слияние, в котором не было никаких изменений в основной ветке, пока работали над функциональной веткой.

Давайте рассмотрим другой сценарий, в котором есть изменения как в основной, так и в функциональной ветке, и как git их обрабатывает. Давайте создадим новый файл в функциональной ветке testBranch2 и зафиксируем изменения:

git checkout testBranch2
git add .
git commit -m "<Commit_Message_Here>"
git push --set-upstream origin testBranch2
git log

После выполнения этих команд мы получим следующее:

Теперь объединим функциональную ветку с основной веткой с помощью команды merge:

git checkout main
git merge testBranch2
git log

Получим в терминале следующее:

Существует отдельный коммит слияния, на который сейчас указывает HEAD, в то время как исходные коммиты присутствуют для обеих функциональных ветвей. Самый верхний коммит имеет дополнительный информационный ключ «Merge», в котором указаны идентификаторы коммитов для обеих ветвей.

Можно проверить граф ветвей и проверить историю репозитория:

git log --graph --oneline

Приведенная выше команда показывает структуру графа, отображающую информацию о коммите в одной строке:

Сценарии использования

Всякий раз, когда требуется, чтобы история нашего репозитория была линейной, необходимо использовать rebase. Но нужно быть осторожными при использовании перебазирования вместо слияния коммитов за пределами наших репозиториев, поскольку другие соавторы могут иметь собственную работу, основанную на существующих коммитах.

Перебазирование уже отправленных коммитов в общедоступный репозиторий приведет к другим идентификаторам коммитов, что может заставить git подумать, что основная ветка других разработчиков и ваша перебазированная основная ветка расходятся. Это может создать потенциально сложную ситуацию для слияния/синхронизации, если есть несколько соавторов.

Заключение

В этой статье рассмотрели основные различия между git merge и git rebase, которые должен знать каждый разработчик при работе с git VCS.

Оригинал