Удалить конфиденциальные файлы и их коммиты из истории Git

Я хотел бы поместить проект Git на GitHub, но он содержит определенные файлы с конфиденциальными данными (имена пользователей и пароли, например /config/deploy.rb для capistrano).

Я знаю, что могу добавить эти имена файлов в .gitignore, но это не удалит их историю в Git.

Я также не хочу начинать все заново, удалив каталог /.git.

Есть ли способ удалить все следы определенного файла в вашей истории Git?

12 ответов

Решение

Для практических целей первое, о чем вы должны беспокоиться, это СМЕНА ВАШИХ ПАРОЛЕЙ! Из вашего вопроса не ясно, является ли ваш git-репозиторий полностью локальным или у вас еще есть удаленный репозиторий; если он удаленный и не защищен от других, у вас есть проблема. Если кто-то клонировал этот репозиторий до того, как вы это исправите, у него будет копия ваших паролей на их локальном компьютере, и вы не сможете заставить их обновиться до "фиксированной" версии, если она ушла из истории. Единственная надежная вещь, которую вы можете сделать, - это сменить свой пароль на другой, где бы вы его не использовали.


С этим из пути, вот как это исправить. GitHub ответил именно на этот вопрос в виде FAQ:

Примечание для пользователей Windows: используйте двойные кавычки (") вместо одинарных в этой команде

git filter-branch --index-filter \
'git update-index --remove filename' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

Имейте в виду, что как только вы отправили этот код в удаленное хранилище, такое как GitHub, а другие клонировали этот удаленный репозиторий, вы находитесь в ситуации, когда вы переписываете историю. Когда другие попытаются свернуть ваши последние изменения после этого, они получат сообщение о том, что изменения не могут быть применены, потому что это не ускоренная перемотка вперед.

Чтобы это исправить, им придется либо удалить свой существующий репозиторий и повторно клонировать его, либо следовать инструкциям в разделе "ВОССТАНОВЛЕНИЕ ОТ РЕБАЗЫ UPSTREAM" на справочной странице git-rebase.


В будущем, если вы случайно зафиксируете некоторые изменения с помощью конфиденциальной информации, но заметите это, прежде чем отправлять в удаленный репозиторий, есть несколько более простых исправлений. Если вы в последний раз добавили конфиденциальную информацию, вы можете просто удалить конфиденциальную информацию и выполнить:

git commit -a --amend

Это изменит предыдущий коммит с любыми внесенными вами новыми изменениями, включая удаление всего файла, выполненное с помощью git rm, Если изменения еще вернулись в историю, но все еще не перенесены в удаленный репозиторий, вы можете сделать интерактивную перебазировку:

git rebase -i origin/master

Это открывает редактор с коммитами, которые вы сделали со времени вашего последнего общего предка с удаленным репозиторием. Измените "выбрать" на "редактировать" в любых строках, представляющих коммит с конфиденциальной информацией, и сохраните и выйдите. Git пройдет через изменения и оставит вас в месте, где вы можете:

$EDITOR file-to-fix
git commit -a --amend
git rebase --continue

Для каждого изменения с конфиденциальной информацией. В конце концов, вы снова окажетесь в своей ветке и сможете спокойно вносить новые изменения.

Изменение ваших паролей - хорошая идея, но для процесса удаления паролей из истории вашего репо я рекомендую BFG Repo-Cleaner, более быструю и простую альтернативу git-filter-branch явно предназначен для удаления личных данных из репозиториев Git.

Создать private.txt файл со списком паролей и т. д., которые вы хотите удалить (по одной записи на строку), а затем выполните следующую команду:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

Все файлы с пороговым размером (по умолчанию 1 МБ) в истории вашего репо будут отсканированы, и любая подходящая строка (которой нет в вашем последнем коммите) будет заменена на строку "***REMOVED***". Вы можете использовать git gc чтобы убрать мертвые данные:

$ git gc --prune=now --aggressive

BFG обычно в 10-50 раз быстрее, чем бег git-filter-branch и варианты упрощены и адаптированы к этим двум распространенным сценариям использования:

  • Удаление сумасшедших больших файлов
  • Удаление паролей, учетных данных и других личных данных

Полное раскрытие: я являюсь автором BFG Repo-Cleaner.

Если вы уже отправили на GitHub, данные будут скомпрометированы, даже если вы принудительно оттолкнете их на одну секунду позже, потому что:

Чтобы проверить это, я создал репо: https://github.com/cirosantilli/test-dangling и сделал:

git init
git remote add origin git@github.com:cirosantilli/test-dangling.git

touch a
git add .
git commit -m 0
git push

touch b
git add .
git commit -m 1
git push

touch c
git rm b
git add .
git commit --amend --no-edit
git push -f

Однако, если вы удаляете репозиторий, коммиты немедленно исчезают даже из API и дают 404, например, https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824 Это работает, даже если вы воссоздаете другой хранилище с тем же именем.

Поэтому мой рекомендуемый курс действий:

  • изменить свои учетные данные

  • если этого недостаточно (например, голые картинки):

    • удалить репозиторий
    • Контактная поддержка

Я рекомендую этот сценарий Дэвида Андерхилла, который для меня сработал.

Он добавляет эти команды в дополнение к ветке фильтра natacado, чтобы убрать беспорядок, который он оставляет:

rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune

Полный сценарий (вся заслуга Дэвида Андерхилла)

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter \
"git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch
# otherwise leaves behind for a long time
rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune

Последние две команды могут работать лучше, если их изменить на следующее:

git reflog expire --expire=now --all && \
git gc --aggressive --prune=now

Ты можешь использовать git forget-blob,

Использование довольно просто git forget-blob file-to-forget, Вы можете получить больше информации здесь

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

Он исчезнет из всех коммитов в вашей истории, рефлогов, тэгов и т. Д.

Время от времени я сталкиваюсь с одной и той же проблемой, и каждый раз, когда мне приходится возвращаться к этому и другим постам, я автоматизировал этот процесс.

Кредиты для авторов из Stack Overflow, которые позволили мне собрать это воедино

Вот мое решение в Windows

git filter-branch --tree-filter "rm -f 'filedir / filename'" HEAD

git push --force

убедитесь, что путь правильный, иначе он не будет работать

Я надеюсь, что это помогает

Используйте фильтр-ветку:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all

git push origin *branch_name* -f

Чтобы было понятно: принятый ответ правильный. Попробуйте сначала. Тем не менее, это может быть излишне сложно для некоторых случаев использования, особенно если вы сталкиваетесь с неприятными ошибками, такими как 'fatal: bad revision --prune-empty', или действительно не заботитесь об истории вашего репо.

Альтернативой будет:

  1. перейдите в базовую ветку проекта
  2. Удалить секретный код / ​​файл
  3. rm -rf .git/ # Удалить всю информацию git из вашего кода
  4. Зайдите на github и удалите свой репозиторий
  5. Следуйте этому руководству, чтобы перенести свой код в новый репозиторий, как обычно, - https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

Это, конечно, удалит все ветки истории коммитов и проблемы как из вашего репозитория github, так и из вашего локального репозитория git. Если это неприемлемо, вам придется использовать альтернативный подход.

Назовите это ядерным вариантом.

В моем проекте Android у меня был admob_keys.xml в виде отдельного XML-файла в папке app/src/main/res/values ​​/. Для удаления этого чувствительного файла я использовал приведенный ниже скрипт и работал отлично.

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all

Я должен был сделать это несколько раз на сегодняшний день. Обратите внимание, что это работает только для 1 файла за раз.

  1. Получить список всех коммитов, которые изменили файл. Внизу будет первый коммит:

    git log --pretty=oneline --branches -- pathToFile

  2. Чтобы удалить файл из истории, используйте первый коммит sha1 и путь к файлу из предыдущей команды и введите их в эту команду:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..

Итак, это выглядит примерно так:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore

Удалить кеш для отслеживаемого файла из Git и добавить этот файл в .gitignore список

Учитывая, что OP использует GitHub, если кто-то передает конфиденциальные данные в репозиторий Git, их можно полностью удалить из истории, используя один из предыдущих вариантов (подробнее о них читайте ниже):

  1. Инструмент ().

  2. Инструмент (это с открытым исходным кодом — см. исходный код на GitHub).

После одного из предыдущих вариантов необходимо выполнить дополнительные шаги. Проверьте раздел «Дополнительно» ниже.

Если цель состоит в том, чтобы удалить файл, который был добавлен в самый последний неотправленный коммит , прочитайте раздел «Альтернатива» ниже.

Для будущих соображений, чтобы предотвратить подобные ситуации, проверьте раздел «На будущее» ниже.


Опция 1

С использованиемсм. исходный код на GitHub. Прежде чем двигаться дальше, обратите внимание, что

Если вы запустите после сохранения изменений, вы не сможете получить свои изменения с помощью других команд хранения. Перед запуском рекомендуется удалить все сделанные вами изменения. Чтобы разблокировать последний набор изменений, которые вы спрятали, запуститеgit stash show -p | git apply -R. Дополнительные сведения см. в разделе Git Tools — Stashingand Cleaning.

Давайте теперь удалим один файл из истории своего репо и добавим его (чтобы предотвратить его повторную фиксацию).

Прежде чем двигаться вперед, убедитесь, чтоgit filter-repoустановлен (читайте здесь, как его установить ), и у него есть локальная копия репозитория (если это не так, см. здесь, как клонировать репозиторий ).

  1. Откройте GitBash и получите доступ к репозиторию.

            cd YOUR-REPOSITORY
    
  2. (Необязательно) Сделайте резервную копию файла.

  3. Бегать

            git filter-repo --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
    

    заменятьPATH-TO-YOUR-FILE-WITH-SENSITIVE-DATAс путем к файлу, который вы хотите удалить, а не только с его именем файла :

    • Заставить Git обрабатывать, но не проверять всю историю каждой ветки и тега.

    • Удалить указанный файл (а также сгенерированные в результате пустые коммиты)

    • Удалите некоторые конфигурации (например, удаленный URL-адрес, хранящийся в.git/configфайл)

    • Перезаписать существующие теги .

  4. Добавьте файл с конфиденциальными данными в.gitignore

            echo "YOUR-FILE-WITH-SENSITIVE-DATA" >> .gitignore
    
    git add .gitignore
    
    git commit -m "Add YOUR-FILE-WITH-SENSITIVE-DATA to .gitignore"
    
  5. Проверьте, все ли было удалено из истории репозитория, и что все ветки извлечены. Только после этого переходите к следующему шагу.

  6. Принудительно отправьте локальные изменения, чтобы перезаписать ваш репозиторий на GitHub.com, а также все ветки, которые вы отправили. Для удаления конфиденциальных данных из истории коммитов требуется принудительное нажатие. Прочтите первое примечание внизу этого ответа для более подробной информации.

            git push origin --force --all
    

Вариант 2

Использование BFG Repo-CleanerBFG Repo-Cleaner . Это быстрее и проще, чемgit filter-branch.

Например, чтобы удалить свой файл с конфиденциальными данными и оставить нетронутым последний коммит, запустите

      bfg --delete-files YOUR-FILE-WITH-SENSITIVE-DATA

Чтобы заменить весь текст, указанный вpasswords.txtвезде, где его можно найти в истории вашего репозитория, запустите

      bfg --replace-text passwords.txt

После удаления конфиденциальных данных необходимо принудительно отправить изменения на GitHub.

      git push --force

Дополнительный

После использования одного из вариантов выше:

  1. Обратитесь в службу поддержки GitHub.

  2. (Если вы работаете с командой) Скажите им перебазировать , а не объединять любые ветки, которые они создали из своей старой (испорченной) истории репозитория. Одна фиксация слияния может повторно ввести часть или всю испорченную историю, которую только что потрудились очистить.

  3. По прошествии некоторого времени, когда вы уверены, что у вас нет непреднамеренных побочных эффектов, вы можете принудительно разыменовать все объекты в своем локальном репозитории и собрать мусор с помощью следующих команд (используя Git 1.8.5 или новее):

            git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
    
    git reflog expire --expire=now --all
    
    git gc --prune=now
    

Альтернатива

Если файл был добавлен с самой последней фиксацией, и он не был отправлен на GitHub.com, можно удалить файл и изменить фиксацию:

  1. Откройте GitBash и получите доступ к репозиторию.

            cd YOUR-REPOSITORY.l
    
  2. Чтобы удалить файл, введитеgit rm --cached:

            git rm --cached GIANT_FILE
    # Stage our giant file for removal, but leave it on disk
    
  3. Зафиксируйте это изменение, используя--amend -CHEAD:

            git commit --amend -CHEAD
    # Amend the previous commit with your change
    # Simply making a new commit won't work, as you need
    # to remove the file from the unpushed history as well
    
  4. Отправьте свои коммиты на GitHub.com:

            git push
    # Push our rewritten, smaller commit
    

Для будущего

Чтобы предотвратить раскрытие конфиденциальных данных, другие передовые методы включают:

  • Используйте визуальную программу, чтобы зафиксировать изменения. Существуют различные альтернативы (такие как GitHub Desktop, GitKraken, gitk, ...), и было бы проще отслеживать изменения.

  • Избегайте универсальных командgit add .иgit commit -a. Вместо этого используйтеgit add filenameиgit rm filenameдля индивидуальной подготовки файлов.

  • Использоватьgit add --interactiveдля индивидуального просмотра и внесения изменений в каждый файл.

  • Использоватьgit diff --cachedдля просмотра изменений, подготовленных для фиксации. Это именно тот diff, которыйgit commitбудет производить до тех пор, пока не используется-aфлаг.

  • Создавайте секретные ключи в безопасном оборудовании (блоки HSM, аппаратные ключи, такие как Yubikey / Solokey), которое никогда не покидает его.

  • Обучите команду на x508.


Примечания:

Другие вопросы по тегам