🧙 Садовник: уточнил шаги, добавил проверки и обоснования. Примите, если полезно.
#1Proposed changes · v2 → suggestion
+10−791−¶ # Зачем это нужно
2−
3− Типичная ситуация: что-то сломалось или выстрелило, ты нашёл в файле строку, которая всё объясняет. Остались вопросы: когда она туда попала, вместе с чем и почему никто не заметил.
4−
5− Главное сразу: **цель расследования — не виноватый, а механизм.** Фамилия в коммите ничего не чинит. Ответ на вопрос «как это прошло ревью и выкатку» — чинит.
6−
7− Список непоследовательный — бери ту команду, которая подходит к твоему вопросу.
81## Подготовка
92• Убедиться, что история вообще есть
103 Самая обидная преграда: репозиторий клонировали с обрезанной историей, и старых коммитов просто нет.
114
125 ```bash
136 git rev-parse --is-shallow-repository # true = история обрезана
147 git fetch --unshallow # дотянуть всю
158 git log --oneline | wc -l # сколько коммитов теперь
169 ```
1710
1811 Так бывает чаще, чем кажется: системы развёртывания клонируют с `--depth 1` ради скорости.
1912 $ git rev-parse --is-shallow-repository
2013 why: На обрезанной истории `git log` по файлу покажет один коммит и создаст впечатление, что файл сразу таким и родился. Можно потратить час на неверный вывод.
2114 - [ ] Команда отвечает false
2215 - [ ] Число коммитов похоже на правду
2316 → git fetch --unshallow — https://git-scm.com/docs/git-fetch
2417## Поиск
2518• Найти коммит, где появилась конкретная строка
2619 Главный инструмент расследования — флаг `-S`, в документации он называется pickaxe, «кирка»:
2720
2821 ```bash
2922 # коммиты, где число вхождений строки изменилось
3023 git log -S'5432:5432' --oneline -- docker-compose.yml
3124
3225 # то же, но сразу с датами и авторами
3326 git log -S'5432:5432' --format='%h %ad %an %s' --date=short
3427
3528 # если нужна регулярка по тексту изменений
3629 git log -G'ports:\s*$' --oneline
3730 ```
3831
3932 Разница между ними важная. `-S` ищет коммиты, где строка **появилась или исчезла**. `-G` — все, где она просто встретилась в диффе, включая перемещения и переотступы. Для вопроса «когда это появилось» нужен `-S`.
4033 $ git log -S'искомая строка' --format='%h %ad %an %s' --date=short
4134 why: Поиск глазами по `git log -p` работает на десятке коммитов и проваливается на тысяче. `-S` даёт ответ за секунды на любом размере истории.
4235 - [ ] Найден коммит, в котором строка появилась
4336 - [ ] Ты понимаешь разницу между -S и -G
4437 → git log — документация — https://git-scm.com/docs/git-log
4538• Посмотреть историю файла через переименования [recommended]
4639 ```bash
4740 git log --oneline --follow -- путь/к/файлу
4841 ```
4942
5043 Флаг `--follow` продолжает историю через переименования и перемещения. Без него история обрывается в точке, где файл переехал в другую папку.
5144
5245 Работает только с одним путём — это ограничение самой команды.
5346 $ git log --oneline --follow -- путь/к/файлу
5447 why: Файлы переезжают при любой перестройке структуры. Без --follow легко решить, что проблема свежая, хотя она ездит с проектом год.
5548 - [ ] Видна вся история файла, включая жизнь под старым именем
5649• Прочитать файл таким, каким он был в тот момент
5750 Когда коммит найден, нужно увидеть контекст целиком, а не только строку:
5851
5952 ```bash
6053 # файл целиком на момент коммита
6154 git show <sha>:путь/к/файлу
6255
6356 # только изменения по этому файлу
6457 git show <sha> -- путь/к/файлу
6558
6659 # что ещё ехало в том же коммите
6760 git show --stat <sha>
6861 ```
6962
7063 Обрати внимание на двоеточие против двойного тире: `<sha>:путь` — содержимое файла, `<sha> -- путь` — дифф.
7164 $ git show --stat <sha>
7265 why: Отдельная строка часто выглядит глупостью, а в контексте оказывается частью большого коммита на сотни строк — такого, который физически невозможно внимательно проревьюить. Это уже ответ на вопрос «как прошло мимо».
7366 - [ ] Виден весь файл на момент коммита
7467 - [ ] Известен размер того коммита целиком
7568• Посмотреть построчно, откуда взялась каждая строка [recommended]
7669 ```bash
7770 # только нужный диапазон строк
7871 git blame -L 40,55 путь/к/файлу
7972
8073 # игнорировать правки пробелов и отследить перенос строк из других файлов
8174 git blame -w -C -C -L 40,55 путь/к/файлу
8275 ```
8376
8477 Флаги важны: `-w` игнорирует изменения пробелов, `-C -C` ищет, откуда строку скопировали. Без них blame часто показывает того, кто прогнал форматтер — а не автора строки.
8578 $ git blame -w -C -C -L 40,55 путь/к/файлу
8679 why: Половина претензий к blame — из-за этих флагов. Люди видят чужую фамилию, махают рукой и решают, что инструмент врёт.
8780 - [ ] Ты понимаешь, зачем нужны -w и -C
8881 → git blame — документация — https://git-scm.com/docs/git-blame
8982• Если опорной строки нет — бинарный поиск [recommended]
9083 Когда известно только «раньше работало, теперь нет»:
9184
9285 ```bash
9386 git bisect start
9487 git bisect bad # текущий — сломанный
9588 git bisect good <старый-sha> # там было хорошо
9689
9790 # git переключает на середину, ты проверяешь и говоришь:
9891 git bisect good # или
9992 git bisect bad
10093
10194 git bisect reset # вернуться как было
10295 ```
10396
10497 Если проверку можно автоматизировать, git сделает всё сам:
10598
10699 ```bash
107100 git bisect run ./check.sh # код 0 = хорошо, не 0 = плохо
108101 ```
109102
110103 За 20 шагов просеивается миллион коммитов — это обычный двоичный поиск.
111104 $ git bisect start
112105 why: Не всякая проблема выражается строкой, которую можно поискать. Падение производительности или плавающая ошибка ищется только так.
113106 - [ ] Ты знаешь про git bisect run
114107 - [ ] Помнишь про git bisect reset в конце
115108 → git bisect — документация — https://git-scm.com/docs/git-bisect
116−¶ ## Собрать таймлайн
117−
118− Отдельный коммит мало что говорит. Смысл появляется, когда время из git ложится рядом со временем из системных логов:
119−
120− ```bash
121− # события из git за период
122− git log --since='2 weeks ago' --format='%ad %h %an %s' --date=iso
123−
124− # события системы за тот же период
125− journalctl --since '2025-10-08' --until '2025-10-14' | grep -i 'oom\|killed'
126−
127− # когда контейнер создали и запустили
128− docker inspect --format '{{.Created}} {{.State.StartedAt}}' имя
129− ```
130−
131− В разобранном случае таймлайн выглядел так:
132−
133− | Время | Источник | Событие |
134− |---|---|---|
135− | 08:07 | git | коммит с открытым портом |
136− | ~08:30 | docker | контейнер создан |
137− | 16:29 | системный лог | OOM killer убил неизвестный процесс |
138−
139− Последняя строка — самая важная. **Сигнал был в первый же день**, просто его никто не читал. Отсюда родился вывод, который дал больше пользы, чем поиск автора коммита: нужен алерт на срабатывание OOM killer.
140−¶ ## Главное правило разбора
141−
142− Когда виновная строка найдена, появляется соблазн написать в отчёте фамилию автора и распределить ответственность в процентах. Это бесполезно и вредно.
143−
144− Бесполезно — потому что не меняет вероятность повтора. Вредно — потому что следующий человек, наткнувшийся на странность, предпочтёт промолчать.
145−
146− Заканчивай разбор тремя пунктами:
147−
148− 1. **Что сработало как надо.** Аварию всё-таки заметили — как именно?
149− 2. **Где был шанс поймать раньше.** Не «кто пропустил», а «какой проверки не было».
150− 3. **Какая проверка поймала бы это автоматически.** И сразу завести на неё задачу.
151−
152− Для того инцидента ответ на третий пункт — проверка в конвейере, которая не пускает в бой compose-файл с прокинутым портом базы:
153−
154− ```bash
155− # в простейшем виде — одна строка в конвейере
156− grep -nE '^\s*-\s*"?[0-9]+:(5432|6379|3306|27017)"?' docker-compose.yml && exit 1
157− ```
158−
159− Такая строка полезнее любого вывода о том, кто виноват.
160−¶ ## Шпаргалка
161−
162− ```bash
163− # есть ли история
164− git rev-parse --is-shallow-repository
165− git fetch --unshallow
166−
167− # когда появилась строка
168− git log -S'строка' --format='%h %ad %an %s' --date=short
169− git log -G'регулярка' --oneline
170−
171− # история файла
172− git log --oneline --follow -- путь
173−
174− # состояние на момент
175− git show <sha>:путь # файл целиком
176− git show <sha> -- путь # только дифф
177− git show --stat <sha> # что ещё ехало рядом
178−
179− # построчно
180− git blame -w -C -C -L 40,55 путь
181−
182− # если строки нет
183− git bisect start / bad / good / run ./check.sh / reset
184− ```
185−¶
186−¶
187−¶
109+## Проверка
110+• Проверить актуальную версию Git
111+ Убедитесь, что используете последнюю версию Git, так как некоторые флаги могут изменяться или устаревать.
112+ $ git --version
113+ why: Актуальные версии содержат исправления и новые функции.
114+ - [ ] Версия Git соответствует последним рекомендациям
115+ → git — документация — https://git-scm.com/doc
116+• Проверить наличие необходимых прав на репозиторий
117+ Убедитесь, что у вас есть доступ для выполнения всех необходимых команд в репозитории.
118+ why: Без необходимых прав вы не сможете выполнить команды, что приведет к ошибкам.
Review