Git永久刪除文件和歷史記錄
造成你想從git存儲庫中永久刪除文件和歷史記錄的可能有:
- 你不小心將一個不該加入版本管理的文件加了進去,敏感數據或大文件或別的沒用的文件;
- 你不小心將一個涉及到破解某著名軟件的文章加了進Github倉庫,這時你就會收到github官方的郵件來提醒你需要完全刪除該文件,不然就會遭到git倉庫被封禁。
- 你希望將敏感數據或無用文件從版本庫中永久刪除不留痕跡,不僅僅在版本歷史里看不出來,還要把它占用的空間也釋放出來。
參考官方鏈接,github 的幫助文檔:
https://help.github.com/articles/remove-sensitive-data
很詳細的說明了步驟和提供了一種使用BFG工具的思路(更便利)
這里我只說使用git命令的方式,以windows平台下為例,linux類似做法:
使用filter-branch
注意: 如果在存儲更改后運行git filter-branch
,則無法使用其他存儲命令檢索更改。
在運行git filter-branch
之前,建議取消所做的任何更改。要unstash
最后一組隱藏的更改,運行git stash show -p | git apply -R
。有關更多信息,請參閱https://git-scm.com/book/en/v1/Git-Tools-Stashing。
演示如下:
進入git存儲倉庫,運行以下命令,用要刪除文件的相對路徑(而不僅僅是文件名)替換PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
。
這行長命令的參數做到的事是:
-
強制Git處理(但不檢出)每個分支和標記的全部歷史;
-
刪除指定的文件,以及由此生成的任何空提交;
-
覆蓋你現有的tags
$ 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
其中,
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
就是你要刪除的文件的相對路徑(相對於git倉庫的根目錄), 替換成你要刪除的文件路徑即可. 注意一點,這里的文件或文件夾,如果以 '/' 開頭,則文件或文件夾會被認為是從 git 的安裝目錄開始。如果你要刪除的目標不是文件,而是文件夾,那么請在
git rm --cached
命令后面添加-r
命令,表示遞歸的刪除(子)文件夾和文件夾下的文件,類似於rm -rf
命令。如果你要刪除的文件很多, 可以寫進一個
.sh
文件批量執行, 如果文件或路徑里有中文, 由於MinGW或CygWin對中文路徑設置比較麻煩, 你可以使用通配符*
號, 例如:sound/music_*.mp3
, 這樣就把sound目錄下以music_
開頭的mp3文件都刪除了.例如:新建一個 bash 腳本文件,
del-music-mp3.sh
:#!/bin/bash git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch projects/Moon.mp3' --prune-empty --tag-name-filter cat -- --all git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch sound/Music_*.mp3' --prune-empty --tag-name-filter cat -- --all
如果你看到類似下面這樣的, 就說明刪除成功了:
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (266/266) # Ref 'refs/heads/master' was rewritten
如果顯示
xxxxx unchanged
, 說明repo里沒有找到該文件, 請檢查路徑和文件名是否正確.
實際例子操作如圖:
添加到.gitignore文件里並push修改后的repo
如果想以后也不會再上傳這個文件或文件夾, 請把這個文件或文件夾添加到.gitignore
文件里, 然后再push你的repo.
添加到.gitignore
文件:
$ echo "YOUR-FILE-WITH-SENSITIVE-DATA" >> .gitignore
$ git add .gitignore
$ git commit -m "Add YOUR-FILE-WITH-SENSITIVE-DATA to .gitignore"
[master 051452f] Add YOUR-FILE-WITH-SENSITIVE-DATA to .gitignore
1 files changed, 1 insertions(+), 0 deletions(-)
再次檢查是否已經從存儲庫的歷史記錄中刪除了所有想要刪除的內容,以及是否檢出了所有分支。
以強制覆蓋的方式推送你的repo, 命令如下:
git push origin --force --all
Counting objects: 1074, done.
Delta compression using 2 threads.
Compressing objects: 100% (677/677), done.
Writing objects: 100% (1058/1058), 148.85 KiB, done.
Total 1058 (delta 590), reused 602 (delta 378)
To https://github.com/YOUR-USERNAME/YOUR-REPOSITORY.git
+ 48dc599...051452f master -> master (forced update)
這個過程其實是重新上傳我們的repo, 比較耗時, 雖然跟刪掉重新建一個repo有些類似, 但是好處是保留了原有的更新記錄, 所以還是有些不同的. 如果你實在不在意這些更新記錄, 也可以刪掉重建, 兩者也差不太多, 也許后者還更直觀些。
為了能從打了 tag 的版本中也刪除你所指定的文件或文件夾,您可以使用這樣的命令來強制推送您的 Git tags:
$ git push origin master --force --tags
告訴您的協作者,從您的舊(受污染的)存儲庫歷史中重新創建分支,而不是合並它們。一個合並提交可能會重新引入一些或所有您剛剛陷入清除麻煩的受污染的歷史。
清理和回收空間
經過一段時間之后,您確信git filter-branch
沒有意外的副作用,您可以使用以下命令強制解除對本地存儲庫中的所有對象的引用和垃圾回收(GC)。
$ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
$ git reflog expire --expire=now --all
$ git gc --prune=now
Counting objects: 2437, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (1378/1378), done.
Writing objects: 100% (2437/2437), done.
Total 2437 (delta 1461), reused 1802 (delta 1048)
您還可以通過將過濾后的歷史推入一個新的或空的存儲庫,然后從GitHub創建一個新的克隆來實現這一點。
上面命令的第一句也可以換成:
$ rm -rf .git/refs/original/
參考自:
https://help.github.com/articles/removing-sensitive-data-from-a-repository/
https://www.cnblogs.com/shines77/p/3460274.html
文:鐵樂與貓
2018-11-17
【end】