我目前的基地总规模大约是。200 mb。

但是我的。git文件夹有惊人的5GB(!)。因为我把我的工作推到一个外部服务器,我不需要任何大的本地历史…

如何缩小。git文件夹以释放笔记本上的一些空间?我可以删除超过30天的所有更改吗?


当前回答

最好的选择是使用BFG Repo Cleaner (BitBucket推荐使用,其他选项要快得多): https://rtyley.github.io/bfg-repo-cleaner/

我也尝试过使用史蒂夫·洛雷克的解决方案,它也有效:https://web.archive.org/web/20190207210108/http://stevelorek.com/how-to-shrink-a-git-repository.html

其他回答

您不应该删除所有超过30天的更改(我认为这在某种程度上可以利用Git,但真的不推荐)。

你可以调用git gc——aggressive——prune,它将在你的存储库中执行垃圾收集并修剪旧对象。你是否有很多经常变化的二进制文件(归档文件、图像、可执行文件)?这通常会导致巨大的.git文件夹(记住,Git为每个修订存储快照,二进制文件压缩得很差)

我尝试了这些,但我的存储库仍然非常大。问题是我不小心检入了一些生成的大文件。经过一番搜索,我发现了一个很好的教程,可以很容易地删除生成的大文件。本教程允许我将存储库从60 MB缩小到< 1 MB。

Steve Lorek,如何缩小Git存储库

更新:这里是博客文章的复制粘贴版本。

如何缩小Git存储库

我们的主Git存储库的大小突然膨胀。它一夜之间就增长到180MB(压缩),而且克隆的时间很长。

原因是显而易见的;有人,在某个地方,某个时间,以某种方式,提交了大量文件。但我们不知道那些文件在哪里。

经过几个小时的试验、错误和研究,我确定了一个流程:

发现大文件 从存储库中清除它们 修改远程(GitHub)存储库,使文件不再被下载

除非你能够保证所有团队成员都能够创造出全新的副本,否则你便不应该尝试这一过程。它涉及更改历史记录,并要求任何对存储库做出贡献的人在向存储库推送任何东西之前删除新清理的存储库。

深度克隆存储库

如果您还没有相关存储库的本地克隆,现在就创建一个:

git clone remote-url

现在,您可能已经克隆了存储库,但还没有拥有所有远程分支。这是确保适当的“深层清洁”的必要条件。要做到这一点,我们需要一个Bash脚本:

