IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    三个 Git 问题(上)

    子龙山人发表于 2015-09-19 14:44:31
    love 0

    本系列文章主要回答三个 Git 问题:

    1. 为什么不推荐用 Git 保存二进制大文件?
    2. 如果两个 Branch 修改同一个文件的同一行代码,各自 Commit 一次,在 Merge 的时候为什么不会有冲突?
    3. 同样是两个 Branch 修改同一个文件的同一行代码,各自 Commit 一次,在 Rebase 的时候为何有一个 Commit 会被优化掉?

    要回答好这三个问题,我们需要理解 Git 的内部工作原理。

    在保存文件历史的时候,Git 跟 SVN 最大的不一样在于,它保存的不是文件的 Diff 而是整个文件的快照。

    什么是文件快照

    mkdir testGit
    cd testGit
    echo "hello" >> a.txt
    echo "world" >> b.txt
    git init
    git add .
    

    此时我们在 testGit 目录下面新建了一个 Git 仓库,并且新建了两个 txt 文件。此时我们可以在.git/objects 目录下面找到这两个文件。当调用完 git add .之后, Git 会对当前目录下面的所有文件进行“快照”并保存。

    大概就是 a.txt 和 b.txt 经过 zlib 压缩后,使用 SHA-1 算法把文件内容和文件头生成一个 40 位的 hash 值,并且使用此 hash 值的前两个字符作为目录,后面 38 位作为文件名来保存压缩后的文件对象。

    我们可以通过 find 命令来查找.git/objects 目录下面所有存储的对象:

    find .git/objects -type f
    

    下图是输出: 2015-09-12-three-git-question_find-objects-1.png

    a.txt 和 b.txt 的文件内容存储在 628ccd10742baea8241c5924df992b5c019f71 和 013625030ba8dba906f756967f9e9ca394464a 文件中。

    我们可以通过 git cat-file -p [hash value]来查看保存的对象值(注意需要加上文件目录得到完整的 hash 值)

    git cat-file -p cc628ccd10742baea8241c5924df992b5c019f71
    git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
    

    下图是输出: 2015-09-12-three-git-question_cat-file-1.png

    这里的 hash 值所代表的文件就是文件快照。此时,如果我们 commit 的话,会生成新的 hash 值对象。

    git commmit -m 'first commit'
    find .git/objects -type f
    

    下图是输出: 2015-09-12-three-git-question_cat-file-2.png

    这里的 18eb80fbbbf9160491c007668d5298f1e86cd40a 和 f6042cce01150551255ec1e892d04b1c129a5fbd 是新生成的对象。

    我们用 git cat-file -p 来看看它具体是什么?

    git cat-file -p 18eb80fbbbf9160491c007668d5298f1e86cd40a
    

    下图是输出: 2015-09-12-three-git-question_cat-file-3.png

    git cat-file -p f6042cce01150551255ec1e892d04b1c129a5fbd
    

    下图是输出: 2015-09-12-three-git-question_cat-tree-1.png

    这里的 18eb80fbbbf9160491c007668d5298f1e86cd40a 表示的是当前目录树的 hash 值,里面包含了每个文件的权限,类型,hash code 和名字信息。

    而 f6042cce01150551255ec1e892d04b1c129a5fbd 则是我们的 commit 号,也就是平常我们用 git log 得到的内容。

    如果此时我修改 a.txt 或者 b.txt,新修改后的文件会再用 zlib 压缩后生成一个 hash code 来当作名字。

    为什么 Git 切换分支特别快?

    因此要把版本的历史 checkout 到工作区,Git 只需要把 commit 对应的目录树的所有的文件解压缩即可。

    如果是 svn,则需要找到一个参考版本,然后不断地应用 Diff 才来得到相应的分支,这个速度是非常之慢的。

    为什么不推荐使用 Git 保存二进制大文件

    因为 Git 使用的是文件快照来保存版本历史,但是二进制文件在压缩上几乎没有效果,所以,二进制文件只要有一点点修改,保存的却是整个文件内容。

    所以大的二进制文件是禁止放到 Git 里面去管理的。那么多大才算大呢?一般的标准是单个二进制文件的大小不要超过 100kb。

    参考文献

    • Git - Git Objects


沪ICP备19023445号-2号
友情链接