Перебазирование

В Git есть два способа внести изменения из одной ветки в другую: слияние (Слить...) и перебазирование (Перебазировать...).

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

Простейшее перебазирование

Допустим, вы разделили свою работу и сделали коммиты в две разные ветки: master и experiment.

Простейший способ выполнить слияние двух веток, как вы выяснили ранее, это команда Слить.... Она осуществляет трехстороннее слияние между двумя последними снимками сливаемых веток (master-issue-58 и experiment-issue-57) и самого недавнего общего для этих веток родительского снимка (issue-56), создавая новый снимок (и коммит).

Тем не менее, есть и другой способ: вы можете взять те изменения, что были представлены в experiment-issue-57 и применить их поверх master-issue-58. В Git это называется перебазированием.

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

В данном примере для этого необходимо выполнить следующее:
  1. Сначала переключитесь на ветку experimentГрупповая разработка > Переключить на > experiment;
  2. После этого выполните Групповая разработка > Перебазировать... > master.

Это работает следующим образом: берется общий родительский снимок двух веток (той, в которой вы находитесь, и той, поверх которой вы выполняете перебазирование). Берется дельта каждого коммита той ветки, на который вы находитесь, эти дельты сохраняются во временные файлы. Текущая ветка устанавливается на тот же коммит, что и ветка, поверх которой вы выполняете перебазирование, и, наконец, ранее сохраненные дельты применяются по-очереди.

Важно заметить, что, в отличие от слияния, при котором оба сливаемых коммита сохраняются, при перебазировании старый коммит experiment-issue-57 удаляется и вместо него создается новый коммит experiment-issue-57 с новым id.

На этом моменте вы можете переключиться обратно на ветку master и выполнить слияние перемоткой вперед: Групповая разработка > Переключить на > master, затем Групповая разработка > Слить... > experiment.

Теперь снимок, на который указывает a78cb60 абсолютно такой же, как тот, на который указывал 62c3c2a в примере с трехсторонним слиянием.

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

Часто вы будете делать так для уверенности, что ваши коммиты могут быть бесконфликтно влиты в удаленную ветку. Например тогда, когда вы не являетесь владельцем того проекта, которому принадлежит удаленная ветка. В этом случае вам следует работать в своей ветке и затем перебазировать вашу работу поверх origin/master, когда вы будете готовы отправить свои изменения в основной проект. Тогда владельцу проекта не придется делать никакой лишней работы, все решится простой перемоткой вперед или бесконфликтным слиянием.

Учтите, что снимок, на который ссылается ваш последний коммит (является ли он последним коммитом после перебазирования или коммитом слияния после слияния) в обоих случаях это один и тот же снимок, отличаются только истории коммитов. Перебазирование повторяет изменения из одной ветки поверх другой в порядке, в котором эти изменения были представлены, в то время как слияние берет две конечные точки и сливает их вместе.

Более интересные перемещения

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

Вы создаете тематическую ветку server, чтобы добавить в проект некоторые функциональности для серверной части, и делаете коммит. Затем вы выполнили ответвление, чтобы сделать изменения для клиентской части, и несколько раз выполнили коммиты. Наконец, вы вернулись на ветку server и сделали еще несколько коммитов.

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

Вы можете взять изменения из ветки client, которых нет в server (client-issue-58 и client-issue-59), и применить их на ветке master.

Для этого переключитесь на ветку client, а затем выполните интерактивное перебазирование:
  1. В панели Навигатор нажмите Групповая разработка > Переключить на > client в контекстном меню проекта;
  2. В панели История нажмите Interactive Rebase в контекстно меню на ветке master.
В результате 1C:EDT откроет панель Interactive Rebase, в которой будут перечислены все коммиты ветки client.

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

После этого нажмите Старт в командной панели и 1C:EDT выполнит перебазирование оставшихся двух коммитов в ветку master. Несмотря на некоторую сложность этого способа, результат впечатляет.

Теперь вы можете выполнить перемотку вперед для ветки master:
  1. Групповая разработка > Переключить на > master;
  2. Затем Групповая разработка > Слить... > client.

Представим, что вы решили добавить наработки и из ветки server. В этом случае вы можете поступить как и раньше: переключиться на ветку server и перебазировать ее в master:
  1. Групповая разработка > Переключить на > server;
  2. Затем Групповая разработка > Перебазировать... > master.

Эти команды поместят результаты работы в ветке server в начало ветки master.

После чего вы сможете выполнить перемотку вперед основной ветки master:
  1. Групповая разработка > Переключить на > master;
  2. Затем Групповая разработка > Слить... > server.

Теперь вы можете удалить ветки client и server, поскольку весь ваш прогресс уже включен в основную линию разработки, и больше нет нужды сохранять эти ветки.

Опасности перемещения

Но даже перебазирование, при всех своих достоинствах, не лишено недостатков, которые можно выразить одной строчкой:

Не перемещайте коммиты, уже отправленные в публичный репозиторий.

Если вы будете придерживаться этого правила, все будет хорошо. Если не будете, люди возненавидят вас, а ваши друзья и семья будут вас презирать.

Когда вы что-то перемещаете, вы отменяете существующие коммиты и создаете новые, похожие на старые, но являющиеся другими. Представьте, что вы отправили свои коммиты куда-нибудь (Push to origin), и другие получили их себе (Получить из origin), и на их основе выполнили свои доработки. Теперь вы переделываете эти коммиты командой Перебазировать..., и выкладываете их снова. Это значит, что ваши коллеги будут вынуждены заново выполнять слияние для своих доработок. В итоге, когда вы в очередной раз попытаетесь включить их работу в свою, вы получите путаницу.

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

Теперь кто-то другой, например, Василий, работает с репозиторием, использует слияние, и отправляет результаты своей работы на сервер.

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

Далее Василий решает вернуться, и вместо слияния (Слить...) выполнить перебазирование своей работы (Перебазировать...). Он выполняет это и отправляет изменения на сервер, перезаписывая его историю.

Когда вы получаете изменения с сервера (Получить из origin), вы извлекаете этот новый коммит (3cd83ea).

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

Если вы посмотрите на свою историю, вы увидите два коммита с одинаковыми авторами, датой, и сообщением, что может сбить с толку. Помимо этого, если вы отправите (Push to origin) в таком состоянии свою историю на удаленный сервер, вы вернете все эти перебазированные коммиты (которые остались у вас, но которых нет на сервере) на центральный сервер, что еще больше всех запутает.

Перебазирование или слияние

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

Одна из точек зрения заключается в том, что история коммитов в вашем репозитории это запись того, что на самом деле произошло. Это исторический документ, ценный сам по себе, и его нельзя подделывать. С этой точки зрения изменение истории коммитов почти кощунственно, вы лжете о том, что на самом деле произошло. Но что, если произошла путаница в коммитах слияния? Если это случается, то репозиторий должен сохранить это для потомков.

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

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

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

По материалам книги Pro Git (авторы Scott Chacon и Ben Straub, издательство Apress). Книга распространяется по лицензии Creative Commons Attribution Non Commercial Share Alike 3.0 license.