Почти каждая система контроля версий поддерживает ветвление в той или иной форме. С помощью ветвления можно отклоняться от основной линии разработки и продолжать работу независимо от неё, не затрагивая основную ветку. Во многих СКВ создание веток является ресурсозатратным процессом, часто требующим создания новой копии каталога с исходным кодом, что может занять значительное время для больших проектов.
Ветки в Git, как и коммиты, чрезвычайно легковесны. Ветка в Git — это просто перемещаемый указатель, не более того. Именно поэтому многие фанаты Git повторяют мантру:
делай ветки сразу, делай ветки часто
Так как создание большого количества веток почти не влияет на использование памяти или места на жёстком диске, удобно создавать отдельные ветки для каждой задачи. Можно сказать, что ветка хранит изменения текущего коммита и всех его предшественников.
По умолчанию, основной ветке присваивается имя main
или master
. Как только вы начинаете делать коммиты, указатель основной ветки (main
) всегда будет указывать на последний коммит.
Пример работы с ветками
Создадим новую ветку с именем newImage
:
Мы создали новый указатель для ветки newImage
, который указывает на последний коммит в ветке main
. Однако мы остаёмся на ветке main
.
Команда git branch
не только создаёт ветки, но и позволяет просматривать существующие. Ветка, на которой вы находитесь, помечена звёздочкой:
$ git branch
* main
newImage
main
, а не к новой ветке newImage
.Чтобы переключиться на новую ветку, используем команду:
$ git checkout newImage
Переключено на ветку «newImage»
Теперь изменения будут сохраняться в ветке newImage
и не затронут ветку main
. Например, давайте изменим файл file.txt
и сделаем коммит:
echo "new branch" >> file.txt
git add file.txt
git commit -m "Четвертый коммит"
Результат:
[newImage 378501d] Четвертый коммит
1 file changed, 1 insertion(+)
Теперь, чтобы увидеть, как работают ветки, переключимся обратно на ветку main
и сделаем ещё один коммит:
git checkout main
echo "change main" >> three-file.txt
git add three-file.txt
git commit -m "Пятый коммит"
Результат:
[main 60e062b] Пятый коммит
1 file changed, 1 insertion(+)
Четвёртый и Пятый коммиты имеют одного и того же родителя (третий коммит), но они независимы друг от друга. Изменения в ветке newImage
не затрагивают разработку в ветке main
. Поэтому рекомендуется создавать отдельную ветку для каждой задачи и потом сливать её с основной веткой разработки.
Для создания новой ветки и переключения на неё в одной команде используйте:
$ git checkout -b your_branch_name
Merge
Мы уже знаем, как создавать ветки и коммитить изменения. Теперь нужно разобраться, как объединять изменения из двух разных веток после выполнения задачи в отдельной ветке.
Первый способ объединения изменений, который мы рассмотрим — это команда git merge
. Слияние (merge) создаёт специальный тип коммита, который имеет двух родителей. Коммит с двумя родителями обычно указывает на то, что мы объединяем изменения из одного коммита с другим и всеми их предшествующими коммитами.
Допустим, у нас есть две ветки: main
и newImage
. Мы сделали некоторую работу в ветке newImage
, например, это был наш четвёртый коммит. Теперь нужно влить эти изменения в ветку main
.
Находясь в ветке main
, проверим, что файл file.txt
не содержит изменений, сделанных ранее в ветке newImage
:
$ cat file.txt
12345
67890
Теперь выполним слияние ветки newImage
с веткой main
:
$ git merge newImage
Результат слияния:
Merge made by the 'recursive' strategy.
file.txt | 1 +
1 file changed, 1 insertion(+)
Мы создали коммит, который имеет двух родителей. Теперь проверим, что изменения из ветки newImage
появились в файле file.txt
в ветке main
:
$ cat file.txt
12345
67890
new branch
Также посмотрим историю коммитов (log), чтобы убедиться, что был создан новый коммит слияния:
$ git log -2
commit b5c01a39ed365c8c61ba7becbb84b6a45cd96052 (HEAD -> main)
Merge: 60e062b 378501d
Author: uPagge
Date: Tue Jun 15 22:20:47 2021 +0300
Merge branch 'newImage'
commit 60e062b14597415fbea93ed73746db7d14c161ce
Author: uPagge
Date: Tue Jun 15 22:16:11 2021 +0300
Пятый коммит
Rebase
Второй способ объединения изменений из разных веток — это rebase. При выполнении ребейза Git фактически копирует набор коммитов и переносит их в другое место.
Хотя это может звучать немного сложно, основное преимущество rebase заключается в том, что он позволяет создавать чистую и красивую линейную последовательность коммитов. История становится более упорядоченной, если использовать rebase.
Создадим новую ветку bugFix
от ветки main
:
$ git checkout -b bugFix
Переключено на новую ветку «bugFix»
Внесём изменения и сделаем коммит в ветке bugFix
:
$ echo "bugFix" >> file.txt
$ git add file.txt
$ git commit -m "Коммит в ветке bugFix"
[bugFix 7feb311] Коммит в ветке bugFix
1 file changed, 1 insertion(+)
Теперь вернёмся в ветку main
, сделаем изменения и коммит:
$ git checkout main
Переключено на ветку «main»
$ echo "main" >> new-file.txt
$ git add new-file.txt
$ git commit -m "Коммит в ветке main"
[main 3a59d58] Коммит в ветке main
1 file changed, 1 insertion(+)
Перед выполнением rebase посмотрим лог коммитов в ветке main
:
$ git log --oneline
3a59d58 (HEAD -> main) Коммит в ветке main
b5c01a3 Merge branch 'newImage'
60e062b Пятый коммит
378501d (newImage) Четвёртый коммит
b64191a Третий коммит
b66f9c7 Второй коммит
06f7fc0 Первый коммит
Теперь выполним rebase ветки bugFix
на ветку main
:
$ git rebase bugFix
Сначала перематываем указатель текущего коммита, чтобы применить ваши изменения поверх него...
Применение: Коммит в ветке main
После выполнения rebase снова проверим лог:
git log --oneline
8868e84 (HEAD -> main) Коммит в ветке main
7feb311 (bugFix) Коммит в ветке bugFix
b5c01a3 Merge branch 'newImage'
60e062b Пятый коммит
378501d (newImage) Четвертый коммит
b64191a Третий коммит
b66f9c7 Второй коммит
06f7fc0 Первый коммит
Как видно, коммит из ветки bugFix
теперь находится перед коммитом из ветки main, создавая линейную последовательность.
Также проверим содержимое файла file.txt
:
$ cat file.txt
12345
67890
new branch
bugFix
Git перенёс копию коммита из ветки bugFix
после коммита из ветки main
и обновил указатель ветки.
Теперь можно удалить ветку bugFix, так как её изменения уже были применены:
$ git branch -d bugFix
Ветка bugFix удалена (была 7feb311).
Проверим лог ещё раз:
git log --oneline
8868e84 (HEAD -> master) Коммит в ветке main
7feb311 Коммит в ветке bugFix
b5c01a3 Merge branch 'newImage'
60e062b Пятый коммит
378501d (newImage) Четвертый коммит
b64191a Третий коммит
b66f9c7 Второй коммит
06f7fc0 Первый коммит
Коммит из ветки bugFix
остался в истории, однако указатель на ветку был удалён.
Заключение
Мы рассмотрели работу с ветками в Git и процесс их слияния. В идеальном проекте все ветки стремятся быть объединёнными в основную ветку. Не забывайте создавать отдельные ветки от основной ветки разработки для каждой отдельной задачи. Это позволяет изолировать изменения и поддерживать чистоту истории проекта.