Видаліть конфіденційні файли та їх коміти з історії Git


353

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

Я знаю, що можу додати ці імена файлів до .gitignore , але це не видалить їхню історію в Git.

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

Чи є спосіб видалити всі сліди певного файлу з вашої історії Git?



Відповіді:


448

З усіх практичних цілей, перше , про що слід потурбуватись, - це ЗМІНА ВАШИХ ПАРОЛІВ! З вашого запитання незрозуміло, чи ваше сховище git повністю локальне, чи у вас ще є віддалене сховище в іншому місці; якщо він віддалений і не захищений від інших, у вас є проблеми. Якщо хтось клонував це сховище перед тим, як виправити це, він отримає копію ваших паролів на локальній машині, і немає ніякого способу змусити їх оновити до вашої "фіксованої" версії, коли він пішов з історії. Єдине безпечне, що ви можете зробити - це змінити пароль на щось інше, де б ви його не використовували.


Ось це не виходить, ось як це виправити. GitHub відповів саме на це запитання як FAQ :

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

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

Оновлення 2019 року:

Це поточний код із FAQ:

  git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" \
  --prune-empty --tag-name-filter cat -- --all
  git push --force --verbose --dry-run
  git push --force

Майте на увазі, що як тільки ви перемістили цей код у віддалене сховище, наприклад, GitHub та інші, клонували це віддалене сховище, зараз ви потрапили в ситуацію, коли ви переписуєте історію. Коли інші спробують зняти ваші останні зміни після цього, вони отримають повідомлення про те, що зміни не можна застосувати, оскільки це не швидкий перемотка вперед.

Щоб виправити це, їм доведеться або видалити існуючий сховище, і його повторно клонувати, або дотримуватися вказівок у розділі "ВІДНОВЛЕННЯ ВІДГОТОВЛЕНОГО ЗВ'ЯЗКУ" на сторінці git-rebase .

Порада : Виконатиgit rebase --interactive


Надалі, якщо ви випадково вчинили якісь зміни з конфіденційною інформацією, але помітили, перш ніж переходити до віддаленого сховища, є деякі простіші виправлення. Якщо ви востаннє зобов'язуєтеся додати конфіденційну інформацію, ви можете просто видалити конфіденційну інформацію та виконати:

git commit -a --amend

Це дозволить внести зміни до попереднього зобов’язання з будь-якими новими внесеними вами змінами, включаючи цілі видалення файлів, виконані за допомогою git rm. Якщо зміни надалі повернулися в історію, але все ще не пересунуті до віддаленого сховища, ви можете зробити інтерактивну базу даних:

git rebase -i origin/master

Це відкриває редактор із зобов’язаннями, які ви зробили з моменту останнього спільного предка з віддаленим сховищем. Змініть "Вибрати" на "редагувати" в будь-яких рядках, що представляють комісію з конфіденційною інформацією, та збережіть та вийдіть із системи. Git пройде через зміни і залишить вас там, де ви можете:

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

Для кожної зміни з конфіденційною інформацією. Врешті-решт, ви знову опинитесь у своєму відділенні, і зможете спокійно натиснути нові зміни.


5
Ідеальний чувак, це чудова відповідь. Ти врятуєш мій день.
zzeroo

