Модуль 3 · Ветки

Справочник по Git и GitHub · модуль 3 · ветки

Глава 3.1

3.1

Ветки:
зачем нужны и как устроены

В главе разобрано, что такое ветка в Git, для чего ветки заводят и как Git их хранит. На проекте sait-vizitka показано, где в VS Code видно имя ветки, в каком файле оно записано и как ветка сдвигается при коммите. Создание веток и переключение между ними разобраны в главе 3.2.

Категория
Ветки
Уровень
Начальный
Что получится
Понимание, что такое ветка, HEAD и чем main отличается от origin/main
Сколько занимает
Около двадцати минут за компьютером

§1Зачем нужны ветки

В модуле 2 история проекта была одной цепочкой: каждый новый коммит вставал после предыдущего. Такой порядок подходит, пока в проекте идёт одна работа. Когда работ две, их коммиты в одной цепочке перемешиваются, и незаконченная работа попадает в проект вместе с готовой.

Ветка — отдельная линия коммитов внутри одного репозитория. Коммиты одной ветки не попадают в другую, пока ветки не объединят. Папка проекта при этом одна: Git показывает в ней файлы той ветки, на которой вы стоите.

коммит 1 коммит 2 коммит 3 срочная правка коммиты новой страницы основная линия вторая ветка
От третьего коммита работа идёт двумя линиями. Коммиты новой страницы лежат во второй ветке и в основную линию не входят, пока ветки не объединят.

Зачем это нужно в работе

  • Новая страница и работающий сайт. Форму обратной связи делают несколько дней. Её коммиты лежат в отдельной ветке, а основная линия остаётся в рабочем состоянии.
  • Срочное исправление. Во время работы над формой на сайте нашли опечатку. Её исправляют в основной линии, незаконченная форма в это исправление не попадает.
  • Пробный вариант. Новое оформление пробуют в отдельной ветке. Если вариант не подошёл, ветку удаляют, основная линия не меняется.
  • Несколько участников. Каждый ведёт свою задачу в своей ветке. Готовую задачу проверяют и объединяют с основной линией.

В проекте sait-vizitka ветка уже есть. Она называется main, и все коммиты модуля 2 сделаны в ней. В этой главе на ветке main разбирается, как Git хранит ветку. Вторая ветка создаётся в главе 3.2.

В главе используется проект sait-vizitka в том виде, в каком он остался после главы 2.6: четырнадцать коммитов, десять из них на GitHub не отправлены. Имена коммитов, сделанных вами, будут отличаться от показанных.

§2Шаг 1. Найти имя ветки в VS Code

Имя ветки, на которой вы стоите, VS Code показывает в строке состояния, в графе и в подсказке поля сообщения.

Открываем проект и панель Source Control: CtrlCmd + Shift + G.

Панель Source Control: в графе у верхнего коммита метка main, в строке состояния внизу слева имя main и счётчик 10 со стрелкой вверх
Имя main стоит в строке состояния внизу слева и в графе у верхнего коммита. Подсказка в поле сообщения тоже называет ветку: commit on "main".

Строка состояния показывает ветку, на которой вы стоите. Метка в графе показывает коммит, которым эта ветка заканчивается. В главе 2.4 main называлась меткой. Её точное название — ветка.

