Статьи

Git лаконично: ветви

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

Рисунок 18: Основная разветвленная разработка
Основное разветвленное развитие

Это дает вам такое же спокойствие, как и создание «безопасной» копии вашего проекта, но теперь у вас есть дополнительные возможности для работы с несколькими версиями одновременно. Ветви обеспечивают нелинейный рабочий процесс — способность разрабатывать несвязанные функции параллельно. Как мы обнаружим позже, нелинейный рабочий процесс является важным предшественником распределенной природы модели сотрудничества Git.

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


Git разделяет функциональность ветки на несколько разных команд. Команда git branch используется для перечисления, создания или удаления веток.

Прежде всего, вам нужно будет просмотреть свои существующие ветки:

1
git branch

Это выведет все ваши текущие ветки вместе со звездочкой рядом с той, которая в данный момент «проверена» (подробнее об этом позже):

1
2
3
* master
some-feature
quick-bug-fix

Основная ветвь — это ветка Git по умолчанию, которая создается с первым коммитом в любом репозитории. Многие разработчики используют эту ветку как «основную» историю проекта — постоянную ветку, которая содержит все важные изменения, через которые он проходит.

Вы можете создать новую ветку, передав имя ветви той же команде git branch :

1
git branch <name>

Это создает указатель на текущий HEAD , но не переключается на новую ветвь (для этого вам понадобится git checkout ). Сразу после запроса новой ветки ваш репозиторий будет выглядеть примерно так:

Рисунок 19: Создание новой ветки
Создание новой ветки

Ваша текущая ветвь ( master ) и новая ветвь ( some-feature ) оба ссылаются на один и тот же коммит, но любые новые зафиксированные вами коммиты будут эксклюзивными для текущей ветки. Опять же, это позволяет вам параллельно работать с несвязанными функциями, сохраняя при этом разумную историю. Например, если ваша текущая ветвь была some-feature , ваша история будет выглядеть следующим образом после фиксации снимка.

Рисунок 20: Фиксация в ветке some-feature
Фиксация в some-feature ветке

Новый HEAD (обозначенный выделенным коммитом) существует только в ветви some-feature . Он не будет отображаться в журнале вывода master , и его изменения не появятся в рабочем каталоге после того, как вы извлечете master .

Вы действительно можете увидеть новую ветку во внутренней базе данных, открыв файл .git/refs/heads/<name> . Файл содержит идентификатор ссылочного коммита и является единственным определением ветки Git. По этой причине филиалы настолько легки и просты в управлении.

Наконец, вы можете удалить ветки с помощью флага -d :

1
git branch -d <name>

Но преданность Git тому, чтобы никогда не терять свою работу, не позволяет ему удалять ветки с неотправленными коммитами. Чтобы принудительно удалить, используйте вместо этого флаг -D :

1
git branch -D <name>

Неизмененные изменения будут потеряны, поэтому будьте очень осторожны с этой командой.


Конечно, создание веток бесполезно без возможности переключения между ними. Git называет это «проверкой» ветки:

1
git checkout <branch>

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

Рисунок 21: Проверка разных веток
Проверка разных веток

Имея это в виду, обычно рекомендуется иметь чистый рабочий каталог перед проверкой ветки. Чистый каталог существует, когда нет незафиксированных изменений. Если это не так, git checkout может перезаписать ваши модификации.

Как и при совершении «безопасной» ревизии, вы можете экспериментировать с новой веткой, не боясь разрушить существующую функциональность. Но теперь у вас есть отдельная история для работы, поэтому вы можете записывать ход эксперимента, используя точно такие же команды git add и git commit ранее в этой книге.

Рисунок 22: Разработка нескольких функций параллельно
Разработка нескольких функций параллельно

Эта функциональность станет еще более мощной, когда мы научимся объединять расходящиеся истории обратно в «основную» ветвь (например, master ). Мы вернемся к этому через мгновение, но сначала есть важный пример использования git checkout который необходимо учитывать …

Git также позволяет вам использовать git checkout с тегами и идентификаторами коммитов, но при этом вы переводите вас в состояние HEAD . Это означает, что вы больше не находитесь на ветке — вы непосредственно просматриваете коммит.

Рисунок 23: Проверка старого коммита
Проверка старого коммита

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

1
git checkout -b <new-branch-name>

Это ярлык для git branch <new-branch-name> за которым следует git checkout <new-branch-name> . После этого у вас будет новая блестящая ссылка на ветвь для ранее отсоединенного HEAD . Это очень удобная процедура для выведения экспериментов из старых ревизий.