#!/bin/bash
for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do
    git branch --track ${branch##*/} $branch
done

感谢bigfish在StackOverflow上的这个脚本,这是逐字复制。

将此代码复制到chmod +x filename.sh文件中,然后使用。/filename.sh执行。现在您将拥有所有远程分支(遗憾的是Git没有提供此功能)。

发现大文件

这里要归功于Antony Stubbs -他的Bash脚本识别了本地Git存储库中最大的文件,并逐字复制如下:

#!/bin/bash
#set -x 

# Shows you the largest objects in your repo's pack file.
# Written for osx.
#
# @see http://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/
# @author Antony Stubbs

# set the internal field spereator to line break, so that we can iterate easily over the verify-pack output
IFS=$'\n';

# list all objects including their size, sort by size, take top 10
objects=`git verify-pack -v .git/objects/pack/pack-*.idx | grep -v chain | sort -k3nr | head`

echo "All sizes are in kB. The pack column is the size of the object, compressed, inside the pack file."

output="size,pack,SHA,location"
for y in $objects
do
    # extract the size in bytes
    size=$((`echo $y | cut -f 5 -d ' '`/1024))
    # extract the compressed size in bytes
    compressedSize=$((`echo $y | cut -f 6 -d ' '`/1024))
    # extract the SHA
    sha=`echo $y | cut -f 1 -d ' '`
    # find the objects location in the repository tree
    other=`git rev-list --all --objects | grep $sha`
    #lineBreak=`echo -e "\n"`
    output="${output}\n${size},${compressedSize},${other}"
done

echo -e $output | column -t -s ', '

像以前一样执行这个脚本,你会看到一些类似于下面的输出:

All sizes are in kB. The pack column is the size of the object, compressed, inside the pack file.

size     pack    SHA                                       location
1111686  132987  a561d25105c79aa4921fb742745de0e791483afa  08-05-2012.sql
5002     392     e501b79448b9e970ab89b048b3218c2853fdfc88  foo.sql
266      249     73fa731bb90b04dcf79eeea8fdd637ba7df4c089  app/assets/images/fw/iphone.fw.png
265      43      939b31c563bd40b1ca70e4f4a9f7d67c27c936c0  doc/models_complete.svg
247      39      03514d9e84418573f26b205bae7e4e57057c036f  unprocessed_email_replies.sql
193      49      6e601c4067aaddb26991c4bd5fbddef003800e70  public/assets/jquery-ui.min-0424e108178defa1cc794ee24fc92d24.js
178      30      c014b20b6fed9f17a0b2809ac410d74f291da26e  foo.sql
158      158     15f9e56bc0865f4f303deff053e21909661a716b  app/assets/images/iphone.png
103      36      3135e15c5cec75a4c85a0636b154b83221020c97  public/assets/application-c65733a4a64a1a885b1c32694574b12a.js
99       85      c1c80bc4c09e692d5e2127e39c87ecacdb1e816f  app/assets/images/fw/lovethis_logo_sprint.fw.png

是的,看起来有人把一些不必要的文件推到了某个地方!包括一个可爱的1.1GB的SQL转储文件。

清理文件

清理文件需要一段时间,这取决于存储库的繁忙程度。你只需要一个命令就可以开始这个过程:

git filter-branch --tag-name-filter cat --index-filter 'git rm -r --cached --ignore-unmatch filename' --prune-empty -f -- --all

这个命令改编自其他来源——主要的增加是——tag-name-filter cat,它确保标签也被重写。

在该命令完成执行之后,您的存储库现在应该得到了清理,所有分支和标记都得到了清理。 回收空间

虽然我们可能已经重写了存储库的历史,但这些文件仍然存在于其中,窃取磁盘空间,通常会造成麻烦。让我们用核武器消灭这些混蛋:

rm -rf .git/refs/original/

git reflog expire --expire=now --all

git gc --prune=now

git gc --aggressive --prune=now

现在我们有了一个全新的、干净的存储库。在我的例子中,它从180MB增加到7MB。

推送清理后的存储库

现在我们需要将更改推回远程存储库,这样其他人就不会遭受180MB下载的痛苦。

git push origin --force --all

——all参数也会推送所有分支。这就是为什么我们需要在一开始就克隆它们。

然后推送新重写的标签:

git push origin --force --tags

告诉你的队友

任何拥有本地存储库克隆的人都需要使用git rebase,或者创建一个新的克隆,否则当他们再次推送时,这些文件将被一起推送,存储库将被重置到之前的状态。

如何缩小你的。git文件夹在你的git回购

总结

按照这个顺序,从最不危险和/或最有效和/或最快到更危险和/或最不有效和/或最慢。

注意,git lfs行只适用于安装了git lfs的情况。谷歌,你会看到它是一个第三方独立应用程序。如果你没有安装git lfs,忽略这些行即可。看看我在这个答案下面的评论,从这里开始。

这些测试结果是针对du -hs——exclude=的repo。git。显示总回购大小,不包括。git目录,大约是80gb, du -hs .git显示。git文件夹单独开始大约162 GB:

#                                                                   Memory Saved
#                                               Time it took        in .git dir
#                                               ------------        ------------
time git lfs prune                              #  1~60 min          62 GB
time git gc                                     #  3 min            < 1 GB
time git prune                                  #  1 min            < 1 GB
time git repack -a -d --depth=250 --window=250  #  2 min            < 1 GB
# (Note: `--prune` does nothing extra here; `man git gc` says 
# `--prune is on by default`)
time git gc --aggressive --prune                #  1.25 hrs         < 1 GB

正如您所看到的,最后一个命令花费了很长时间,但收效甚微,所以甚至不要运行它!

此外,运行git lfs prune的另一种方法是手动删除整个.git/lfs目录,然后从头重新获取lfs (git大文件系统)的内容。 注意:不要不小心删除整个。git目录!你将会丢失所有的git历史记录,分支和提交!只删除“。git/lfs”目录。类似这样的方法可能有用:

# 1. Delete the whole git lfs directory
rm -rf .git/lfs


# 2. Re-fetch the git lfs contents again from scratch.
# See my answer here: https://stackoverflow.com/a/72610495/4561887

# Option 1 (recommended): fetch (to the ".git/lfs" dir) AND check out just the
# git lfs files for just the one branch or commit you currently have
# checked-out. 
# - this might download ~20 GB of data on a large corporate mono-repo
git lfs pull
# OR do this (these two commands do the exact same thing as `git lfs pull`)
git lfs fetch
git lfs checkout

# Option 2: fetch (to the ".git/lfs" dir) ALL git lfs files for ALL branches on
# the remote
# - this might download ~1000 GB of data on the same large corporate mono-repo
#   as above
git lfs fetch --all
# Also check out, or "activate" the git lfs files for your currently-checked-out
# branch or commit, by updating all file placeholders or pointers in your
# active filesystem for the current branch with the actual files these git lfs
# placeholders point to.
git lfs checkout

关于上面所示的git lfs命令的详细信息,请参阅我的另一个回答:如何作为基本用户使用git lfs: git lfs fetch、git lfs fetch——all、git lfs pull和git lfs checkout之间有什么区别?

细节

首先,您需要知道.git文件夹中什么占用了这么多空间。一种方法是在repo中运行基于NCurses(类似gui)的ncdu (NCurses Disk Usage)命令。另一种方法是运行这个:

du -h --max-depth=1 .git

旁注:看看你的repo有多大,不包括你的。git文件夹,运行这个代替:

du -h --max-depth=1 --exclude=.git .

上面第一个命令的输出示例:

$ du -h --max-depth=1 .git
158G    .git/lfs
6.2M    .git/refs
4.0K    .git/branches
2.5M    .git/info
3.7G    .git/objects
6.2M    .git/logs
68K .git/hooks
162G    .git

如你所见,我的。Git文件夹总大小为162 GB,但其中158 GB是我的。Git /lfs文件夹,因为我使用第三方“Git大文件存储”(Git lfs)工具来存储大型二进制文件。所以,运行这个程序可以显著减少这个量。注意:以下所有命令的时间部分都是可选的:

time git lfs prune

(如果git lfs修剪失败,提示“panic: runtime error: invalid memory address or nil pointer derefence”,请参阅下面的注释。)

来源:如何收缩一个git LFS repo 官方文档:git-lfs-prune(1)—从本地存储中删除旧的LFS文件

这花了60秒!

现在我刚刚释放了62 GB!我的.git/lfs文件夹现在只有96 GB,如下所示:

$ du -h --max-depth=1 .git
96G .git/lfs
6.2M    .git/refs
4.0K    .git/branches
2.5M    .git/info
3.0G    .git/objects
6.2M    .git/logs
68K .git/hooks
99G .git

接下来,运行这个命令将.git/objects文件夹缩小几百MB到大约1gb:

time git gc
time git prune

Git gc大约需要3分钟,Git prune大约需要1分钟。

再次使用du -h——max-depth=1 .git检查磁盘使用情况。如果你想节省更多的空间,运行这个:

time git repack -a -d --depth=250 --window=250

这大约需要2分钟,并节省几百MB。

现在,你可以停在这里,或者你可以运行最后一个命令:

time git gc --aggressive --prune

最后一个命令将节省几百MB,但需要大约1.25小时。

如果git lfs修剪失败,提示“panic:运行时错误:无效的内存地址或空指针解引用”

如果git lfs修剪失败:

Panic:运行时错误:无效的内存地址或空指针解引用

然后,您可能已经安装了旧版本的git-lfs,需要更新它。以下是如何做到的:

首先,查看您安装了什么版本。运行man git-lfs并滚动到底部查看日期。例如,也许它说的是2017年。现在,用这些命令更新您的版本。第一个命令来自这里:https://packagecloud.io/github/git-lfs/install。

curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
sudo apt update
sudo apt install git-lfs

再运行man git-lfs,滚动到底部。我现在认为我的日期是“2021年3月”,而之前是2017年的某个日期。

此外,如果我再次运行sudo apt install git-lfs,它会告诉我:

Git-lfs已经是最新版本(2.13.3)。

所以,git-lfs的更新工作,现在错误已经消失,git lfs修剪工作再次!

我首先在GitHub上的一个评论中记录了这一点:https://github.com/git-lfs/git-lfs/issues/3395#issuecomment-889393444。

引用:

如何缩小。git文件夹 如何缩小。git文件夹 git lfs prune:如何收缩一个git lfs repo Linus Torvalds对git重新打包-a -d——depth=250——window=250: https://gcc.gnu.org/legacy-ml/gcc/2007-12/msg00165.html https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-prune.1.ronn

参见:

My answer: Unix & Linux: All about finding, filtering, and sorting with find, based on file size - see the example near the end, titled "(Figure out which file extensions to add to git lfs next)". Other really useful git lfs info: Great article!: my developer planet: Git LFS: Why and how to use https://git-lfs.github.com/ My repo and notes: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles#how-to-clone-this-repo-and-all-git-submodules ***** [my Q&A] How to use git lfs as a basic user: What is the difference between git lfs fetch, git lfs fetch --all, git lfs pull, and git lfs checkout? [my Q&A] How to resume `git lfs post-checkout` hook after failed `git checkout` Note: for pure synchronization, try FreeFileSync or rsync, as I explain in my answer here. That being said, occasionally I use git for synchronization too, as I explain for my sync_git_repo_from_pc1_to_pc2.sh tool here, and in my other answer here: Work on a remote project with Eclipse via SSH.

5GB vs 200MB有点奇怪。尝试运行git gc。

但是,除非您将存储库拆分为模块,否则您无法减小.git目录的大小。

git repo的每个克隆都是一个完整的存储库,可以充当服务器。这是分布式版本控制的基本原理。

我更多地将git用作同步机制,而不是版本历史。所以我对这个问题的解决方案是确保我所有的当前源都处于令人满意的状态,然后删除.git并重新初始化repo。磁盘空间问题解决。:-)历史消失了:-( 我这样做是因为我的回购是在一个小的u盘上。我不想要也不需要我的全部历史。 如果我有一种截断历史的方法,我就会使用它。

If I were interested in keeping my history I would archive the current repository. At some point later I could clone the original repository, copy over all the changes from the new repo (let's assume I haven't done much (any) renaming or deleteing). And then make one big commit that would represent all the changes made in the new repo as a single commit in the old repo. Is it possible to merge the histories? Maybe if I used a branch and then deleted the objects I didn't need. (I dont' know enough about git internals to start fooling around like that).