1

例子

所以假设我有一个本地 git repo,有 10 个提交,SHA 摘要为 0-9,所以我的 git 日志看起来像这样

9 (HEAD -> master)
8
7
6
5
4
3
2
1
0 <- initial commit

我决定提交 5-9 是垃圾,我想从存储库和他们引入的磁盘空间中永久删除它们的所有记录。Baiscally,我希望我的 repo 的状态与提交 4 时的状态相同,并且就像 5-9 从未发生过一样。

我知道这git reset --hard 4会使我的 repo似乎已经倒退到提交 4,但据我了解,这只是将提交master点从 9 更改为 4,但实际上并没有删除任何内容。所有数据仍然存在,如果您知道提交 9 的 SHA,则可以恢复。

我也知道,git filter-branch但这只会从历史记录中删除文件,而不是提交。

我试过做:

git reset --hard 4
git gc --prune=now

但是这样做之后,我的.git目录的磁盘空间使用量相同或更大,我仍然可以恢复历史记录git checkout 9。为什么不git gc --prune=now修剪提交 5-9?我需要过期reflog吗?

更普遍:

如果我有一个包含许多分支、标签、提交、合并和不同历史记录的复杂存储库,我如何才能永久自动删除所有提交,以及它们引入的更改以及它们消耗的磁盘空间,这些都是在一段时间后发生的。有效地将整个 repo 倒回到那个时间,并永久销毁该日期之后发生的所有活动。

4

2 回答 2

0

git reset不删除内容。它只需更改您的HEAD以指向您要求的新 SHA-1。

如何删除内容?

我也知道 git filter-branch 但这只会从历史记录中删除文件,而不是提交。

让我纠正你。

一旦你做了一个 git filter 分支,它就会更新内容并创建一个新的提交。

那么旧的提交在哪里?

旧的提交它仍然在您的存储库中。它成为一个悬空对象,这意味着有一些内容无法从任何分支访问。

首先阅读此答案以了解什么是HEAD

现在你必须使用git filter-branchBFG并且只执行git gc.

在此处输入图像描述


...我认为提交 5-9 是垃圾,我想从存储库和他们引入的磁盘空间中永久删除它们的所有记录

你有几个选择来实现它。这是一个简单的:

# Get back to the desired commit
git checkout <commit> # in your case 4

# now delete the old branch with the 5-9 commits
git branch -D <branch name>

# now create a new branch from commit #4
git checkout -b <branch>

# now you have to clean the leftovers.
# first lets see them (not required just for us to prove that we delete them)
git fsck --full 

# now you will get a list of all the "removed" commit.
# lets clean the repo right now.
git gc --aggressive --prune=now

为什么 git gc --prune=now 不修剪提交 5-9?

它不会删除提交,因为 reset 只会更改 HEAD 而不会更改存储库的内容。

于 2016-05-27T13:09:36.137 回答
0

让我们分部分...

我知道这git reset --hard 4会使我的 repo 似乎已经倒退到提交 4,但据我了解,这只是将提交master点从 9 更改为 4,但实际上并没有删除任何内容。所有数据仍然存在,如果您知道提交 9 的 SHA,则可以恢复。

这是对的。此外,有两个 reflog 可以保留指向提交 5、6、7、8 和 9 的指针:一个 for HEAD,它记住何时HEAD指向这些提交(如果HEAD曾经指向它们),一个 for master,它记住何时被master指向那些提交(如果master确实指向它们——我们肯定知道它指向,9因为它在 之前的位置reset,但我们不知道它是否单独指向之前的每一个,或者你是否可能以某种方式以某种方式一次将它们全部带入,例如,从另一个分支)。

可能有也可能没有指向这些提交的其他分支和/或引用日志。

我也知道,git filter-branch但这只会从历史记录中删除文件,而不是提交。

这是不正确的,尽管正如沃尔夫冈泡利所说的那样,“这是不对的。这甚至都没有错!” 特别是,这个措辞意味着git filter-branch删除事物。它没有:它添加了新的提交

Git 基本上是围绕着添加新东西的想法而构建的,并且永远不会删除任何东西。这包括git commit --amendgit rebasegit filter-branch: 它们添加新的提交。真正删除过期内容的唯一 Git 命令是与 gc 相关的命令(git prunegit reflog expiregit repackgit prune-packed等等,当然还有git gc它自己)。

我试过做:

git reset --hard 4
git gc --prune=now

但是这样做之后,我的.git目录的磁盘空间使用量是相同或更大的,我仍然可以恢复历史记录git checkout 9。为什么不git gc --prune=now修剪提交 5-9?我需要让我的 reflog 过期吗?

是的。

要让旧对象消失,您必须:

  • 追捕并销毁所有引用,包括引用日志中的引用
  • 修剪松散的物体,不管它们的年龄(--prune=now上面的部分)
  • 重新打包这些对象的任何打包版本。

git gc --prune=now处理最后两个步骤,但不处理第一个。使用git reflog --expire=now --expire-unreachable=now会清除所有的 reflogs(这太过分了:--expire-unreachable可能就是你所需要的)。如果您有其他杂散引用(其他分支、标签、一个stash或两个松散的,甚至可能是和之类的东西ORIG_HEADCHERRY_PICK_HEAD,您将不得不手动清理它们。另请注意,git filter-branch将原始引用集.git/refs/original/保留在 中,并且保留所有原始(预过滤副本)对象。

于 2016-05-27T15:21:55.633 回答