[英]Remove sensitive files and their commits from Git history
我想在 GitHub 上放置一個 Git 項目,但它包含某些帶有敏感數據的文件(用戶名和密碼,如 capistrano 的 /config/deploy.rb)。
我知道我可以將這些文件名添加到.gitignore ,但這不會刪除它們在 Git 中的歷史記錄。
我也不想通過刪除 /.git 目錄重新開始。
有沒有辦法刪除 Git 歷史記錄中特定文件的所有痕跡?
出於所有實際目的,您應該擔心的第一件事是更改您的密碼! 從您的問題中不清楚您的 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 年更新:
這是常見問題解答中的當前代碼:
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
完成的整個文件刪除。 如果更改進一步追溯到歷史但仍未推送到遠程存儲庫,您可以執行交互式 rebase:
git rebase -i origin/master
這將打開一個編輯器,其中包含自您與遠程存儲庫的最后一個共同祖先以來所做的提交。 在任何代表帶有敏感信息的提交的行上將“pick”更改為“edit”,然后保存並退出。 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
將掃描存儲庫歷史記錄中低於閾值大小(默認為 1MB)的所有文件,並且任何匹配的字符串(不在您的最新提交中)將替換為字符串“***REMOVED***”。 然后您可以使用git gc
清除死數據:
$ git gc --prune=now --aggressive
BFG 通常比運行git-filter-branch
快 10-50 倍,並且選項圍繞這兩個常見用例進行了簡化和定制:
完全披露:我是 BFG Repo-Cleaner 的作者。
如果你推送到 GitHub,強制推送是不夠的,刪除倉庫或聯系支持
即使您在之后強推一秒鍾,也不夠,如下所述。
唯一有效的行動方案是:
是什么泄露了像密碼這樣的可變憑證?
強推一秒鍾是不夠的,因為:
GitHub 長時間保持懸空提交。
但是,如果您與 GitHub 員工聯系,他們確實有權刪除此類懸空提交。
當我將所有 GitHub 提交電子郵件上傳到一個 repo時,我親身體驗了這一點,他們要求我將其刪除,所以我做了,他們做了一個gc
。 但是,必須刪除包含數據的拉取請求:由於此原因,在初始刪除后一年內,該存儲庫數據仍然可以訪問。
懸空提交可以通過以下任一方式看到:
在提交時獲取源代碼的一種便捷方法是使用下載 zip 方法,該方法可以接受任何參考,例如: https : //github.com/cirosantilli/myrepo/archive/SHA.zip
可以通過以下方式獲取丟失的 SHA:
type": "PushEvent"
API 事件type": "PushEvent"
。例如我的: https type": "PushEvent"
( Wayback machine )有像http://ghtorrent.org/和https://www.githubarchive.org/這樣的爬蟲程序,它們定期匯集 GitHub 數據並將其存儲在其他地方。
我找不到他們是否抓取了實際的提交差異,這不太可能,因為數據太多,但技術上是可能的,而且 NSA 和朋友們可能有過濾器來只存檔與人或感興趣的提交相關的內容。
但是,如果您刪除存儲庫而不是強制推送,則即使從 API 中提交也會立即消失並給出 404,例如https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a382即使您重新創建另一個具有相同名稱的存儲庫。
為了測試這一點,我創建了一個 repo: 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 中刪除懸空提交?
現在正式推薦git filter-repo
超過git filter-branch
這在 Git 2.5 本身的git filter-branch
頁中提到。
使用 git filter repo,您可以刪除某些文件: 從 git/GitHub 的歷史記錄中刪除文件夾及其內容
pip install git-filter-repo
git filter-repo --path path/to/remove1 --path path/to/remove2 --invert-paths
這會自動刪除空提交。
或者您可以將某些字符串替換為: 如何替換整個 Git 歷史記錄中的字符串?
git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')
我推薦大衛安德希爾的這個劇本,對我來說就像一個魅力。
除了 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
。 你可以在這里獲得更多信息
它將從您的歷史記錄、引用日志、標簽等中的所有提交中消失
我不時遇到同樣的問題,每次我必須回到這篇文章和其他文章時,這就是我自動化流程的原因。
感謝 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
需要明確的是:接受的答案是正確的。 先試試。 但是,對於某些用例,它可能會不必要地復雜,特別是如果您遇到令人討厭的錯誤,例如“致命:錯誤修訂 --prune-empty”,或者真的不關心您的倉庫的歷史記錄。
另一種選擇是:
這當然會從你的 github 倉庫和你的本地 git 倉庫中刪除所有提交歷史分支和問題。 如果這是不可接受的,您將不得不使用替代方法。
稱之為核選項。
迄今為止,我不得不這樣做了幾次。 請注意,這一次僅適用於 1 個文件。
獲取修改文件的所有提交的列表。 底部的將是第一次提交:
git log --pretty=oneline --branches -- pathToFile
要從歷史記錄中刪除文件,請使用第一個提交 sha1 和上一個命令中的文件路徑,並將它們填充到此命令中:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..
在我的 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
所以,它看起來像這樣:
git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore
從 git 中刪除跟蹤文件的緩存並將該文件添加到
.gitignore
列表
filter-branch
命令例子:
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch *file_relative_path*' --prune-empty --tag-name-filter cat -- --all
上面使用的術語是:
--prune-empty
:如果您只想修剪變為空的提交,那么您甚至不需要指定此標志。 如果您想修剪在您的存儲庫中開始為空的提交,那么您需要始終指定 --prune-empty。--tag-name-filter <command>
:如果您只是指定--tag-name-filter cat
,那么正確的翻譯是不指定額外的標志。 filter-branch
要求的事實是它被延遲的證據; 它應該是自動處理的。 (如果你使用'cat'以外的東西,即你真的在重命名標簽,那么有一個--tag-rename
選項。)-- --all
:這是另一個證據,表明filter-branch
在讓用戶指定本來應該是默認值的東西方面受到了阻礙。 扔掉它。--index-filter <command>
:這是用於重寫索引的過濾器。 它類似於樹過濾器,但不檢查樹,這使得它更快。 大多數人幾乎總是使用它來根據文件名修剪或保留文件,在這種情況下,您想使用各種 --path* 選項,如果指定修剪路徑而不是保留路徑,請使用--invert-paths
。filter-repo
命令 git filter-repo
現在被 git 項目推薦,而不是 git filter-branch
因為filter-branch
非常慢(比它應該慢多個數量級)。
例子:
git filter-repo --path *file_relative_path* --invert-paths
這里的(唯一)術語是:
--invert-paths
:從指定的--path-{match,glob,regex}
選項反轉文件選擇,即僅 select 文件與這些選項都不匹配。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.