merge
(слияние) и rebase
(перемещение). В этом разделе вы узнаете, что такое перемещение, как его осуществлять, почему это удивительный инструмент и в каких случаях вам не следует его использовать.
Основы перемещения
Если вы вернетесь назад к примеру из раздела Слияние (смотри Рисунок 3-27), вы увидите, что вы разделили вашу работу на два направления и выполняли коммиты на двух разных ветках.
Рисунок 3-27. Рисунок 3-27.
Рисунок 3-28. Слияние ветки для объединения разделившейся истории разработки
rebase
вы можете взять все изменения, которые попали в коммиты на одной из веток, и повторить их на другой.
В этом примере вы выполните следующее:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Это работает следующим образом: находится общий предок для двух веток (на которой вы находитесь сейчас и на которую вы выполняете перемещение); берётся разница, представленная в каждом из коммитов на текущей ветке, и сохраняется во временные файлы; текущая ветка устанавливается на такой же коммит, что и ветка, на которую вы выполняете перемещение; и, наконец, последовательно применяются все изменения. Рисунок 3-29 иллюстрирует этот процесс.
Рисунок 3-29. Перемещение изменений, сделанных в C3, на C4
master
и выполнить слияние-перемотку (fast-forward merge) (смотри Рисунок 3-30).
Рисунок 3-30. Перемотка ветки master
log
) перемещённой ветки, то увидите, что он выглядит как линейная история работы: выходит, что вся работа выполнялась последовательно, когда в действительности она выполнялась параллельно.
Часто вы будете делать это, чтобы удостовериться, что ваши коммиты правильно применяются для удаленных веток — возможно для проекта, владельцем которого вы не являетесь, но в который вы хотите внести свой вклад. В этом случае вы будете выполнять работу в ветке, а затем, когда будете готовы внести свои изменения в основной проект, выполните перемещение вашей работы на origin/master
. Таким образом, владельцу проекта не придется делать никаких действий по объединению — просто перемотка (fast-forward) или чистое применение патчей.
Заметьте, что снимок состояния, на который указывает последний коммит, который у вас получился, является ли этот коммит последним перемещенным коммитом (для случая выполнения перемещения) или итоговым коммитом слияния (для случая выполнения слияния), есть один и тот же снимок — разной будет только история. Перемещение применяет изменения из одной линии разработки в другую в том порядке, в котором они были представлены, тогда как слияние объединяет вместе конечные точки двух веток.
Более интересные перемещения
Вы также можете выполнять перемещение не только для перемещения ветки. Возьмём, например, историю разработки как на Рисунке 3-31. Вы создали тематическую ветку (server
), чтобы добавить в проект некоторый функционал для серверной части, и сделали коммит. Затем вы выполнили ответвление, чтобы сделать изменения для клиентской части, и несколько раз выполнили коммиты. Наконец, вы вернулись на ветку server
и сделали ещё несколько коммитов.
Рисунок 3-31. История разработки с тематической веткой, ответвленной от другой тематической ветки
client
, которых нет на server (C8 и C9), и применить их на ветке master
при помощи опции --onto
команды git rebase
:
$ git rebase --onto master server client
По сути, это указание “переключиться на ветку client, взять изменения от общего предка веток client и server и повторить их на master
”. Это немного сложно; но результат, показанный на Рисунке 3-32, достаточно классный.
Рисунок 3-32. Перемещение тематической ветки, ответвленной от другой тематической ветки
$ git checkout master
$ git merge client
Рисунок 3-33. Перемотка ветки master
, чтобы включить изменения из ветки client
server
в основную ветку. Вы можете выполнить перемещение ветки server
на ветку master
без предварительного переключения на эту ветку при помощи команды git rebase [осн. ветка] [тем. ветка]
— которая устанавливает тематическую ветку (в данном случае server
) как текущую и применяет её изменения на основной ветке (master
):
$ git rebase master server
Эта команда применит изменения из вашей работы над веткой server
на вершину ветки master
, как показано на Рисунке 3-34.
Рисунок 3-34. Перемещение вашей ветки server
на вершину ветки master
master
):
$ git checkout master
$ git merge server
Вы можете удалить ветки client
и server
, так как вся работа из них включена в основную линию разработки и они вам больше не нужны. При этом полная история вашего рабочего процесса выглядит как на Рисунке 3-35:
$ git branch -d client
$ git branch -d server
Рисунок 3-35. Финальная история коммитов
Возможные риски перемещения
Все бы хорошо, но кое-что омрачает всю прелесть использования перемещения. Это выражается одной строчкой:Не перемещайте коммиты, которые вы выложили в публичный репозиторий.
Если вы будете следовать этому указанию, все будет хорошо. Если нет — люди возненавидят вас, вас будут презирать ваши друзья и семья. Когда вы что-то перемещаете, вы отменяете существующие коммиты и создаете новые, которые являются похожими на старые, но в чем-то другими. Если вы выкладываете ваши коммиты куда-нибудь, и другие забирают их себе и в дальнейшем основывают на них свою работу, а затем вы переделываете эти коммиты командойgit rebase
и выкладываете их снова, ваши коллеги будут вынуждены заново выполнять слияние для своих наработок. Все запутается, когда вы в очередной раз попытаетесь включить их работу в свою.
Давайте рассмотрим пример того, как выполненное вами перемещение наработок, представленных для общего доступа, может вызвать проблемы. Представьте себе, что вы клонировали себе репозиторий с центрального сервера и поработали в нем. Ваша история коммитов выглядит как на Рисунке 3-36.
Рисунок 3-36. Клонирование репозитория и выполнение в нём какой-то работы
Рисунок 3-37. Извлечение коммитов и слияние их со своей работой
merge
) переместить (rebase
) свою работу; он выполняет git push --force
, чтобы переписать историю на сервере. Затем вы извлекаете изменения с этого сервера, включая и новые коммиты.
Рисунок 3-38. Кто-то выложил перемещённые коммиты, отменяя коммиты, на которых вы основывали свою работу
Рисунок 3-39. Вы снова выполняете слияние для той же самой работы в новый коммит слияния
git log
когда ваша история выглядит таким образом, вы увидите два коммита, которые имеют одинакового автора и одни и те же сообщения. Это сбивает с толку. Более того, если вы отправите такую историю обратно на сервер, вы добавите все эти перемещенные коммиты в репозиторий центрального сервера, что может ещё больше запутать людей.
Если вы рассматриваете перемещение как возможность наведения порядка и работы с коммитами до того, как выложили их, и если вы перемещаете только коммиты, которые никогда не находились в публичном доступе — всё нормально. Если вы перемещаете коммиты, которые уже были представлены для общего доступа, и люди, возможно, основывали свою работу на этих коммитах, тогда вы можете получить наказание за разные неприятные проблемы.
Pro Git