由于已经删除的大文件,无法将代码推送到GitHub

487

目前我有

  1. 空的GitHub存储库
  2. SSH服务器存储库(主要)
  3. 本地存储库

SSH服务器存储库是最新的存储库(生产站点),所以我从那里克隆了一个Git到本地。然后我尝试对GitHub进行git push

一切都很顺利,但然后它说某个filename.gz对于GitHub来说太大了。我不需要这个文件,所以我运行了几个Git命令将其从Git缓存中删除,然后推回SSH服务器。

我在本地看不到这个大文件,但它仍然在SSH服务器上,即使git diff没有返回任何东西,并且git push返回“Everything is up-to-date” - 即使在我尝试推送到GitHub时该文件在本地存储库中不可见,我仍然会收到关于它的错误

remote:error:文件fpss.tar.gz为135.17 MB;这超出了GitHub 100 MB的文件大小限制

我按照GitHub帮助列表中的步骤解决了问题,这不应该足够吗?

当文件不在本地,并且未列在git status/diff/push中时,它仍然存在于以太网中吗?

3
文件仍然存在于历史记录中。你需要销毁历史记录,可能是通过压缩添加和删除该文件的提交来实现。 - Shahbaz
1
@Shahbaz 我按照此网站上列出的“解决问题”步骤操作了...这难道不够吗?https://help.github.com/articles/working-with-large-files - DevKev
1
如果你不在意你的历史记录,那么你可以这样做。但这并不好。你可以创建一个专门用于 Github 的分支,将所有历史记录压缩成一个提交(实际上与你所说的相同,但不会删除其他分支),然后只将该特定分支推送到 Github。稍后,当例如 master 分支中有提交时,你可以挑选所有提交并应用到 Github 分支。(不确定合并是否可行,但如果可以,那就更好了) - Shahbaz
1
全心全意地操你妈,Github。 - Tobias Wilfert
1
我对Github真是心有余悸。 - undefined
显示剩余10条评论
28个回答

620
你可以使用
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch <file/dir>' HEAD

这将删除文件历史记录中的所有内容。问题在于该文件存在于历史记录中。

此命令更改您提交的哈希值,可能会成为实际问题,尤其是在共享存储库上。不了解后果的情况下不应执行此操作。

编辑:git项目现在建议用户使用git filter-repo而不是git filter-branch


使用git filter-repo

WARNING: git-filter-branch has a glut of gotchas generating mangled history
         rewrites.  Hit Ctrl-C before proceeding to abort, then use an
         alternative filtering tool such as 'git filter-repo'
         (https://github.com/newren/git-filter-repo/) instead.  See the
         filter-branch manual page for more details; to squelch this warning,
         set FILTER_BRANCH_SQUELCH_WARNING=1.

安装

[brew|pip3|...] install git-filter-repo

使用方法

要删除路径前缀为example/path/to/something的任何文件,您可以运行以下命令:

git filter-repo --path example/path/to/something--invert-paths

要删除任何没有路径前缀 example/path/to/something 的文件,您可以运行以下命令:

删除没有路径前缀的文件 example/path/to/something,您可以运行:

git filter-repo --path example/path/to/something

36
这个命令可以解决我的问题,但是我必须“强制”执行它:git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch <file/dir>' -f HEAD。 - alexoviedo999
44
该命令会更改您提交的哈希值,这可能会在共享存储库中造成实际问题。如果不理解后果,则不应执行此命令。 - Chris
10
你应该用导致问题的文件或目录的名称替换<file/dir>吗? - David Rhoden
16
请注意,如果您想将这些更改应用于所有分支,您需要使用“--all”标志而不是“HEAD”。 - Nick Spreitzer
12
我得到了这个信息:Rewrite 657560fa18c030bcfac9132ce1c3541e84a5bc2c (1/10) (0 秒已过去,剩余 0 秒预测) /usr/lib/git-core/git-filter-branch: 1: eval: 语法错误: 意外的文件结尾 - João Abrantes
显示剩余24条评论

269

我发现相比于 filter-branch压缩提交 更加有用。我执行了以下步骤:

  1. 本地删除大文件。
  2. 提交本地删除操作。
  3. 软重置到之前的第 X 个提交(对我来说是 3):git reset --soft HEAD~3
  4. 然后一起重新提交所有更改(也称为压缩提交):git commit -m "新的合并提交消息"
  5. 推送压缩的提交。

特殊情况(由用户 @lituo 提供):如果上述方法不起作用,那么您可能遇到这种情况。提交 1 包含了大文件,但由于大文件错误提交 1 的推送失败了。提交 2 通过 git rm --cached [file_name] 删除了大文件,但提交 2 的推送仍然失败。您可以按照上面的步骤进行操作,但使用 HEAD~2 而不是 HEAD~3


34
这个翻译的结果如下:比起排名第一的回答,这个好多了。排名第一的回答会搞乱你所有的提交历史记录。 - manic.coder
13
这绝对是唯一一个可以修复大型未提交或已提交文件,而不完全摧毁代码仓库的答案!我点赞让它排在最前面 :-) - Ælex
3
“更好”是一个很强烈的词。这只是一种不同的解决方案。你应该选择哪种取决于你想要什么结果和/或你当前的状态。 - Liam
1
如果在推送合并提交时出现错误,您可以在分支名称后面添加一个+号。git push origin +name-of-branch加号强制远程分支接受您重写的历史记录,否则您将最终得到不同的分支。 - AnnABlue
1
记录一下:第五步应该是: git push --force。这对我很有效,可以从日志中删除大文件历史记录。 - Ben L
显示剩余4条评论

