Ветки как указатели: branch, switch и detached HEAD

Рейтинг: 52.3% · 11 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

Ветки как указатели: branch, switch и detached HEAD

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск
  8. .gitignore: что не должно попасть в репозиторий
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD (вы здесь)
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться
Представь, что в другой системе контроля версий ты хочешь начать эксперимент: завести отдельную линию разработки, не сломав основную. Готовься к боли. Где-то копируется весь рабочий каталог, где-то ветка - тяжёлая серверная операция на минуты. В Git всё иначе, и в этом один из секретов его скорости. Ветка в Git - это не копия проекта и не контейнер с файлами. Это крошечный текстовый файл, в котором лежит один SHA коммита. Сорок символов хеша, перевод строки - и всё, ветка готова. Поэтому в Git ты создаёшь ветки не задумываясь, десятками, под каждую мелкую идею. Сегодня разберём, как это устроено физически, и снимем главный страх новичка - detached HEAD.

Что такое git ветка на самом деле

Когда ты пишешь

Код: Выделить всё

git branch feature
, Git не делает почти ничего. Он создаёт файл

Код: Выделить всё

.git/refs/heads/feature
и пишет туда хеш коммита, на котором ты сейчас стоишь. Давай посмотрим вживую.

Код: Выделить всё

$ git switch -c feature
Switched to a new branch 'feature'

$ cat .git/refs/heads/feature
3f5a1c9b2e7d4a08f1c6b9e2d3a4b5c6d7e8f901
$ wc -c .git/refs/heads/feature
41 .git/refs/heads/feature
Сорок один байт: сорок символов хеша плюс символ конца строки. Это и есть вся git ветка целиком. Никаких файлов проекта внутри, никакой истории - история живёт в самих коммитах, а ветка лишь показывает пальцем на последний из них. Каждый коммит знает своего родителя, тот - своего, и так до корня. Получается цепочка: ветка указывает на верхушку, а дальше история разматывается назад сама.
Ветка - это подвижный ярлык на коммите. Сделал новый коммит на этой ветке - ярлык автоматически переехал на него. Вот и весь механизм продвижения ветки вперёд.
Слово "подвижный" тут ключевое. Тег (об этом будет отдельный урок) прибит к одному коммиту намертво. А ветка едет за тобой: каждый раз, когда ты коммитишь, Git переписывает её файл новым хешем. Поэтому ветвление в Git дешёвое - создать ярлык и потом двигать его стоит копейки по сравнению с копированием данных.

В современном Git (на 2026 это серия 2.5x) у ссылок появился ещё и быстрый бэкенд reftable: вместо россыпи мелких файлов в

Код: Выделить всё

.git/refs
ссылки складываются в компактную бинарную таблицу. Это сильно ускоряет работу на репозиториях с тысячами веток. Но логика та же - ветка по-прежнему подвижный указатель на коммит, просто хранится иначе. Дефолтом reftable станет в Git 3.0.

Изображение

HEAD: где я сейчас нахожусь

Если ветка отвечает на вопрос "где верхушка линии разработки", то HEAD отвечает на вопрос "а где сейчас стою я". HEAD - это указатель на текущую ветку. Тоже файл, тоже можно прочитать.

Код: Выделить всё

$ cat .git/HEAD
ref: refs/heads/feature
Обрати внимание: внутри HEAD не хеш, а ссылка на ветку -

Код: Выделить всё

ref: refs/heads/feature
. Это нормальное, "пристёгнутое" состояние HEAD. Цепочка получается такая: HEAD показывает на ветку feature, ветка feature показывает на коммит, коммит знает родителей. Когда ты делаешь новый коммит, Git двигает ветку, на которую смотрит HEAD, а сам HEAD продолжает смотреть на ту же ветку. Поэтому новый коммит "прилипает" именно к текущей ветке, а не к соседней.

Создать ветку git и переключиться: switch против checkout

Исторически всем заведовала одна перегруженная команда -

Код: Выделить всё

git checkout
. Она и ветки переключала, и файлы из истории восстанавливала, и коммиты отсоединяла. Слишком много ролей в одной команде - источник путаницы для новичков. Поэтому в Git завезли две специализированные команды: git switch для веток и git restore для файлов. С версии Git 2.51 (август 2025) они официально вышли из экспериментального статуса, интерфейс зафиксирован и совместим вперёд. На 2026 это рекомендованный способ. Но checkout никуда не делся - его полно в старых статьях и чужих скриптах, так что знать обе формы обязательно.

Создать ветку git и сразу на неё перейти:

Код: Выделить всё

# современный способ (рекомендуется)
$ git switch -c hotfix
Switched to a new branch 'hotfix'

# классика - делает ровно то же самое
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
Флаг (от create) у switch соответствует (branch) у checkout. Просто переключить ветку git на уже существующую:

Код: Выделить всё

$ git switch main
Switched to branch 'main'

$ git checkout main      # то же самое классикой
Switched to branch 'main'
Маленький, но любимый трюк - переключиться на предыдущую ветку, где ты только что был, через дефис:

Код: Выделить всё

$ git switch -
Switched to branch 'feature'
Это аналог в шелле: прыгаешь между двумя ветками туда-сюда, не печатая имена. Работает и

Код: Выделить всё

git checkout -
. Только что просто посмотреть список веток - команда

Код: Выделить всё

git branch
без аргументов:

Код: Выделить всё

$ git branch
  feature
* hotfix
  main
Звёздочка слева - на этой ветке стоит HEAD прямо сейчас. Добавь , чтобы увидеть верхушечный коммит каждой ветки, или - чтобы заодно увидеть привязку к удалённым веткам.

Detached HEAD: что это и как вернуться без паники

Теперь самое страшное на вид и самое безобидное по сути. Иногда HEAD показывает не на ветку, а прямо на конкретный коммит по хешу. Это и есть detached HEAD - "отсоединённая голова". Случается это, когда ты переключаешься на коммит, а не на ветку: например, хочешь посмотреть, как выглядел проект три коммита назад.

Код: Выделить всё

$ git switch --detach 3f5a1c9
HEAD is now at 3f5a1c9 add cache layer

# классикой это происходит просто при checkout по хешу:
$ git checkout 3f5a1c9
Note: switching to '3f5a1c9'.

You are in 'detached HEAD' state. You can look around, make
experimental changes and commit them, and you can discard any
commits you make in this state without impacting any branches
by switching back to a branch.
...
HEAD detached at 3f5a1c9
Заглянем внутрь - и увидим разницу с нормальным состоянием:

Код: Выделить всё

$ cat .git/HEAD
3f5a1c9b2e7d4a08f1c6b9e2d3a4b5c6d7e8f901
Вот оно: теперь в HEAD лежит голый хеш, а не

Код: Выделить всё

ref: refs/heads/...
. HEAD больше не пристёгнут ни к какой ветке - он сам по себе указывает на коммит. Это не поломка и не ошибка. Это абсолютно валидное состояние, в котором удобно: посмотреть старую версию файлов, собрать и протестировать конкретный коммит, поковыряться без последствий. Файлы рабочего каталога честно переключились на тот коммит.

Сразу рецепт спасения, без паники. Запомни три выхода, и detached HEAD перестанет пугать навсегда:
  • Просто посмотрел и хочешь обратно на свою ветку -

    Код: Выделить всё

    git switch -
    (вернёт туда, где был) или

    Код: Выделить всё

    git switch main
    по имени.
  • В detached HEAD ты насочинял коммитов, которые нужно сохранить - заведи отсюда ветку:

    Код: Выделить всё

    git switch -c saved-work
    . Все коммиты, что ты сделал в отсоединённом состоянии, окажутся на новой ветке, и ничего не пропадёт.
  • Уже ушёл и потерял хеш тех коммитов - не беда,

    Код: Выделить всё

    git reflog
    помнит, где побывал HEAD. Найди там хеш и создай ветку от него:

    Код: Выделить всё

    git switch -c rescue <хеш>
    .
Почему важна именно ветка. Коммиты, сделанные в detached HEAD, ни к какому ярлыку не привязаны. Пока на них смотрит HEAD - они живы. Но стоит переключиться на другую ветку, и на эти коммиты не указывает ничто. Они станут недостижимыми и однажды их подберёт сборка мусора (git maintenance, где с Git 2.54 дефолтом стала геометрическая стратегия). Заведи ветку - дашь им постоянный ярлык, и они в безопасности. До сборки мусора у тебя обычно дни, так что reflog почти всегда спасает, но надёжнее не доводить.

Удаление, переименование и orphan-ветки

Ветки живут и умирают пачками - это норма. Влил feature в main, ветка больше не нужна:

Код: Выделить всё

$ git branch -d feature
Deleted branch feature (was 7a2b9c4).

$ git branch -d feature
error: the branch 'feature' is not fully merged.
hint: ...
If you are sure you want to delete it, run 'git branch -D feature'.
Строчный (delete) - безопасный: Git откажется удалять ветку, если в ней есть коммиты, которых нет в текущей ветке - то есть ты рискуешь потерять работу. Прописной (force) удаляет всё равно, без вопросов. Привычка: пробуй сначала , переходи на только осознанно, прочитав предупреждение. Удаляется-то по сути просто файл-указатель - сами коммиты остаются достижимы из других веток, если те на них смотрят.

Переименование - флаг (move):

Код: Выделить всё

$ git branch -m old-name new-name