Слияние — это процесс извлечения коммитов из одной ветви в другую. Есть много способов объединить филиалы, но цель всегда состоит в том, чтобы обмениваться информацией между филиалами. Это делает слияние одной из самых важных функций Git. Две наиболее распространенные методологии слияния:

  • Быстрое слияние
  • «Трехстороннее» слияние

Они оба используют одну и ту же команду git merge , но метод определяется автоматически на основе структуры вашей истории. В каждом случае ветвь, в которую вы хотите объединиться, должна быть извлечена , и целевая ветвь останется неизменной. В следующих двух разделах представлены два возможных сценария слияния для следующих команд:

1
2
git checkout master
git merge some-feature

Опять же, это объединяет ветвь с some-feature в master ветвь, оставляя прежнюю нетронутой. Обычно вы запускаете эти команды после завершения функции и хотите интегрировать ее в стабильный проект.

Первый сценарий выглядит так:

Рисунок 24: Перед слиянием в ускоренном режиме
Перед слиянием в ускоренном режиме

Мы создали ветку для разработки новой функции, добавили два коммита, и теперь она готова для интеграции в основную базу кода. Вместо того, чтобы переписать два коммита, отсутствующие в master , Git может «перемотать вперед» указатель master ветки, чтобы найти расположение some-feature .

Рисунок 25: После ускоренного слияния
После ускоренного слияния

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

Конечно, мы могли бы сделать два коммита непосредственно в master ветке; однако использование специальной ветки функций дало нам безопасную среду для экспериментов с новым кодом. Если бы это оказалось не совсем правильно, мы могли бы просто удалить ветку (в отличие от сброса / возврата). Или, если мы добавили несколько промежуточных коммитов, содержащих неработающий код, мы могли бы очистить его перед тем, как объединить его с master (см. Перебазирование ниже). По мере того, как проекты становятся все более сложными и приобретают все больше соавторов, такая разветвленная разработка делает Git фантастическим организационным инструментом.

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

Рисунок 26: До трехстороннего слияния
Перед 3-сторонним слиянием

Это началось как быстрое слияние, но мы добавили коммит в ветку master пока еще разрабатывали some-feature . Например, мы могли бы перестать работать над этой функцией, чтобы исправить чувствительную ко времени ошибку. Конечно, исправление ошибки должно быть добавлено в основной репозиторий как можно скорее, поэтому мы попадаем в сценарий, показанный выше.

В этом контексте объединение ветви компонента с master приводит к «трехстороннему» слиянию. Это выполняется с помощью тех же команд, что и при ускоренном слиянии из предыдущего раздела.

Рисунок 27: После трехстороннего слияния
После трехстороннего слияния

Git не может быстро переместить master указатель к some-feature без возврата. Вместо этого он генерирует новый коммит слияния, который представляет объединенный снимок обеих ветвей. Обратите внимание, что этот новый коммит имеет два родительских коммита, предоставляя ему доступ к обеим историям (действительно, при запуске git log после трехстороннего слияния отображаются коммиты из обеих ветвей).

Название этого алгоритма слияния происходит от внутреннего метода, используемого для создания коммита слияния. Git просматривает три коммита для генерации конечного состояния слияния.

Если вы попытаетесь объединить две ветви, которые вносят различные изменения в одну и ту же часть кода, Git не будет знать, какую версию использовать. Это называется конфликтом слияния . Очевидно, что это никогда не может произойти во время быстрого слияния. Когда Git встречает конфликт слияния, вы увидите следующее сообщение:

1
2
3
Auto-merging index.html
CONFLICT (content): Merge conflict in <file>
Automatic merge failed;

Вместо того, чтобы автоматически добавлять коммит слияния, Git останавливается и спрашивает вас, что делать. Запуск git status в этой ситуации вернет что-то вроде следующего:

1
2
3
4
# On branch master
# Unmerged paths:
#
# both modified: <file>

Каждый файл с конфликтом хранится в разделе «Unmerged paths». Git комментирует эти файлы, чтобы показать вам содержимое обеих версий:

1
2
3
4
5
<<<<<<< HEAD
  This content is from the current branch.
=======
    This is a conflicting change from another branch.
>>>>>>> some-feature

Часть перед ======= от master ветви, а остальная часть от ветви, которую вы пытаетесь интегрировать.

Чтобы разрешить конфликт, удалите <<<<<< , ======= и >>>>>>> и измените код на тот, который вы хотите сохранить. Затем скажите Git, что вы решили проблему с помощью команды git add :

1
git add <file>

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

1
git commit

