Поиск по истории: log, blame, bisect
Код сломался. Тесты были зелёные, а теперь красные. Или хуже - баг на проде, и никто не знает, когда он появился. Git хранит полную историю каждого изменения, и в нём есть инструменты, чтобы найти виновный коммит за минуты, а не за часы ручного перебора. Чем аккуратнее ты оформлял коммиты, тем проще этот поиск.
Умение читать и искать по истории Git - это навык, который отличает разработчика, который «просто коммитит», от разработчика, который понимает, что происходит в проекте. log, blame и bisect - три основных инструмента для расследования.
git log: чтение истории
Базовые команды
# Стандартный вывод (полный формат)
git log
# Компактный формат: один коммит - одна строка
git log --oneline
# С графом веток
git log --oneline --graph --decorate
# С графом и всеми ветками (не только текущей)
git log --oneline --graph --decorate --all
Фильтрация по времени
# Коммиты за последнюю неделю
git log --since="1 week ago"
# Коммиты за конкретный период
git log --after="2026-04-01" --before="2026-04-30"
# Последние 10 коммитов
git log -10
Фильтрация по автору
# Все коммиты конкретного автора
git log --author="Dmitriy"
# Поддерживается regex
git log --author="dmitriy\|popov" -i
Фильтрация по сообщению
# Коммиты с "fix" в сообщении
git log --grep="fix"
# Case-insensitive
git log --grep="fix" -i
# Несколько паттернов (OR)
git log --grep="feat:" --grep="fix:"
# Несколько паттернов (AND)
git log --grep="feat:" --grep="auth" --all-match
Фильтрация по содержимому (pickaxe)
Самый мощный фильтр - поиск по тому, что было изменено в коде:
# Коммиты, где строка "MaxRequestSize" появилась или исчезла
git log -S "MaxRequestSize"
# С показом diff
git log -S "MaxRequestSize" -p
# Поиск по регулярному выражению
git log -G "func.*Handler" --oneline
-S ищет коммиты, где количество вхождений строки изменилось (добавлена или удалена). -G ищет коммиты, где diff содержит совпадение с regex.
Форматирование вывода
# Кастомный формат
git log --format="%h %an %ar %s"
# a1b2c3d Dmitriy 2 hours ago feat: add rate limiting
# Полезные плейсхолдеры:
# %h - короткий хеш
# %H - полный хеш
# %an - имя автора
# %ae - email автора
# %ar - относительная дата (2 hours ago)
# %ad - дата (формат настраивается через --date)
# %s - тема коммита (первая строка)
# %b - тело коммита
# Красивый однострочный формат с датой
git log --format="%C(yellow)%h%C(reset) %C(green)%ad%C(reset) %s %C(blue)<%an>%C(reset)" --date=short
История конкретного файла
# Все коммиты, менявшие файл
git log - backend/handler/auth.go
# С показом diff для этого файла
git log -p - backend/handler/auth.go
# Следить за переименованиями
git log --follow - backend/handler/auth.go
git shortlog: статистика по авторам
# Количество коммитов по авторам
git shortlog -s -n
# 142 Dmitriy Popov
# 38 Ivan Ivanov
# 12 Bot
# За период
git shortlog -s -n --since="2026-04-01"
git blame: кто написал эту строку
blame показывает, кто последний раз менял каждую строку файла и в каком коммите:
git blame backend/handler/auth.go
# a1b2c3d4 (Dmitriy Popov 2026-04-15 14:23:01 +0300 1) package handler
# a1b2c3d4 (Dmitriy Popov 2026-04-15 14:23:01 +0300 2)
# d4e5f6g7 (Ivan Ivanov 2026-04-20 09:12:33 +0300 3) import (
# d4e5f6g7 (Ivan Ivanov 2026-04-20 09:12:33 +0300 4) "net/http"
Blame для диапазона строк
Если интересует конкретный участок кода:
# Строки 50-70
git blame -L 50,70 backend/handler/auth.go
# От строки 50 до конца функции (если функция начинается на строке 50)
git blame -L 50,+20 backend/handler/auth.go
# По имени функции (Go, PHP)
git blame -L :HandleLogin backend/handler/auth.go
Полезные флаги
# Игнорировать изменения пробелов (whitespace)
git blame -w backend/handler/auth.go
# Показать email вместо имени
git blame -e backend/handler/auth.go
# Определить, откуда код был скопирован (внутри файла)
git blame -M backend/handler/auth.go
# Определить, откуда код был скопирован (из других файлов)
git blame -C backend/handler/auth.go
Blame в IDE и GitLab
В IDE (GoLand, VS Code) blame встроен - наведи на строку и увидишь автора, дату, коммит. В GitLab: открой файл → кнопка Blame сверху. Это удобнее терминала для быстрого просмотра. На практике blame чаще всего нужен не чтобы найти виноватого, а чтобы найти контекст решения - именно поэтому осмысленные комментарии в ревью и внятные сообщения коммитов окупаются.
git bisect: бинарный поиск бага
bisect - самый мощный инструмент отладки в Git. Он выполняет бинарный поиск по истории коммитов, чтобы найти тот, который сломал код. Если между «всё работало» и «всё сломалось» 100 коммитов, bisect найдёт виновный за 7 шагов (log2(100) ≈ 7).
Ручной bisect
# Начать сессию bisect
git bisect start
# Текущий коммит - плохой (баг есть)
git bisect bad
# Указать коммит, где всё работало (здесь пригодятся теги релизов)
git bisect good v1.1.0
# Bisecting: 50 revisions left to test after this (roughly 6 steps)
# Git переключит на середину. Проверяешь - работает?
go test ./...
# Если баг есть:
git bisect bad
# Если бага нет:
git bisect good
# Повторяй, пока Git не найдёт:
# a1b2c3d is the first bad commit
# commit a1b2c3d
# Author: Someone
# Date: ...
# feat: add caching to user service
# Завершить (вернуться к исходному состоянию)
git bisect reset
Автоматический bisect
Если у тебя есть команда, которая возвращает 0 при успехе и не-0 при ошибке (например, тест), bisect может работать автоматически:
git bisect start
git bisect bad HEAD
git bisect good v1.1.0
# Запустить автоматический поиск
git bisect run go test ./internal/service/user_test.go -run TestGetUser -count=1
# Git сам будет переключать коммиты и запускать тест
# Результат: первый коммит, где тест падает
Можно использовать скрипт:
git bisect run bash -c '
cd backend && go build ./... && go test ./internal/service/... -count=1
'
Пропуск коммитов
Иногда коммит невозможно проверить (не компилируется, например):
git bisect skip
# Git выберет соседний коммит
git show: просмотр конкретного коммита
# Показать коммит: метаданные + diff
git show a1b2c3d
# Только статистику изменений (без diff)
git show --stat a1b2c3d
# Показать конкретный файл в конкретном коммите
git show a1b2c3d:backend/handler/auth.go
# Показать файл на момент тега
git show v1.1.0:backend/handler/auth.go
Здесь видно, зачем нужны теги релизов: v1.1.0 в команде читается однозначно, а a1b2c3d - нет.
git diff: сравнение
# Разница между working directory и staging
git diff
# Разница между staging и последним коммитом
git diff --staged
# Разница между двумя коммитами
git diff a1b2c3d d4e5f6g
# Разница между двумя ветками
git diff develop..main
# Разница только для конкретного файла
git diff develop..main - backend/handler/auth.go
# Только имена изменённых файлов
git diff --name-only develop..main
# Статистика изменений
git diff --stat develop..main
git reflog: восстановление потерянного
reflog записывает каждое движение HEAD. Если ты потерял коммит после reset или неудачного rebase - он почти наверняка в reflog:
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~5 ← вот тут ты потерял коммиты
# d4e5f6g HEAD@{1}: commit: feat: add caching
# h7i8j9k HEAD@{2}: commit: fix: user service
# Восстановить
git reset --hard d4e5f6g
# Или создать ветку из потерянного коммита
git branch recovered-work d4e5f6g
Практический workflow отладки
Сценарий: тесты на CI красные, но ты не знаешь, какой коммит сломал.
# 1. Найти, когда последний раз тесты проходили
git log --oneline -20
# Знаешь, что v1.1.0 был стабильный
# 2. Запустить автоматический bisect
git bisect start
git bisect bad HEAD
git bisect good v1.1.0
git bisect run make test
# 3. bisect нашёл коммит a1b2c3d
# Посмотреть, что там изменилось
git show a1b2c3d
# 4. Понять, кто автор и спросить контекст
git log -1 --format="%an <%ae>" a1b2c3d
# 5. Посмотреть blame конкретной строки, которая ломает
git blame -L :problematicFunction backend/internal/service/user.go
# 6. Завершить bisect
git bisect reset
Весь процесс занимает 5-10 минут вместо часов ручного поиска.
Мини-задание
- Посмотри историю проекта:
git log --oneline --graph --allиgit shortlog -s -n - Найди все коммиты с "fix" в сообщении:
git log --grep="fix" --oneline - Запусти
git blameна любом файле с флагом-wи найди автора конкретной строки - Сравни две ветки:
git diff main..develop --stat - Попробуй
git bisectна учебном репозитории (создай 10 коммитов, в одном сломай тест, найди его через bisect)