Про ветки обычно говорят метафорами — «параллельная линия разработки», «отдельная копия проекта». Обе мешают, потому что ветка в Git устроена гораздо проще: это именованная ссылка на один коммит.
В обычном репозитории с SHA-1 такая ссылка может храниться отдельным файлом. В нашем примере:
cat .git/refs/heads/main
9d677caf9b911ca6f87f0e06fcbc85cb81fd7428
stat -c%s .git/refs/heads/main
git rev-parse HEAD
41
9d677caf9b911ca6f87f0e06fcbc85cb81fd7428
Здесь сорок один байт: сорок символов SHA-1 и перевод строки. Это конкретный способ хранения, не свойство любой ветки: ссылки могут быть упакованы в packed-refs или храниться в reftable, а Git поддерживает и SHA-256. Создание ветки дёшево, потому что не копирует проект; коммит обновляет ссылку, а удаление ветки убирает указатель, не сами коммиты.
Ветки — указатели на вершины истории
git switch зависит в том числе от объёма изменений рабочего каталога; папок project-v2 заводить не нужно.Создать, посмотреть, переключиться
git switch -c feature/login
git branch --show-current
feature/login
git branch
* feature/login
main
Звёздочка отмечает текущую ветку — ту, на которую сейчас показывает HEAD. Флаг -v добавляет к каждой ветке её коммит:
git branch -v
* main 9d677ca первый коммит
Переключение обратно — git switch main. Старый git checkout умеет то же самое, но заодно ещё десяток вещей, поэтому в Git 2.23 работу с ветками вынесли в switch, а с файлами — в restore. В новом коде пиши switch.
Три разных списка, которые все зовут «ветками»
Это место путают чаще всего: git branch показывает не все ветки, которые есть.
git branch # только локальные
feature/login
* main
git branch -r # отслеживаемые копии удалённых
origin/feature/login
origin/main
git branch -a # и те и другие
feature/login
* main
remotes/origin/feature/login
remotes/origin/main
fetch обновляет снимок сервера, а не локальную ветку
Удаление
git branch -d feature/login
error: the branch 'feature/login' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/login'
-d — «удали, если всё уже слито». Git проверяет, слита ли ветка в её upstream, а если upstream не задан — в HEAD. Отказ не доказывает, что этих коммитов нет в других ветках. Если ветка слита, он удаляет её без разговоров:
git branch -d tmp/clean
Deleted branch tmp/clean (was 9d677ca).
Заглавная -D пропускает проверку слияния, но не разрешает удалять ветку, открытую в рабочем дереве:
git branch -D feature/login
Deleted branch feature/login (was b1be398).
Коммиты не удаляются сразу и становятся недостижимыми только тогда, когда на них больше не ведёт другая ссылка. Вместе с веткой удаляется её reflog; запись может остаться в reflog HEAD, но это не гарантировано. Восстановление по известному хешу возможно, пока объект не удалён сборщиком мусора; подробности — в разборе git reset.
На сервере она осталась
Локальное удаление сервера не касается:
git branch -r
origin/feature/login
origin/main
Удалять там нужно отдельно:
git push origin --delete feature/login
- [deleted] feature/login
Ветка исчезла у коллеги, а у меня всё ещё в списке
Обратная ситуация: ветку удалили на сервере, но git branch -r продолжает её показывать. Это тот самый кэш из среднего столбца — он не протухает сам:
git fetch --prune
- [deleted] (none) -> origin/feature/old
git branch -r
origin/HEAD -> origin/main
origin/main
При этом локальная копия ветки остаётся жить своей жизнью:
git branch
feature/old
* main
Это не сбой: локальная ветка твоя, и Git не удаляет твои указатели по чужому решению. Удалять — руками, через git branch -d.
Переименование
git branch -m main master
git branch
* master
Флаг -m переименовывает текущую ветку; чтобы переименовать другую, укажи два имени: git branch -m старое новое. Если ветка уже отправлена, переименование локального указателя серверу ничего не скажет — там придётся создать новую и удалить старую.
Что стоит помнить
- Ветка не хранит коммитыКоммиты принадлежат репозиторию, ветка — лишь указатель на последний. Поэтому один и тот же коммит может быть в трёх ветках сразу, не занимая места трижды.
- origin/main — не ветка, а снимокЭто копия того, каким сервер был на момент последнего
fetch. Напрямую её не двигают и в неё не коммитят. - -d защищает, -D нетНачинай с маленькой: её отказ — не препятствие, а сообщение о том, что ты теряешь неслитую работу. Прочитай, потом решай.
- Локальное и удалённое удаление независимы
git branch -dубирает у себя,git push origin --delete— на сервере,git fetch --pruneподчищает устаревшие отслеживаемые. Три разные операции. - Имя ветки — это путьСлеш в
feature/loginне создаёт вложенности, но задаёт группировку в выводе и в интерфейсах. Заводить одновременноfeatureиfeature/loginнельзя: первое станет каталогом.
Частые вопросы
Как узнать текущую ветку
git branch --show-current печатает только имя, что удобно в скриптах. В интерактивной работе хватает git branch — текущая помечена звёздочкой, и она же видна в первой строке git status.
Чем git switch отличается от git checkout
switch работает только с ветками и потому предсказуем. checkout совмещает переключение веток и восстановление файлов, из-за чего опечатка в имени может привести к неожиданному результату. Разделение появилось в Git 2.23; старая команда работает, но новую писать безопаснее.
Почему я не вижу ветку, которую создал коллега
Потому что список удалённых веток у тебя — это снимок последнего fetch. Выполни git fetch, и она появится в git branch -r. Отдельно скачивать ничего не нужно: git switch имя-ветки создаст локальную копию автоматически, если такая есть у origin.
Что делать, если удалил ветку по ошибке
Попробовать найти последний коммит в git reflog или взять его хеш из вывода удаления. Если объект ещё доступен, создать ветку заново: git switch -c имя <хеш>. Reflog самой удалённой ветки исчезает, поэтому наличие записи в reflog HEAD и срок восстановления не гарантированы.
Сколько веток можно держать
Веток можно держать много: они не создают копию проекта, хотя ссылки и их reflog занимают место. Практически — чем дольше ветка живёт в стороне, тем дороже её вливать, поэтому длинные ветки конфликтуют, а однодневные почти никогда. Подробнее об этом в разборе конфликтов слияния.
Где потренироваться
Заведи одноразовый репозиторий, сделай пару коммитов и посмотри cat .git/refs/heads/main своими глазами — после этого метафоры про «параллельные линии» больше не понадобятся. Команды, из которых складывается ежедневная работа, разобраны в основных командах Git.