我如何找出哪个文件花了这么长时间才写入我的远程 git 存储库?

How do I figure out what file is taking so long to write to my remote git repository?

我在 Mac Sierra 上使用 Git 2.8。当我尝试将本地存储库推送到远程时遇到问题。这样做需要很长时间。我认为这是因为它试图推送一个我一定是无意中签入的非常大的文件。这就是我尝试推送内容时发生的情况。它只是挂起,然后我必须按 Ctrl + C 退出它。

On branch master
Your branch is ahead of 'origin/master' by 62 commits.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean
Counting objects: 609, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (608/608), done.
Writing objects:  20% (124/609), 33.04 MiB | 1.03 MiB/s 
localhost:myproject nataliab$ Killed by signal 2.

如何找出导致挂断的 file/files 是什么?我试过“git状态”,但它什么也没告诉我……

localhost:myproject nataliab$ git status
On branch master
Your branch is ahead of 'origin/master' by 62 commits.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean

感谢您的帮助,-

听起来您已经将构建的二进制文件添加到您的 Git 存储库中。 在 macOS 中,您可以在 Finder 中搜索时设置高级过滤选项:

1.) 打开 Finder 并转到您的存储库

2.) 单击搜索或按 Command+F,然后将搜索位置从 "This Mac" 更改为您的实际文件夹

3.) 单击 "Kind" 过滤器和 select "Other",然后从属性列表 select "File Size"

4.) 单击第二个过滤器并选择 "is greater than"

5.) 在第三个 space 中,输入大小以搜索大于(例如:500KB 或 1MB)的任何内容,然后选择 KB 或 MB 作为最终过滤器

TL;DR:进行交互式变基并用更好的提交替换错误的提交,或者使用 BFG(参见 How to remove/delete a large file from commit history in Git repository?)。

Git 推送提交,而不是文件

在Git中,每次提交都是永久且不可更改的。此外,提交 历史:您的最新提交指向您的 second-latest 提交,后者指向您的 third-latest,依此类推,一直回到第一次提交。

现在假设您提交了一个大文件(例如 4.7 GB 左右的 DVD 映像)。稍后,您删除该文件并再次提交。

当您转到 git push 结果提交时,Git 将——必须——不仅推送 new 提交, 删除 文件,还有 旧的 提交 创建 文件。

如果Git 未能做到这一点,您将无法召回包含大文件的提交。 Git 的全部要点是能够召回每一次提交,所以这与版本控制相反。如果Git只发送你最新的,那将是不受控制的unversion。

这些文件是提交的副作用。 Git 是关于提交的。文件只是一种意外的奖励。当然,文件首先是提交的目的,但是 Git 仍然是关于提交的。

这对你意味着什么

您的大文件位于您拥有的提交中的某处,而它们没有:

localhost:myproject nataliab$ git status
On branch master
Your branch is ahead of 'origin/master' by 62 commits.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean

在这 62 次(可能是 1)次提交中,您添加了一些大文件。稍后,您可能删除了它们——但是 Git 必须推送 所有 提交。

此外,提交是永久且不可更改的。您不能 更改添加文件的旧提交。这只剩下一个可能的解决方案:根本不要推送这些提交

你可能——而且应该,真的——object。大概您 do 想要推送(至少其中一些)这些提交。但我要告诉您的是,您不想推送 这些 提交。相反,您想推动一些稍微改变的 更好 提交。


1"Probably",因为origin/master是你Git对master这个名字的记忆other Git 存储库位于 origin。此内存并不总是最新的。你可以 运行 git fetch origin 从他们那里获取最新的提交,从而让你的 Git 更新它的记忆。但是如果你是唯一一个使用其他存储库的人,你的 Git 的记忆将足够准确。


将 "bad" 提交复制到新的、不同的 "better" 提交

使用git log查看您当前推送的提交:

$ git log --name-status origin/master..master

--name-status 参数告诉 Git 将每个提交与上一个提交进行比较(像往常一样),但随后不显示完整的 git diff,只显示添加了哪些文件、修改和删除。

您将有一个删除一些大文件的提交,然后是添加这些相同大文件的较早提交。您现在的工作是更正较早的提交,以便它根本不会添加这些文件。

您实际上无法更改 之前的提交!但是你 可以 复制 它,到一个非常相似的提交:做一个几乎完全相同的提交,除了它不添加大文件。您所做的新提交将具有相同的 parent ID——这就是 Git 跟踪哪些提交先于哪些其他提交的方式。它将具有相同的作者(您)、相同的提交者(您)、相同的日志消息,甚至可能是相同的日期……但它不会有大文件。

作为将这个特别糟糕的提交复制到一个新的、更好的提交的副作用,您将被迫复制每个后续提交。原因是每个提交都记录了它之前的 (parent) 提交 ID,而你的 new-and-improved 复制提交将有一个不同的 parent。所以现在你需要复制它的child。新的 "child copy" 与之前的 child 相同,除了两件事:parent ID 和大文件消失的事实。

每次提交都会重复这一过程,直到删除大文件为止。现在,if 特定提交 just 删除了大文件,此时您可以丢弃该提交:您一直在制作的每个副本无论如何都缺少这些文件,因此无事可做。但是,如果该提交除了删除大文件之外还做了一些其他事情,您可能希望保留它的其他部分。

在那之后,您可能只想复制每个剩余的提交,只更改其 parent ID。

有两个 Git 命令可以执行这种提交复制: git filter-branchgit rebase -i。前者有点难用,所以如果你要坚持使用那些使用 Git,我通常建议使用 rebase,除非您在需要复制的那些提交中有合并提交(任何此类合并将显示在 git log 输出中)。

使用 filter-branchrebase -i 的说明在 above-linked 问题的 Greg Bacon's linked answer 中。

虽然我没用过BFG,但是据说操作简单多了。它做的事情几乎没有 filter-branch 和交互式变基那么多,所以它没有那么复杂的控件。不过,它仍然 复制 提交。

一旦所有提交都被复制,你只需"forget"坏的

Git 分支的工作方式是 namemaster 简单地指向 latest 在分支 master 上提交。每个提交都指向其较早的对应项。因此,一旦您将 "bad" 提交复制到 "better" 提交,您的 master 将指向最新复制的提交。该提交指向它的 parent,依此类推,因为无论有多少人(现在可能有 61 个)都提交了到达 origin/master 点的位置。

另一个 Git 存储库,在 origin 上,已经有那个提交和每个更早的提交。但是 现在 你可以 git push origin master,你的 Git 会调用他们的 Git,找到要推送的提交,然后开始推送——然后推将是新的,更好的副本,而不是原件。

(原件会怎样?最终,它们会过期并过期并被删除。如果您想要它们,您至少有 30 天的时间来取回它们。)