То же в терминале (Ctrl + `):

терминал в папке проектакоманда и ответ
git branch --show-current
main

Что получилось.Имя текущей ветки main найдено в строке состояния, в графе и в ответе команды.

§3Шаг 2. Открыть список веток

В репозитории может быть несколько веток. Список веток открывается из строки состояния.

Нажимаем на имя main в строке состояния. Вверху окна открывается список:

Список веток вверху окна: пункты Create new branch, Create new branch from, Checkout detached, затем ветка main и ветка origin/main
Три верхние строки — команды. Под ними две ветки: main в группе branches и origin/main в группе remote branches.
Строка спискаЧто это
Create new branch…Команда создания ветки. Используется в главе 3.2
main, группа branchesВетка на вашем компьютере. Под именем указаны автор, имя и сообщение её последнего коммита
origin/main, группа remote branchesЗапись о ветке main в копии проекта на GitHub. Её последний коммит — «Добавил README с описанием проекта»

Список закрываем клавишей Esc, ничего не выбирая. То же в терминале:

терминал в папке проектакоманда и ответ
git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main

Команда git branch без ключа печатает только ветки на компьютере, с ключом -a — ещё и записи о ветках сервера. Звёздочка отмечает ветку, на которой вы стоите. Строка remotes/origin/HEAD -> origin/main сообщает, какая ветка на сервере считается главной.

Что получилось.В репозитории одна ветка на компьютере, main, и одна запись о ветке сервера, origin/main.

§4Шаг 3. Найти файл ветки

Ветка хранится в папке .git как небольшой файл. Найдём его.

В терминале выводим содержимое папки, в которой Git хранит ветки:

терминал в папке проектакоманда и ответ
ls .git/refs/heads
main

В папке один файл, и он называется так же, как ветка. В PowerShell ответ выглядит как таблица, имя файла в ней то же. Читаем файл:

терминал в папке проектакоманда и ответ
cat .git/refs/heads/main
cd668045e081279c0fc52e58236a16491577a258

В файле одна строка из сорока символов. Так выглядит полное имя коммита, оно разобрано в главе 2.4. Сравниваем с последним коммитом проекта:

терминал в папке проектакоманда и ответ
git log --oneline -1
cd66804 Revert "Увеличил отступы страницы"

Первые семь символов совпадают: в файле ветки записан последний коммит.

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

… b2b1492Добавил ссылку 7346da3Покрасил заголовок cd66804Revert … main
Пунктирные стрелки: в каждом коммите записано имя предыдущего. Имя main указывает на один коммит — последний. Остальные Git находит по цепочке.

Где в папке .git записаны ветки

.git/репозиторий
  ├── HEADимя ветки, на которой вы стоите
  ├── refs/heads/ветки на компьютере, по одному файлу на ветку
  │   └── mainимя последнего коммита ветки main
  └── refs/remotes/origin/записи о ветках сервера
На полях: если файла ветки нет

При служебной упаковке репозитория Git переносит записи о ветках из отдельных файлов в один общий файл .git/packed-refs. Тогда команда cat сообщит, что файла нет. Имя коммита ветки в любом случае показывает команда git rev-parse main.

Что получилось.Найден файл .git/refs/heads/main. В нём записано имя последнего коммита ветки.

§5Шаг 4. Прочитать запись HEAD

Веток может быть несколько, а стоите вы всегда на одной. Какая ветка текущая, Git записывает в файле HEAD.

терминал в папке проектакоманда и ответ
cat .git/HEAD
ref: refs/heads/main

В файле записан путь к файлу ветки из шага 3. HEAD — запись о том, на какой ветке вы стоите. По ней VS Code выводит имя ветки в строке состояния, а команда git branch ставит звёздочку.

… b2b1492Добавил ссылку 7346da3Покрасил заголовок cd66804Revert … main HEAD
В HEAD записано имя ветки, в ветке — имя коммита. По этим двум записям Git определяет коммит, на котором вы стоите.

В главе 2.6 запись HEAD в командах означала коммит, на котором вы стоите. Git находит его в два шага: читает в HEAD имя ветки, затем в файле ветки имя коммита. Запись HEAD~ означает коммит перед ним.

Что получилось.В HEAD записано ref: refs/heads/main: текущая ветка — main.

§6Шаг 5. Сделать коммит и проверить ветку

В файле ветки записан последний коммит. Проверим, что происходит с этой записью, когда появляется новый коммит.

В index.html под ссылкой на контакты добавляем строку:

index.htmlбыло
  <p><a href="kontakty.html">Контакты</a></p>
</body>
index.htmlстало
  <p><a href="kontakty.html">Контакты</a></p>
  <p>Страница обновлена в октябре 2026 года.</p>
</body>

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

терминал в папке проектато же, что плюс и Commit
git add index.html
git commit -m "Добавил дату обновления страницы"
Панель Source Control: в графе сверху коммит «Добавил дату обновления страницы» с меткой main, на кнопке Sync Changes 11 со стрелкой вверх
Метка main перешла на новый коммит. Коммит revert, у которого она стояла, теперь вторая строка графа. На кнопке 11↑.

Читаем оба файла ещё раз:

терминал в папке проектакоманды и ответ
cat .git/refs/heads/main
82fd14a052d87f56416c01e19570b560b6e7a884
cat .git/HEAD
ref: refs/heads/main
git log --oneline -2
82fd14a Добавил дату обновления страницы
cd66804 Revert "Увеличил отступы страницы"

В файле ветки теперь имя нового коммита. Запись в HEAD осталась прежней: ветка та же, изменился её последний коммит.

… b2b1492Добавил ссылку 7346da3Покрасил заголовок cd66804Revert … 82fd14aДобавил дату … main HEAD
Новый коммит 82fd14a записал cd66804 как предыдущий. Имя main сдвинулось на него, запись в HEAD не менялась.

При каждом коммите Git делает два действия. Создаёт коммит, в котором предыдущим записан коммит из файла текущей ветки. Затем записывает в файл ветки имя нового коммита. Ветка сдвигается на один коммит вперёд.

Команды главы 2.6 меняли этот же файл. git reset HEAD~ записывает в файл ветки имя предыдущего коммита, поэтому последний коммит пропадает из графа. Сам коммит при этом не удаляется, и его находит git reflog.

Что получилось.После коммита в .git/refs/heads/main записан новый коммит 82fd14a, в проекте пятнадцать коммитов.

§7Шаг 6. Сравнить main и origin/main

В списке шага 2 была вторая строка, origin/main. Сравним её с веткой main.

терминал в папке проектакоманда и ответ
git branch -a -v
* main                82fd14a [ahead 11] Добавил дату обновления страницы
  remotes/origin/HEAD -> origin/main
  remotes/origin/main 745f2e1 Добавил README с описанием проекта

Ключ -v добавляет к каждой ветке имя и сообщение её последнего коммита.

ЗаписьГде ведётсяПоследний коммитКогда сдвигается
mainНа вашем компьютере82fd14a, сделан в шаге 5При каждом вашем коммите, amend, reset и revert
origin/mainНа вашем компьютере, как запись о сервере745f2e1, получен при клонированииТолько при обмене с GitHub

origin/main — запись о том, на каком коммите ветка main была на GitHub при последнем обмене. Обмена после клонирования не было, поэтому запись стоит на том же коммите, что в главе 2.2. Ваши коммиты её не сдвигают.

Пометка [ahead 11] означает, что в ветке main одиннадцать коммитов, которых нет в origin/main. То же число стоит на кнопке Sync Changes и в строке состояния: 11↑. Отправка коммитов разобрана в модуле 4.

Что получилось.main стоит на коммите 82fd14a, origin/main — на 745f2e1. Между ними одиннадцать неотправленных коммитов.

§8Справка: что добавляет вторая ветка

Ветка — файл с именем одного коммита. Поэтому вторая ветка добавляет в репозиторий один файл в папке .git/refs/heads. В нём записан тот же коммит, на котором вы стоите.

… cd66804Revert … 82fd14aДобавил дату … main forma-kontaktov HEAD ↓
Новая ветка — второе имя у того же коммита. Коммиты и файлы проекта при этом не копируются.
ЧтоПри создании ветки
Файлы проектаНе копируются. Папка проекта одна
КоммитыНе копируются. Обе ветки ведут к одним и тем же коммитам
Папка .git/refs/headsПоявляется второй файл из сорока одного символа: имя коммита и перевод строки
Запись HEADМеняется, когда вы переходите на другую ветку

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

… cd66804Revert … 82fd14aДобавил дату … новый коммитв ветке формы main forma-kontaktov HEAD →
Коммит сделан, когда в HEAD записана ветка forma-kontaktov. Её имя перешло на новый коммит, имя main осталось на 82fd14a.

Если затем перейти на main и сделать коммит там, сдвинется main. У каждой ветки появляется свой последний коммит, а общая история заканчивается на 82fd14a:

… cd66804Revert … 82fd14aДобавил дату … новый коммитв ветке формы новый коммитв ветке main main forma-kontaktov HEAD →
Две линии из схемы в начале главы. Коммит ветки forma-kontaktov в main не входит, коммит ветки main не входит в forma-kontaktov.

Ассоциация: закладки в тетради

В жизни

В тетради с записями лежит закладка на последней исписанной странице. Тетрадь одна. Закладка ничего не содержит, она отмечает место. Вторая закладка не добавляет в тетрадь страниц.

В Git

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

Где не совпадаетЗакладку передвигают рукой, а ветка сдвигается сама при каждом коммите. В тетради после страницы идёт одна следующая, а в Git после одного коммита могут идти два разных: по одному в каждой ветке.

§9Справка: имена веток

Имя первой ветки задаёт настройка init.defaultBranch из главы 1.3, в этом курсе это main. Остальным веткам имя даёт тот, кто их создаёт. Имя ветки становится именем файла, поэтому на него действуют ограничения:

ПравилоПодходитНе подходит
Латинские буквы, цифры, дефисforma-kontaktovформа контактов: кириллица неудобна в командах, пробел запрещён
Имя называет задачуtemnaya-temaproba2, new: по имени не видно, что в ветке
Косая черта делит имя на группу и задачуfix/opechatka-v-kontaktahfix/: имя не может заканчиваться косой чертой
Без служебных знаковstranica-proektovИмена с ~, ^, :, ?, *, двумя точками подряд или с дефисом в начале

Ветку main Git обрабатывает так же, как остальные. Главной её считают потому, что в ней держат проверенное состояние проекта и GitHub показывает её на странице репозитория по умолчанию.

На полях: ветку main не переименовывают

В проектах этого курса имя main менять нельзя. Команда переименования в Git и в VS Code есть, но она меняет имя только на вашем компьютере. На GitHub ветка по-прежнему называется main, и запись origin/main относится к ней. После переименования имена у вас и на сервере не совпадают, и отправка коммитов останавливается с ошибкой The upstream branch of your current branch does not match the name of your current branch. На имя main настроены и другие части проекта: страница репозитория, публикация сайта, проверки. Новым веткам имя выбирают свободно, main оставляют как есть.

§10Частые вопросы

Обычно думаютВетка — это копия папки проекта.
На самом делеПапка проекта одна. Ветка — файл с именем одного коммита в .git/refs/heads.
Обычно думаютВетки нужны только в команде.
На самом делеОдин человек ведёт в ветке незаконченную работу или пробный вариант, а основная линия остаётся рабочей.
Обычно думаютВ ветке записан список её коммитов.
На самом делеВ ветке записан один коммит, последний. Остальные Git находит по записи о предыдущем коммите внутри каждого коммита.
Обычно думаютorigin/main показывает, что сейчас лежит на GitHub.
На самом делеorigin/main показывает, что было на GitHub при последнем обмене. Запись обновляется только при обмене с сервером.
Обычно думаютHEAD и main — одно и то же.
На самом делеmain — ветка, в ней записан коммит. В HEAD записано имя ветки, на которой вы стоите. Сейчас это main, после перехода на другую ветку запись изменится.

§11Проверьте себя

Что записано в файле .git/refs/heads/main?

Полное имя одного коммита, сорок символов. Это последний коммит ветки main.

В ветке записан только последний коммит. Как Git показывает всю историю ветки?

В каждом коммите записано имя предыдущего. Git начинает с коммита из файла ветки и идёт по этим записям до первого коммита проекта.

Что изменится в папке .git после нового коммита в ветке main?

Появится новый коммит, и в файле .git/refs/heads/main будет записано его имя. Файл HEAD не изменится: в нём по-прежнему ref: refs/heads/main.

Команда cat .git/HEAD напечатала ref: refs/heads/main. Что это значит?

Вы стоите на ветке main. Новый коммит сдвинет эту ветку. То же имя показано в строке состояния VS Code.

В ответе git branch -a -v у ветки main стоит [ahead 11]. Что это значит?

В ветке main одиннадцать коммитов, которых нет в origin/main, то есть они не отправлены на GitHub. То же число показано на кнопке Sync Changes.

Сколько места займёт в репозитории новая ветка, созданная на текущем коммите?

Один файл из сорока одного символа в папке .git/refs/heads. Файлы проекта и коммиты не копируются.

§12Шпаргалка по главе

Что такое ветка имя → последний коммит линии файл в .git/refs/heads
Что такое HEAD запись о текущей ветке файл .git/HEAD
Где видно в VS Code строка состояния, слева внизу метка в разделе GRAPH щелчок по имени — список веток
Команды git branch --show-current git branch -a -v git rev-parse main
Файлы cat .git/HEAD cat .git/refs/heads/main ls .git/refs/heads
main и origin/main main — ваша ветка origin/main — запись о сервере ahead N — не отправлено N

Итог главы

Ветка — отдельная линия коммитов в одном репозитории. Git хранит её как файл в папке .git/refs/heads, в котором записано имя одного коммита, последнего в линии. Остальные коммиты находятся по цепочке: в каждом записан предыдущий. В файле .git/HEAD записано имя ветки, на которой вы стоите. VS Code показывает его в строке состояния и меткой в графе. Новый коммит сдвигает текущую ветку: в её файл записывается имя нового коммита. origin/main — запись о ветке на GitHub, она сдвигается только при обмене с сервером. Вторая ветка добавляет в репозиторий один небольшой файл, файлы проекта и коммиты при этом не копируются. В следующей главе создаётся вторая ветка, и на ней проверяется, что её коммиты не попадают в main.

Откуда взяты факты
  1. Кадры и ответы команд получены на клоне проекта sait-vizitka в Git версии 2.51.1 и VS Code версии 1.138.
  2. Ветка как указатель на коммит, запись HEAD — книга Pro Git, раздел «О ветвлении в двух словах».
  3. Папки refs/heads, refs/remotes, файлы HEAD и packed-refs — документация gitrepository-layout.
  4. Ключи git branch — документация git-branch; ограничения на имена веток — git-check-ref-format.
  5. Имя ветки в строке состояния и список веток — документация VS Code, раздел Source Control.
Глава 3.1 · Ветки: зачем нужны и как устроены · Справочник по Git и GitHub Автор: Макаров Максим Николаевич