关于github:无法从git存储库中删除文件?

Can't delete file from the git repository?

本问题已经有最佳答案,请猛点这里访问。

我已经向我的Git存储库添加了大于100MB的文件,它在本地Git中工作。不幸的是,GitHub有100 MB的限制。

所以,我重写了代码,使它不需要这个大文件,然后删除它,然后提交。

不幸的是,我仍然不能推到gothub,因为文件仍在存储库中。

我试着跑

1
2
3
4
5
git rm --cached my_file.dat

git rm --cached -r my_file.dat

git rm --cached mypath/my_file.dat

所有命令都失败了

1
fatal: pathspec ... did not match any files

如何删除没有指定确切路径的文件?

更新

我试着跑

1
java -jar bfg.jar --strip-blobs-bigger-than 100M

但它失败了

1
2
Scanning packfile for large blobs completed in 2 ms.
Warning : no large blobs matching criteria found in packfiles - does the repo need to be packed?

但仍然无法

1
git push origin master

具有

1
File my_path/my_file.dat is 257.62 MB; this exceeds GitHub's file size limit of 100.00 MB


顺便提一句,如果您不需要repo中的文件,那么直接删除文件是最简单的选择(尽管如您所见,这并不完全简单)。另一种选择是使用像git lfs这样的工具,允许您的repo引用该文件,而不直接将该文件放入repo中。这解决了与git中的大型文件相关的许多问题,如果您确实需要该文件,则应予以考虑;但重写repo以对已提交的文件使用lfs是另一个整体主题…好的。

所以,回到移除的问题。要提供更多的上下文:好的。

在Git中,有三个位置可以找到文件。好的。

1)工作树-只是你工作的普通文件。git不做特别的工作来保存这里的数据,它只存在于本地。您可以通过在git之外的方式或使用git rm从这里删除文件(特别是当您还需要从索引中删除文件时)。好的。

2)索引-这是文件"分段"进行新提交的地方。当你说git add时,你更新了索引。git将保留这里的数据,而不依赖于工作副本,但它仍然只是本地的,没有特别的努力来保存历史。git rm将从索引中取出一个文件。好的。

3)数据库-这是您的项目历史存在的地方。当你说git commit时,你将代表你的项目的"对象"添加到数据库中。数据库是git保存历史的地方,您必须竭尽全力使git丢失这里的任何数据。数据库基本上是在pushfetch操作期间repos共享的。git rm对数据库没有影响。好的。

现在,正如其他人所指出的,由于您创建了一个包含文件的提交,所以您需要做的不仅仅是git rm。第一步是重写包含包含该文件的提交的任何引用的历史记录。好的。

有人说你需要处理"引入"文件的承诺;这是误导。您需要处理对该文件的所有引用(或者从技术上讲,是对表示该文件的BLOB对象的引用)。好的。

由于rebase根据其相对于其父级的更改来解释提交,因此,如果添加文件后没有进行大量的分支和合并,则可以以相对方便的方式处理这一问题。例如,如果该文件是在commit A中创建的,并且只有A可访问的引用是master,并且没有比master中的A更新的合并提交,那么rebase是最简单的解决方案。假设A不是根提交,好的。

1
git rebase -i A^ master

(其中A^是引入文件的提交的sha);但如果A是根提交,表示A^无效,则好的。

1
git rebase -i --root master

在弹出的TODO列表中,将A的命令更改为edit,当提示编辑commit时,删除该文件,然后通知rebase继续。好的。

此时,由于git不必发送整个数据库,所以git push可能会工作,因为它只发送参考文件的历史记录,您告诉它要推。但不要混淆:您仍然没有从本地数据库中删除该文件。要做到这一点,您必须确保没有任何东西(甚至reflog)可以到达文件,然后使用git gc。如果您成功地从所有引用的历史记录中删除了该文件,则最终会发生这种情况;除非您的本地存储受到限制,否则这可能是文件。好的。

上面的过程中有几个重要的假设,如果您最近提交了文件,这些假设可能会保存。但是如果有多个分支可以达到commit A,和/或如果有合并commit可以达到A,那么执行rebase会变得更加困难。这就是你将git filter-branchBFG Repo Cleaner视为解决方案的时候。其中,BFG对于这个任务来说要简单得多,速度也快得多;如果您搜索它,您可以找到许多源(包括一些so条目),概述它的用法。因为filter-branch更通用,所以很难正确使用,但它又是"内置的"——不需要下载额外的软件。好的。

所有这些技巧都改写了历史。因为你不能推动你现有的历史,这可能不是什么大问题(假设你没有第二个遥控器,你已经推动了改变)。好的。好啊。


您需要以某种方式从所有提交中删除此文件。

有几种方法可以做到这一点:

  • 如果要编辑的提交数量相当少:使用git rebase -i手动编辑提交
  • 如果您必须大规模执行(许多提交,几个分支):使用git filter-branch --index-filter。或者@sirko建议的bfg-repo-cleaner

如何使用git-rebase-i:

如果您的历史记录如下:

1
2
3
      big file added here
        v
--*--A--B--C--D--E--F <- master

要修改b的内容,需要从其父级重新调整b的基值:

1
git rebase -i A

这将打开一个文本编辑器,询问您希望对从BF的每一个提交采取什么操作。

它将从以下内容开始:

1
2
3
4
 pick  B   message
 pick  C   message
 pick  D   message
 ...

您要更改B,以从此提交中删除大文件

1
2
3
4
5
# set the action on b to 'edit' (or e) :
e B  message
pick  C   message
pick  D   message
...

保存并关闭。

现在Git将应用你告诉他的行动:

  • 他会把你的报告倒带到A
  • 你让Git编辑B:他会应用B,然后停止,这样你就可以随心所欲了。
  • 要从此提交中删除大文件,请执行以下操作:

    1
    2
    git rm --cached big/file
    git commit --amend
  • 现在,您要告诉Git继续重新调整:

    1
    git rebase --continue

  • 您应该会看到一些消息,指示Git正在重播C,然后是D。F以下


文件仍在存储库的历史记录中…您需要删除引入它的提交…

如果您可以清楚地标识引入它的提交,请尝试以下操作:

1
git rebase -i ${COMMIT_ID}^

这将向您提供承诺列表,您可以在其中选择editdrop某些项目。要么将默认的pick替换为drop,以简单地删除它(以及该提交所做的所有其他更改),从而标记违规提交!或用edit标记违规行为,删除文件,重新提交并继续。

完成后,再次尝试推动。

Git并不是专为大型二进制文件设计的,因此请避免签入它们。如果您"需要",那么它可能值得签出Git大型文件存储项目。