Что такое git ветка на самом деле
Когда ты пишешь
Код: Выделить всё
git branch featureКод: Выделить всё
.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 (на 2026 это серия 2.5x) у ссылок появился ещё и быстрый бэкенд reftable: вместо россыпи мелких файлов в
Код: Выделить всё
.git/refs
HEAD: где я сейчас нахожусь
Если ветка отвечает на вопрос "где верхушка линии разработки", то HEAD отвечает на вопрос "а где сейчас стою я". HEAD - это указатель на текущую ветку. Тоже файл, тоже можно прочитать.
Код: Выделить всё
$ cat .git/HEAD
ref: refs/heads/feature
Код: Выделить всё
ref: refs/heads/featureСоздать ветку git и переключиться: switch против checkout
Исторически всем заведовала одна перегруженная команда -
Код: Выделить всё
git checkoutСоздать ветку git и сразу на неё перейти:
Код: Выделить всё
# современный способ (рекомендуется)
$ git switch -c hotfix
Switched to a new branch 'hotfix'
# классика - делает ровно то же самое
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
Код: Выделить всё
-cКод: Выделить всё
-bКод: Выделить всё
$ git switch main
Switched to branch 'main'
$ git checkout main # то же самое классикой
Switched to branch 'main'
Код: Выделить всё
$ git switch -
Switched to branch 'feature'
Код: Выделить всё
cd -Код: Выделить всё
git checkout -Код: Выделить всё
git branchКод: Выделить всё
$ git branch
feature
* hotfix
main
Код: Выделить всё
-vКод: Выделить всё
-vvDetached 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
Код: Выделить всё
ref: refs/heads/...Сразу рецепт спасения, без паники. Запомни три выхода, и detached HEAD перестанет пугать навсегда:
- Просто посмотрел и хочешь обратно на свою ветку - (вернёт туда, где был) или
Код: Выделить всё
git switch -по имени.Код: Выделить всё
git switch main - В detached HEAD ты насочинял коммитов, которые нужно сохранить - заведи отсюда ветку: . Все коммиты, что ты сделал в отсоединённом состоянии, окажутся на новой ветке, и ничего не пропадёт.
Код: Выделить всё
git switch -c saved-work - Уже ушёл и потерял хеш тех коммитов - не беда, помнит, где побывал HEAD. Найди там хеш и создай ветку от него:
Код: Выделить всё
git reflog.Код: Выделить всё
git switch -c rescue <хеш>
Удаление, переименование и 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'.
Код: Выделить всё
-dКод: Выделить всё
-DКод: Выделить всё
-dКод: Выделить всё
-DПереименование - флаг
Код: Выделить всё
-mКод: Выделить всё
$ git branch -m old-name new-name
# переименовать текущую ветку - имя источника можно опустить
$ git branch -m main-2026
Отдельный зверь - 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Код: Выделить всё
git checkout --orphan gh-pagesКод: Выделить всё
git rm -rf .Типичные грабли
- Запаниковал в detached HEAD и сделал reset --hard. Самый частый способ реально потерять работу. Не надо. Сначала , сохрани коммиты, потом разбирайся.
Код: Выделить всё
git switch -c имя - Удалил ветку через -D, не влив её. Коммиты стали недостижимы. Лечится через , пока не прошла сборка мусора. Привычка к
Код: Выделить всё
git reflogвместоКод: Выделить всё
-dстрахует от этого.Код: Выделить всё
-D - Создал ветку не от того коммита. всегда отталкивается от текущего HEAD. Хочешь от конкретного коммита или ветки - укажи явно:
Код: Выделить всё
git branch featureилиКод: Выделить всё
git switch -c feature main.Код: Выделить всё
git branch feature <хеш> - Ждёшь, что переключение унесёт незакоммиченные правки. Если правки конфликтуют с целевой веткой, Git не даст переключиться и попросит сначала закоммитить или спрятать в stash. Это защита, а не баг.
- Думаешь, что -m переименовывает удалённую ветку. Нет, трогает только локальную. На удалёнке придётся удалить старую и запушить новую.
Код: Выделить всё
git branch -m
Делай по шагам в любом тестовом репозитории - и механика уляжется в голове:
- Создай ветку и загляни в её файл: , затем
Код: Выделить всё
git switch -c lab. Сравни хеш с выводомКод: Выделить всё
cat .git/refs/heads/lab- они совпадут.Код: Выделить всё
git rev-parse HEAD - Посмотри, на что смотрит HEAD: . Увидишь
Код: Выделить всё
cat .git/HEAD.Код: Выделить всё
ref: refs/heads/lab - Сделай пустой коммит и снова прочитай файл ветки - хеш изменился, ярлык переехал.
Код: Выделить всё
git commit --allow-empty -m wip - Отсоединись: . Прочитай
Код: Выделить всё
git switch --detach HEAD~1- теперь там голый хеш. Это detached HEAD.Код: Выделить всё
.git/HEAD - Сделай тут пустой коммит, потом спокойно вернись: . Коммит сохранён на новой ветке.
Код: Выделить всё
git switch -c rescued - Попробуй и
Код: Выделить всё
git switch -. Удали ненужное черезКод: Выделить всё
git branch -m rescued saved, почитай предупреждение.Код: Выделить всё
git branch -d
- Сколько примерно весит файл ветки и что физически в нём лежит?
- Чем отличается содержимое в нормальном состоянии и в detached HEAD?
Код: Выделить всё
.git/HEAD - Ты насочинял три коммита в detached HEAD и хочешь их сохранить - какая ровно одна команда спасёт?
- Зачем нужна orphan-ветка и чем её история отличается от обычной?
Ветка в Git - это подвижный указатель на коммит, физически файл в 41 байт, а не копия проекта. Потому ветвиться дёшево и можно не жалея. HEAD - это "где я сейчас": в норме он пристёгнут к ветке, в detached HEAD смотрит прямо на коммит. Detached HEAD не поломка, а рабочий режим для разглядывания истории, и выход из него всегда один из трёх: switch обратно, либо завести ветку отсюда, либо вытащить хеш из reflog. Учи git switch и git restore как основной инструмент, помни про checkout для чтения чужого кода, держи в кармане orphan-ветку для gh-pages и чистой истории - и ветки перестанут быть магией.