Сообщение журнала сопровождается уведомлением о слиянии, а также списком «конфликтов», который может быть особенно полезен при попытке выяснить, где что-то пошло не так в проекте.

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


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

Все ветвящиеся рабочие процессы вращаются вокруг команд git branch , git checkout и git merge представленных ранее в этой главе.

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

Все ветки могут быть классифицированы как постоянные ветки или тематические ветки . Первые содержат основную историю проекта (например, master ), в то время как последние являются временными ветвями, используемыми для реализации определенной темы , а затем отбрасываются (например, some-feature ).

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

Кроме того, многие пользователи добавляют второй уровень абстракции в другую ветвь интеграции (условно называемую develop , хотя подойдет любое имя). Это освобождает master ветвь для действительно стабильного кода (например, открытых коммитов) и использует develop как внутреннюю интеграционную ветвь для подготовки к публичному выпуску. Например, на следующей диаграмме показано несколько функций, интегрируемых в develop , а затем одно окончательное слияние с master , которое символизирует публичный выпуск.

Рисунок 28: Использование главной ветки исключительно для публичных релизов
Использование master ветки исключительно для публичных релизов

Ветви тем обычно делятся на две категории: ветки функций и ветки исправлений . Ветви компонентов — это временные ветви, которые инкапсулируют новую функцию или рефакторинг, защищая основной проект от непроверенного кода. Обычно они происходят из другой ветви функций или ветви интеграции, но не из «супер стабильной» ветви.

Рисунок 29: Разработка функции в изолированной ветке
Разработка функции в изолированной ветке

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

Рисунок 30: Мастер исправлений с веткой исправлений
master исправлений с веткой исправлений

Опять же, значения, назначенные каждой из этих веток, являются чисто условными — Git не видит различий между master , develop , функциями и исправлениями. Имея это в виду, не бойтесь адаптировать их к своим собственным целям. Прелесть Git в его гибкости. Когда вы поймете механизм, лежащий в основе веток Git, легко спроектировать новые рабочие процессы, соответствующие вашему проекту и индивидуальности.


Перебазирование — это процесс перемещения ветки на новую базу . Возможности Git по перебазированию делают ветки еще более гибкими, позволяя пользователям вручную организовывать свои ветки. Как и слияние, git rebase требует извлечения ветки и принимает новую базу в качестве аргумента:

1
2
git checkout some-feature
git rebase master

Это переместит всю ветвь some-feature на кончик master :

Рисунок 31: Перебазирование некоторых функций в главную ветвь
Перебазирование some-feature в master ветку

После перебазирования ветвь функций представляет собой линейное расширение master , что является более понятным способом интеграции изменений из одной ветки в другую. Сравните эту линейную историю с объединением master с some-feature , что приводит к точно такой же базе кода в конечном снимке:

Рисунок 32: Интеграция мастера в некоторые функции с 3-сторонним слиянием
Интеграция master в some-feature с 3-сторонним слиянием

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

Эти дополнительные коммиты слияния излишни — они существуют только для того, чтобы перенести изменения из master в some-feature . Как правило, вы хотите, чтобы ваши коммиты слияния что- то значили, например, завершение новой функции. Вот почему многие разработчики предпочитают вносить изменения с помощью git rebase , поскольку это приводит к совершенно линейной истории в ветви функций.

Интерактивный ребазинг делает еще один шаг вперед и позволяет вам изменять коммиты по мере их перемещения на новую базу. Вы можете указать интерактивное перебазирование с флагом -i :

1
git rebase –i master

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

1
2
pick 58dec2a First commit for new feature
squash 6ac8a9f Second commit for new feature

Команда pick умолчанию перемещает первый коммит в новую базу точно так же, как и обычная git rebase , но затем команда squash говорит Git объединить второй коммит с предыдущим, так что вы получите один коммит, содержащий все ваши изменения:

Рисунок 33: Интерактивная перебазировка ветки some-feature
Интерактивно перебирая ветку some-feature

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

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

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

Как вы можете себе представить, это имеет драматические последствия для совместных рабочих процессов. Уничтожить публичный коммит (например, что-нибудь в master ветке) — это все равно, что вырвать основу работы всех остальных. Git не будет иметь никакого представления о том, как объединить все изменения, и вам придется извиниться. Мы более подробно рассмотрим этот сценарий после того, как научимся взаимодействовать с удаленными репозиториями.

А пока просто соблюдайте золотое правило перебазирования: никогда не перебрасывайте ветку, которая была перенесена в публичный репозиторий .

Этот урок представляет собой главу от Git Succinctly , бесплатной электронной книги от команды Syncfusion .