是否可以在不重写历史记录的情况下缩小.git存储库?

时间:2013-07-04 12:49:24

标签: git egit large-files jgit git-filter-branch

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

我们正要进行git filter-branch这些存储库的练习,在它们被使用的任何地方重新克隆它们(从每个部署数十次到数百次,取决于回购)并给予{{3我想知道是否还有其他解决方案。

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

problems with rewriting history开始,我能找到最接近问题解决方案的是git-annex,但就像删除大文件一样,这需要重新编写历史记录来转换原始git addgit annex add

从那里开始,我开始查看How to retroactively annex a file already in a git repo上列出的其他项目,因此我检查了what git-annex is notgit-bigfilesgit-media。不幸的是,我们不能使用git git-bigfiles 分支,因为我们是Eclipse 商店,并使用git和{{的混合3}}。它看起来不像 git-media git-fat <​​/ em>也可以做我想要的,因为虽然你可以用外部等价物替换现有的大文件,但你仍然会需要重写历史记录才能删除已经提交的大文件。

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


顺便说一下,相信这个应该可能,但可能与git当前git-fat实施的限制相同。

Git已经支持同一个blob的多个可能位置,因为任何给定的blob都可以在EGit.git/objects)或shallow clone(.git / objects)中,所以理论上您只需要将git-annex这样的内容挂钩到该级别而不是更高级别(例如,如果您愿意,可以按需下载概念远程blob )。不幸的是,我找不到任何人已经实施或甚至建议这样的事情。

4 个答案:

答案 0 :(得分:9)

排序。您可以使用Git's replace feature来保留大膨胀历史记录,以便仅在需要时下载。它就像一个浅层克隆,但没有浅层克隆的限制。

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

有关详细说明和演练,请参阅Scott Chacon's excellent blog post

这种方法的优点:

  • 不修改历史记录。如果你需要回到一个较旧的提交,包括它的大.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挂钩来拒绝任何会重新引入历史根提交的推送。

答案 1 :(得分:8)

不,这是不可能的 - 您将不得不重写历史记录。但是这里有一些指示:

  • As VonC mentioned:如果它适合您的情况,请使用BFG- repo cleaner - 它比git filter-branch更容易使用。
  • 您无需再次克隆!只需运行这些命令而不是git pull,您就可以了(用您的远程和分支替换originmaster):

    git fetch origin
    git reset --hard origin/master
    

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

  • 如果您(或您团队中的其他人)完全理解git如何看待历史记录,以及git pullgit mergegit rebase(以及{{1} })。然后让每个人都参与一个关于如何处理这种重写情况的快速培训(5-10分钟应该足够了,基本的注意事项和不应该)。
  • 请注意git rebase --onto本身不会造成任何伤害,但会导致许多标准工作流程造成伤害。如果人们没有采取相应行动并合并旧历史,如果你不及时注意到,你可能只需要重新编写历史记录。
  • 您可以通过在服务器上写入(5行)适当的update hook来阻止人们合并(更准确地推送)旧历史记录。只需检查推头的历史记录是否包含特定的旧提交。

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

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