记一次删除git记录中的大文件的过程(代码片段)

author author     2022-12-11     154

关键词:

记一次删除Git记录中的大文件的过程

最近在开发一个新应用,有一天在gitlab上clone代码的时候发现我的应用竟然有170+M,明明是一个全新的应用,代码都没有几行呢,为什么会有这么大呢?

后来经过了解Git的原理,解决了这个问题,把相关内容记录下来。分享一下。

Why


我的一个新应用竟然要170+M,这是打死我我也不会信的,于是就开始分析为什么会这么大。

step 1. 把代码拉到本地


git clone git@github.com:hollischuang/Architecture-Evolution.git

只是用这个地址举例,实际并不是这个项目。

step 2. 查看哪个文件占用的空间比较大

$cd Architecture-Evolution
$du -d 1 -h
 174M   ./.git
 264K   ./test
 96K    .

于是,发现是.git目录自己就占用了174M,了解Git的人都知道,.git目录是git自己生成的,记录了git仓库的相关信息的。看到这里其实并不难知道原因。

Git 维护着一个微型的文件系统,其中的文件也被称作数据对象。所有的数据对象均存储于项目下面的 .git/objects中。

经过我的验证,确实是.git/objects这个文件夹中的文件占了磁盘上174M的空间。

也就是说,只要我有一次将一个大文件误提交了,那么即使我后面把它删除了,但是,实际上在.git中,这个文件还是存在的,虽然我们可能再也不需要他了,但是他还在那里默默的存在着。。。

Git与大部分版本控制系统的差别是很大的,比如Subversion、CVS、Perforce、Mercurial 等等,使用的是“增量文件系统” (Delta Storage systems), 就是说它们存储每次提交(commit)之间的差异。Git正好与之相反,它会把你的每次提交的文件的全部内容(snapshot)都会记录下来。这会是在使用Git时的一个很重要的理念。

也就是说,如果我又一次把一个大文件务提交到git仓库中了,那么,下次提交时,即使你只改动了某个文件的一行内容,Git 也会生成一个全新的对象来存储新的文件内容。

因为以上两个特性,我回想起我的一次手残行为: 刚刚创建一个应用之后,我快速的写完代码,编译,运行,发现没啥问题之后,我准备先把他发布掉,于是我开始创建git仓库,并尝试把代码提交上去,这时我并没有创建.gitignore文件,我直接git add .git commit -m ‘init‘ git push一气呵成的执行了熟悉的操作。

相信聪明的人已经发现了,逗比啊,我在编译代码之后,会有很多jar被我down到target目录下。我直接git add.把target下面的jar包,war包等这些也直接提交了。。。虽然后面我意识到,并且删除了这些文件,然后再次提交,但是由于刚我们说过的原因,这些文件依然占用着我的空间。。。

更多关于git的原理内容参见:Git 内部原理

How


问题已经定位到了,接下来就是要解决问题了。如果对git的原理及命令了解的比较多的话,这个问题还是比较好解决的,由于当时博主并不十分了解git的原理,所以做了一些知识储备之后才开始动手的。(Git 之术与道 -- 对象、为什么你的 Git 仓库变得如此臃肿)

Step 1 查看哪些历史提交过文件占用空间较大

使用以下命令可以查看占用空间最多的五个文件:

git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk ‘print$1‘)"

rev-list命令用来列出Git仓库中的提交,我们用它来列出所有提交中涉及的文件名及其ID。 该命令可以指定只显示某个引用(或分支)的上下游的提交。

--objects:列出该提交涉及的所有文件ID。

--all:所有分支的提交,相当于指定了位于/refs下的所有引用。

verify-pack命令用于显示已打包的内容。

step 2. 重写commit,删除大文件

使用以下命令,删除历史提交过的大文件:

git filter-branch --force --index-filter ‘git rm -rf --cached --ignore-unmatch big-file.jar‘ --prune-empty --tag-name-filter cat -- --all

上面脚本中的big-file.jar请换成你第一步查出的大文件名,或者这里直接写一个目录。

filter-branch命令可以用来重写Git仓库中的提交

--index-filter参数用来指定一条Bash命令,然后Git会检出(checkout)所有的提交, 执行该命令,然后重新提交。

--all参数表示我们需要重写所有分支(或引用)。

在重写提交的过程中,会有以下日志输出:

Rewrite 6cdbb293d453ced07e6a07e0aa6e580e6a5538f4 (266/266)
# Ref ‘refs/heads/master‘ was rewritten

如果显示 xxxxx unchanged, 说明repo里没有找到该文件, 请检查路径和文件名是否正确,重复上面的脚本,把所有你想删除的文件都删掉。

step 3. 推送修改后的repo

以强制覆盖的方式推送你的repo, 命令如下:

git push origin master --force

step 4. 清理和回收空间

虽然上面我们已经删除了文件, 但是我们的repo里面仍然保留了这些objects, 等待垃圾回收(GC), 所以我们要用命令彻底清除它, 并收回空间,命令如下:

rm -rf .git/refs/original/

git reflog expire --expire=now --all

git gc --prune=now

至此,我们已经彻底的删除了我们不想要的文件。

参考资料


Git 内部原理

Git 之术与道 -- 对象

为什么你的 Git 仓库变得如此臃肿

Git如何永久删除文件(包括历史记录)

技术图片

git进阶|03-如何彻底删除git中的大文件(代码片段)

...t管理了一个大文件的时候,如果将该文件从文件系统删除之后再commit到仓库,这个时候仓库中依然有该文件的记录。这样会导致.git仓库一直比较大,所以,如果确保不会回退到之前的版本了,那么可以将该大... 查看详情

