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

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

 

来源:http://www.hollischuang.com/archives/1708

由于某种原因ssh的端口号由原来的22调整为92573,因此在使用git仓库时,地址也相应进行变更,详细说明如下:

1、克隆仓库

由原来的克隆命令

git clone git@192.168.4.46:test.git

变更为

git clone ssh://git@192.168.4.46:92573/test.git

2、修改仓库url地址

如果之前已经克隆过仓库,则只需修改url地址即可。
编辑.git/config文件
将url地址由git@192.168.4.46:test.git 修改为 ssh://git@192.168.4.46:92573/test.git
保存文件
git pull 同步即可。

3、改动的地方

增加了ssh://
增加了端口号
把用户名前面的冒号改成了斜杠(/)

首先从整体上介绍git服务器的工作原理:多个客户端,其中可以包括仓库管理员,通过将自己的ssh公钥上传到服务器仓库keydir目录,统一调用Git专用账号git进行访问git仓库,不同的用户可以根据不同的ssh公钥校验登录,进行项目版本的各种操作,包括clone,commit,push,pull等等。
    下面具体介绍Git服务器的搭建,机器环境:ubuntu12.04,Git服务器软件采用gitosis(https://github.com/res0nat0r/gitosis ,网上很多文章给的地址根本无法使用,都是copy的,不给力啊! )。
一、软件安装
    1.1 安装ssh的服务端和客户端:sudo apt-get install openssh-server openssh-client
    1.2 安装git-core软件,这个是git服务的基础:sudo apt-get install git-core
    1.3 安装 Gitosis,这个是git服务器软件

de>sudo apt-get install python-setuptoolsde>de>
de>de>cd /tmp
de>de>git clone https://github.com/res0nat0r/gitosis.git
de>de>
de>de>cd gitosisde>

sudo python setup.py install 

 

    ok,软件的安装都此结束。
 
二、创建Git专用账号git
    sudo useradd -m -s /bin/bash git    //创建git账号,用户家目录默认为/home/git,shell为/bin/bash
    sudo passwd git    //设置git用户的密码
 
三、初始化Git仓库
    1、初始化Git仓库需要一个管理员账号,如上图所示,管理员也是一个客户端用户,所以需要在客户端主机(我的客户端机器是Win7系统)生成一个用户,并且生成ssh-key。具体操作如下:
        
        在客户端安装ssh服务,包括客户端和服务端。如果你的客户端系统是:
            (1)Windows机器,建议直接安装git bash软件,其中包括了ssh这个服务;
            (2)Linux系统,可以按照先前的命令apt-get install openssh-server openssh-client安装ssh服务。
        
        在客户端机器我的账号是huixiao200068,在用户家目录中生成ssh-key:
            (1)Windows机器,打开git bash软件,输入命令
                        cd ~
                        ssh-keygen -t rsa
            (2)Linux系统,也执行以上相同的命令,即可以生成当前用户的ssh-key。
            执行完成之后,输入命令:ls -al,如果出现了.ssh目录,则表示ssh-key成功创建。
    2、把ssh的公钥上传到服务器,我这里假设上传到服务器的临时目录/tmp。
        scp  ~/.ssh/id_rsa.pub git@server:/tmp    //命令中的server改成你自己服务器的IP
    到此为止,仅仅只是做好了初始化仓库的准备工作,上面的2步操作都是在客户端操作的。下面是关键的第三步——初始化,该步骤在服务器端执行。
 
    3、初始化
        sudo -H -u git gitosis-init < /tmp/id_rsa.pub    //将git仓库目录初始化到了git用户家目录下
        此时,git用户的home目录中将出现repositories目录,该目录为git的仓库。
        修改目录权限:sudo chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update,该步骤尚不清楚其具体的作用,修改了这个目录的权限有啥用呢?望各位指教。
恭喜你,到此为止,你已经创建了一个git仓库,而且账号huixiao200068是git仓库的管理员啦。下面要做的就是仓库配置啦,管理项目和用户。
四、下载仓库配置项目gitosis-admin到本地客户端
        因为git仓库的配置文件都是以git方式来管理的,所以你需要先下载一份到客户端本地 。
        在你的用户目录下面创建一个临时目录work,
        然后 进入到该目录:cd work,
        然后执行命令:
        git clone git@server:gitosis-admin.git    // 命令中的server改成你自己服务器的IP
        执行完成之后,work目录下会生成gitosis-admin目录,目录下面有一个gitosis.conf文件和一个keydir目录,它们将是下面配置任务的主要操作对象,请牢记它们的位置。
五、新建项目
       1、修改配置文件gitosis.conf,增加如下内容。
            [group first-pro]    //用户组名
            members = huixiao200068    //成员名,多个成员可以用空格隔开
            writable = first-pro    //项目名及其用户对于此项目的权限,目前是可写
        2、创建项目目录 mkdir first-pro
             初始化该目录 cd first-pro;    git init
             添加远程仓库 git remote add origin git@SERVER:first-pro.git
             创建工程文件 touch 1.txt
             添加工程文件到本地仓库中 git add ./1.txt
             提交整个项目到本地仓库 git commit -m “comment”
             提交整个项目到远程仓库 git push origin master    //只有这样子,团队其他开发人员才能看到你修改的代码
             查看提交日志 git log
             查看本地库当前的状态,比如是否有文件需要提交。 git status
        新的项目仓库已经生成,其他开发人员可以git clone 命令从服务器上下载一份工程到自己的本地机器上,协同开发啦!
 
六、新建用户

        关于上一节最后提到的内容,“其他开发人员”,他们是需要管理员来增加和配置的,这一节主要讲怎么添加用户。

(1)客户端操作:
        首先要生成ssh-key,方法和上述说明的一样。
        cd ~
        ssh-keygen -t rsa
        然后一直回车,就OK。然后将生成的id_rsa.pub文件传给GIT服务器管理员
 
(2)服务器端操作:
        管理员将客户上传的id_rsa.pub文件移到gitosis-admin/keydir目录中,并且改名为CLIENT_NAME.pub。注意:如果客户端如下图所示
        ,则CLIENT_NAME为sean@bogon,否则后续操作会出现“Repository read access denied”的错误。
         给项目first-pro增加新的开发者,编辑gitosis.conf文件,vi gitosis.conf。
          [group first-pro]    //用户组名
          members = huixiao200068 sean@bogon    //成员名,多个成员可以用空格隔开
          writable = first-pro    //项目名及其用户对于此项目的权限,目前是可写
         提交修改的管理文件:
            git commit -a -m “add user sean@bogon”
            git push origin master
完成上述2步之后,即可以使用该账号共同开发项目first-pro啦!
        cd ~
        git clone git@SERVER:first-pro.git    //克隆项目到本地
        ……    //do anything you want to do
        commit -am “comment”
        commit push origin master
link: http://blog.163.com/xiaohui_1123@126/blog/static/398052402012102751349705/

Git Protocol

A guide for programming within version control.

Maintain a Repo

  • Avoid including files in source control that are specific to your development machine or process.
  • Delete local and remote feature branches after merging.
  • Perform work in a feature branch.
  • Rebase frequently to incorporate upstream changes.
  • Use a pull request for code reviews.

Write a Feature

Create a local feature branch based off master.

git checkout master
git pull
git checkout -b <branch-name>

Rebase frequently to incorporate upstream changes.

git fetch origin
git rebase origin/master

Resolve conflicts. When feature is complete and tests pass, stage the changes.

git add --all

When you’ve staged the changes, commit them.

git status
git commit --verbose

Write a good commit message. Example format:

Present-tense summary under 50 characters

* More information about commit (under 72 characters).
* More information about commit (under 72 characters).

http://project.management-system.com/ticket/123

If you’ve created more than one commit, use a rebase to squash them into cohesive commits with good messages:

git rebase -i origin/master

Share your branch.

git push origin <branch-name>

Submit a GitHub pull request.

Ask for a code review in the project’s chat room.

Review Code

A team member other than the author reviews the pull request. They follow Code Review guidelines to avoid miscommunication.

They make comments and ask questions directly on lines of code in the GitHub web interface or in the project’s chat room.

For changes which they can make themselves, they check out the branch.

git checkout <branch-name>
./bin/setup
git diff staging/master..HEAD

They make small changes right in the branch, test the feature on their machine, run tests, commit, and push.

When satisfied, they comment on the pull request Ready to merge.

Merge

Rebase interactively. Squash commits like “Fix whitespace” into one or a small number of valuable commit(s). Edit commit messages to reveal intent. Run tests.

git fetch origin
git rebase -i origin/master

Force push your branch. This allows GitHub to automatically close your pull request and mark it as merged when your commit(s) are pushed to master. It also makes it possible to find the pull request that brought in your changes.

git push --force origin <branch-name>

View a list of new commits. View changed files. Merge branch into master.

git log origin/master..<branch-name>
git diff --stat origin/master
git checkout master
git merge <branch-name> --ff-only
git push

Delete your remote feature branch.

git push origin --delete <branch-name>

Delete your local feature branch.

git branch --delete <branch-name>

link: https://github.com/thoughtbot/guides/tree/master/protocol/git

问题:Git仓库中的.gitignore文件失效

解决方案:

曲线救国,在每个clone下来的仓库中手动设置不要检查特定文件的更改情况,命令如下:

git update-index –assume-unchanged PATH

在PATH处输入要忽略的文件。

注:

建立.gitignore文件,写入文件名或目录名,编译器就可以忽略这些文件的修改,例如:

.gitignore .classpath .project bin/

在我的情况里,这些文件一般是载入工程时,Eclipse自动修改的文件,当然还有.gitignore文件本身。