Запуск 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 показывает не только все доступные ветки, но так же и отмечает текущую символом *.

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

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

Любой односвязный список содержит голову (HEAD). Это последний добавленный в него элемент (в списках добавление всегда идет в начало, то есть новая голова "нанизывается" на предыдущую голову). Гит работает точно так же: когда мы добавляем любой коммит, он становится 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) отражается на всех ветках, в которые попал этот коммит.

Merge

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

Rebase

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

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

http://learngitbranching.js.org/