如何追溯和完全删除添加到.gitignore的文件和文件夹的痕迹

时间:2019-08-06 19:46:51

标签: git

请注意:我已阅读thisthisthis等。 他们要么不能完全回答我的问题,要么我经验不足,无法从他们那里提取解决方案。

我错误地将敏感信息提交给了本地git仓库。现在,我已将有关文件和文件夹添加到.gitignore。如何从仓库中删除这些文件的所有痕迹?

我有一个很大的项目,其中一些敏感信息保存在整个项目的不同文件夹中。出于无知,我没有将这些文件夹添加到.gitignore中。现在,我如何确保所有这些文件都已从git历史记录中完全删除?

如果有帮助,相关文件和文件夹将遵循类似的模式。

自从我开始这个项目以来,我也做了很多提交工作。

有关的文件夹在我的.gitignore中如下所示:

js/*/sensitiveData
python/*/sensitiveData

是否有办法在保留其余git历史记录的同时将其删除?

理想情况下,我会从git历史记录中删除所有添加到.gitignore的文件夹/文件,同时将它们保存在本地磁盘上并保留git提交。

如果有什么帮助,我还没有任何遥控器。一切都保存在我的本地磁盘上。

1 个答案:

答案 0 :(得分:2)

请参见Remove sensitive files and their commits from Git history,但是-这非常重要-您的问题更简单,因为:

  

如果有什么帮助,我还没有任何遥控器。一切都保存在我的本地磁盘上。

这确实非常有帮助。无论您选择哪种方式,要做的事情-必须 要做的事情都是“重写历史记录”。在Git中,历史仅是Git存储库中的一组提交。每次提交保存每个文件的完整快照, 1 以及一些元数据,例如谁进行提交(名称和电子邮件),何时(日期和时间戳)以及为什么(日志消息) )。元数据的一部分指定哪个提交是上一个提交:此一次提交的即时历史记录。

历史记录仅表示:从(所有)最后一个提交开始,然后从每个点向后追溯到其先前的(父)提交。就是这样,实际上就是全部。但是,每个提交都被永久冻结:您无法更改其拥有的文件,也不能更改其标识的父提交。因此,要“更改历史记录”,您必须构造一个完整的 new 历史记录,从具有您不希望它们拥有的文件的任何提交开始。从那时起,每个后代也必须进行更改:没有文件,和/或将没有文件的提交列为的即时历史。

在一个拥有大量提交的大型存储库中,这往往等于:将每个提交复制到一个新的和改进的提交中。然后,您只需从使用旧提交切换到使用新提交即可。 。无法找到的旧版本最终被清理 2 并确实消失了。同时,您只需携带所有内容的双份副本-由于Git存储文件的方式,实际上并不会占用太多空间。

接下来,尽管我从未真正使用过BFG,但我建议对链接的问题考虑this answer

最后,无论您使用Remove sensitive files and their commits from Git history中的哪种方法,我都建议您采用这种方式:

  1. 复制存储库(有关复制方法,请参见下文)。
  2. 将您选择的“重写历史记录”方法应用于副本。
  3. 检查结果。好吗?如果是这样,请切换为使用副本。如果不是,请删除副本,然后从步骤1重新开始。

如果您选择的方法是git filter-branch,则实际上第1步中的副本不是不必要。对于不熟悉Git的人来说,这变得容易得多,因为如果您不修改原始内容,只需尝试删除 ,您就会感到非常安全。原件仍在原处。


1 很明显,每次提交实际上仅保存使用该提交保存的每个文件的完整副本。但这就是您上次提交的所有文件,加上您添加的所有文件,再减去您明确删除的所有文件。

这不会使您的存储库几乎立即大量增长的原因是,某些先前提交中文件的冻结压缩副本可以并且已经在任何中重复使用稍后使用相同数据的提交。这是完全安全的,因为所有时间都冻结了所有提交。最多,提交本身可以被忘记,然后最终被删除:如果其他提交仍在使用其文件中的某些文件,则文件数据仍然存在。仅当 no 提交正在使用文件数据时,文件数据才会消失。

2 “最终”基于两个对提交的隐藏引用(保留在每个存储库的 reflogs 中)和后台清理处理。仅当背景清洁剂一眼看上去有利可图时,它就会启动。您可以自己运行git gc来强制进行清洁。清理程序将找到所有引用(包括所有隐藏的引用),以查看需要保留哪些提交,以及那些保留提交的 使用了哪些文件。提交和文件以及不再不再需要的其他内部对象,至少有特定的使用期限(默认为14天),然后可以删除。


复制存储库

最简单的方法是使用系统拥有的任何文件树复制器,以复制整个工作树,包括.git目录/文件夹:

cd $HOME/src
cp -r original copy

例如。使用Git可以很好地工作,尽管它还会复制技术上不属于存储库的任何随机内容。 注意::如果您使用过git worktree add,则它不会复制位于original/区域之外的已添加工作树,但两者都不会我将要展示的另一种技术。

另一种方法是利用存储库的每个克隆都是一个存储库这一事实。这里最棘手的部分是克隆不要复制一些内容:

  • 默认情况下,克隆中没有原始存储库的远程跟踪名称。遥控器均不执行任何操作,因此复制此类名称没有任何意义。您没有遥控器,因此无关紧要。

  • 默认情况下,新克隆将原始存储库作为其唯一的远程存储库。该遥控器名为origin。没关系,您以后可以根据需要删除此origin

  • 默认情况下,新克隆重命名来自原始存储库的所有分支。如果原始存储库具有分支B1B2B3master,则新克隆具有origin/B1origin/B2,{{1 }}和origin/B3作为其远程跟踪名称

一个远程跟踪名称只是Git记住的方式:我在其他Git上看到了这个分支!我上次看到它时,它说使用commit _____ (根据此Git从origin/master Git中看到的内容来填写空白。)

因此,如果您这样做:

origin

然后,git clone file://$HOME/src/original copy 中的新副本将./copy作为存储在其file://$HOME/src/original中的URL,并将您的分支从origin重命名为{ {1}}。

克隆的最后一步是original,因此副本现在具有自己的 origin/*,但没有自己的copygit checkout mastermaster。因此,在重写副本中的历史记录之前,您需要创建分支。

您可以通过运行以下命令简单地手动完成此操作:

B1

这些命令使用的机制与B2基于B3得到的git checkout B1 git checkout B2 git checkout B3 的{​​{1}}在git clone中制作master中的copy来自copy(即原始存储库)。因此,现在,您的副本有五个分支,就像原始副本一样。

(如果您有很多分支,并且需要经常执行此操作,则需要编写脚本。但是,如果您需要经常执行此操作,则首先是在做错事。:- )