app/test/target/

#查看大文件
git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')" #删除大文件或者目录
git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch app/test/target/' --prune-empty --tag-name-filter cat -- --all #强制覆盖分支
git push origin ANT01983399_20170525_kbinfocollector --force

最近在开发一个新应用,有一天在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 --allgit gc --prune=now

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

参考资料

Git 内部原理

Git 之术与道 -- 对象

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

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

记一次删除Git记录中的大文件的过程的更多相关文章

  1. 删除Git记录里的大文件

    删除Git记录里的大文件 仓库自身的增长 大多数版本控制系统存储的是一组初始文件,以及每个文件随着时间的演进而逐步积累起来的差异:而 Git 则会把文件的每一个差异化版本都记录在案.这意味着,即使你只 ...

  2. 更好的在 Git 项目中保存大文件(Git LFS 的使用)

    珠玉在前, 大家可以参考 Git LFS的使用 - 简书 为什么要用 Git LFS 原有的 Git 是文本层面的版本控制, 为代码这种小文件设计的, 保存大文件会导致 repo 非常臃肿, push ...

  3. 使用BFG移除git库中的大文件或污点提交

  4. 彻底删除 Git 项目中的文件(BFG Repo-Cleaner 用法)

    一些时候由于开发初期经验不足和贪图方便, 会把一些不应该提交到 Git 的文件上传到 Github, 带来一系列安全问题, 更有可能是把一些大文件上传到 GitHub 上, 导致项目非常臃肿, 每次 ...

  5. 如何永久删除git仓库中敏感文件的提交记录

    如何永久删除git仓库中敏感文件的提交记录 参考: 1. https://help.github.com/articles/remove-sensitive-data/

  6. git 彻底删除历史记录中的大文件

    Reference 大家一定遇到过在使用Git时,不小心将一个很大的文件添加到库中,即使删除,记录中还是保存了这个文件.以后不管是拷贝,还是push/pull都比较麻烦. === 删除大文件方法 方法 ...

  7. git忽略文件并删除git仓库中的文件

    问题描述 不慎在创建.gitignore  文件之前的时候将文件push到了 git仓库,即使之后在.gitignore文件中写入新的过滤规则,这些规则也不会起作用的,git依然会对所有git仓库中的 ...

  8. 如何删除GIT仓库中的敏感信息

    如何删除GIT仓库中的敏感信息 正常Git仓库中应该尽量不包含数据库连接/AWS帐号/巨大二进制文件,否则一旦泄漏到Github,这些非常敏感信息会影响客户的信息安全已经公司的信誉.公司可能其它还有相 ...

  9. Git .gitignore中已添加文件路径,但仍未被忽略

    当文件之前已经被提交到仓库后,后面即使将文件路径添加到 .gitignore ,使用 git status 命令,依然会看到文件被修改. $ git status 位于分支 master 您的分支与上 ...

随机推荐

  1. Linux 如何查看一个进程的堆栈

    有两种方法: 第一种:pstack 进程ID 第二种,使用gdb 然后attach 进程ID,然后再使用命令 thread apply all bt 第三种:strace -f -p pid  该方法 ...

  2. [Docker] Download and Remove Docker Images

    Learn the basics of downloading and pulling Docker images from Docker Hub. Learn the difference betw ...

  3. Android 自定义RadioButton样式

     上面这种3选1的效果如何做呢?用代码写? 其实有更简单的办法,忘了RadioButton有什么特性了吗? 我就用RadioButton实现了如上效果,其实很简单的. 首先定义一张background ...

  4. 一个好汉一个帮:前端UI改造

    今天是周六,继续工作中. 只是,不是自己亲自参与搞代码,让一起好的同事帮我美化界面. 虽说前端,我也可以搞定, but,but呀,所有的工作都让我来搞,实在是太累太烦了. 前端,样式,目前做很多是模仿 ...

  5. DDoS ATTACK PROCESSING APPARATUS AND METHOD IN OPENFLOW SWITCH

    An OpenFlow switch in an OpenFlow environment includes an attack determination module to collect sta ...

  6. .net程序客户端更新方案

    原文:.net程序客户端更新方案 客户端程序一个很大的不便的地方就是程序集更新,本文这里简单的介绍一种通用的客户端更新方案.这个方案依赖程序集的动态加载,具体方案如下: 将程序集存储在一个文件数据库中 ...

  7. Linux硬件信息查询命令

    系统 uname -a              # 查看内核/操作系统/CPU信息 Linux hostname 2.6.18-128.el5 #1 SMP Wed Dec 17 11:41:38 ...

  8. 12个被滥用的Android应用程序权限

    Android应用程序须要权限才干正常运作,只是网络犯罪分子会将其用在个人私利上.来看看最常被要求的权限以及它们会怎样被滥用. .网络定位功能 这代表什么:同意应用程序通过网络定位(像是基地台或无线网 ...

  9. 解决eclipse 保存卡顿的问题

    开发十年,就只剩下这套Java开发体系了 >>>   eclipse 如果启动慢,还可以接收. 可是如果是 保存的时候卡顿, 有时候会 卡顿 3秒-5 秒的,感觉到写代码特别的不顺畅 ...

  10. 微信小程序的轮播图swiper问题

    微信小程序的轮播图swiper,调用后,怎样覆盖系统的 点,达到自己想要的效果 不多说,先上一图望大家多给意见: 这个是效果图: 微信小程序效果图就成这样子: <view class=" ...