Pickaxe и продвинутый поиск в истории
В предыдущих уроках мы уже встречали -S (“pickaxe”) как часть git log. В этом уроке смотрим на него глубже, изучаем -G (regex pickaxe), --all --source для поиска по всем веткам, и собираем набор DE-сценариев: “когда мы добавили этот SQL запрос”, “кто менял схему таблицы”, “был ли этот код раньше где-то ещё”.
Pickaxe — это один из самых полезных и недооценённых инструментов для DE, которые часто разбираются в legacy кодовых базах с pipelines, SQL и configs.
-S "string": точное вхождение
-S ищет коммиты, в которых изменилось количество вхождений заданной строки в файлы.
git log -S "SELECT * FROM orders" --oneline
Это найдёт все коммиты, где строка SELECT * FROM orders:
- Появилась (добавили эту строку).
- Исчезла (удалили).
- Изменилось количество (было 1 вхождение, стало 3).
То есть -S ловит появление и удаление, а не каждое касание соседних строк.
Преимущество перед grep
Можно искать так:
grep -rn "SELECT * FROM orders" .
Это найдёт текущие вхождения в working tree. Но не покажет:
- Когда строка была добавлена.
- Когда удалена в прошлом.
- В какой ветке.
Pickaxe смотрит в историю, не текущее состояние.
-S с -p: увидеть контекст
-p добавит diff каждого найденного коммита:
$ git log -S "SELECT * FROM orders" -p --oneline
abc1234 feat: add orders dashboard query
diff --git a/sql/dashboard.sql b/sql/dashboard.sql
@@ -10,0 +11,3 @@
+
+SELECT * FROM orders
+WHERE created_at > NOW() - INTERVAL 7 DAY
def5678 refactor: replace orders query
diff --git a/sql/dashboard.sql b/sql/dashboard.sql
@@ -11,3 +11,0 @@
-SELECT * FROM orders
-WHERE created_at > NOW() - INTERVAL 7 DAY
-
+SELECT id, total FROM orders ...
Ты сразу видишь: коммит abc1234 добавил, def5678 заменил.
-G "regex": regex pickaxe
-G похож на -S, но:
- Принимает regex, не точную строку.
- Ловит любое касание строки, соответствующей regex — даже без изменения количества вхождений.
# Все коммиты, которые трогали строки с DB_TIMEOUT = <число>
git log -G "DB_TIMEOUT\s*=\s*[0-9]+" --oneline -p
# Все коммиты с любым `SELECT` из orders таблицы
git log -G "SELECT.*FROM orders" --oneline
-G мощнее, но медленнее (regex matching на каждом diff’е).
-S vs -G: когда что
| Сценарий | Выбор |
|---|---|
| Знаешь точную строку, ищешь “когда добавили” | -S "exact string" |
| Ищешь паттерн (любая версия timeout) | -G "DB_TIMEOUT\s*=.*" |
| Не нужно ловить cosmetic touches | -S |
| Нужно поймать каждое изменение, даже если строка осталась | -G |
| Быстрее | -S |
--all: поиск по всем веткам
По дефолту git log показывает историю только текущей ветки. Чтобы искать во всех ветках:
git log -S "magic_value" --all --oneline
--all включает в поиск все локальные и remote-tracking branches. Полезно, когда не знаешь, в какой ветке могла появиться строка.
--source: показать, где найдено
--source добавляет к каждому коммиту имя ref’а (ветка, тег), где он находится:
$ git log -S "magic_value" --all --source --oneline
abc1234 refs/heads/feature/x feat: add magic
def5678 refs/heads/main feat: backport magic
Видишь: одна и та же строка появлялась в двух разных ветках.
--diff-filter: фильтр по типу изменения
Часто хочется только “когда добавлен” или только “когда удалён”.
# Только коммиты, добавившие новый файл со строкой
git log -S "magic" --diff-filter=A --oneline
# Только коммиты, удалившие файл со строкой
git log -S "magic" --diff-filter=D --oneline
# Modified
git log -S "magic" --diff-filter=M --oneline
Буквы: A=added, M=modified, D=deleted, R=renamed, C=copied.
Path filtering: только в определённых файлах
# Pickaxe только в SQL файлах
git log -S "FROM orders" -- '*.sql'
# В директории
git log -S "magic" -- src/dags/
# В файле
git log -S "magic" -- src/etl.py
Полезно, когда повторяющаяся строка может быть в разных местах, а ты хочешь только конкретные.
DE-сценарии
Сценарий 1: “Когда добавился этот SQL запрос?”
git log -S "SELECT user_id, SUM(amount)" -p -- 'sql/'
Найдёт коммит, добавивший этот запрос, и покажет полный контекст diff’а — увидишь, в какой файл он попал и почему.
Сценарий 2: “Кто менял схему таблицы users”
# Найди все миграции, трогавшие таблицу users
git log -G "(CREATE|ALTER|DROP)\s+TABLE.*users" --oneline -- 'migrations/'
# Или для DBT
git log -G "users" --oneline -- 'models/'
Сценарий 3: “Был ли этот код в legacy ветке”
# Кросс-branch поиск
git log -S "legacy_function" --all --source --oneline
# legacy/v1: a1b2c3d feat: implement legacy_function
# main: rebased version
# Открой коммит из legacy
git show a1b2c3d
Сценарий 4: “Когда мы заменили pandas на polars”
git log -S "import pandas" --diff-filter=D --oneline
# Найдёт коммит, в котором импорт pandas удалили
git log -S "import polars" --diff-filter=A --oneline
# Когда добавили polars
Сценарий 5: “Найди все коммиты, трогавшие connection strings”
# Будь осторожен: pickaxe найдёт даже коммиты с secrets!
git log -G "postgres://[^/]+:[^/]+@" --all --oneline
Если найдётся — это сигнал, что где-то в истории был запушен пароль. Действуй: revoke credentials, rotate, и BFG repo-cleaner для полной чистки. Подробно про секреты в Module 17 курса.
Сценарий 6: “Какие SQL запросы менялись чаще всего”
Это уже не pickaxe, но в тему — топ-меняющиеся файлы:
git log --pretty=format: --name-only --since="6 months ago" -- 'sql/' | \
sort | uniq -c | sort -rn | head -10
Дополнительно ranjа git log -S по каждому из этих файлов покажет, какие конкретные строки/запросы меняются.
--follow: проследить переименования
-S с --follow следит за переименованиями файлов. Это только работает для одного файла, не директории:
git log -S "magic" --follow --oneline -- src/etl.py
Если src/etl.py раньше назывался src/extract.py, поиск пройдёт и в старом имени тоже.
Limit by commit count
При работе с большими репо --all может быть очень медленным. Ограничения:
# Только последние 100 коммитов
git log -S "magic" -100 --oneline
# Последний месяц
git log -S "magic" --since="1 month ago" --oneline
# Между двумя SHA
git log -S "magic" abc1234..HEAD --oneline
git grep: поиск в текущем состоянии
Дополнительная команда, не путать с git log -S. git grep ищет в working tree или в указанной revision:
# Текущее состояние — как grep, но быстрее (использует Git index)
git grep "SELECT * FROM"
# В конкретном коммите
git grep "SELECT * FROM" abc1234
# Во всех файлах конкретной revision
git grep "magic" main
# Игнор case
git grep -i "SELECT"
# Show line numbers
git grep -n "magic"
# Path filter
git grep "magic" -- '*.py'
Это инструмент “что СЕЙЧАС в коде”. git log -S — “что было/появилось в истории”.
Часто используют комбинацию:
# 1. Где сейчас есть строка
git grep "DB_TIMEOUT"
# 2. Когда она появилась
git log -S "DB_TIMEOUT" --oneline
Поиск среди удалённых файлов
git log -S найдёт упоминания и в удалённых файлах:
git log -S "old_function" --all --oneline
Найдёт коммит, который “удалил” — то есть последний коммит, где old_function уменьшила вхождения до 0.
Можно увидеть содержимое удалённого файла:
# Найди коммит до удаления
git log --diff-filter=D --oneline -- src/old.py
# abc1234 chore: remove old module
# Посмотри коммит-родитель удаления — там файл ещё существовал
git show abc1234^:src/old.py
Performance tips
Pickaxe на больших репо может занимать минуты. Способы ускорить:
-
Ограничь по path:
git log -S "magic" -- src/etl.pyнамного быстрее, чем по всему репо. -
Ограничь по дате:
--since="6 months ago"сильно режет диапазон. -
Используй
-Sвместо-Gгде возможно: regex дороже. -
Используй
--max-count=N: остановиться после N результатов. -
--no-renames: отключает detection переименований, ускоряет.
git log -S "magic" --since="3 months ago" --no-renames --max-count=20 --oneline -- src/
GUI alternatives
В IDE поиск истории часто проще:
- VS Code GitLens: команда “Search Commits” — UI с filters и preview.
- PyCharm: Git -> Show History -> встроенный pickaxe (вкладка Filter -> Contains).
- GitHub UI:
https://github.com/<org>/<repo>/search?q=magic+language%3APython&type=codeищет в текущем состоянии. История по строке — open issue, GitHub Search ограничен.
CLI всё ещё мощнее для сложных запросов.
Попробуй сам
# Клонируй большой репо
git clone https://github.com/apache/airflow.git
cd airflow
# Найди когда добавили kubernetes executor
git log -S "KubernetesExecutor" --diff-filter=A --oneline | head -3
# Когда удаляли что-то связанное с Python 2
git log -G "python.?2" --since="3 years ago" --oneline | head -5
# История изменений в конкретном файле
git log --oneline --follow -- airflow/models/dag.py | head -10
# Pickaxe по двум критериям комбинированно
git log -S "DagBag" --grep="fix" --oneline | head -10
# Только коммиты, в которых менялся DagBag И слово 'fix' в сообщении
# Поиск в одной директории, недавнее
git log -G "celery" --since="1 year ago" --oneline -- airflow/executors/ | head -10
# Кросс-branch проверка
git log -S "PythonOperator" --all --source --oneline | head -10
Parquet, снapppy, gzip: выбор сжатия для колоночных форматов