Удаление конфиденциальных данных из репозитория — это двухэтапный процесс, который включает в себя действия как локально, так и в удаленном репозитории:
Локальная очистка истории. На этом этапе используйте git-filter-repo для перезаписи истории репозитория на локальной машине. Процесс включает удаление файлов, замену текста в содержимом файлов и/или сообщениях коммитов. После этого локальная копия вашей истории уже не будет содержать конфиденциальных данных.
Обновление удаленного репозитория и последующая очистка на сервере. После локальной очистки вы принудительно отправляете (git push --force) измененную историю на удаленный репозиторий. Однако этого часто недостаточно, так как удаленные платформы могут кэшировать старые версии данных или сохранять ссылки на них в запросах на слияние. Поэтому второй критически важный шаг — это обращение в службу поддержки GitVerse для окончательной очистки кешей и удаления всех оставшихся ссылок на конфиденциальные данные на стороне сервера.
Локальная очистка истории
Программные средства для очистки истории локального репозитория
Известное ПО для очистки истории локального репозитория:
git-filter-repo — современный инструмент для перезаписи истории Git, рекомендуем использовать его.
git filter-branch — старый, медленный и имеет более сложный синтаксис.
По умолчанию git-filter-repo обрабатывает все ветки и теги в репозитории:
Все локальные ветки.
Все теги.
Всю историю коммитов, достижимую из этих веток.
Что НЕ обрабатывается автоматически:
Удаленные ветки (remote branches) — они не включаются по умолчанию.
Ветки, которые не были получены локально (fetch).
Для обработки ВСЕХ веток включая удаленные:
# Сначала получите все удаленные веткиgit fetch --all# Создайте локальные копии всех удаленных ветокgit branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}" "$remote" 2>/dev/null || truedone# Затем запустите git-filter-repogit-filter-repo --replace-text sensitive_data_to_clean.txt
Дополнительный аргумент --path используется, когда файл существовал по другим путям из-за перемещений или переименований. Иначе для удаления файла может потребоваться повторный запуск с альтернативным путем.
--replace-text
Замена текста в небинарных файлах истории с помощью подготовленного файла. Замена текста применяется и к сообщениям коммитов, если не указывать конкретные пути:
Файл, который вы передаете в качестве аргумента --replace-text, должен содержать строки в формате search_string==>replacement_string.
Каждая строка должна содержать искомый текст, затем два знака равенства (==>), а затем текст для замены.
Пример подготовленного файла со строками для замены:
Очистка текста в сообщениях коммитов. Этот метод позволяет выполнять более гибкую очистку или замену текста непосредственно в сообщениях коммитов, используя Python-скрипт:
git-filter-repo --message-callback 'secret_to_find = r"секрет" # Переменная для текста с конфиденциальными даннымиreplacement_text = b"Сообщение очищено от конфиденциальных данных" # Сообщение на месте заменыif re.search(secret_to_find, message.decode("utf-8")): return replacement_textreturn message'
В данном примере, чтобы конфиденциальный текст был стерт без замены, переменной replacement_text следует присвоить пустую байтовую строку replacement_text = b"".
--mailmap
Замена email адресов и имен авторов в метаданных коммитов. Этот метод позволяет изменить информацию об авторах коммитов во всей истории репозитория:
git-filter-repo --mailmap <(echo "Новое Имя <new-email@example.com> <old-email@personal.com>")
Warning
Не используйте <(echo ...) в Windows — это работает только в bash, но можно также создать отдельный файл .mailmap со списком замен и передать его в качестве аргумента.
В mailmap-файле обязательно используйте угловые скобки для email. Формат строки mailmap: Новое Имя <новый-email@example.com> <старый-email@personal.com>.
Пример файла mailmap.txt с несколькими заменами (обязательно используйте угловые скобки):
Личная почта <public@example.com> <sensitive_personal@example1.com>Рабочая почта <new@company.com> <old@company.com>Анонимный Пользователь <anon@example.com> <sensitive_personal@example2.com>
Пример запуска команды git-filter-repo --mailmap с файлом mailmap.txt (файл размещен в папке уровнем выше):
Гибкое изменение метаданных коммитов с помощью Python-скрипта. Этот метод позволяет программно изменять любые данные коммита, включая email адреса, имена, даты:
Linux
git-filter-repo --commit-callback ' # Замена email в метаданных коммита if commit.author_email == b"sensitive_personal@example.com": commit.author_email = b"public@email.com" if commit.committer_email == b"sensitive_personal@example.com": commit.committer_email = b"public@email.com" # Замена имен авторов if commit.author_name == b"Личное Имя": commit.author_name = b"Публичное Имя" '
git-filter-repo --commit-callback '# Удаляем номера задач из сообщенийmessage_str = commit.message.decode("utf-8")# Удаляем паттерны вида "#1234" или "TASK-5678"clean_message = re.sub(r"#\d+|TASK-\d+", "", message_str).strip()commit.message = clean_message.encode("utf-8")'
Проверка обработанных веток
После выполнения git-filter-repo можно проверить, какие ветки были изменены в процессе очистки:
cat .git/filter-repo/changed-refs
Эта команда покажет все ссылки (refs), которые были изменены, включая:
Локальные ветки (refs/heads/).
Удаленные ветки (refs/remotes/).
Теги (refs/tags/).
Запросы на слияние (refs/pull/).
Info
Если файл .git/filter-repo/changed-refs не существует, это означает, что
git-filter-repo не обнаружил изменений или команда была выполнена без
создания истории изменений.
Для подсчета количества измененных веток используйте:
# Подсчет всех измененных ссылокwc -l .git/filter-repo/changed-refs# Подсчет только локальных ветокgrep -c '^refs/heads/' .git/filter-repo/changed-refs# Подсчет только удаленных ветокgrep -c '^refs/remotes/' .git/filter-repo/changed-refs
Проверка затронутых запросов на слияние
После перезаписи истории git-filter-repo вы можете узнать, сколько запросов на слияние потенциально затронуты, используя команду grep. Это важно, так как GitVerse может продолжать ссылаться на старые, содержащие конфиденциальные данные, коммиты.
Эта команда выводит количество строк, соответствующих шаблону ^refs/pull/.*/head$, в файле .git/filter-repo/changed-refs. Каждая такая строка указывает на то, что связанный с ней запрос на слияние может ссылаться на старую историю до очистки.
Чтобы увидеть список затронутых запросов на слияние вместо их количества, уберите флаг -c:
Если при очистке не был создан файл .git/filter-repo/changed-refs, то
команда вернет сообщение grep: .git/filter-repo/changed-refs: No such file or directory.
Info
После успешного выполнения этих команд ваш локальный репозиторий будет полностью синхронизирован с удаленным репозиторием, и все изменения git-filter-repo будут отменены.
Обновление удаленного репозитория и последующая очистка на сервере
После того как вы успешно очистили историю репозитория локально, необходимо перенести эти изменения в удаленный репозиторий и убедиться, что все следы конфиденциальных данных удалены на сервере. Этот процесс включает принудительную отправку изменений и, при необходимости, взаимодействие со службой поддержки.
Принудительная отправка изменений
Команда позволяет перезаписать всю историю удаленного репозитория вашей локальной, уже очищенной версией:
git push --force --mirror origin
где:
--force — флаг принудительной отправки изменений для перезаписи истории изменений на удаленном сервере;
--mirror — флаг гарантирует, что отправятся все ссылки (ветки, теги и другие специальные ссылки, такие как refs/pull/) из вашего локального репозитория в удаленный. Это обеспечивает точное соответствие удаленного репозитория вашей очищенной локальной копии, включая удаление ссылок на старые, содержащие конфиденциальные данные коммиты;
origin — указывает на удаленный репозиторий, куда вы отправляете изменения. Обычно это основной удаленный репозиторий по умолчанию.
Warning
Перед выполнением этой команды обязательно согласуйте действия со всеми
участниками репозитория. Все соавторы должны удалить свои локальные клоны
репозитория и создать их заново, либо выполнить жесткий сброс (git reset --hard) и синхронизировать свои ветки с новой историей.
Info
Если у вас включена защита веток, то команда
git push --force --mirror может быть отклонена. В этом случае вам придется
временно отключить защиту веток, выполнить отправку изменений, а затем снова
включить защиту.
Пример очистки репозитория от конфиденциальных данных
Подготовительные действия
Установите git-filter-repo, например:
sudo apt updatesudo apt install git-filter-repo
Сохраните все изменения. Если есть непомеченные (unstaged) или незакомиченные (uncommitted) изменения, команда git-filter-repo не сработает корректно.
Сделайте резервную копию вашего репозитория. Это критически важно, так как git-filter-repo необратимо изменяет историю.
Идентификация и подготовка конфиденциальных данных
Предположим, вы случайно закоммитили API-ключ MY_SECRET_API_KEY_123 в файл config.js и также упомянули его в сообщении коммита.
Создайте файл для замены/удаления текста, например, sensitive_data_to_clean.txt:
MY_SECRET_API_KEY_123==>***API_KEY_REMOVED***
Если вы хотите просто удалить, то:
MY_SECRET_API_KEY_123
Очистка файлов и сообщений коммитов локально
Выполните git-filter-repo для удаления API-ключа из содержимого файлов и сообщений коммитов:
Если результат больше нуля, вам потребуется связаться со службой поддержки GitVerse.
Принудительная отправка изменений в удаленный репозиторий
git push --force --mirror origin
Warning
Перед выполнением убедитесь, что все коллеги осведомлены, и временно отключите
защиту веток, если она есть.
Связь со службой поддержки GitVerse (при необходимости)
Если запросы на слияние были затронуты (шаг 4), свяжитесь со службой поддержки GitVerse! и предоставьте им необходимую информацию, чтобы они могли очистить серверные кэши и ссылки.