10

我打算在存储库上问这个,但 SO 似乎是一个更合适的地方问这个。

我能够使用 BFG Repo Cleaner(很棒的工具,谢谢!)将我们的.git文件夹大小减少了 1GB 以上,就我们的存储库而言,这是一个巨大的成功。我还没有将我的裸克隆推送到远程,因为我担心在了解推送而不重新克隆的后果之前提出这些更改。

我知道最佳实践表明,当历史以这种方式发生变化时,最好的解决方案是执行新的克隆。然而,我与一个超过 50 人的团队一起工作,他们拥有超过 2GB 和 23k 次提交的存储库,在我们的结构下,跨团队协调可能非常困难。结果,我有一些问题:

  1. 如果我推送这些更改的 refs,而人们要拉到他们现有的副本而不是创建一个新的克隆,后果会是什么?
  2. 如果可行的话,他们是否需要做任何其他事情来减轻这些后果,作为拉动的一部分,或者除了拉动之外?
  3. 如果您认为已删除的 blob 来自至少一年且最多三年的历史,那么此建议是否会发生变化?
  4. 最后,鉴于新的克隆不会包含任何未在上游同步的工作,您是否有关于将未跟踪的分支从一个克隆转移到另一个克隆的最佳方法的建议?如果已经存在执行此操作的 Git 命令,我很想听听您的见解。

再次感谢您创建了如此方便的工具,希望我能完成使其对我团队的项目有用。与此同时,我将继续在我的叉子上进行实验。

4

1 回答 1

21

前言

在我们开始讨论之前,让我澄清一下在活跃的开发人员团队中清理 Git 历史的推荐过程(无论使用什么技术进行清理——无论是BFG Repo-Cleaner还是git filter-branch):

  1. 在存储库的本地一次性副本上练习几次清理,这样您就有信心可以做到并获得所需的结果,并且您知道需要多长时间。
  2. 与您的团队沟通。这是必不可少的,不可避免的(因为如果历史被改写,Git 是专门为抱怨和妨碍而设计的),这对任何团队来说都是一种很好的做法:-) 你需要告诉他们:
    • 为什么要进行清理(例如较小的回购!)
    • 计划清洁时- 给他们适当的提前警告。
    • 在清理开始之前将他们的所有工作推送到主仓库- 它不需要合并到主分支,但所有工作都需要在一个或另一个分支上推送。
    • 建议他们在清理完成后需要删除旧的存储库副本,并重新克隆新清理的存储库
  3. 当所有工作都推送到主存储库时,对主存储库进行镜像克隆。对此克隆进行备份,以便在出现问题时可以随时返回。
  4. 运行清理(使用BFG Repo-Cleaner或较慢的工具,如git filter-branch),并使用git gc修剪死对象。
  5. 一旦你对清理工作感到满意,将清理过的历史推送回主仓库(因为它是一个mirror克隆,所有旧的分支/标签都将被覆盖到新的清理过的历史记录)
  6. 告诉你的团队是时候删除他们旧的 repo 副本,并重新克隆清理过的存储库。

所以,对于你的问题:

如果:拥有旧存储库的用户从清理后的存储库中提取,该怎么办?

如果我推送这些更改的 refs,而人们要拉到他们现有的副本而不是创建一个新的克隆,后果会是什么?

坏的。根据经验,我可以说会有一团糟,人们会感到困惑不安

具体来说,在那个人的机器上发生的事情是该git pull命令会将旧的脏历史和新的清理历史合并在一起,有两个不同的历史(最初与您历史中的第一个“脏”提交不同,在您的情况下是 3几年前)与一个全新且非常混乱的合并提交一起加入。用户很少清楚这已经发生了——大多数 Git 日志可视化器不会以一种可能使其明显的方式呈现它——如果你幸运的话,用户可能会说“我现在每个提交都有两个副本,怎么回事?!” - 但前提是他们真的很细心。

如果该用户稍后进行了一些新的提交,并将其推送回主存储库,他们会将脏历史推送回已清理的主存储库,否定您的工作,使您的历史再次变脏,并创建一个非常混乱的 Git 历史您的所有其他用户下次从主 Git 存储库中提取时将接触到这些内容。

通过规划,有没有办法让用户保留他们的旧回购但更新它以拥有清理的历史记录?

如果可行的话,他们是否需要做任何其他事情来减轻这些后果,作为拉动的一部分,或者除了拉动之外?

从技术上讲,是的。在实践中,这个过程很复杂,容易出错,如果只有一个用户弄错了,你就像以前一样被搞砸了。

在这一点上,我们必须弄清楚你为什么要试图躲避这个程序。是不是因为:

  • 您是否试图让用户不必了解和处理 Git 历史的更改?听起来这可能是您的目标,基于您所说的“跨团队协调在我们的结构下可能非常困难” - 但不幸的是,这不是一个可以实现的目标,因为 Git 不会让您在没有用户注意的情况下更改历史。用户必须做一些事情,他们需要与你协调。
  • 您想减少对真正庞大的存储库进行全新克隆的下载时间,希望 Git 只下载已更改的 blob,而不是所有更改的内容?对于需要数小时才能下载的巨大的数 GB 存储库来说,这是一个稍微合理的目标(如果您使用 BFG 使存储库变得更小,那么动力就会减少)-不幸的是,由于 Git 协议的细节,您将不会无法实现这些好处。Git 协议旨在确定远程服务器上哪些提交不在本地存储库中,并发送定制的包文件,其中仅包含更新本地存储库所需的内容。这很好,但请注意比较单位是提交. 当你重写历史时,提交的文件树几乎没有改变——但提交 id全部改变,因为提交 id 是它的parental history的哈希,以及它的文件树内容。Git 协议只是比较提交 id,它们都是不同的——所以所有的提交都会连同它们的文件树对象一起被发送。该协议没有深入挖掘以意识到它不需要发送大部分文件树对象 - 因此您不会获得在本地存储库中已经拥有它们的副本的好处。

坏东西在历史上多久以前有关系吗?

如果您认为已删除的 blob 来自至少一年且最多三年的历史,那么此建议是否会发生变化?

如果最近才提交了坏东西,并且还没有其他用户将其拉出(因此,在过去的几个小时或几分钟内),您可能会在其他人拉出之前快速清理主仓库上的历史记录。一旦其他人提取脏数据,就需要对其进行净化,最简单的方法是删除并重新克隆。

如果坏事是几年前犯的,那么每个人都有,需要净化。

清理时没有推送到主存储库的杂散提交/分支呢?

最后,鉴于新的克隆不会包含任何未在上游同步的工作,您是否有关于将未跟踪的分支从一个克隆转移到另一个克隆的最佳方法的建议?

处理此问题的推荐方法是确保它不会发生。与您的团队沟通,告诉他们将要进行存储库清理,并且他们所要做的就是确保他们在开始清理之前已将任何分支上的所有工作推送到主存储库.

如果有人不这样做,他们可以尝试将他们关心的分支重新定位到已清理的历史记录上。对于每个feature分支,例如:

$ git rebase --onto clean-origin/feature unclean-origin/feature feature

...(大致翻译为“获取我的功能分支上的所有提交,在它被清理之前我没有推送到主仓库,并在主仓库的该分支的清理版本之上重播它们) .

如果用户弄错了,或者忘记了只为一个分支执行此操作,您将回到糟糕的混合脏/干净历史场景。

结论

你了解你的团队,你确定他们都能完美地执行深奥的 Git 变基操作吗?如果他们这样做有什么好处?毕竟,告诉他们删除旧的回购并重新克隆不是更容易吗?

于 2018-03-24T23:32:46.527 回答