Как убрать пароль из истории Git
Первым делом смените пароль, токен или ключ — в том сервисе, от которого он. Даже если коммит провисел на сервере минуту, его могли скопировать: открытые репозитории постоянно просматривают программы, которые ищут секреты. Чистка истории убирает следы, но не отменяет утечку. Старый секрет считайте раскрытым.
Как убрать секрет из истории
Способ зависит от того, где коммит с секретом: последний и ещё не отправлен, один из нескольких неотправленных или уже на сервере.
Последний коммит, ещё не отправленпересоберите его
Уберите секрет из файла (или файл из индекса) и пересоберите последний коммит:
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit --amend --no-editЕсли секрет был строкой в обычном файле — исправьте файл, git add файл и git commit --amend --no-edit. Коммит с секретом заменится новым, и на сервер он не попадёт.
Один из нескольких неотправленныхперепишите их через rebase
Перепишите неотправленные коммиты интерактивным rebase:
- Выполните
git rebase -i HEAD~3(3 — сколько последних коммитов просмотреть). - В открывшемся списке замените
pickнаeditу коммита с секретом, сохраните и закройте. - Git остановится на этом коммите: уберите секрет, как в ветке выше, и сделайте
git commit --amend --no-edit. - Продолжите:
git rebase --continue.
Уже на сервере или глубоко в историиперепишите всю историю
Git-проект рекомендует для этого отдельную программу git filter-repo вместо старой встроенной git filter-branch. В Ubuntu и Debian она ставится пакетом (sudo apt install git-filter-repo), для других систем — способы на странице проекта.
Работайте в свежем клоне — filter-repo по умолчанию отказывается переписывать репозиторий, который не похож на свежий клон:
git clone https://github.com/ivan/todo.git todo-clean
cd todo-cleanДальше — одно из двух: заменить сам пароль или удалить файл целиком.
Заменить пароль во всех версиях файлов
Создайте файл ../passwords.txt (вне репозитория) и запишите в него пароль отдельной строкой. Затем:
git filter-repo --replace-text ../passwords.txtКаждое вхождение заменится на ***REMOVED***. Своя замена записывается так: пароль==>ЗАМЕНА. Когда всё готово, удалите сам файл passwords.txt — в нём тот же пароль.
Удалить файл целиком из всей истории
git filter-repo --invert-paths --path config/secret.iniОтправьте новую историю
После переписывания filter-repo нарочно отключает связь с сервером (origin), чтобы старая и новая история случайно не смешались. Верните её и отправьте новую историю принудительно:
git remote add origin https://github.com/ivan/todo.git
git push --force --all
git push --force --tagsПереписанная история заменяет старую на сервере: у всех коммитов после изменённого — новые хеши. Всем участникам нужно склонировать репозиторий заново. Если кто-то отправит свою старую копию, секрет вернётся на сервер. Защищённые ветки хостинг может не дать перезаписать — это снимается в настройках репозитория.
Что остаётся на сервере
- Форки — копии репозитория у других пользователей — содержат старую историю, и вы их не почистите.
- Pull request и кэш страниц у хостинга могут ещё показывать старые коммиты по прямой ссылке. Убрать их может только поддержка хостинга — у GitHub для этого есть отдельная процедура в документации.
- Клоны у коллег и на серверах, где проект развёрнут, — их нужно удалить и склонировать заново.
Поэтому главное — смена секрета, а чистка истории — уборка за ним.
Как не допустить снова
- Храните секреты вне кода: в переменных окружения или в файле
.env, который записан в.gitignore(см. урок Файл .gitignore). - Кладите в репозиторий образец без настоящих значений —
.env.example. - Перед коммитом смотрите
git diff --staged: что в индексе, то и уйдёт на сервер.