# переименовать текущую ветку - имя источника можно опустить
$ git branch -m main-2026
Удалить ветку нельзя, стоя на ней самой - сначала перейди на другую. Логично: нельзя пилить сук, на котором сидишь, ведь HEAD на неё смотрит.

Отдельный зверь - orphan-ветка, ветка-сирота. Обычно новая ветка наследует историю того места, откуда создана. А orphan стартует с чистого листа - без родителей, без единого коммита в истории. Создаётся через

Код: Выделить всё

git switch --orphan
:

Код: Выделить всё

$ git switch --orphan gh-pages
Switched to a new branch 'gh-pages'

$ git log
fatal: your current branch 'gh-pages' does not have any commits yet
История пустая - это и задумано. Зачем такое надо. Классика - ветка

Код: Выделить всё

gh-pages
для GitHub Pages: там лежит собранный сайт, и ему незачем тащить за собой всю историю исходников. Так же удобно держать отдельную линию документации, генерированных артефактов или начать историю проекта заново, оставив старую как архив. Классический эквивалент -

Код: Выделить всё

git checkout --orphan gh-pages
. После переключения рабочий каталог не трогается, но индекс по-прежнему полон старых файлов - обычно делают

Код: Выделить всё

git rm -rf .
, кладут что нужно и коммитят первый коммит новой, ни с чем не связанной истории.

Типичные грабли
  • Запаниковал в detached HEAD и сделал reset --hard. Самый частый способ реально потерять работу. Не надо. Сначала

    Код: Выделить всё

    git switch -c имя
    , сохрани коммиты, потом разбирайся.
  • Удалил ветку через -D, не влив её. Коммиты стали недостижимы. Лечится через

    Код: Выделить всё

    git reflog
    , пока не прошла сборка мусора. Привычка к вместо страхует от этого.
  • Создал ветку не от того коммита.

    Код: Выделить всё

    git branch feature
    всегда отталкивается от текущего HEAD. Хочешь от конкретного коммита или ветки - укажи явно:

    Код: Выделить всё

    git switch -c feature main
    или

    Код: Выделить всё

    git branch feature <хеш>
    .
  • Ждёшь, что переключение унесёт незакоммиченные правки. Если правки конфликтуют с целевой веткой, Git не даст переключиться и попросит сначала закоммитить или спрятать в stash. Это защита, а не баг.
  • Думаешь, что -m переименовывает удалённую ветку. Нет,

    Код: Выделить всё

    git branch -m
    трогает только локальную. На удалёнке придётся удалить старую и запушить новую.
Мини-лаба: пощупать ветки руками

Делай по шагам в любом тестовом репозитории - и механика уляжется в голове:
Контрольные вопросы
  • Сколько примерно весит файл ветки и что физически в нём лежит?
  • Чем отличается содержимое

    Код: Выделить всё

    .git/HEAD
    в нормальном состоянии и в detached HEAD?
  • Ты насочинял три коммита в detached HEAD и хочешь их сохранить - какая ровно одна команда спасёт?
  • Зачем нужна orphan-ветка и чем её история отличается от обычной?
Итог

Ветка в Git - это подвижный указатель на коммит, физически файл в 41 байт, а не копия проекта. Потому ветвиться дёшево и можно не жалея. HEAD - это "где я сейчас": в норме он пристёгнут к ветке, в detached HEAD смотрит прямо на коммит. Detached HEAD не поломка, а рабочий режим для разглядывания истории, и выход из него всегда один из трёх: switch обратно, либо завести ветку отсюда, либо вытащить хеш из reflog. Учи git switch и git restore как основной инструмент, помни про checkout для чтения чужого кода, держи в кармане orphan-ветку для gh-pages и чистой истории - и ветки перестанут быть магией.
👍3 ❤️2 🔥 😄 🤔
Аватара пользователя
lriedel45
Сообщения: 1
Зарегистрирован: 12 май 2026, 11:23

Re: Ветки как указатели: branch, switch и detached HEAD

Сообщение lriedel45 »

Я раньше реально боялся надписи про detached HEAD и каждый раз делал reset hard, потерял из-за этого кусок работы. Теперь дошло: надо было просто git switch -c. Спасибо за рецепт из трёх выходов.
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
ElixirSmith
Сообщения: 2
Зарегистрирован: 12 май 2026, 04:17

Re: Ветки как указатели: branch, switch и detached HEAD

Сообщение ElixirSmith »

А можно подробнее про reftable? У меня в одном репо несколько тысяч веток и фетч реально тормозит. Это в 2.5x уже включается само или надо git init с флагом и потом мигрировать?
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
.gitattributes: окончания строк, бинарники и атрибуты
Следующая глава →
Слияние веток: fast-forward против трёхстороннего merge

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
  • Похожие темы
Похожие запросы: Как удалить ветку в Gitgit branch: работа с ветками

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей