Чтобы удалить пароль из истории git, нужно переписать коммиты, которые его содержат, командой git filter-repo --invert-paths --path <file>, а затем вычистить reflog и запустить сборку мусора, чтобы старые коммиты пропали и из .git. Делайте это только после ротации ключа — утёкший секрет считается скомпрометированным в момент коммита, независимо от того, почистите вы историю или нет. .gitignore и git rm --cached никак не влияют на уже существующие коммиты.
1. Убедиться, что секрет всё ещё в истории
$ clai проверь, остался ли этот ключ где-нибудь в истории репозитория→ git log --all --oneline -S 'sk-live-abc123secret'0fb8c94 stop tracking .env4c1d9ff add app and env
-S ищет коммиты, где число вхождений строки изменилось — то есть где секрет появился и где исчез. Поиск идёт по всей истории всех веток, а не по рабочему дереву.
2. Убедиться, что удаление из индекса не помогло
$ clai достань файл .env из того коммита, где он был добавлен→ git show 4c1d9ff:.envAPI_KEY=sk-live-abc123secret
Вот и вся проблема в одной команде. .env удалён из рабочего дерева, добавлен в .gitignore, git rm --cached уже выполнен — а секрет достаётся одной командой любому, у кого есть клон. Коммит по-прежнему хранит всё содержимое.
3. Посмотреть, в каких коммитах фигурирует путь
$ clai покажи все коммиты, где упоминается файл .env→ git log --all --oneline -- .env0fb8c94 stop tracking .env4c1d9ff add app and env
Это список того, что придётся переписать. Два коммита — и вы легко отделаетесь. Двести, разбросанные по веткам, — и сначала придётся согласовать план со всей командой.
4. Сначала ротация, потом чистка
Секрет, попавший в git, нужно считать утёкшим — без исключений. Репозиторий мог быть склонирован, форкнут, проиндексирован ботом или закеширован CI ещё до того, как вы заметили ошибку. Отзовите ключ и выпустите новый до того, как займётесь историей: чистая история с действующим украденным ключом хуже грязной истории с уже отозванным.
5. Переписать историю правильным инструментом
$ clai удали файл .env из всей истории репозитория→ git filter-repo --invert-paths --path .env⚠ DANGER — rewrites all history, every commit hash changes
git filter-repo — то, что рекомендует сам проект git. git filter-branch объявлен устаревшим: он на порядки медленнее и при неаккуратном использовании способен тихо испортить репозиторий. Альтернатива — BFG Repo-Cleaner: быстрее на больших репозиториях и умеет заменять строку на ***REMOVED***, не удаляя файл целиком.
6. Подчистить то, что осталось локально
$ clai удали старые ссылки и запусти сборку мусора агрессивно→ git reflog expire --expire=now --all && git gc --prune=now --aggressive
После переписывания старые коммиты остаются достижимыми через reflog — то есть секрет всё ещё лежит в .git. Эта команда убирает их окончательно. На стороне GitHub или GitLab придётся отдельно попросить поддержку почистить кеш, а форки за вас никто не почистит.
Грабли
- Переписанная история ломает клоны у всей команды. После force-push каждому придётся переклонировать или сделать
git rebase --onto. Предупредите заранее, иначе кто-нибудь запушит старую историю обратно. - Форки и пул-реквесты остаются. На GitHub коммит из закрытого PR может быть доступен по прямой ссылке даже после чистки основного репозитория. Ещё один аргумент в пользу ротации ключа.
git filter-branchустарел. Сам git при запуске печатает предупреждение и советует filter-repo. Половина статей в выдаче об этом ещё не знает.
Похожие вопросы
Достаточно ли добавить файл в .gitignore? Нет. .gitignore влияет только на новые файлы. Уже закоммиченное остаётся в истории целиком.
Как не допустить этого впредь? Хук pre-commit со сканером секретов: gitleaks, detect-secrets или trufflehog. Они ловят ключ до коммита, а не после публикации.
Что делать, если секрет уже на GitHub? Отозвать ключ немедленно, потом почистить историю, потом попросить поддержку GitHub убрать кешированные представления. Порядок именно такой.
Читайте также
- Отменить последний коммит в git, сохранив изменения
- Git-задачи, произнесённые вслух
- SAFE, CAUTION, DANGER: как CliAI классифицирует то, что вот-вот запустится
CliAI помечает git filter-repo как DANGER и требует подтверждения вводом, прежде чем тронуть хоть один коммит. Установка одной строкой.