在三月九日的不正确的操作导致了release分支代码的丢失,让我认识到了自己对于Git的认识还不够清晰,所以写了这篇文章,总结和分享Git内部存储的知识,加深自己的印象,以及可以方便回忆,有不对的地方欢迎大家指出。
首先,我们可以通过git init命令行初始化,创建git仓库所需要的文件,其中基本目录结构如下
│──COMMIT_EDITMSG // 最近的一次提交的描述信息
│
│──config // git仓库的配置文件
│
│──description // git仓库的描述信息
│
│──HEAD // 包含了一个分支的引用,它是一个指向你正在工作中的本地分支的指针
│
│──index // 暂存区,二进制文件
│
├─hooks // 存放shell脚本,可以通过一些特殊的命令触达脚本
│ applypatch-msg.sample
│ commit-msg.sample
│ fsmonitor-watchman.sample
│ post-update.sample
│ pre-applypatch.sample
│ pre-commit.sample
│ pre-push.sample
│ pre-rebase.sample
│ pre-receive.sample
│ prepare-commit-msg.sample
│ update.sample
│
├─info
│ exclude // 存放git仓库的其他信息
│
├─logs // 保存所有更新引用的记录
│ │ HEAD // 保存最后一次提交的记录
│ │
│ └─refs
│ ├─heads
│ │ master
│ │
│ └─remotes
│ └─origin
│ master
│
├─objects // 所有对象的存储,对象的哈希串值的前两位是文件夹名称,后38位作为对象文件名,包括三类对象commit,tree和blob
│
└─refs // 引用
├─heads // 存储本地所有分支文件最后一次提交的记录
│ master
│
├─remotes // 存储远程仓库的分支文件最后一次提交的记录
│ └─origin
│ master
│
└─tags
再介绍一下工作区,暂存区,版本库的概念
概念
工作区:用来编辑保存项目文件的地方,也是用户能直接操作到的地方,在目录中,除了.git文件外的区域都是工作区。
暂存区:保存了下次将提交的文件列表信息,一般在Git仓库目录中,是一个叫index的文件,也叫暂存区域,可以通过 git add 将工作区的文件添加到暂存区。
版本库:文件中有一个.git隐藏文件,这个不算工作区,而是Git的版本库。也叫本地版本库,之所以说git快,是因为它是分布式版本控制系统,大部分提交都是对本地仓库而言的,不依赖网络,最后一次会推送的到远程仓库。
关系如下图
当对工作区修改(或新增)的文件执行 "git add" 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。
结合几个比较常见的场景
git add .
echo 'hello' > add.txt
echo 'test' > README
git add .
git commit -m 'init'
查看object文件,可以看到objct对象库中新增了4个文件,分别为两个文件blob对象,一个commit对象,一个tree对象,可以知道git根据文件的信息生成的hash值,并且前两位作为目录名。可以通过git cat-file -p/t去查看对象库中的内容和类型,输入命令行cat .git/HEAD根据HEAD指针可以看到refs/heads/master下的hash值存储着commit对象,再查看tree对象的内容,为blob对象,存储着提交的文件的信息。
这几个对象的关系如下
git stash
这个算是一个比较常见的操作了,在基于一个分支进行操作的时候,添加了许多代码,但是我们又要切换到另一个分支去做其他,就可以通过stash暂时的将这些代码存储起来了。
echo 'hello world' >> add.txt
git stash
可以看到refs文件中生成了stash文件,其实git stash也是生成了一个commit对象,查看该对象,存储了修改后add.txt文件,和父节点。git stash生成的commit对象有两个parent,一个是前面一次git commit命令生成的commit,另一个对应于保存到stage中的commit.
回滚代码的正确姿势 git reset
echo 'wrong something' >> add.txt
git add .
git commit -m 'wrong'
git log
git reset --hard [commitId]
可以使用git log找出需要回退commit哈希值,可以使用--hard使得修改的内容不被保存在暂存区里面,可以看到HEAD也是指向回退的commit哈希值。
对象的存储
Git object是通过下面的方式处理并存储在git内部的文件系统中的:
1)首先创建一个header,header的值为 “对象类型 内容长度\0”
2)将header和文件内容连接起来,计算得到其hash值
3)将连接得到的内容压缩
4)将压缩后的内容写入到以 “hash值前两位命令的目录/hash值后38位命令的文件” 中
总结
对于git的三种对象,blob可以理解为存储文件的内容,tree理解为目录的结构,commit理解为存储每一次提交信息。git内部通过object对象,来实现git对版本的控制,理解了git内部的存储可以更好的去管理我们的代码。