149

我将大量文件检查到一个分支并合并,然后不得不删除它们,现在我留下了一个我不知道如何摆脱的大 .pack 文件。

我删除了所有使用的文件,git rm -rf xxxxxx并且还运行了该--cached选项。

有人可以告诉我如何删除当前位于以下目录中的大型 .pack 文件:

.git/objects/pack/pack-xxxxxxxxxxxxxxxxx.pack

我只需要删除我仍然拥有但不再使用的分支吗?或者还有什么我需要运行的吗?

我不确定它有多大的不同,但它显示了一个针对文件的挂锁。

谢谢


编辑

以下是我的 bash_history 的一些摘录,应该可以让我了解我是如何设法进入这种状态的(假设此时我正在处理一个名为“my-branch”的 git 分支,并且我有一个包含更多文件夹的文件夹/文件):

git add .
git commit -m "Adding my branch changes to master"
git checkout master
git merge my-branch
git rm -rf unwanted_folder/
rm -rf unwanted_folder/     (not sure why I ran this as well but I did)

我以为我也运行了以下内容,但它没有与其他人一起出现在 bash_history 中:

git rm -rf --cached unwanted_folder/

我还以为我运行了一些 git 命令(如git gc)来尝试整理包文件,但它们也没有出现在 .bash_history 文件中。

4

7 回答 7

228

The issue is that, even though you removed the files, they are still present in previous revisions. That's the whole point of git, is that even if you delete something, you can still get it back by accessing the history.

What you are looking to do is called rewriting history, and it involved the git filter-branch command.

GitHub has a good explanation of the issue on their site. https://help.github.com/articles/remove-sensitive-data

To answer your question more directly, what you basically need to run is this command with unwanted_filename_or_folder replaced accordingly:

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch unwanted_filename_or_folder' --prune-empty

This will remove all references to the files from the active history of the repo.

Next step, to perform a GC cycle to force all references to the file to be expired and purged from the packfile. Nothing needs to be replaced in these commands.

git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
# or, for older git versions (e.g. 1.8.3.1) which don't support --stdin
# git update-ref $(git for-each-ref --format='delete %(refname)' refs/original)
git reflog expire --expire=now --all
git gc --aggressive --prune=now
于 2012-06-30T21:45:51.103 回答
16

场景 A:如果你的大文件只是添加到一个分支,你不需要运行git filter-branch. 您只需要删除分支并运行垃圾收集:

git branch -D mybranch
git reflog expire --expire-unreachable=all --all
git gc --prune=all

场景 B:但是,根据您的 bash 历史记录,您确实将更改合并到了 master 中。如果您尚未与任何人共享更改(git push还没有)。最简单的方法是将 master 重置回与具有大文件的分支合并之前。这将消除分支中的所有提交以及合并后对 master 所做的所有提交。因此,除了大文件之外,您可能会丢失您可能真正想要的更改:

git checkout master
git log # Find the commit hash just before the merge
git reset --hard <commit hash>

然后运行场景 A 中的步骤。

场景 C:如果您想要保留来自分支的其他更改合并后 master 上的更改,最好重新设置 master 并选择性地包含您想要的提交:

git checkout master
git log # Find the commit hash just before the merge
git rebase -i <commit hash>

在您的编辑器中,删除与添加大文件的提交相对应的行,但保留其他所有内容。保存并退出。你的主分支应该只包含你想要的,而不是大文件。请注意,git rebase没有-p将消除合并提交,因此您将获得 master after 的线性历史记录<commit hash>。这对您来说可能没问题,但如果不是,您可以尝试使用-p,但会git help rebasecombining -p with the -i option explicitly is generally not a good idea unless you know what you are doing

然后运行场景 A 中的命令。

于 2015-03-25T14:26:40.483 回答
11

正如 loganfsmyth 在他的回答中已经说过的那样,您需要清除 git 历史记录,因为即使从 repo 中删除文件后,这些文件仍然存在。官方 GitHub 文档推荐 BFG,我发现它比filter-branch

从历史记录中删除文件

从他们的网站下载BFG。确保您已安装 java,然后创建镜像克隆并清除历史记录。确保替换YOUR_FILE_NAME为您要删除的文件的名称:

git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --delete-files YOUR_FILE_NAME some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push

删除文件夹

与上述相同,但使用--delete-folders

java -jar bfg.jar --delete-folders YOUR_FOLDER_NAME some-big-repo.git

其他选项

BFG 还允许更高级的选项(请参阅文档),例如:

从历史记录中删除所有大于 100M 的文件:

java -jar bfg.jar --strip-blobs-bigger-than 100M some-big-repo.git

重要的!

运行 BFG 时,请注意两者YOUR_FILE_NAMEYOUR_FOLDER_NAME只是文件/文件夹名称。它们不是路径,所以类似的东西foo/bar.jpg不起作用!相反,所有具有指定名称的文件/文件夹都将从回购历史记录中删除,无论它们存在于哪个路径或分支。

于 2018-04-04T08:18:17.977 回答
11

运行以下命令,替换PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA为要删除的文件的路径,而不仅仅是文件名。这些论点将:

  1. 强制 Git 处理但不签出每个分支和标签的整个历史记录
  2. 删除指定的文件,以及作为结果生成的任何空提交
  3. 覆盖现有标签
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" --prune-empty --tag-name-filter cat -- --all

这将从 repo 的活动历史记录中强制删除对文件的所有引用。

下一步,执行 GC 循环以强制对文件的所有引用过期并从包文件中清除。在这些命令中不需要替换任何内容。

git update-ref -d refs/original/refs/remotes/origin/master
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --aggressive --prune=now
于 2019-07-11T17:09:05.933 回答
7

一种选择:

手动运行git gc将多个包文件压缩成一个或几个包文件。此操作是持久的(即大包文件将保留其压缩行为),因此定期压缩存储库可能是有益的git gc --aggressive

另一种选择是将代码和 .git 保存在某处,然后删除 .git 并使用此现有代码重新开始,创建一个新的 git 存储库 ( git init)。

于 2012-06-15T12:36:45.013 回答
3

我的节目有点晚了,但如果上面的答案没有解决问题,那么我找到了另一种方法。只需从 .pack 中删除特定的大文件。我遇到了这个问题,我不小心签入了一个 2GB 的大文件。我按照此链接中说明的步骤操作:http ://www.ducea.com/2012/02/07/howto-completely-remove-a-file-from-git-history/

于 2018-01-08T18:34:29.987 回答
-5

这比编码解决方案更方便。压缩文件。以文件视图格式打开 zip(不同于解压缩)。删除 .pack 文件。解压并替换文件夹。奇迹般有效!

于 2019-02-01T15:16:55.837 回答