214

如果在向他人寻求帮助之前,您已经对代码库进行了修改,则以下操作可能会有所帮助。首先输入:

git status

完成后,您应该看到类似以下内容的东西

On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

重要的部分是“2次提交”!从这里开始,继续输入:

git reset HEAD~<HOWEVER MANY COMMITS YOU WERE BEHIND>

所以,对于上面的例子,一个人会键入:

git reset HEAD~2

在你输入完这句话后,你的"git status"命令会显示:

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

在那之后,你可以删除大文件(假设你还没有这么做),然后重新提交所有内容,而不会丢失你的工作。


35
胜利者。简单、干净、有效的Git构建解决方案。喜欢这样的答案。 - Ruben Murray
1
我感谢您提供的有关如何回溯提交次数的建议,但是不包括“--soft”选项会产生什么影响(或者不会产生什么影响)? - mikemtnbikes
5
同意,毫无疑问这是获胜者。 - Francisco Colina
2
这个解决方案非常好用,真的帮了我很大忙。回答非常清晰明了,据我所知没有任何缺点。 - Tiago Marques
2
我喜欢这个答案!简单、直接,而且符合预期。 - Daggie Blanqx - Douglas Mwangi
显示剩余5条评论

48
如果文件是在您最近的提交中添加的,并且您还没有将其推送到远程仓库,则可以删除该文件并修改提交。引用自此处
git rm --cached giant_file
    # Stage "giant_file" for removal with "git rm"
    # Leave it on disk with "--cached". if you want to remove it from disk
    # then ignore the "--cached" parameter
git commit --amend -CHEAD
    # Commit the current tree without the giant file using "git commit"
    # Amend the previous commit with your change "--amend" 
    # (simply making a new commit won't work, as you need
    # to remove the file from the unpushed history as well)
    # Use the log/authorship/timestamp of the last commit (the one we are
    # amending) with "-CHEAD", equivalent to --reuse-message=HEAD
git push
    # Push our rewritten, smaller commit with "git push"