18
Просто додати один біт - у Windows слід використовувати подвійні лапки (") замість синглів.
ripper234

4
Зробив це на роботі. Я загубився в перекладах. Тут я використав посилання замість команди. Крім того, команда Windows закінчила необхідність подвійних лапок, як згадується ripper234, повний шлях, як пропонує MigDus, і не включаючи символи "\", які посилання проклеїли як нові індикатори загортання рядків. Заключна команда виглядала приблизно так: git filter-branch --force --index-filter "git rm --cached --ignore-unmatch src [Project] [File]. [Ext]" --prune-empty --tag- name-filter cat - - всі
Ерік Свонсон

3
Здається, що між вашим filter-branchкодом та цим кодом є деякі істотні відмінності на сторінці github, на яку ви пов’язані. Наприклад, їх 3-й рядок --prune-empty --tag-name-filter cat -- --all. Чи змінилося рішення чи я щось пропускаю?
геотеорія

2
Це рішення виглядає досить непогано, але якщо я ввів файл для видалення в початковій фіксації, <introduction-revision-sha1>..HEADце не працює. Він видаляє файл лише від другого фіксації. (Як я включаю початкову комісію до git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all
діапазону комітетів

91

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

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

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

Усі файли з розміром порогового значення (1 МБ за замовчуванням) в історії вашого репо будуть скановані, а будь-яка відповідна рядок (що не є у вашому останньому комітеті) буде замінена рядком "*** ВЗАЄМО ***". Потім можна використовувати git gcдля очищення мертвих даних:

$ git gc --prune=now --aggressive

BFG, як правило, на 10-50 разів швидше, ніж працює, git-filter-branchі параметри спрощені та адаптовані навколо цих двох загальних випадків використання:

  • Видалення божевільних великих файлів
  • Видалення паролів, облікових даних та інших приватних даних

Повне розкриття інформації: Я є автором BFG Repo-Cleaner.


Це варіант, але він може порушити вашу програму при використанні паролів, наприклад, для встановлення з'єднання з базою даних. Я вважаю за краще прийняту на даний момент відповідь, оскільки можна зберігати паролі у робочій копії та ігнорувати файли, що містять їх .gitignore.
Генридв

6
Це великий виграш саме тут. Після декількох спроб, я зміг використати це, щоб знімати коміти, що містять конфіденційну інформацію приватного репо, дуже ретельно та сильно оновлювати віддалене репо з переглянутою історією. Одне бокове зауваження полягає в тому, що ви повинні забезпечити, щоб наконечник репо-репортажу (HEAD) був чистим, не маючи конфіденційних даних, оскільки ця комісія вважається "захищеною" і не буде переглянута цим інструментом. Якщо це не так, просто очистіть / замініть вручну та git commit. В іншому випадку +1 для нового інструмента в інструментарій розробника :)
Метт Борджа

1
@Henridv На мій останній коментар, він не повинен порушувати вашу заявку, як ви могли передбачити, якщо припустити, що ваша заявка зараз розташована на кінчику або в голові вашої філії (тобто, остання комісія). Цей інструмент буде явно повідомляти про останню комісію These are your protected commits, and so their contents will NOT be alteredпід час проходження та перегляду решти вашої історії комісій. Якщо вам потрібно було відкатати, тоді так, вам потрібно було б просто здійснити пошук ***REMOVED***у комітеті, до якого ви щойно відкотили.
Метт Борха

1
+1 для BFG (якщо у вас встановлена ​​Java або не проти встановити її). Одним із заперечень є те, що BFG відмовляється видалити файл, якщо він міститься в HEAD. Тож краще спочатку зробити фіксацію, де потрібні файли будуть видалені, і лише після цього запустити BFG. Після цього ви можете скасувати останнє зобов'язання, тепер це нічого не змінить.
Fr0sT

1
Це насправді слід сприймати як правильну відповідь. Робить те, що написано на коробці!
gjoris

21

Якщо ви натиснули на GitHub, примусового натискання недостатньо, видаліть сховище або зв’яжіться зі службою підтримки

Навіть якщо ви змушуєте натиснути одну секунду після цього, цього недостатньо, як пояснено нижче.

Єдиними дійсними напрямками дії є:

  • це те, що просочилося змінним обліковим записом, як пароль?

    • так: негайно змініть свої паролі та подумайте про використання більше ключів OAuth та API!
    • ні (голі фотографії):

      • чи вам байдуже, якщо всі проблеми в сховищі будуть зведені нанівець?

        • ні: видаліть сховище
        • так:

          • зверніться в службу підтримки
          • якщо витік дуже важливий для вас, до того, що ви готові отримати деякий час простою сховища, щоб зменшити його швидкість, зробіть його приватним, поки ви будете чекати, коли підтримка GitHub відповість вам

Сила натискання секунди пізніше недостатня, оскільки:

Якщо ви видалите сховище замість того, щоб просто натиснути примусово, коміти зникають навіть з API негайно і дають 404, наприклад https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824. Це працює навіть якщо ви відтворили інше сховище з такою ж назвою.

Щоб перевірити це, я створив репо: 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

Дивіться також: Як видалити звисаючу комітку з GitHub?


20

Я рекомендую цей сценарій Девіда Андерхілла, який працював як шарм для мене.

Ці команди додають додатково фільтр-гілку 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

1
Зауважте, що використання терміну придатності та чорносливу є невірним, якщо ви не вказуєте дату, то за замовчуванням для всіх чорнослив старіші за 2 тижні. Все, що ви хочете, - це все, що потрібно:git gc --aggressive --prune=now
Адам Паркін

@Adam Parkin Я буду залишати код у відповіді тим самим, оскільки це зі сценарію на сайті Девіда Андерхіла, ви можете там прокоментувати, і якщо він змінить його, я змінив би цю відповідь, оскільки я справді не знаю, що це добре. Команда закінчується до перерізу не впливає, чи не так?
Джейсон Гімаат

1
@MarkusUnterwaditzer: Це не працюватиме для переданих комітетів.
Макс Бейкірх

Можливо, вам слід просто ввести у відповідь усі команди; це було б набагато послідовніше і не вимагало б розумового поєднання окремих постів :)
Ендрю Мао

9

Щоб було зрозуміло: прийнята відповідь правильна. Спробуйте спочатку. Однак це може бути надмірно складним для деяких випадків використання, особливо якщо ви стикаєтесь з нечесними помилками, такими як "фатальна: погана ревізія - порожній-порожній", або насправді не цікавить історія вашого репо.

Альтернативою може бути:

  1. cd до базової галузі проекту
  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 repo, так і з місцевого git repo. Якщо це неприйнятно, вам доведеться використовувати альтернативний підхід.

Назвіть це ядерним варіантом.


9

Можна використовувати 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/

Він зникне з усіх зобов’язань у вашій історії, рефлогування, тегів тощо

Раз у раз я стикаюся з однією і тією ж проблемою, і кожен раз, коли мені доводиться повертатися на цю посаду та інші, саме тому я автоматизував процес.

Кредити для учасників програми «Переповнення стека», які дозволили мені скласти це разом


8

Ось моє рішення у Windows

git filter-branch --tree-filter "rm -f" fileir / filename "" HEAD

git push - сила

переконайтеся, що шлях правильний, інакше він не працюватиме

Я сподіваюся, що це допомагає


8

Використовуйте фільтр-гілку :

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

3

Мені довелося це робити кілька разів на сьогодні. Зауважте, що це працює лише за 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>..


3

Отже, це виглядає приблизно так:

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

Видаліть кеш відстежуваного файлу з git та додайте цей файл до .gitignoreсписку


2

У своєму проекті для 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.