機密ファイルとそのコミットをGit履歴から削除する


353

GitHubにGitプロジェクトを配置したいのですが、機密データ(ユーザー名とパスワード、capistranoの/config/deploy.rbなど)を含む特定のファイルが含まれています。

これらのファイル名を.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マンページの「UPSREAM 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上で、あなたの代わりにシングルの二重引用符( ")を使用する必要があります- 1ビットを追加するだけ。
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---all
Eric Swanson

3
あなたのfilter-branchコードとあなたがリンクしたgithubページのコードとの間にはいくつかの実質的な違いがあるようです。たとえば、3行目--prune-empty --tag-name-filter cat -- --all。ソリューションが変更されたか、何か不足していますか?
地質学2015

2
このソリューションは非常に良いように見えますが、最初のコミットで削除するファイルを導入した場合<introduction-revision-sha1>..HEADは機能しません。2番目以降のコミットからのみファイルを削除します。(どのように私は、最初のコミットの範囲にコミット含まれていますか?)方法は、ここで指摘されている割引:help.github.com/articles/... 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
white_gecko

91

パスワードを変更することは良い考えですが、リポジトリの履歴からパスワードを削除するプロセスのために、Gitリポジトリからプライベートデータを削除するために明示的に設計されたより高速でシンプルな代替手段であるBFG Repo-Cleanerをお勧めしgit-filter-branchます。

削除するprivate.txtパスワードなどをリストしたファイルを作成し(1行に1エントリ)、次のコマンドを実行します。

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

リポジトリの履歴のしきい値サイズ(デフォルトでは1 MB)未満のすべてのファイルがスキャンされ、一致する文字列(最新のコミットにないもの)は文字列 "*** REMOVED ***"に置き換えられます。次に、を使用git gcして、死んだデータを削除できます。

$ git gc --prune=now --aggressive

BFGは通常、実行よりも10〜50倍高速でgit-filter-branch、オプションは簡素化され、次の2つの一般的なユースケースに合わせて調整されます。

  • Crazy Big Filesの削除
  • パスワード、資格情報、その他のプライベートデータの削除

完全な開示:私はBFG Repo-Cleanerの作成者です。


これはオプションですが、データベース接続のセットアップなど、パスワードが使用されている場合、アプリケーションが壊れる可能性があります。作業コピーにパスワードを保持し、.gitignoreを使用してそれらを含むファイルを無視することが可能であるため、現在受け入れられている回答をお勧めします。
Henridv 2013

6
これはここで大きな勝利です。数回試行した後、私はこれを使用して機密情報を含むコミットをプライベートレポから完全に削除し、改訂された履歴でリモートレポを強制的に更新することができました。一方の注意点は、このコミットは「保護されている」と見なされ、このツールによって変更されないため、リポジトリの先端(HEAD)自体が機密データなしでクリーンであることを確認する必要があることです。そうでない場合は、手動でクリーニング/交換してくださいgit commit。それ以外の場合は、開発者のツールボックスの新しいツールの+1 :)
Matt Borja

1
@Henridv私の最近のコメントによると、アプリケーションが現在ブランチの先端または先頭(つまり、最新のコミット)にあると想定すると、アプリケーションが予期したとおりに壊れないはずです。このツールはThese are your protected commits, and so their contents will NOT be altered、残りのコミット履歴を調べて修正している間、最後のコミットを明示的に報告します。ただし、ロールバックする必要がある場合は、ロールバック***REMOVED***したコミットで検索を実行する必要があります。
Matt Borja

1
BFGの+1(Javaがインストールされているか、Javaをインストールしてもかまわない場合)。1つの問題は、HEADに含まれているファイルをBFGが削除しないことです。したがって、目的のファイルが削除されるコミットを最初に実行してから、BFGを実行することをお勧めします。その後、最後のコミットを元に戻すことができますが、現在は変更されていません。
Fr0sT 2014年

1
これは実際には正しい答えとして受け入れられるべきです。箱に書かれていることをします!
gjoris

21

GitHubにプッシュした場合、強制プッシュでは不十分です。リポジトリを削除するか、サポートにお問い合わせください

1秒後に強制プッシュしても、以下で説明するように十分ではありません。

唯一の有効な行動方針は次のとおりです。

  • パスワードのような変更可能な認証情報が漏洩したものは何ですか?

    • はい:パスワードをすぐに変更し、より多くのOAuthおよびAPIキーの使用を検討してください!
    • いいえ(裸の写真):

      • リポジトリ内のすべての問題が削除されるかどうかを気にしますか?

        • いいえ:リポジトリを削除します
        • はい:

          • サポート問い合わせ先
          • リークが非常に重要である場合は、リポジトリのダウンタイムを取得してリークの可能性を低くできる程度に、GitHubサポートからの返信を待つ間、プライベートにしてください。

次の理由により、強制的に1秒後に押すだけでは不十分です。

あなたが代わりしかし、押しただ力のリポジトリを削除した場合、コミットはすぐにでも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

このスクリプトをお勧めしますDavid Underhillによるします。私にとっては魅力のように機能しました。

natacadoのフィルターブランチにこれらのコマンドを追加して、残された混乱をクリーンアップします。

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

完全な脚本(すべてはDavid Underhillの功績です)

#!/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

最後の2つのコマンドは、次のように変更するとより適切に機能する場合があります。

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

1
有効期限とプルーニングの使用法は正しくないことに注意してください。日付を指定しない場合、プルーニングのデフォルトは2週間より古いすべてのコミットになります。何が欲しいのは、すべてのコミットがそうである:git gc --aggressive --prune=now
アダム・パーキン

@Adam Parkinデビッド・アンダーヒルのサイトのスクリプトからのものなので、同じ答えをコードに残しておきます。そこでコメントでき、彼がそれを変更した場合、私は本当にgitを知らないので、この答えを変更します。上手。プルーンの前の期限切れコマンドは影響しませんか?
Jason Goemaat 2012

1
@MarkusUnterwaditzer:プッシュされたコミットでは機能しません。
Max Beikirch 2013

多分あなたはあなたの答えにすべてのコマンドを入れるべきです。それははるかに一貫性があり、別々の投稿の精神的な組み合わせを必要としません:)
Andrew Mao

9

明確にするために:受け入れられた答えは正しいです。まずお試しください。ただし、一部のユースケースでは、特に「致命的:不正なリビジョン--prune-empty」などの不愉快なエラーが発生した場合や、リポジトリの履歴を気にしない場合は、不必要に複雑になる可能性があります。

別の方法は次のとおりです。

  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リポジトリとローカルgitリポジトリの両方から問題が削除されます。これが受け入れられない場合は、別の方法を使用する必要があります。

これを核オプションと呼んでください。


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/

履歴、reflog、タグなどのすべてのコミットから消えます

私は時々同じ問題に遭遇し、この投稿や他の記事に戻る必要があるたびに、プロセスを自動化したのはそのためです。

これをまとめることができたスタックオーバーフローからの貢献者へのクレジット


8

これがWindowsでの私の解決策です

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

git push --force

パスが正しいことを確認してください。それ以外の場合は機能しません

それが役に立てば幸い


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. 履歴からファイルを削除するには、最初のcommit 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.xmlapp / src / main / res / values /フォルダーに分離されたxmlファイルとして入れました。この機密ファイルを削除するために、以下のスクリプトを使用して完全に動作しました。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.