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】