想要深入的了解一个工具,就必须完一些比较hack的用法。
为了更好的了解git内部的工作机制,本文就试图通过手动的编辑.git目录下的
文件,来完成一次commit的提交。
.git目录下面的文件
# git --version // 当前git版本
git version 2.3.2 (Apple Git-55)
# ls -1 .git
objects <=
branches description info refs
HEAD <= 存放当前branch的HEAD指针
branches <= 新版git没有使用该目录
config <= 本地git仓库的配置文件
description <= 仅用于gitweb程序
hooks <= 一定在特定时间发生后被调用的脚本,可理解为钩子函数
index <= 文件暂存区信息
info/exclude <= 功能类似.gitignore的全局性排除文件
objects <= 存放真实的数据文件的地方,文件名是SHA1哈希值
refs/heads <= 存放各个分支的HEAD指针
refs/tags <= 存放各个tag的commit指针
refs/remotes <= 存放remote分支的HEAD指针
通过分析可以看到,对于一个普通的commit而言,比较相关的应该是objects和HEAD指针
正常的commit过程
# echo "hello world" > hello
# git add hello
# git commit -am "add file"
在接下来的内容中,将介绍如何在直接编辑.git目录文件的情况下达到与上面命令一样的效果。
手动提交一个commit的步骤
git存储内容时,会有一个头部信息一并被保存。
比如如果是要存储”hello world\n”字符串,可以通过以下ruby脚本得到
|
|
由于hello文件的内容是”hello world\n”,因此可以通过以上脚本首先生成
hello文件内容对应的object文件,文件类型是blob。可以通过一下命令判断生成的文件内容是否正确
# git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
# git cat-file -t 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
以上的object也可以通过一下一条命令得到
# echo "hello world" | git hash-object -w --stdin
随后更新生成.git/index文件
# git update-index --add --cacheinfo 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad hello
以上步骤仅是生成了”hello world” 对应的blob文件,但并没有制定这个内容对应的
文件名叫什么,也就是少了tree类型的object。接着执行一下命令
# git write-tree // 生成指向3b18e5的tree文件,可以用下面两条命令验证
# git cat-file -p 7604755fe13e27f5327d6d13dc6663d44847562d
# git cat-file -t 7604755fe13e27f5327d6d13dc6663d44847562d
一个真正的commit还需要创建一个commit类型的object指向一个特定的tree节点
# git commit-tree 7604755fe -m "add file"
# git cat-file -p df36f6b4884ecf2ec519ddec85f959a83b4adec8
# git cat-file -t df36f6b4884ecf2ec519ddec85f959a83b4adec8
接着更新master的HEAD指针
# git update-ref refs/heads/master df36f6b4884ecf2ec519ddec85f959a83b4adec8
# git log <= 至此就能看到一个完整的commit log
# git checkout hello <= 将hello文件从.git库checkout出来,就算彻底的完成了一个commit了
总的来说
// 第一部分是完成git add的操作
# echo "hello world" | git hash-object -w --stdin
3b18e512dba79e4c8300dd08aeb37f8e728b8dad // 生成blob文件
# git update-index --add --cacheinfo 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad hello
// 第二部分是完成git commit的操作
# git write-tree
7604755fe13e27f5327d6d13dc6663d44847562d // 生成tree文件
# git commit-tree 7604755fe -m "add file"
df36f6b4884ecf2ec519ddec85f959a83b4adec8 // 生成commit
可以看出一个commit对应会有三种object文件生成,每种object文件的命名都是以sha1哈希值为依据的。
除了commit文件由于带有日期信息所以hash值会变化之外,其他两个文件的hash值都是固定不变的。