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

Запуск git status в чистой рабочей копии выводит следующую информацию:

git_course$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

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

racket$ git branch
* master

Для понимания значения слова «ветка» давайте посмотрим, что из себя представляет история коммитов:

Git Master

Слева первый коммит, затем второй, который ссылается на первый, а в конце третий. Каждый новый коммит содержит ссылку на предыдущий. Это позволяет прослеживать связь между коммитами, а способ организации коммитов называется линейный список.

Представьте ситуацию, в которой появилось два коммита, родителем которых является один и тот же коммит:

Git Branches

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

Только что инициализированный репозиторий содержит только одну ветку — master. Она используется в качестве основной и выбрана сразу. Как только вы начинаете работать с новыми репозиторием, вы работаете в ветке master, её не нужно специально выбирать. Всё остальное опционально. Вот основные причины использовать ветвление:

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

Ниже пример того, как в git работать с ветками:

racket$ git branch new-feature # создание новой ветки
racket$ git branch
* master
  new-feature

racket$ git checkout new-feature # переключение на новую ветку
Switched to branch 'new-feature'

racket$ git branch
  master
* new-feature

racket$ git log --oneline --decorate --graph
* c082d77 (HEAD -> new-feature, master) fix stripping
* 442c2fd Correct typo
* b2b53d6 fix test that uses IPv6

Некоторые выводы из приведённого листинга:

  1. Каждая ветка имеет уникальное имя, которое задаётся при создании ветки.
  2. Команда git branch показывает не только все доступные ветки, но также и отмечает текущую символом *.

У внимательного читателя возможно возник вопрос: как Git определяет, где мы сейчас находимся? Ответ на этот вопрос можно увидеть в этой строчке:

* c082d77 (HEAD -> new-feature, master) fix stripping

Любой односвязный список содержит голову (HEAD). Это последний добавленный в него элемент (в списках добавление всегда идёт в начало, то есть новая голова "нанизывается" на предыдущую голову). Git работает точно так же: когда мы делаем любой коммит, он становится HEAD. Другими словами, понятие HEAD это, по сути, ссылка на некоторый коммит. А отсюда следует интересная вещь. Помните, что мы можем переключиться на любой коммит (абсолютно любой) и загрузить его снимок в рабочую копию командой git checkout идентификатор? Так вот, после того, как мы это проделаем, HEAD начинает указывать именно на загруженный коммит. Внутри происходит изменение указателя HEAD и загрузка в рабочую копию соответствующего снимка.

racket$ git checkout ed8145e
racket$ git status
HEAD detached at ed8145e

racket$ git checkout master
Previous HEAD position was ed8145e... fix doc typo
Switched to branch 'master'

Каждая ветка – это, всего лишь, точно такой же указатель на определённый коммит, а сами ветки определяются исключительно связями между коммитами. В этом легко убедиться, если изучить папку refs внутри .git:

racket$ ls .git/refs/heads/
master      new-feature

racket$ cat .git/refs/heads/new-feature
c082d77c971f7a042cf69de9df150c80e9384b71

racket$ cat .git/HEAD
ref: refs/heads/new-feature

Видно, что ветка – это файл с именем ветки и записанным в него идентификатором коммита, а HEAD – это файл, внутри которого стоит ссылка, в данном случае на new-feature. То есть это состояние ситуации, при которой мы только что переключились на ветку new-feature, и HEAD совпадёт с последним коммитом в new-feature.

Из всего сказанного следует, что коммиты не принадлежат веткам. Структура коммитов существует сама по себе безотносительно них. Ветки накладываются поверх коммитов как указатели. Это, например, означает, что изменение коммита общего для нескольких веток (а такое можно делать используя rebase) отражается на всех ветках, в которые попал этот коммит.

Слияние

Предположим, что вы реализовали фичу и теперь хотите, чтобы этот код оказался в ветке master. Делается это командой git merge, которая сливает ветки между собой. Результатом слияния, становится особенный merge commit. Его отличие от остальных коммитов в том, что этот коммит не содержит изменений, но он ссылается сразу на двух родителей, которыми выступают последние коммиты в какой-либо ветке и ветке master в момент слияния. Именно благодаря тому, что он ссылается на последний (на момент слияния) коммит из ветки, Git узнает о том, какие были выполнены изменения и как нужно изменить код мастера.

Git Merge

racket$ git checkout master
racket$ git merge new-feature
Updating c082d77..d7b64b2
Fast-forward
 INSTALL.txt | 1 +
 1 file changed, 1 insertion(+)

Команда git merge выполняется из той ветки, в которую мы сливаем изменения, а аргументом передаётся имя той ветки, которую мы сливаем.

Перемещение

Команда git rebase относится к продвинутым техникам Git, но именно она делает вас настоящим властелином репозиториев. Как только основы будут освоены, окунитесь в её изучение.

Дополнительно

https://learngitbranching.js.org/

Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →