Ветвление, теги и продвинутая история
К этому моменту ты уже знаешь базовый цикл Git: изменить файл, добавить в index, сделать commit, создать ветку, слить ветку и отправить изменения на remote. Дальше начинается рабочая часть Git: как команда организует ветки, как помечает релизы, как временно откладывает изменения, как безопасно отменяет плохие коммиты и как читает сложную историю.
Стратегии ветвления
Стратегия ветвления - это договорённость команды о том, какие ветки существуют и как изменения попадают в стабильную версию.
Git технически не заставляет использовать конкретный процесс. Можно коммитить прямо в main, можно создавать десятки веток, можно делать релизные ветки. Но без общей договорённости история быстро становится непонятной.
Git Flow
Git Flow - классический подход для проектов с выраженным релизным циклом:
main --------o-----------o-----------
\ ^ ^
develop -----o------o-----o-----o-----o-----
\ \ \ \
feature/A o--o \ feature/C
feature/B
Основные ветки:
main- только стабильные релизы;develop- интеграционная ветка разработки;feature/*- разработка функциональности;release/*- подготовка релиза;hotfix/*- срочные исправления от production-версии.
Плюс Git Flow в явной структуре. Минус - много долгоживущих веток и больше merge-сложности. Для больших enterprise-проектов он всё ещё встречается, но для быстрых CI/CD-команд часто тяжеловат.
Trunk-Based Development
Trunk-Based Development строится вокруг короткоживущих веток и частого попадания изменений в main.
main ---o---o---o---o---o---o---o---
\ \ \
feature o o o
Идея: маленькие изменения вливаются быстро, CI постоянно проверяет main, незавершённую функциональность прячут за feature flags.
Плюсы:
- меньше долгих расхождений истории;
- меньше больших конфликтов;
- быстрее feedback от CI/CD;
- проще деплоить часто.
Минус: нужна дисциплина. Коммиты должны быть маленькими, тесты быстрыми, main нельзя ломать.
GitHub Flow / GitLab Flow
Упрощённый процесс, который часто используют с Pull Request или Merge Request:
- Обновить
main. - Создать ветку от
main. - Сделать небольшую порцию изменений.
- Запушить ветку.
- Открыть Pull Request.
- Пройти CI и code review.
- Слить в
main.
Типовые команды:
git switch main
git pull origin main
git switch -c feature/add-healthcheck
# работа, коммиты
git add .
git commit -m "Add healthcheck endpoint"
git push -u origin feature/add-healthcheck
Этот подход проще Git Flow и хорошо сочетается с защищённой веткой main.
git log: продвинутые возможности
git log - главный инструмент чтения истории.
Граф всех веток:
git log --oneline --graph --all --decorate
Коммиты конкретного автора:
git log --author="student" --oneline
История конкретного файла:
git log -- README.md
git log --oneline -- README.md
Поиск по сообщению:
git log --grep="fix" --oneline
Изменения за период:
git log --since="1 week ago" --oneline
git log --until="2026-05-01" --oneline
Формат с датой:
git log --format="%h %ad %s" --date=short
Для финального графа с датами удобно:
git log --oneline --graph --all --decorate --date=short --format="%h %ad %d %s"
Сохранять вывод можно обычным перенаправлением:
git log --oneline --graph --all --decorate > /root/branch_graph.txt
Диапазоны: main..develop
В Git часто используются диапазоны вида:
git log main..develop
git diff main..develop
Смысл зависит от команды, но направление важно.
Для git log main..develop это обычно означает: показать коммиты, которые есть в develop, но недостижимы из main.
Для git diff main..develop это означает: показать, как состояние develop отличается от состояния main.
Поменять порядок - значит поменять направление сравнения:
git diff main..develop
git diff develop..main
Список изменённых файлов между ветками:
git diff --name-only main..develop
Это удобно, когда нужен не полный diff, а только перечень затронутых файлов.
git show и git diff
Показать конкретный коммит:
git show abc1234
git show выведет метаданные коммита и patch: какие строки были добавлены или удалены.
Разница между двумя коммитами:
git diff abc1234..def5678
Разница между ветками:
git diff main..feature/my-feature
Только имена файлов:
git diff --name-only main..HEAD
HEAD означает текущую позицию. Если ты находишься на feature-ветке, main..HEAD покажет отличие текущей ветки от main.
git blame: кто написал строку
git blame показывает, какой коммит последний раз менял каждую строку файла:
git blame src/app.py
git blame -L 10,20 src/app.py
Это не инструмент для поиска виноватого человека. Его нормальная цель - найти контекст: когда появилась строка, какой коммит её добавил, с каким сообщением и рядом с какими изменениями.
Частый порядок:
git blame app.py
git show <commit>
Сначала находишь коммит по строке, потом смотришь полный контекст через git show.
Теги
Тег - это имя для конкретного коммита. Чаще всего тегами помечают релизы:
v1.0.0 -> commit abc1234
v1.1.0 -> commit def5678
Lightweight tag - просто указатель:
git tag v1.0.0
Annotated tag хранит автора, дату и сообщение:
git tag -a v1.0.0 -m "First release"
Для релизов предпочтительнее annotated tags, потому что они дают больше информации для аудита.
Список тегов:
git tag
git tag -l "v1.*"
Показать тег:
git show v1.0.0
Отправить тег на remote:
git push origin v1.0.0
git push origin --tags
Теги не всегда пушатся вместе с обычным git push, поэтому релизный тег часто нужно отправлять явно.
git stash: временно отложить работу
git stash сохраняет незакоммиченные изменения во временное хранилище и очищает working tree.
Типичный сценарий: ты работаешь в feature/payment, но нужно срочно переключиться на main для hotfix.
git status
git stash push -m "wip payment form"
git switch main
# делаешь hotfix, commit
git switch feature/payment
git stash pop
Основные команды:
git stash
git stash push -m "message"
git stash list
git stash pop
git stash apply
git stash drop
git stash clear
Разница между pop и apply:
git stash popприменяет изменения и удаляет запись из stash;git stash applyприменяет изменения, но оставляет запись в stash.
После stash pop всегда проверяй:
git status
Stash - временный инструмент, а не полноценная замена коммитам. Если работу нужно сохранить надолго или передать команде, делай commit.
git revert: безопасная отмена
git revert создаёт новый коммит, который отменяет изменения старого коммита.
git revert HEAD
git revert abc1234
git revert --no-edit abc1234
--no-edit использует стандартное сообщение и не открывает редактор. Это удобно в автоматизированных или учебных сценариях.
Почему revert безопасен для shared branches:
A -- B -- C -- R
Коммит C остаётся в истории, а новый коммит R отменяет его изменения. История не переписывается, поэтому коллеги не получают конфликт из-за исчезнувших коммитов.
Это отличается от git reset, который двигает указатель ветки назад.
reset: когда осторожно
git reset меняет положение текущей ветки и состояние index/working tree в зависимости от режима.
Отменить последний коммит, но оставить изменения в working tree:
git reset HEAD~1
Это --mixed по умолчанию.
Сохранить изменения в staging:
git reset --soft HEAD~1
Полностью сбросить состояние:
git reset --hard HEAD~1
--hard удаляет незакоммиченные изменения из working tree. На общей ветке и в production-репозитории это опасная команда. В shared history обычно выбирай git revert, а не reset.
git cherry-pick
git cherry-pick берёт один конкретный коммит из другой ветки и применяет его поверх текущей ветки.
Типичный сценарий: в ветке hotfix есть один нужный коммит, и его нужно перенести в main, но всю ветку сливать не надо.
git switch main
git log --oneline hotfix
git cherry-pick abc1234
После cherry-pick в main появится новый коммит с теми же изменениями. Хеш будет другим, потому что у коммита новый родитель и новая позиция в истории.
Можно взять последний коммит ветки:
git cherry-pick hotfix
Но в учебных и production-сценариях часто лучше явно посмотреть git log и выбрать конкретный хеш.
Риск cherry-pick: если тот же коммит позже попадёт через merge, история может стать дублированной или конфликтной. Поэтому cherry-pick используют точечно, когда понятно, как изменения будут интегрироваться дальше.
commit --amend
Если ты только что сделал коммит и заметил ошибку в сообщении:
git commit --amend -m "Better message"
Если забыл добавить файл:
git add forgotten_file.txt
git commit --amend --no-edit
amend переписывает последний коммит: создаётся новый коммит вместо старого. Это нормально для локальной ветки до push. После push в общую ветку используй осторожно, потому что история уже могла попасть к коллегам.
Короткая памятка
| Задача | Команда |
|---|---|
| Граф всех веток | git log --oneline --graph --all --decorate |
| Коммиты автора | git log --author="student" --oneline |
| История файла | git log -- README.md |
| Изменённые файлы между ветками | git diff --name-only main..develop |
| Annotated tag | git tag -a v1.0.0 -m "First release" |
| Список тегов | git tag |
| Спрятать изменения | git stash push -m "message" |
| Вернуть stash | git stash pop |
| Безопасно отменить коммит | git revert --no-edit <commit> |
| Перенести один коммит | git cherry-pick <commit> |
Продвинутый Git - это не набор редких команд ради красоты. Это умение читать историю и выбирать операцию, которая не сломает работу команды.
