折叠git仓库的历史


85

我们有一个历史悠久的git项目。

具体来说,在项目初期,项目中有很多二进制资源文件,由于它们实际上是外部资源,因此已被删除。

但是,由于这些文件先前已提交,因此我们的存储库大小大于200MB(当前总签出约为20MB)。

我们想要做的是“折叠”历史记录,以使该存储库似乎是从比以前更高的修订版创建的。例如

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. 储存库已建立
  2. 添加了大量二进制文件
  3. 删除了大量二进制文件
  4. 储存库的新预期“开始”

因此,有效的是我们希望在某一点之前丢失项目历史记录。此时,只有一个分支,因此尝试处理多个起点等并不复杂。但是,我们不想丢失所有历史记录并使用当前版本启动新的存储库。

这是可能的,还是我们注定要永远拥有一个ated肿的存储库?

Answers:


89

您可以删除二进制膨胀,并保留其余的历史记录。Git允许您重新排序和“压缩”先前的提交,因此您可以仅合并添加和删除大二进制文件的提交。如果所有添加都在一个提交中完成,而删除都在另一个提交中完成,则比处理每个文件要容易得多。

$ git log --stat       # list all commits and commit messages 

在此搜索以查找添加和删除二进制文件的提交,并记下它们的SHA1,例如2bcdef3cdef3

然后,要编辑存储库的历史记录,请使用rebase -i命令及其交互选项,从添加二进制文件的提交的父项开始。它将启动您的$ EDITOR,您将看到以开头的提交列表2bcdef

$ git rebase -i 2bcdef^    # generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
#
pick 2bcdef   Add binary files and other edits
pick xxxxxx   Another change
  .
  .
pick 3cdef3   Remove binary files; link to them as external resources
  .
  .

插入 squash 3cdef3作为第二行,然后pick 3cdef3从列表中删除该行。现在,您有一个用于交互式的动作列表rebase,它将将添加和删除二进制文件的提交合并为一个提交,其区别只是这些提交中的任何其他更改。然后,当您告诉它完成时,它将按顺序重新应用所有后续提交:

$ git rebase --continue

这将需要一两分钟。
现在,您有了一个不再有二进制文件进出的仓库。但是它们仍然会占用空间,因为默认情况下,Git会将更改保留30天左右,然后再进行垃圾收集,以便您可以改变主意。如果要立即删除它们:

$ git reflog expire --expire=1.minute refs/heads/master
      #all deletions up to 1 minute  ago available to be garbage-collected
$ git fsck --unreachable      # lists all the blobs(files) that will be garbage-collected
$ git prune
$ git gc                      

现在,您已消除了膨胀,但保留了其余的历史记录。


7
您只需要记住是否其他人已经从该存储库中提取数据,重写历史记录会混淆他们的提取。git-rebase手册介绍了如何恢复其他仓库。 kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto

对于用户的特定问题,这是一个很好的答案,但对于实际问题却不是!davitenio的答案对于实际问题是一个很好的答案。
山姆·沃特金斯

27

您可以使用git filter-branch嫁接使提交编号4成为分支的新根提交。只需创建一个文件,.git/info/grafts其中只有一行包含提交编号4的SHA1。

如果现在执行agit loggitk,则将看到这些命令将显示提交编号4作为分支的根。但是您的存储库中实际上没有任何更改。您可以删除.git/info/graftsgit log或的输出gitk将与以前一样。要使提交号4真正成为新的根目录,您将必须运行git filter-branch,且不带参数。


这比重新设置要好得多,因为它没有保存合并提交的问题,并且不会导致时间戳更改。比所有的变基方法更容易和更快。
mmrobins 2011年

实际上,是否有一种方法可以物理删除不再属于该分支的所有提交?git gc --prune=0似乎没有清理它们。
Verhogen

1
@verhogen会git gc --prune=now物理清除所有不再引用的提交。如果这对您不起作用,则您可能有一些远程跟踪分支,该分支仍引用旧根目录。用列出git branch -r,然后用除去远程分支git branch -rd origin/master,然后git gc --prune=now再次运行。
kayahr 2012年

20

多亏了JesperE的帖子,我研究了git-filter-branch—实际上可能正是您想要的。看起来您也可以保留以前的提交,但由于大文件已删除,因此它们将被修改。从git-filter-branch手册页

假设您要从所有提交中删除文件(包含机密信息或侵犯版权的文件):

git filter-branch --tree-filter'rm文件名'HEAD

一定要阅读该手册页...显然,您希望在存储库的备用克隆上执行此操作,以确保其按预期运行。


2
查看github的链接... git-filter-branch命令提供了一些强大的选项:help.github.com/articles/remove-sensitive-data
ricosrealm

5

git-fast-export你在找什么?

NAME
   git-fast-export - Git data exporter

SYNOPSIS
   git-fast-export [options] | git-fast-import

DESCRIPTION
   This program dumps the given revisions in a form suitable to be piped into git-fast-
   import(1).

   You can use it as a human readable bundle replacement (see git-bundle(1)), or as a kind
   of an interactive git-filter-branch(1).
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.