git进阶|03-如何彻底删除git中的大文件(代码片段)

...t管理了一个大文件的时候,如果将该文件从文件系统删除之后再commit到仓库,这个时候仓库中依然有该文件的记录。这样会导致.git仓库一直比较大,所以,如果确保不会回退到之前的版本了,那么可以将该大... 查看详情

记一次bindissue解决过程(代码片段)

在IBM的大机测试环境下,编辑好的程序已经通过编译,并且拷贝到了RND的目录中。但是用之前能够正常BIND其它DB2程序的JCL去做RND环境下的BIND的时候,总是BIND错误。通过查看JCL的错误信息,定位到如下的SQL错误......SQLCODE=-206SQLSTA... 查看详情

linux学习记录:记一次手动编译安装组件的过程(代码片段)

linux学习记录:记一次手动编译安装组件的过程  本次安装的组件为ApacheHTTPServer,本篇笔记中记录了全部过程,以供参考。  apache组件的依赖项比较多,所以安装起来会繁杂一点,不过流程都是大同小异... 查看详情

linux学习记录:记一次手动编译安装组件的过程(代码片段)

linux学习记录:记一次手动编译安装组件的过程  本次安装的组件为ApacheHTTPServer,本篇笔记中记录了全部过程,以供参考。  apache组件的依赖项比较多,所以安装起来会繁杂一点,不过流程都是大同小异... 查看详情

git填坑记录----记一次git低版本引发的问题(代码片段)

git版本略低,引发的血案原本部署在阿里云上的项目,准备放到内网jenkins上发布,配置完成后发版,jenkins变红了,详细错误信息如下ERROR:Errorcloningremoterepo‘origin‘hudson.plugins.git.GitException:Command"gitfetch--tags--progresshttps://gitlab.... 查看详情

记一次uboot升级过程的两个坑(代码片段)

背景之前做过一次uboot的升级,当时留下了一些记录,本文摘录其中比较有意思的两个问题。启动失败问题问题简述uboot代码中用到了一个库,考虑到库本身跟uboot版本没什么关系,就直接把旧的库文件拷贝过来使用。结果编译链... 查看详情

golang记一次datarace排查过程(代码片段)

golang记一次datarace排查过程背景datarace现场解决思路经验总结datarace在写并发代码时候经常遇到,相关基础概念的介绍可以参考之前一篇文章:golangdatarace竞态条件https://louyuting.blog.csdn.net/article/details/103606831这篇文章主要是... 查看详情

记一次简单的清理挖矿程序过程(代码片段)

收到告警信息,有一台服务器被攻击,留下了挖矿程序。清理过程如下:1、top命令找到可疑程序:2、找到之后,就把这个程序kill掉了,但是过了一会儿,发现程序又自动起来了3、怀疑有定时任务,于是查看定时配置,发现了... 查看详情

记一次socket.io的debug记录(代码片段)

背景:  项目开发客服聊天系统,使用socket.io进行开发,前端采用vue-element-admin,后端语言php,项目在本地运行功能正常,但是发布到测试环境的时候,socket的连接一直不成功,可以成功返回socketid,但是请求时并没有将sid作... 查看详情

重写azuredevopsserver(tfs)中的git版本记录(代码片段)

....应用场景一:修改某开发人员的电子邮件3.应用场景二:删除服务器中的大文件4.用户场景三:整理代码库的目录结构4.常见问题  1.系统提示![rejected],没有权限  2.其他开发人员拉取(PULL)代码时,出现错误refusingt... 查看详情

git如何删除历史记录中的大文件详解

...ame-filtercat----all其中, path-to-your-remove-file 就是你要删除的文件的相对路径(相对于git仓库的跟目录),替换成你要删除的文件即可.注意一点,这里的文件或文件夹,都不能以'/'开头,否则文件或文件夹会被认为是从git的... 查看详情

记一次xmrig挖矿木马排查过程(代码片段)

问题现象Linux服务器收到报警信息,主机CPU跑满。自动创建运行Docker容器xmrig,导致其他运行中容器被迫停止。问题原因通过top命令可以看到有一个xmrig进程占用了99%的CPU。经定位,该进程是一个挖矿木马程序,通过上述截图可以... 查看详情

记一次从sqlserver中图片二进制流还原回图片的开发过程(代码片段)

 背景:最近在客户现场做项目上线时,需要数据迁移工作。客户之前用的一个BS桌面应用,其中关于图片的存储全部以二进制流的方式写入到SqlServer数据库表中的某个字段中,如下图所示,由于新开发应用采... 查看详情

记一次改造react脚手架的过程(代码片段)

公司突然组织需要重新搭建一个基于node的论坛系统,前端采用react,上网找了一些脚手架,或多或少不能满足自己的需求,最终在基于YeoMan的react脚手架generator-react-webpack上搭建改造,这里作为记录。代码在这... 查看详情

记一次md5妙用(代码片段)

记一次MD5妙用最近项目组中在做历史记录的改造工作,主持讨论了多次,但每次讨论完都觉的很完美了,但实际在写这部分逻辑的时候还是会发现一些问题出来,很难受,反反复复的暴露智商是硬伤,人艰不拆,暂先不扯这些真... 查看详情

text删除意外提交给git的大文件(代码片段)

查看详情

记一次java程序从6h到30min的优化过程(代码片段)

背景我们有一个java系统,需要从固定的文件目录中读取csv格式的文件,然后将文件内容读取后在进行一系列的转换以及数据库操作,包含文件内容转换为java对象、内容去重、查询oracle、生成目标数据集合、数据存入o... 查看详情