第14章 Git库管理

版本库管理?那不是管理员要干的事情么,怎么放在“Git独奏”这一部分了?

没有错,这是因为对于Git,每个用户都是自己版本库的管理员,所以在“Git独奏”的最后一章,我们来谈一谈Git版本库管理的问题。如果下面的问题您没有遇到或不感兴趣,大可以放心地跳过这一章。

从网上克隆来的版本库,为什么对象库中找不到对象文件?而且引用目录里也看不到所有的引用文件?

不小心添加了一个大文件到Git库中,用重置命令丢弃了包含大文件的提交,可是版本库不见小,大文件仍在对象库中。

本地版本库的对象库里的文件越来越多,这可能导致Git性能的降低。

14.1 对象和引用哪里去了

从Github上克隆一个示例版本库,这个版本库在“第11章历史穿梭”一章就已经克隆过一次了,现在要重新克隆一份。为了和原来的克隆相区别,我们将克隆到另外的目录。执行下面的命令。


$cd/path/to/my/workspace/

$git clone git://github.com/ossxp-com/gitdemo-commit-tree.git i-am-admin

Cloning into i-am-admin…

remote:Counting objects:65,done.

remote:Compressing objects:100%(53/53),done.

remote:Total 65(delta 8),reused 0(delta 0)

Receiving objects:100%(65/65),78.14 KiB|42 KiB/s,done.

Resolving deltas:100%(8/8),done.


进入克隆的版本库,使用git show-ref命令看看所包含的引用。


$cd/path/to/my/workspace/i-am-admin

$git show-ref

6652a0dce6a5067732c00ef0a220810a7230655e refs/heads/master

6652a0dce6a5067732c00ef0a220810a7230655e refs/remotes/origin/HEAD

6652a0dce6a5067732c00ef0a220810a7230655e refs/remotes/origin/master

c9b03a208288aebdbfe8d84aeb984952a16da3f2 refs/tags/A

1a87782f8853c6e11aacba463af04b4fa8565713 refs/tags/B

9f8b51bc7dd98f7501ade526dd78c55ee4abb75f refs/tags/C

887113dc095238a0f4661400d33ea570e5edc37c refs/tags/D

6decd0ad3201ddb3f5b37c201387511059ac120c refs/tags/E

70cab20f099e0af3f870956a3fbbbda50a17864f refs/tags/F

96793e37c7f1c7b2ddf69b4c1e252763c11a711f refs/tags/G

476e74549047e2c5fbd616287a499cc6f07ebde0 refs/tags/H

76945a15543c49735634d58169b349301d65524d refs/tags/I

f199c10c3f1a54fa3f9542902b25b49d58efb35b refs/tags/J


其中以refs/heads/开头的是分支;以refs/remotes/开头的是远程版本库分支在本地的映射,这会在后面的章节中介绍;以refs/tags/开头的是里程碑。按照之前的经验,在.git/refs目录下应该有这些引用所对应的文件才是。看看都在么?


$find.git/refs/-type f

.git/refs/remotes/origin/HEAD

.git/refs/heads/master


为什么才有两个文件?实际上当运行下面的命令后,引用目录下的文件会更少:


$git pack-refs—all

$find.git/refs/-type f

.git/refs/remotes/origin/HEAD


那么本应该出现在.git/refs/目录下的引用文件都到哪里去了呢?答案是这些文件被打包了,放到一个文本文件.git/packed-refs中了。查看一下这个文件中的内容。


$head-5.git/packed-refs

pack-refs with:peeled

6652a0dce6a5067732c00ef0a220810a7230655e refs/heads/master

6652a0dce6a5067732c00ef0a220810a7230655e refs/remotes/origin/master

c9b03a208288aebdbfe8d84aeb984952a16da3f2 refs/tags/A

^81993234fc12a325d303eccea20f6fd629412712


再来看看Git的对象(commit、blob、tree、tag)在对象库中的存储。通过下面的命令,会发现对象库也不是原来熟悉的模样了。


$find.git/objects/-type f

.git/objects/pack/pack-969329578b95057b7ea1208379a22c250c3b992a.idx

.git/objects/pack/pack-969329578b95057b7ea1208379a22c250c3b992a.pack


对象库中只有两个文件,本应该一个一个独立保存的对象都不见了。您应该能够猜到,所有的对象文件都被打包到这两个文件中了,其中以.pack结尾的文件是打包文件,以.idx结尾的是索引文件。打包文件和对应的索引文件只是扩展名不同,都保存于.git/objects/pack/目录下。Git对于以SHA1哈希值作为目录名和文件名保存的对象有一个术语,称为松散对象。松散对象打包后会提高访问效率,而且不同的对象可以通过增量存储节省磁盘空间。

通过Git一个底层的命令可以查看索引中包含的对象:


$git show-index<.git/objects/pack/pack-*.idx|head-5

661 0cd7f2ea245d90d414e502467ac749f36aa32cc4(0793420b)

63020 1026d9416d6fc8d34e1edfb2bc58adb8aa5a6763(ed77ff72)

3936 15328fc6961390b4b10895f39bb042021edd07ea(13fb79ef)

3768 1a588ca36e25f58fbeae421c36d2c39e38e991ef(86e3b0bd)

2022 1a87782f8853c6e11aacba463af04b4fa8565713(e269ed74)


为什么克隆远程版本库就可以产生对象库打包及引用打包的效果呢?这是因为克隆远程版本库时,使用了“智能”的通信协议,远程Git服务器将对象打包后传输给本地,形成本地版本库的对象库中的一个包含所有对象的包及索引文件。无疑这样的传输方式——按需传输、打包传输——效率最高。

克隆之后的版本库在日常的提交中,产生的新对象仍旧以松散对象存在,而不是以打包的形式,日积月累会在本地版本库的对象库中形成大量的松散文件。松散对象只是进行了压缩,而没有(打包文件才有的)增量存储的功能,会浪费磁盘空间,也会降低访问效率。更为严重的是一些非正式的临时对象(暂存区操作中产生的临时对象)也以松散对象的形式保存在对象库中,造成磁盘空间的浪费。下一节就着手处理临时对象的问题。