1
这个解决方案行不通,因为该文件不再在git索引中(在git status中它会被列为“未跟踪”的文件列表)。 - loretoparisi
什么都没有发生。应用这个后,文件的总数减少了,但在显示进程99%之后,它又卡住了。有什么建议我漏掉了什么吗? - CoDe
4
"-CHEAD" 是什么意思? - aerin
1
如果我想从特定的提交中尝试这个,而不是最后一个提交怎么办?我尝试了 git rm --cached giant_file commit_id 但它没有起作用 :( - puifais
@puifais 我会回滚到之前的提交,执行这些步骤,然后再与当前的提交合并。我不确定这是否是最佳方法,因为我不是 Git 专家。 - BlueMoon93

23

即使我删除了大文件,为什么GitHub还是拒绝我的repo?

Git存储项目的完整历史记录,所以即使您从项目中“删除”一个文件,Git repo仍然在其历史记录中有该文件的副本。如果您尝试推送到另一个仓库(例如托管在GitHub上的仓库),则Git 需要 远程仓库具有与本地仓库相同的历史记录(即相同的大文件)。

如何使GitHub接受我的repo?

您需要在本地清理项目的Git历史记录,从所有历史记录中删除不需要的大文件,并且只使用“清理后”的历史记录进行操作。影响提交的Git commit id将会发生改变。

如何清理Git repo中的大文件?

清理Git历史纪录中不需要的大文件最好的工具是BFG Repo-Cleaner-它是一个更简单、更快的工具,专门用于从Git历史记录中删除不需要的文件。

请仔细遵循使用说明,核心部分只需执行以下操作:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

如果 Git 仓库中的某个文件大小超过 100MB(且不在最新提交中),将会从仓库历史记录中删除。然后你可以使用 git gc 命令清理无用数据:

$ git gc --prune=now --aggressive

BFG通常比运行git-filter-branch至少快10-50倍, 并且一般来说更易于使用。

全面披露:我是BFG Repo-Cleaner的作者。


1
我的情况有额外的复杂性,无法压缩。BFG工具非常好用。谢谢。 - dantopa
这是一个非常出色的解决方案。 - SexualPotatoes
1
值得注意的是,BFG 工具已经有好几年没有得到维护了(参见 https://github.com/rtyley/bfg-repo-cleaner/graphs/contributors)。 - Liam
1
你的工具真是神奇。感谢你的帮助。 - pantonis
1
你的工具是唯一一个最终为我工作的。它在闪存中工作得几乎像魔术一样。非常感谢您的帮助和这个令人惊叹的工具。 - ashish.gd

18

我遇到了类似的问题,并使用上面的步骤来删除文件。它完美地解决了我的问题。

然后我需要删除第二个文件时出现了错误: remote: error: 文件 <路径/文件名> 的大小为109.99 MB,超过了GitHub的100.00 MB文件大小限制

我尝试了相同的步骤,但出现了错误:"先前的备份已经存在于 <路径/文件名> 中"

这个网站上做了研究后,我使用了以下命令:git filter-branch --force --index-filter "git rm --cached --ignore-unmatch <路径/文件名>" --prune-empty --tag-name-filter cat -- --all

非常成功,大文件已被删除。

令人难以置信的是,推送仍然失败,并出现另一个错误:error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 104 fatal: The remote end hung up unexpectedly

我通过直接修改.git配置文件解决了这个问题 - postBuffer = 999999999

之后推送成功了!


1
我在删除一个大文件时遇到了一个额外的问题,就是其中一个文件夹中有一个哈希#字符。这对于正常的git操作没有任何问题,但是对于git rm命令,我需要提供文件的完整存储库路径名称,并使用反斜杠转义#才能使其正常工作。 - jacanterbury
这对我也起作用了。我通过简单的推送避免了页面底部的“硬重置”步骤。https://www.czettner.com/2015/07/16/deleting-big-files-from-git-history.html - Monte Hayward
在运行“git push -f origin”之后,这个问题得到了解决。 - kezzos

11

我已经尝试了以上所有方法,但它们都对我无效。

然后我想出了自己的解决方案。

  1. 首先,您需要一个干净、最新的本地存储库。删除所有大文件。

  2. 现在,在您的存储库文件夹之外创建一个新文件夹,并使用“Git在此处创建存储库”将其变为新的Git存储库,我们称其为new_local_repo。就是这样!以上所有方法都说你必须清除历史记录……好吧,我厌倦了,让我们创建一个没有任何历史记录的新存储库!

  3. 将旧的、混乱的本地存储库中的文件复制到新的、漂亮的存储库中。请注意,文件夹图标上的绿色徽标将消失,这是有前途的,因为这是一个新存储库!

  4. 提交到本地分支,然后推送到远程新分支。我们称其为new_remote_branch。如果您不知道如何从新的本地存储库推送,请搜索一下。

  5. 恭喜!您已经将干净、最新的代码推送到GitHub。如果您不再需要远程主分支,您可以将new_remote_branch作为新的主分支。如果您不知道如何做,请搜索一下。

  6. 最后一步,是时候删除混乱的旧本地存储库了。今后,您只使用new_local_repo。


4

我是一名有帮助的助手,可以为您进行翻译。以下是需要翻译的内容:

我遇到了相同的问题,但没有一个答案能够解决我的问题。我通过以下步骤解决了这个问题:

1. 找出包含大文件的提交记录

git log --all -- 'large_file`

最底部的提交是结果列表中最古老的提交。

2. 找到最古老提交之前的那个提交。

git log

假设您获得了以下内容:
commit 3f7dd04a6e6dbdf1fff92df1f6344a06119d5d32

3. Git rebase

git rebase -i 3f7dd04a6e6dbdf1fff92df1f6344a06119d5d32

提示

  1. 列表项
  2. 我只选择了drop来删除包含大文件的提交。
  3. 在rebase过程中,您可能会遇到冲突,请解决它们并使用git rebase --continue继续,直到完成为止。
  4. 如果在rebase过程中出现任何问题,请使用git rebase --abort取消操作。

第一步对我没用。我需要使用: git log --all --full-history -- "**/large_file" - user1245262

3

3

如何将大文件/文件夹保留在工作目录中

此命令可以解决问题 (来自答案1):

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch <file/dir>' HEAD

如果文件/文件夹在工作树中,此命令也会删除该文件/文件夹。

如果要将该文件/文件夹保留在工作树中,建议执行以下步骤:

  1. 发生错误后运行git reset HEAD^
  2. 将引起问题的文件/文件夹添加到”.gitignore”文件中。

  3. 然后按常规操作进行,例如 git add .(可以包含其他文件/文件夹但必须包含“.gitignore”文件),git commit -m"message"和最后的git push origin <branch_name>


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