Модуль 2: Углубляемся в коммиты и историю
Учимся читать историю, смотреть диффы коммитов, исправлять последний коммит и добавлять изменения частями. Разберём структуру коммита и лучшие практики оформления сообщений.
О чём этот модуль
- Структура коммита: дерево файлов, родитель, автор/коммиттер, сообщение
- Чтение истории: oneline/graph/decorate, фильтры по файлам/авторам/датам
- Правки:
--amendи аккуратное использование - Точечные добавления:
git add -pдля «чистых» коммитов - .gitignore: исключаем лишнее из истории
Хорошие сообщения к коммитам
- Короткая заголовочная строка (до ~72 символов), затем пустая строка и подробности
- Используйте стиль Conventional Commits:
feat:,fix:,docs:,refactor:и т.п. - Пишите почему, а не только что
Чтение истории — это не столько набор флагов к git log, сколько умение формулировать вопрос к истории: «кто и когда трогал этот файл?», «какие коммиты привели к регрессии?», «почему появилось это решение?». Освоив фильтры и форматы вывода, вы превращаете историю в полноценный инструмент анализа.
2.1 Анализ истории: git log
git log --oneline --graph --decorate --all
# Отбор по автору/дате/файлу
git log --author="Ivan" --since="2024-01-01" -- README.md
# Показать статистику
git log --stat
# Дифф внутри лога
git log -p -- foo.txt
Старайтесь подбирать формат под задачу. Краткий «граф» полезен для ориентирования, а детальный -p — для изучения контекста изменений. Помните о диапазонах и селекторах: они позволяют сфокусироваться на интересующем промежутке истории.
2.2 Просмотр конкретного коммита: git show
git show <commit-hash>
# Показать только сообщение
git show -s --format=%B <hash>
git show — это «лупа» для одного коммита: кто автор, какие файлы затронуты, какие строки изменены. Умение быстро читать такие диффы экономит часы на ревью и отладке.
2.3 Изменение последнего коммита: --amend
# Добавить забытые файлы в предыдущий коммит
git add src/missed.py
git commit --amend --no-edit
# Изменить сообщение последнего коммита
git commit --amend -m "fix: корректирует обработку ошибок"
--amend переписывает заголовок истории, создавая новый коммит вместо старого. Это безопасно локально, но опасно для уже опубликованных веток: чужие репозитории потеряют ссылочную целостность.
Важно: не амендьте коммиты, которые уже ушли на общий remote.
2.4 Интерактивное добавление: git add -p
Позволяет выбрать части файла (hunks) для включения в коммит, делая коммиты «цельными».
git add -p
# Клавиши: y (принять), n (пропустить), e (редактировать), s (разбить), q (выход)
Мыслите «единицами смысла»: отдельный фикс — отдельный коммит. Интерактивное добавление помогает отделить косметику от логики, а эксперимент — от результата.
2.5 Игнорирование файлов: .gitignore
# .gitignore
__pycache__/
*.log
.env
build/
Шаблоны в .gitignore работают на уровне путей и масок. Для локальных исключений используйте .git/info/exclude. Не добавляйте в историю артефакты сборки и секреты — это усложняет ревью и раздувает репозиторий.
2.6 Анатомия коммита
Коммит — это объект в базе Git, содержащий:
- tree — ссылка на дерево (полный снимок всех файлов)
- parent — ссылка на родительский коммит (или два при merge)
- author — кто написал изменения (имя, email, время)
- committer — кто записал коммит (может отличаться от автора)
- message — сообщение к коммиту
# Посмотреть внутреннюю структуру коммита
git cat-file -p HEAD
# tree 4a2e3f...
# parent abc1234...
# author Иван <ivan@example.com> 1708000000 +0300
# committer Иван <ivan@example.com> 1708000000 +0300
#
# feat: добавить авторизацию
2.8 Сообщения коммитов и трейлеры
Хорошее сообщение коммита состоит из трёх частей:
# 1. Заголовок (до 72 символов, повелительное наклонение)
feat: добавить авторизацию по JWT
# 2. Тело (через пустую строку, подробности)
Реализована авторизация с использованием JWT-токенов.
Добавлены middleware для проверки токена и обновления
refresh-токена.
# 3. Трейлеры (метаданные для инструментов)
Refs: #142
Co-authored-by: Пётр <petr@example.com>
Signed-off-by: Иван <ivan@example.com>
2.9 Диапазоны и селекторы
# HEAD~N — N коммитов назад по первому родителю
git log HEAD~3 # 3 коммита назад
# HEAD^ — первый родитель (HEAD^2 — второй, для merge-коммитов)
git show HEAD^
# A..B — коммиты, которые есть в B, но нет в A
git log main..feature # что нового в feature
# A...B — коммиты, уникальные для каждой ветки
git log main...feature
# По дате
git log --since="2025-01-01" --until="2025-02-01"
# По автору
git log --author="Иван"
# По слову в сообщении
git log --grep="авторизация"
2.10 Продвинутые diff и log
# Пословное сравнение (для текстов, markdown)
git diff --word-diff
# Игнорировать пробелы
git diff -w
# Показать переименования файлов
git diff --find-renames
# Статистика изменений
git diff --stat
# Кто последний менял каждую строку
git blame path/to/file
# blame с игнорированием пробелов
git blame -w path/to/file
2.11 Поиск: pickaxe и git bisect
# Pickaxe: найти коммиты, которые добавили/удалили строку
git log -S "password" --oneline # где появилось слово "password"
git log -G "def validate_" --oneline # регулярное выражение
# Pathspec: ограничить область поиска
git log -- src/auth/ # только изменения в каталоге auth
# git bisect: бинарный поиск коммита с багом
git bisect start
git bisect bad # текущий коммит — баг
git bisect good abc1234 # этот коммит был без бага
# Git переключается на средний коммит — тестируйте
git bisect good # или git bisect bad
# Повторяйте, пока Git не найдёт виновный коммит
git bisect reset # вернуться к исходному состоянию
2.12 Кастомные форматы вывода
# Кастомный формат
git log --pretty=format:"%h %an %ar %s" -10
# abc1234 Иван 2 hours ago feat: добавить авторизацию
# Алиасы для частых команд (добавить в .gitconfig)
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.st "status -sb"
git config --global alias.last "log -1 --format='%H %s'"
# Теперь можно использовать:
git lg
git st
git last