В фундаментальном рабочем процессе Git вы разрабатываете новую функцию в отдельной ветке тем, а затем объединяете ее с производственной веткой после ее завершения. Это делает git merge
неотъемлемым инструментом для объединения веток. Тем не менее, это не единственный, который предлагает Git.
В качестве альтернативы вышеприведенному сценарию вы можете объединить ветви с помощью команды git rebase
. Вместо того, чтобы связывать ветви вместе с фиксацией слияния, перебазирование перемещает всю ветвь объекта к кончику master
как показано ниже.
Это служит той же цели, что и git merge
, объединяя коммиты из разных веток. Но есть две причины, по которым мы можем захотеть сделать ребаз по сравнению со слиянием:
- Это приводит к линейной истории проекта.
- Это дает нам возможность очистить местные коммиты.
В этом уроке мы рассмотрим эти два распространенных варианта использования git rebase
. К сожалению, преимущества git rebase
приходят к компромиссу. При неправильном использовании это может быть одной из самых опасных операций, которые вы можете выполнять в репозитории Git. Итак, мы также будем внимательно смотреть на опасность перебазирования.
Предпосылки
В этом руководстве предполагается, что вы знакомы с основными командами Git и рабочими процессами совместной работы. У вас должно быть удобное размещение и фиксация снимков, разработка функций в изолированных ветвях, объединение ветвей и добавление / извлечение веток в / из удаленных репозиториев.
1. Перебазирование для линейной истории
Первый вариант использования, который мы рассмотрим, связан с расходящейся историей проекта. Рассмотрим репозиторий, в котором ваша производственная ветвь продвинулась вперед, пока вы разрабатывали функцию:
Чтобы переместить ветвь feature
в master
ветвь, вы должны выполнить следующие команды:
1
2
|
git checkout feature
git rebase master
|
Это трансплантирует ветвь feature
от ее текущего местоположения до вершины master
ветки:
Есть два сценария, где вы хотели бы сделать это. Во-первых, если бы функция полагалась на новые коммиты в master
, она теперь имела бы к ним доступ. Во-вторых, если бы функция была завершена, она теперь была бы настроена для быстрого слияния с master
. В обоих случаях перебазирование приводит к линейной истории, тогда как git merge
приведет к ненужным коммитам слияния.
Например, рассмотрим, что произойдет, если вы объедините восходящие коммиты слиянием вместо ребазирования:
1
2
|
git checkout feature
git merge master
|
Это дало бы нам дополнительный коммит в ветке возможностей. Более того, это будет происходить каждый раз, когда вы захотите включить восходящие коммиты в вашу функцию. В конце концов, история вашего проекта будет завалена бессмысленными слияниями.
Это же преимущество можно увидеть при слиянии в другом направлении. Без перебазирования интеграция готовой ветви feature
в master
требует фиксации слияния. Хотя на самом деле это значимый коммит слияния (в том смысле, что он представляет завершенную функцию), итоговая история полна разветвлений:
Когда вы выполняете ребазинг перед слиянием, Git может быстро перенести master
до конца feature
. Вы увидите линейную историю о том, как ваш проект продвинулся в выводе git log
— коммиты в feature
аккуратно сгруппированы вместе поверх коммитов в master
. Это не обязательно тот случай, когда ветви связаны между собой коммитом слияния.
Разрешение конфликтов
Когда вы запускаете git rebase
, Git берет каждый коммит в ветке и перемещает их один за другим на новую базу. Если любой из этих коммитов изменяет ту же строку (и) кода, что и вышестоящий коммит, это приведет к конфликту.
Команда git merge
позволяет вам разрешить все конфликты ветви в конце слияния, что является одной из основных целей фиксации слияния. Тем не менее, это немного по-другому, когда вы перебазируете. Конфликты разрешаются на основе принятия. Таким образом, если git rebase
обнаружит конфликт, он остановит процедуру rebase и выведет предупреждение:
1
2
3
4
5
6
7
|
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Failed to merge in the changes.
….
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».
|
Визуально, вот как выглядит история вашего проекта, когда git rebase
сталкивается с конфликтом:
Конфликты можно проверить, запустив git status
. Вывод выглядит очень похоже на конфликт слияния:
1
2
3
4
5
6
7
|
Unmerged paths:
(use «git reset HEAD <file>…» to unstage)
(use «git add <file>…» to mark resolution)
both modified: readme.txt
no changes added to commit (use «git add» and/or «git commit -a»)
|
Чтобы разрешить конфликт, откройте конфликтующий файл (readme.txt в приведенном выше примере), найдите затронутые строки и вручную отредактируйте их до нужного результата. Затем скажите Git, что конфликт разрешен путем размещения файла:
1
|
git add readme.txt
|
Обратите внимание, что это точно так же, как вы помечаете конфликт git merge
как разрешенный. Но помните, что вы находитесь в середине перебазирования — вы не хотите забывать об остальных коммитах, которые нужно перенести. Последний шаг — сказать Git закончить перебазирование с опцией --continue
:
1
|
git rebase —continue
|
Это переместит остальные коммиты, один за другим, и если возникнут какие-либо другие конфликты, вам придется повторить этот процесс снова и снова.
Если вы не хотите разрешать конфликт, вы можете выбрать флаги --skip
или --abort
. Последнее особенно полезно, если вы не знаете, что происходит, и просто хотите вернуться к безопасности.
1
2
3
4
5
|
# Ignore the commit that caused the conflict
git rebase —skip
# Abort the entire rebase and go back to the drawing board
git rebase —abort
|
2. Перебазирование для очистки местных комитетов
До сих пор мы использовали только git rebase
для перемещения веток, но он намного мощнее. Передав флаг -i
, вы можете начать сеанс интерактивной перебазировки. Интерактивный перебазирование позволяет точно определить, как каждый коммит будет перемещен на новую базу. Это дает вам возможность очистить историю функции, прежде чем делиться ею с другими разработчиками.
Например, предположим, что вы закончили работу над своей веткой feature
и готовы интегрировать ее в master
. Чтобы начать сеанс интерактивного перебазирования, выполните следующую команду:
1
2
|
git checkout feature
git rebase -i master
|
Откроется редактор, содержащий все коммиты в feature
которые собираются переместить:
1
2
3
|
pick 5c43c2b [Description for oldest commit]
pick b8f3240 [Description for 2nd oldest commit]
pick c069f4a [Description for most recent commit]
|
Этот листинг определяет, как будет выглядеть ветвь компонента после перебазирования. Каждая строка представляет коммит, а команда pick
перед каждым хэшем коммита определяет, что произойдет с ним во время перебазирования. Обратите внимание, что коммиты перечислены от самых старых до самых последних. Изменяя этот список, вы получаете полный контроль над историей вашего проекта.
Если вы хотите изменить порядок коммитов, просто измените порядок строк. Если вы хотите изменить сообщение коммита, используйте команду reword
. Если вы хотите объединить два коммита, измените команду pick
на squash
. Это свернет все изменения в этом коммите в один над ним. Например, если вы раздавили второй коммит в приведенном выше листинге, после сохранения и закрытия редактора ветвь feature
будет выглядеть следующим образом:
Команда edit
особенно мощная. Когда он достигает указанного коммита, Git приостанавливает процедуру перебазирования, так же, как когда он сталкивается с конфликтом. Это дает вам возможность изменить содержимое коммита с помощью git commit --amend
или даже добавить больше git commit
командами git add
/ git commit
. Любые новые коммиты, которые вы добавите, будут частью новой ветки.
Интерактивный перебазирование может оказать глубокое влияние на ваш рабочий процесс разработки. Вместо того, чтобы беспокоиться о разбиении ваших изменений на инкапсулированные коммиты, вы можете сосредоточиться на написании своего кода. Если вы закончили фиксацию того, что должно быть одним изменением, в четыре отдельных снимка, то это не проблема — переписать историю с помощью git rebase -i
и объединить их в один значимый коммит.
3. Опасности перебазирования
Теперь, когда у вас есть понимание git rebase
, мы можем поговорить о том, когда его не использовать. Внутренне, перебазирование фактически не перемещает коммиты в новую ветку. Вместо этого он создает новые коммиты, которые содержат желаемые изменения. Учитывая это, перебазирование лучше визуализировать следующим образом:
После перебазирования коммиты в feature
будут иметь разные хеши коммитов. Это означает, что мы не просто переставили ветку — мы буквально переписали историю нашего проекта. Это очень важный побочный эффект git rebase
.
Когда вы работаете в одиночку над проектом, переписывание истории не имеет большого значения. Однако, как только вы начнете работать в среде совместной работы, это может стать очень опасным. Если вы переписываете коммиты, которые используют другие разработчики (например, коммиты в master
ветке), это будет выглядеть так, как будто эти коммиты исчезли в следующий раз, когда они попытаются выполнить вашу работу. Это приводит к запутанному сценарию, от которого трудно оправиться.
Имея это в виду, вы никогда не должны перебазировать коммиты, которые были перенесены в публичный репозиторий, если вы не уверены, что никто не основывал свою работу на них.
Вывод
В этом руководстве представлены два наиболее распространенных варианта использования git rebase
. Мы много говорили о перемещении веток, но имейте в виду, что перебазирование — это действительно контроль истории вашего проекта. Возможность переписывать коммиты после факта позволяет вам сосредоточиться на задачах разработки, а не разбивать свою работу на отдельные снимки.
Обратите внимание, что перебазирование является совершенно необязательным дополнением к вашей панели инструментов Git. Вы все еще можете делать все, что вам нужно, с помощью простых старых команд git merge
. На самом деле, это безопаснее, так как избегает возможности переписывания публичной истории. Однако, если вы понимаете риски, git rebase
может быть гораздо более чистым способом интеграции веток по сравнению со слиянием коммитов.