从git仓库中删除最早的提交记录

3

可能是重复问题:
如何完全从历史记录中删除(旧的)git提交?

Git非常有用,可以对客户网站进行每夜快照。知道所有内容(php + mysqldump +用户文件上传)都在Git存储库中,可以提供极大的安心感。

由于某些网站的大小很大,我想知道是否有人知道一种相当容易的方法可以删除(例如)30天前的所有提交?


5
我认为Git并不是真正用来作为备份解决方案的,因此这样做并不是既定目标。你可以使用rebase轻松删除它们,但我不确定如何以编程方式执行该操作。 - Andrew Marshall
关于“Git 不是备份解决方案”,我认为我们都同意这一点。(: 然而,从纯粹实用的角度来看,我自己(以及根据 Google)许多其他人发现它非常适合作为备份解决方案。 - Jay
@JohnDouthat 不错的发现!一开始并不明显它们是重复的,因为"问题"是不同的,但结果却是相同的。 - Jay
“压缩”方法确实可行。但它可能会非常慢。我还没有与filter-branch进行时间比较(filter-branch也可能非常慢...)。在这两种情况下,您仍将拥有原始提交记录,通过reflog等方式,因此,如果目的是恢复磁盘空间,则仍需要“清理垃圾”。 - torek
2个回答

4

实际上,您确实可以这样做。 这有点棘手。 以下是一个示例...

$ cd /tmp
$ mkdir rmcommits
$ cd rmcommits
$ git init
Initialized empty Git repository in /tmp/rmcommits/.git/
$ cp /tmp/example/xy.c .
$ git add xy.c
$ git commit -m 'initial commit'
[master (root-commit) 8d5b88c] initial commit
 1 files changed, 273 insertions(+), 0 deletions(-)
 create mode 100644 xy.c
$ echo 'more stuff' > morestuff.txt
$ git add morestuff.txt; git commit -m 'add some stuff'
[master f971ae5] add some stuff
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 morestuff.txt
$ echo 'and still more' >> morestuff.txt 
$ git add morestuff.txt; git commit -m 'add more stuff'
[master bea9192] add more stuff
 1 files changed, 1 insertions(+), 0 deletions(-)

现在我挑选了一个我希望“历史终结”的地方(对于分支主线,也就是HEAD):

$ git rev-parse HEAD^
f971ae5b4225aca364223a44be8be84268385ff3

这是我将保留的最后一次提交。

$ git filter-branch --parent-filter 'test $GIT_COMMIT == f971ae5b4225aca364223a44be8be84268385ff3 && echo "" || cat' HEAD
Rewrite bea9192a53a5aeb7532aa1e174f7f642363396de (3/3)
Ref 'refs/heads/master' was rewritten
$ git log --pretty=oneline
65a246b8320382a64550d2c4b650c942d7bfba70 add more stuff
7892ab45aa33cd5ebdc3090ce2622081059fdd79 add some stuff

(说明:git filter-branch 基本上会运行在分支的所有提交中,这里是master,因为HEAD当前是ref: refs/heads/master,而使用--parent-filter,您可以重写每个提交的父级。当我们找到目标提交时,我们想要历史记录停止,在此之前,我们不会输出任何内容——您不需要空白字符串,这是我的旧习惯,以前没有参数的echo什么都不做——否则我们将使用"cat"来复制现有的-p参数,正如filter-branch手册所述。这使得基于我们测试过的那个新提交没有父级,即现在是一个初始提交——分支的根。这在git存储库中很不寻常,因为现在我们有两个根提交,一个在新的master上,一个在旧的保存的主分支上,如下所述。)
请注意,旧的提交树仍然完整地保存在存储库中,使用了git filter-branch使用的保存名称:
$ git log original/refs/heads/master --pretty=oneline
bea9192a53a5aeb7532aa1e174f7f642363396de add more stuff
f971ae5b4225aca364223a44be8be84268385ff3 add some stuff
8d5b88c468f75750d5a01ab40bfae160c654ac66 initial commit

您需要在删除引用(并清除 reflog)之前执行“git gc”,以便重写的提交(和任何未引用的树、blob 等)真正消失:
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git fsck --unreachable
$

那最后一行显示他们确实离开了。

0
  1. 当前存储库状态中存在的文件被保存为原始添加文件加上一系列更改,因此您无法删除添加文件的提交。

  2. TortoiseGit 有一个操作,您可以从日志中选择多个连续的提交并“合并为一个提交”,但它不是 Git 的本地提供的功能,从我从出现的窗口中推断出来,它是通过创建一个新分支,应用原始分支的更改,仅提交一次并在结果上进行变基实现的。当选择许多提交时,这肯定不是一个快速的操作,我想在大型存储库中它会更慢,并且在使用它之前我总是备份。

总的来说,我怀疑是否存在一种简单的方法来做到这一点。


1
1: 实际上不是这样的。packs 被压缩以便您获得与增量相同的节省空间,但每个文件都完整存储。Git 的“提交”对象指向 Git 的“树”对象,“树”对象列出了“blob”(文件)和更多的“tree”,所有这些都由 SHA1 ID 列出;通过 SHA1 ID,您可以通过那些包的魔力提取整个文件。
  1. 在本地 Git 中,这是在 git rebase --interactive 中的“squash”。是的,在底层它是通过构建一个新分支来完成的。
- torek
我改正了。我需要深入阅读有关Git内部结构的章节,因为之前只是浏览了一遍。 - madth3
Git的压缩技术可以说是非常不寻常的,:-) 但非常有效。它对数据包进行增量压缩,但对象本身仅使用zlib压缩,并且在整个过程中都有校验和(v2包比v1更好)。压缩算法以不同的方式重复使用,以获得拉取和推送操作的增量压缩,但这些操作在接收端被撤消。 - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接