在GIT历史记录中重写文件?

时间:2018-06-26 17:11:04

标签: git

我一直在寻找一种在整个历史记录中更改文件中文本的快捷方式,可以说我有一个添加的贡献者,或者类似的东西

$ diff README.md README.md.new
28a29,32
> ## Contributors
>   - ehime     [Jd Daniel]
>   - thatgguy  [Someone Else]

所以通常我要做的是使用filter-branch从历史记录中完全删除文件:

git filter-branch --force --index-filter     \
'git rm --cached --ignore-unmatch README.md' \  
--prune-empty --tag-name-filter cat -- --all

这显然会破坏我文件的整个历史记录,包括标签等等,然后是mv README.md.new README.md和我的常规git push东西...好吧,不,我没有标签,并且提交的内容不再包含该文件...

那么,如何在文件的整个历史记录中重写此行?我认为我们也可以避免全局保护,但是我不确定。...

我不认为BFG可以做到这一点,因为它专门用于清理而不是重写?

例如,当我们拥有更改的API端点或需要在历史记录的所有点上反映出来以进行测试等的静态/弹性ip时,这非常方便。

1 个答案:

答案 0 :(得分:3)

您可以(也许)实现您想要的,但是让我们从这个开始:

  

如何在文件的整个历史记录中重写此行?

考虑一下这个哲学问题:假设我告诉你,不是1993-2000年=克林顿总统,2001-2008年= GWBush总统,实际上是1993-2001年=克林顿,2002-2008年=布什。进一步假设我以某种方式催眠了您相信。我实际上是否已经更改了历史记录,或者您只是使用了错误的历史记录?如果其他所有人也都相信它怎么办?

请牢记以下事实:任何Git提交都无法更改,因为提交的哈希ID是该提交内容的加密校验和,其中包括文件中的文件。提交永久保存的快照。但是,任何Git提交都可以复制到具有不同哈希ID的新提交中。

所以:如果我们将每个提交复制到一个新的,稍有不同的提交,从而导致一个新的,不同的存储库,然后以某种方式说服所有人,那么这个新的,不同的存储库就是怎么办?真正的存储库,他们以前都使用过伪造的历史记录?

这就是git filter-branch的作用:它将所有提交 1 复制到一个新的提交中,并应用您首先指定的任何过滤器。新提交具有新的,不同的哈希ID(如果有任何区别),即使它与原始哈希甚至有一点不同;当然,作为过滤器分支副本,如果对先前提交进行了更改,则要复制的下一个提交必须更改其父哈希ID,以使其使用 copied 提交而不是原始提交。因此,只要任何地方发生任何变化,这种变化就会在整个历史中逐渐消失。

结果是您的存储库现在包含两组提交(两个完整的历史记录),并且如果您更改了第一个提交的副本,那么这两组提交是不相交的。 (如果您不进行第一次提交,则这两个历史记录会在开始时汇合,然后在您进行第一次更改的地方分开。)Filter-branch重写分支名称,也许还会重写标签名称({{ 1}}),以便他们引用此新历史记录。您的存储库现在相信新的历史,而不是旧的历史。 (--tag-name-filter名称会记住原始历史记录;删除它们后,原始提交将很容易受到垃圾收集器的攻击。)

您现在必须说服所有人进行切换。这是个很大的flag day


1 好吧,更准确地说,每一个 reachable 提交都是基于您传递给refs/original/的{​​{1}}样式参数的。另外,某些过滤器会故意完全忽略某些提交,以使复制的历史记录的提交数少于原始记录。


过滤器机制

最简单的过滤器是git rev-list。该过滤器的工作原理是将每次提交逐字地提取到一个临时目录中,然后在该临时目录中运行您提供的任何命令。因此,您可以使用:

git filter-branch

其中/tmp/edit-readme.sh是您的(可执行)程序,用于在适当位置编辑--tree-filter。请注意,由于Git运行树过滤器时,它位于某个神秘的未知临时目录中,因此我们在此处使用绝对路径名(--tree-filter /tmp/edit-readme.sh 。 (即使您使用README.md,Git也会在您使用的任何参数内创建子目录。)

此过滤器的缺点是它非常慢。提取,修改和重建每次提交都需要很长时间。您可以使用filter-branch的/tmp/edit-readme.sh选项来使用内存中的文件系统,这样可以加快速度,但是您将需要足够的内存来解压缩和重建每个提交。

为此的快速过滤器是-d,但使用起来很棘手:filter-branch将每个提交仅复制 到索引中。 2 您的工作是然后修改索引。您可以检查索引中是否有-d文件,如果存在,则将其提取,修改,然后将新文件放回索引中。在索引过滤器之后,Git将从您保留在索引中的任何内容进行新的提交。

我留给您编写索引或基于树的编辑脚本。现在,您将拥有将存储库重写为新的不兼容存储库的工具。您是否可以强迫其他人放弃他们的现有存储库以支持这个新存储库,以便他们相信伪造的历史,完全是另一个问题。

相关问题