Ветки — это указатели
Ветка в git — это всего лишь файл в 41 байт под .git/refs/heads/ — подвижный указатель на один коммит. HEAD указывает на текущую ветку. Поэтому создание ветки O(1) и дёшево, в отличие от систем, копирующих целые рабочие каталоги.
О ветках в git говорят так, будто каждая — это тяжёлая копия всего проекта, поэтому их боятся создавать, как боялись бы продублировать папку на 2 ГБ. Правда почти комично мала: ветка в git — это один текстовый файл с одним 40-символьным идентификатором коммита. Создание ветки пишет 41 байт. Это вся её стоимость.
К концу этого урока ты сможешь сказать, чем именно ветка является на диске, на что указывает HEAD и почему коммит «двигает» ветку вперёд, ничего при этом не копируя.
После этого урока ты сможешь объяснить, что ветка — это подвижный указатель (ref) на коммит, хранимый файлом под .git/refs/heads/, описать цепочку HEAD → ветка → коммит и сказать, почему создание ветки и коммит в неё — обе дешёвые операции за константное время.
Ветка — это именованный подвижный указатель на один коммит, и не более того. Git хранит её крошечным файлом. Перечисли ветки командой:
git branch
# * main
# feature-login* отмечает ветку, на которой ты сейчас находишься. Каждому из этих имён соответствует файл на диске. Посмотри напрямую:
cat .git/refs/heads/main
# 9d1f7a3c2b8e4a6f0c1d2e3f4a5b6c7d8e9f0a1bЭта 40-символьная шестнадцатеричная строка — идентификатор коммита, на который указывает ветка. Вся ветка — это та одна строка. main ничем особенным для git не является — это просто общепринятое имя ветки по умолчанию, на которой стартует свежий репозиторий (старые репозитории использовали master).
Коммит двигает указатель текущей ветки вперёд; он никогда не копирует ветку. Когда ты делаешь коммит, git записывает новый объект-коммит, а затем переписывает файл текущей ветки, чтобы он держал идентификатор нового коммита. Старый коммит никуда не делся — новый записывает его как своего родителя. Так что ветка всегда является вершиной цепочки, уходящей назад по истории:
git commit -m "Add login form"
# main теперь указывает на новый коммит;
# родитель нового коммита — прежняя вершинаВ остальных ветках ничего не изменилось. Только в файл ветки, на которой ты был, записалась новая 40-символьная строка.
HEAD — это указатель на текущую ветку, указатель на указатель. Git нужно знать, на какой ветке «ты находишься», чтобы коммит знал, какой файл двигать. Это и есть HEAD. Обычно он указывает не на коммит напрямую, а на имя ветки:
cat .git/HEAD
# ref: refs/heads/mainЧитай это как цепочку: HEAD → main → коммит 9d1f7a3…. Когда ты переключаешь ветку, меняется только HEAD — его перенаправляют на файл другой ветки. Когда ты коммитишь, HEAD говорит git, какой файл ветки двигать. Два уровня косвенности, и оба — просто текст.
▸Почему это работает
Эта косвенность — это почему ветвление в git дёшево, а в старых системах не было. В инструментах, которые ветвились копированием рабочего дерева (или серверного пути), ветка стоила времени и диска пропорционально размеру проекта. В git коммиты уже существуют как общие, неизменяемые объекты; ветка лишь добавляет имя, указывающее в этот существующий граф. Создание ветки — O(1), один маленький файл, неважно, 10 файлов в репозитории или 100 000.
«Отсоединённый HEAD» (detached HEAD) — это когда HEAD указывает прямо на коммит, минуя уровень ветки. Если ты переключишься на конкретный коммит вместо ветки, HEAD будет держать идентификатор коммита напрямую, а не строку ref: refs/heads/…:
git checkout 9d1f7a3
# You are in 'detached HEAD' state.
cat .git/HEAD
# 9d1f7a3c2b8e4a6f0c1d2e3f4a5b6c7d8e9f0a1bТы можешь осмотреться и даже коммитить, но эти коммиты не принадлежат никакой ветке — переключишься в другое место, и они станут недостижимыми (со временем их соберёт сборщик мусора). Это полезно для осмотра старых состояний и опасно для новой работы. В следующем уроке ты увидишь, как входить в это состояние и выходить из него осознанно.
Наблюдаем, как двигаются указатели.
Стартуем на main, указывающей на коммит A. То есть HEAD → main → A.
Создай ветку и останься на месте:
git branch featureТеперь под .git/refs/heads/ два файла — main и feature — оба содержат идентификатор A. HEAD по-прежнему указывает на main. Ни один коммит не скопирован; ты лишь добавил один файл в 41 байт.
Переключись на неё и закоммить:
git switch feature
git commit -m "Start feature" # создаёт коммит B, родитель AHEAD → feature → B, и родитель B — это A. Файл main не тронут — он по-прежнему держит A. Две ветки теперь разошлись: main на A, feature на B. Всё, что произошло: записан один новый объект-коммит и переписаны несколько байт файла feature — с идентификатора A на идентификатор B. Это и есть вся механика ветвления.
▸Частая ошибка
Частое заблуждение: «если я удалю ветку, потеряю ли я эти коммиты?» Удаление ветки убирает только указатель — объекты-коммиты остаются в хранилище объектов, пока до них может дотянуться что-то ещё (другая ветка, тег или твой reflog). Удалить feature после слияния в main ничего не теряет, потому что main всё ещё может дотянуться до этих коммитов. Именно об удалении не слитой ветки git предупреждает, потому что тогда указатель был единственным путём назад к той работе.
Что физически происходит на диске, когда ты создаёшь новую ветку в git?
Ветка — это подвижный указатель на один коммит, хранимый файлом в 41 байт под .git/refs/heads/ — этот маленький файл и есть ветка. HEAD — это указатель на текущую ветку (ref: refs/heads/main), образующий цепочку HEAD → ветка → коммит. Коммит двигает файл текущей ветки на новый коммит; ничего не копируется, поэтому создание ветки и коммит — обе дешёвые операции за константное время. Когда HEAD указывает прямо на коммит вместо ветки, ты в отсоединённом HEAD — нормально для осмотра, рискованно для новой работы. Дальше ты выучишь команды, которые создают эти указатели и двигают HEAD между ними: git switch и git branch.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.