22

由于历史上包含二进制测试文件和 java 文件,我们有许多git存储库已经增长到无法管理的大小.jar

我们即将完成git filter-branch这些存储库的练习,在使用它们的任何地方重新克隆它们(每个部署从几十个到数百个,取决于存储库)并且考虑到重写历史的问题,我想知道是否可能任何其他解决方案。

理想情况下,我想在不重写每个存储库的历史记录的情况下将问题文件外部化。从理论上讲,这应该是可能的,因为您正在检查相同的文件,具有相同的大小和相同的哈希值,只是从不同的地方(远程而不是本地对象存储)采购它们。唉,到目前为止,我发现的任何潜在解决方案似乎都不允许我这样做。

git-annex开始,我能找到的最接近问题的解决方案是如何追溯附加 git repo 中已经存在的文件,但与删除大文件一样,这需要重写历史记录以进行转换原来git add变成了一个git annex add

从那里继续,我开始查看列出的其他项目git-annex is not,所以我检查了git-bigfilesgit-mediagit-fat。不幸的是,我们不能使用git-bigfiles fork,git因为我们是一家 Eclipse商店并使用EGitgitEGit的混合物。它看起来不像git-mediagit-fat可以做我想做的事,因为虽然你可以用外部等价物替换现有的大文件,但你仍然需要重写历史以删除已经存在的大文件被承诺。

那么,是否可以在不重写历史记录的情况下精简 .git 存储库,或者我们应该回到使用计划git filter-branch和重新部署的整个负载?


顺便说一句,相信这应该git是可能的,但可能与当前浅克隆实现的限制相同。

Git 已经支持同一个 blob 的多个可能位置,因为任何给定的 blob 都可能位于松散对象存储(而不是更高层(即,如果您愿意,可以使用按需下载远程 blob的概念)。不幸的是,我找不到任何人已经实施甚至建议过这样的事情。.git/objectsgit-annex

4

4 回答 4

11

有点。您可以使用Git 的替换功能来搁置臃肿的历史记录,以便仅在需要时才下载它。它就像一个浅克隆,但没有浅克隆的限制。

这个想法是您通过创建一个新的根提交来重新启动一个分支,然后挑选旧分支的提示提交。通常,您会以这种方式丢失所有历史记录(这也意味着您不必克隆那些大.jar文件),但如果需要历史记录,您可以获取历史提交并用于git replace无缝地将它们缝合回去。

有关详细说明和演练,请参阅Scott Chacon 的优秀博客文章

这种方法的优点:

  • 历史没有被修改。如果你需要回到一个旧的提交完成它的大.jars和一切,你仍然可以。
  • 如果您不需要查看旧历史记录,则本地克隆的大小又小又好,并且您制作的任何新克隆都不需要下载大量无用的数据。

这种方法的缺点:

  • 默认情况下不提供完整的历史记录——用户需要跳过一些障碍才能获得历史记录。
  • 如果您确实需要频繁访问历史记录,那么无论如何您最终都会下载臃肿的提交。
  • 这种方法仍然存在一些与重写历史相同的问题。例如,如果您的新存储库如下所示:

    * modify bar (master)
    |
    * modify foo  <--replace-->  * modify foo (historical/master)
    |                            |
    * instructions               * remove all of the big .jar files
                                 |
                                 * add another jar
                                 |
                                 * modify a jar
                                 |
    

    并且有人从他们合并的历史分支中有一个旧分支:

    * merge feature xyz into master (master)
    |\__________________________
    |                           \
    * modify bar                 * add feature xyz
    |                            |
    * modify foo  <--replace-->  * modify foo (historical/master)
    |                            |
    * instructions               * remove all of the big .jar files
                                 |
                                 * add another jar
                                 |
                                 * modify a jar
                                 |
    

    然后大的历史提交将重新出现在您的主存储库中,您又回到了开始的地方。请注意,这并不比重写历史更糟糕——有人可能会不小心合并到重写前的提交中。

    这可以通过update在共享存储库中添加一个挂钩来拒绝任何会重新引入历史根提交的推送来缓解。

于 2013-07-12T19:53:45.543 回答
8

不,这是不可能的——你将不得不重写历史。但这里有一些提示:

  • 正如 VonC 所说:如果它适合您的场景,请使用BFG-repo 清洁器——它​​比git filter-branch.
  • 您无需再次克隆!只需运行这些命令而不是,git pull你会没事的(用你的远程和分支替换origin和):master

    git fetch origin
    git reset --hard origin/master
    

    但请注意,与 不同的是git pull,您将丢失所有尚未推送到服务器的本地更改。

  • 如果您(或您团队中的其他人)完全理解 git 如何看待历史,以及git pull,git mergegit rebase(也 as git rebase --onto) 做了什么,这将有很大帮助。然后让每个参与的人快速培训如何处理这种重写情况(5-10 分钟就足够了,基本的注意事项)。
  • 请注意,git filter-branch它本身不会造成任何伤害,但会导致许多标准工作流程造成伤害。如果人们不采取相应的行动并合并旧历史,如果您没有及时注意到,您可能只需要再次重写历史。
  • 您可以通过在服务器上编写(5 行)适当的更新挂钩来防止人们合并(更准确地说是推送)旧历史记录。只需检查推送头的历史记录是否包含特定的旧提交。
于 2013-07-11T11:31:13.747 回答
4

我不知道可以避免重写历史的解决方案。

在这种情况下,使用BFG-repo 清洁器之类的工具清洁 rpeo是最简单的解决方案(更容易git filter-branch)。

于 2013-07-04T13:40:21.703 回答
2

老实说,我想不出办法做到这一点。如果您考虑一下 Git 对您作为用户的“承诺”,关于数据完整性,我想不出一种方法可以从存储库中删除文件并保持相同的哈希值。换句话说,如果你的要求是可能的,那么 Git 的可靠性就会低很多......

于 2013-07-04T14:27:19.663 回答