从这篇文章开始,分享一下我们在使用git进行版本管理时候的常用的操作!
我们从创建仓库开始~
获取代码仓库的方式有两种,一种是从零开始初始化一个git仓库,另一种是clone他人的仓库。
首先我们创建一个空目录,然后初始化一个仓库。
/ cd# 创建空目录~ mkdir gitlearn~ cd gitlearn# 初始化仓库~ git init .
就这样我们创建一个了本地仓库, 团队合作的时候这样肯定不行的,不行,后面我们后介绍到。或者 我们还可以克隆一个远程仓库
git clone https://gitee.com/fangjiaxiaobai/gitlearn.git
我们来看一下新建的仓库目录下都有什么文件。
➜ gitlearn git:(master) ltotal 0drwxr-xr-x 3 bjhl wheel 96B 7 13 18:23 .drwxr-xr-x 3 bjhl wheel 96B 7 13 18:23 ..drwxr-xr-x 10 bjhl wheel 320B 7 13 18:25 .git➜ gitlearn git:(master) cd .git➜ .git git:(master) ltotal 24drwxr-xr-x 10 bjhl wheel 320B 7 13 18:30 .drwxr-xr-x 3 bjhl wheel 96B 7 13 18:23 ..-rw-r--r-- 1 bjhl wheel 16B 7 13 20:38 COMMIT_EDITMSG # 存储最新一次的commit信息-rw-r--r-- 1 bjhl wheel 23B 7 13 18:23 HEAD ## 当前被检出的分支drwxr-xr-x 2 bjhl wheel 64B 7 13 18:23 branches # 所有分支-rw-r--r-- 1 bjhl wheel 315B 7 13 18:23 config # 包含项目特有的配置选项-rw-r--r-- 1 bjhl wheel 73B 7 13 18:23 description #Git web页面程序使用drwxr-xr-x 12 bjhl wheel 384B 7 13 18:23 hooks # 包含客户端或者服务器端的钩子脚本drwxr-xr-x 3 bjhl wheel 96B 7 13 18:23 info # 包含一个全局性排除文件,用以防止那些不希望被纳入版本管理的文件,.gitignore文件中记录的-rw-r--r-- 1 bjhl wheel 126B 7 13 20:39 index ## 保存暂存区信息(进行stash操作之后出现的)drwxr-xr-x 4 bjhl wheel 128B 7 13 18:23 objects ## 存储所有数据的内容drwxr-xr-x 4 bjhl wheel 128B 7 13 18:23 refs ## 存储指向数据(分支,远程仓库和标签等)的提交对象的指针。
其中有四个条目非常重要,我用 ##
进行了标注。分别是:HEAD
文件,index
文件,objects
目录,refs
目录。
如果你想只是想了解git的话,那么下面的内容就可以忽略了。
我们来搞一点有趣的东西。
objects/
目录下存放的是什么东西? -- Git对象
前面说过,git
是一个内容寻址文件系统。==> git
是一个简单的键值对数据库。
即,你可以向git
仓库中插入任务类型的内容,它会返回一个唯一的键。通过该键可以在任意时刻再次取回该内容。
上面说过,objects
目录下存放的是所有数据的内容。在git
中,数据是什么呢?是我们工作目录的所有文件的快照!
这里我们使用两个还没有学习的命令。
git add .
:将目录中的文件纳入版本库进行管理起来。此时的状态是:已暂存(新建)
。暂存区的目录树会被更新,同时工作区修改(新增的)文件内容被写入到对象库中的一个新文件中,而该对象的id会被记录在暂存区的文件索引中。git commit -m 'xxx'
:将目录中的文件提交到本地版本库。此时的状态是:已提交
。这时,暂存区的目录树写到版本库中,对应的分支会进行相应的更新。
那下面演示一下:objects
目录下存储的文件。
首先,我们查看下object
目录下的所有文件
# 查看objects下的文件~ l .git/objectstotal 0drwxr-xr-x 4 bjhl wheel 128B 7 14 11:30 .drwxr-xr-x 10 bjhl wheel 320B 7 14 11:30 ..drwxr-xr-x 2 bjhl wheel 64B 7 14 11:30 infodrwxr-xr-x 2 bjhl wheel 64B 7 14 11:30 pack# 创建一个文件:test-objects-files-01.txt➜ gitlearn git:(master) sudo touch test-objects-files-01.txt➜ gitlearn git:(master) ✗ l .git/objectstotal 0drwxr-xr-x 4 root wheel 128B 7 14 15:10 .drwxr-xr-x 10 root wheel 320B 7 14 15:10 ..drwxr-xr-x 2 root wheel 64B 7 14 15:10 infodrwxr-xr-x 2 root wheel 64B 7 14 15:10 pack# 修改文件➜ gitlearn git:(master) ✗ sudo vim test-objects-files-01.txt➜ gitlearn git:(master) ✗ l .git/objectstotal 0drwxr-xr-x 4 root wheel 128B 7 14 15:10 .drwxr-xr-x 10 root wheel 320B 7 14 15:10 ..drwxr-xr-x 2 root wheel 64B 7 14 15:10 infodrwxr-xr-x 2 root wheel 64B 7 14 15:10 pack➜ gitlearn git:(master) ✗ git add .➜ gitlearn git:(master) ✗ l .git/objectstotal 0drwxrwxrwx 5 root wheel 160B 7 14 15:34 .drwxrwxrwx 11 root wheel 352B 7 14 15:34 ..drwxr-xr-x 3 bjhl wheel 96B 7 14 15:34 37drwxrwxrwx 2 root wheel 64B 7 14 15:10 infodrwxrwxrwx 2 root wheel 64B 7 14 15:10 pack➜ gitlearn git:(master) ✗ sh opt/util/dirfile.sh ./.git/objects./.git/objects/37/17929ecb3efabae427dbe3725654b3de1a114b # blob类型(文件)# 使用git cat-file来看下一下文件的内容➜ gitlearn git:(master) ✗ git cat-file -p 3717929ecb3efabae427dbe3725654b3de1a114btest - objects - files - 第一行# 我们进行commit➜ gitlearn git:(master) ✗ git commit -m "test-object-files add"[master (root-commit) 1883fd5] test-object-files addCommitter: bjhl <bjhl@bjhldeMacBook-Pro.local># ..省略部分提示1 file changed, 1 insertion(+)create mode 100755 test-objects-files-01.txt# 来看一下objects目录下的文件。➜ gitlearn git:(master) sh opt/util/dirfile.sh ./.git/objects./.git/objects/18/83fd5aefeb430cb25150e531e92e38f0176f0d./.git/objects/37/17929ecb3efabae427dbe3725654b3de1a114b./.git/objects/54/500d5d74f828d1a3193fb685084b87aaf419c7➜ gitlearn git:(master) git cat-file -p 54500d5d74f828d1a3193fb685084b87aaf419c7# 表示存储的事blob格式的文件,test-objects-files-01.txt100755 blob 3717929ecb3efabae427dbe3725654b3de1a114b test-objects-files-01.txt➜ gitlearn git:(master) git cat-file -p 1883fd5aefeb430cb25150e531e92e38f0176f0d# 存储的是 树对象(后面有介绍)tree 54500d5d74f828d1a3193fb685084b87aaf419c7author bjhl <bjhl@bjhldeMacBook-Pro.local> 1594712728 +0800committer bjhl <bjhl@bjhldeMacBook-Pro.local> 1594712728 +0800test-object-files add
git cat-file 这个命令可以实现从
git
仓库中取回数据。-p参数可以自动判断内容的类型。还有一个写入的命令。git hash-object
.下面简单演示一下。
➜ gitlearn git:(master) echo 'test hash-object function' | git hash-object -w --stdin11a5f11388c345846bbaa060a98d8e93a1114e99➜ gitlearn git:(master) git cat-file -p 11a5f11388c345846bbaa060a98d8e93a1114e99test hash-object function➜ gitlearn git:(master) sh opt/util/dirfile.sh ./.git/objects./.git/objects/11/a5f11388c345846bbaa060a98d8e93a1114e99./.git/objects/18/83fd5aefeb430cb25150e531e92e38f0176f0d # commit-id./.git/objects/37/17929ecb3efabae427dbe3725654b3de1a114b # 文件./.git/objects/54/500d5d74f828d1a3193fb685084b87aaf419c7 # 树对象
从上面的例子我们可以看到,git
中含有,blob
commit-id
,tree
,这三种对象。这其实就是 Git
的对象:数据对象,提交对象,树对象。
接下来我们来看一个git
的对象 - 树对象
树对象
树对象能够解决文件名保存的问题,也允许多个文件组织到一起。所有内容均以树对象和数据独享的形式存储,其中树对象对应了UNIX中的目录项,数据对象则大致上对应了inodes
或者文件内容。
一个树对象包含了一条或多条树对象记录,每条记录含有一个指向数据对象或者子树对象的SHA-1
指针,以及相应的模式,类型,文件名信息。
我们再来看一下,刚才的三个objects
下的校验和。
➜ gitlearn git:(master) git cat-file -p 1883fd5aefeb430cb25150e531e92e38f0176f0dtree 54500d5d74f828d1a3193fb685084b87aaf419c7test-object-files add➜ gitlearn git:(master) git cat-file -p 54500d5d74f828d1a3193fb685084b87aaf419c7100755 blob 3717929ecb3efabae427dbe3725654b3de1a114b test-objects-files-01.txt➜ gitlearn git:(master) git cat-file -p 3717929ecb3efabae427dbe3725654b3de1a114btest - objects - files - 第一行
我根据 数对象 1883fd
找到了 54500d
(表示对应的txt文件),然后可以根据txt文件,看到这次提交的内容371792
.如下图。

git工作目录01.png
这时,我们在尝试从修改一下这个文件,进行提交。
➜ gitlearn git:(master) sudo vim test-objects-files-01.txtPassword:➜ gitlearn git:(master) ✗ git add .➜ gitlearn git:(master) ✗ sh opt/util/dirfile.sh ./.git/objects./.git/objects/18/83fd5aefeb430cb25150e531e92e38f0176f0d./.git/objects/37/17929ecb3efabae427dbe3725654b3de1a114b # 文件./.git/objects/54/500d5d74f828d1a3193fb685084b87aaf419c7 # 树对象./.git/objects/11/a5f11388c345846bbaa060a98d8e93a1114e99 # test hash-object function (本次实验可以忽略)## 下面两行是新增的,本次操作生成的。./.git/objects/5b/22ea83f2e8555371b818a7e441d4156795af04 #内容: test - objects - files - 第一行 \n test - objects - files - 第二行## 提交到版本库中。➜ gitlearn git:(master) ✗ git commit -m "test-object-files updated"[master 200e059] test-object-files updated1 file changed, 1 insertion(+)➜ gitlearn git:(master) sh opt/util/dirfile.sh ./.git/objects./.git/objects/18/83fd5aefeb430cb25150e531e92e38f0176f0d # commit - id./.git/objects/37/17929ecb3efabae427dbe3725654b3de1a114b # 文件./.git/objects/54/500d5d74f828d1a3193fb685084b87aaf419c7 # 树对象./.git/objects/11/a5f11388c345846bbaa060a98d8e93a1114e99 # test hash-object function(本次实现忽略)./.git/objects/5b/22ea83f2e8555371b818a7e441d4156795af04 # 文件# 下面两行是新增的,本次操作生成的./.git/objects/20/0e059f0c0b178378aecd6736f384a74d28df94 # commit-id./.git/objects/20/db6c4af7677b7509d2b323b503733405e4987e # 树对象
我们的操作如下:
在git仓库中创建了一个文件,并且将它纳入版本管理,提交到了版本库中 修改了这个问题,并且再次提交到了版本库中。
经过这个两个步骤之后,出现了如下图的 git
树对象。

这时,我们使用 git log
命令来查看日志. 可以发现。
commit 200e059f0c0b178378aecd6736f384a74d28df94 (HEAD -> master)Author: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 16:24:02 2020 +0800test-object-files updatedcommit 1883fd5aefeb430cb25150e531e92e38f0176f0dAuthor: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 15:45:28 2020 +0800test-object-files add
为了便于大家理解,咱们继续修改这个文件,加上第三行数据: test - objects - files - 第三行
:
然后执行
git add .git commit -m "test-object-files updated 02"
然后查看objects
目录下的文件内容:
➜ gitlearn git:(master) sh opt/util/dirfile.sh ./.git/objects./.git/objects/11/a5f11388c345846bbaa060a98d8e93a1114e99./.git/objects/18/83fd5aefeb430cb25150e531e92e38f0176f0d./.git/objects/20/0e059f0c0b178378aecd6736f384a74d28df94./.git/objects/20/db6c4af7677b7509d2b323b503733405e4987e./.git/objects/37/17929ecb3efabae427dbe3725654b3de1a114b./.git/objects/54/500d5d74f828d1a3193fb685084b87aaf419c7./.git/objects/5b/22ea83f2e8555371b818a7e441d4156795af04# 以下三行是本次commit产生的。./.git/objects/a6/2a79d99b37bc5c44ca083cd40f9e4c00aae0bd # commit-id,提交对象./.git/objects/ba/f6b0843a522f8e77f326d2615cb37ed290d06e # 文件,数据对象./.git/objects/c4/3622f1e8e728f88eb11adfee1ea08d41c792a0 # 树对象➜ gitlearn git:(master) git cat-file -p a62a79d99b37bc5c44ca083cd40f9e4c00aae0bdtree c43622f1e8e728f88eb11adfee1ea08d41c792a0parent 200e059f0c0b178378aecd6736f384a74d28df94test-object-files updated 02➜ gitlearn git:(master) git cat-file -p c43622f1e8e728f88eb11adfee1ea08d41c792a0100755 blob baf6b0843a522f8e77f326d2615cb37ed290d06e test-objects-files-01.txt➜ gitlearn git:(master) git cat-file -p baf6b0843a522f8e77f326d2615cb37ed290d06etest - objects - files - 第一行test - objects - files - 第二行test - objects - files - 第三行
做完上面的操作之后,我们来看一下objects
目录下的树结构图。

objects目录总结
作用:存放所有数据的内容。 git
是一个内容跟踪文件管理系统。也就是一个KV
数据库,对应的key-value
放到了objects
目录下,存储形式是一个40
位十六机制的校验和。key
是文件名,value
是文件内容。学习了两个底层命令: git cat-file
和git hash-object
. 更多底层命令可以看下这篇文章。// TODO演示一个文件的三次更新时变化过程,以及各个版本之间的迭代过程,那么如果是多个文件呢? 这里大家可以去试一下,就会更清晰的理解树对象了。(这里演示一个文件并不能完全的解释清树对象这个概念)
Git引用, git工作目录下的refs目录!
➜ gitlearn git:(master) l ./.git/refs/drwxrwxrwx 3 root wheel 96B 7 14 17:02 headsdrwxrwxrwx 2 root wheel 64B 7 14 15:10 tags
heads目录, 存储各个分支的HEAD指针指向的版本。 tags目录, git tag 命令产生的结果。
heads目录
我们还是根据 objects 部门的操作来演示这部分的内容。如下图。

通过查看我们目前是在 a62a79d99b37bc5c44ca083cd40f9e4c00aae0bd
这个版本上。
➜ gitlearn git:(master) cat ./.git/refs/heads/mastera62a79d99b37bc5c44ca083cd40f9e4c00aae0bd
我们可以通过 git update-ref
命令来实现切换版本(回退到制定的版本上)
# 我们先使用 git log 命令来查看一下提交记录commit a62a79d99b37bc5c44ca083cd40f9e4c00aae0bd (HEAD -> master)Author: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 17:02:46 2020 +0800test-object-files updated 02commit 200e059f0c0b178378aecd6736f384a74d28df94Author: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 16:24:02 2020 +0800test-object-files updatedcommit 1883fd5aefeb430cb25150e531e92e38f0176f0dAuthor: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 15:45:28 2020 +0800test-object-files add# 我们要回退到第二个版本上。使用 git update-ref,更新引用。➜ .git git:(master) git update-ref refs/heads/master 200e059f0c0b178378aecd6736f384a74d28df94# 再去查看git的Head目录下的版本号➜ gitlearn git:(master) ✗ cat ./.git/refs/heads/master200e059f0c0b178378aecd6736f384a74d28df94# 这个时候再使用 git log 去看日志:commit 200e059f0c0b178378aecd6736f384a74d28df94 (HEAD -> master)Author: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 16:24:02 2020 +0800test-object-files updatedcommit 1883fd5aefeb430cb25150e531e92e38f0176f0dAuthor: bjhl <bjhl@bjhldeMacBook-Pro.local>Date: Tue Jul 14 15:45:28 2020 +0800test-object-files add# 再去看我们的文件: 是不会更新工作区内容的。➜ gitlearn git:(master) ✗ cat test-objects-files-01.txttest - objects - files - 第一行test - objects - files - 第二行test - objects - files - 第三行# 我们还可以使用 git update-ref 来创建新的分支。➜ gitlearn git:(master) ✗ cd .git➜ .git git:(master) git update-ref refs/heads/test 200e059f0c0b178378aecd6736f384a74d28df94# 查看test分支的日志➜ .git git:(master) git log --pretty=oneline test200e059f0c0b178378aecd6736f384a74d28df94 (HEAD -> master, test) test-object-files updated1883fd5aefeb430cb25150e531e92e38f0176f0d test-object-files add# 这时我们去查看我们所处的分支, 发现我们仍然处在master上,并没有进行切换。➜ .git git:(master) git branch* mastertest
上面的演示中,我们发现了,虽然只是修改的记录的文件,但是并没有对我们工作区的文件进行更新。怎么解决呢?
HEAD文件
HEAD
文件通常是一个符号引用,指向目前所在的分支。所谓符号引用,表示它是一个指向其他引用的指针。
我们查看一下 HEAD
文件中的内容
➜ gitlearn git:(master) ✗ cat .git/HEADref: refs/heads/master
这里呢,有一个命令可以帮助我们实现修改 HEAD
文件的内容
git symbolic-ref
# 查看当前所在的分支➜ gitlearn git:(master) ✗ git symbolic-ref HEADrefs/heads/master# 设置当前所在的分支➜ .git git:(master) git symbolic-ref HEAD refs/heads/test# 已经可以看到我们已经把分支切换到了test➜ .git git:(test)# 我们去看 工作区里的文件,发现并没有变化!➜ .git git:(test) cat ../test-objects-files-01.txttest - objects - files - 第一行test - objects - files - 第二行test - objects - files - 第三行
文件的内容的,修改就必须使用 git checkout
/ git reset
了。
我们回来接着看 refs目录的另一个目录:tags.
tags 是 git tag 命令的产物。
然后,在git中,tags 其实是标签对象,包含了一个标签创建者信息,一个日期,一段注释信息,以及一个指针。标签对象通常指向一个提交对象,而不是一个树对象。永远都会指向同一个提交对象。
标签分为 附注标签 和 轻量标签。
使用命令
# 常见一个轻量标签 -> 一个固定的引用➜ .git git:(test) git update-ref refs/tags/v1.0 1883fd5aefeb430cb25150e531e92e38f0176f0d
创建一个附注标签的时候,git会创建一个标签对象,并记录一个引用来指向该标签对象,而不是直接指向要提交的对象。
➜ .git git:(test) git tag -a v1.1 1883fd5aefeb430cb25150e531e92e38f0176f0d -m '附注标签'# 查看标签对象。➜ .git git:(test) cat refs/tags/v1.13f224b87d02aa196b0b8331aa28389a172e3f723# 查看附注标签详细的内容➜ .git git:(test) git cat-file -p 3f224b87d02aa196b0b8331aa28389a172e3f723object 1883fd5aefeb430cb25150e531e92e38f0176f0dtype committag v1.1tagger bjhl <bjhl@bjhldeMacBook-Pro.local> 1594727545 +0800附注标签
远程引用
在git的引用中,还有一个远程引用。
这个目录会在我们配置远程仓库的时候出现,我们一起配置一下远程仓库
# 配置远程仓库➜ gitlearn git:(master) git remote add origin https://gitee.com/fangjiaxiaobai/gitlearn.git# push 到远程分支。➜ gitlearn git:(master) git push origin masterCounting objects: 6, done.Delta compression using up to 8 threads.Compressing objects: 100% (5/5), done.Writing objects: 100% (6/6), 548 bytes | 548.00 KiB/s, done.Total 6 (delta 0), reused 0 (delta 0)remote: Powered by GITEE.COM [GNK-5.0]To https://gitee.com/fangjiaxiaobai/gitlearn.git* [new branch] master -> master# 查看 .git目录下,出现了remotes➜ gitlearn git:(master) l ./.git/refs/total 8drwxrwxrwx 6 root wheel 192B 7 14 19:58 .drwxrwxrwx 16 root wheel 512B 7 14 19:58 ..drwxrwxrwx 4 root wheel 128B 7 14 19:56 headsdrwxr-xr-x 3 bjhl wheel 96B 7 14 19:58 remotes-rw-r--r-- 1 bjhl wheel 41B 7 14 19:56 stash # 进行git stash命令后出现drwxrwxrwx 4 root wheel 128B 7 14 19:52 tags# 查看对应remote配置文件➜ gitlearn git:(master) cat .git/refs/remotes/origin/master200e059f0c0b178378aecd6736f384a74d28df94
这个校验码表示 ,远程版本库中和本地的版本库中最新一次交互的commit id 。
远程引用和分支之间最主要的区别,就是远程引用是只读的。Git永远不会讲HEAD引用指向该远程引用,因此,不可能通过commit来更新远程引用。
Index文件
这个文件是 git stage
的文件信息存放地,根据该文件中的内容,可以查看当前哪些文件,或者哪些文件发生了变化,而在commit
之后,则会把相应的信息存入 .git/objects
文件夹下。通过 git ls-files -s
可以查 看 index
文件中的 stage
文件的信息。
Index
文件是用二进制存储的,包含有 ctime
和 mtime
时间信息,文件存储的设备信息,磁盘的inode
信息,文件的 mode
信息,UID
,GID
,文件大小,文件的SHA-1
码,flag
,文件的file path
等信息。
各个文件的信息按照一定的排列顺序进行排布。提交的时候,按照这种约定进行解析。
包文件
在文章一开始,我们就说过,git是基于快照的方式来进行版本控制的。这就意味着,我们每进行一次commit操作,git就是存储一份我们所有的文件。那么,一个大项目,那岂不是会占用很大的存储空间??
我们先做个实验。看看git会不会占用很大的存储空间?
首先,我们新建一个仓库
# 创建仓库➜ gitlearn git init .Initialized empty Git repository in /private/tmp/gitlearn/.git/# 添加文件➜ gitlearn git:(master) touch test-package-file➜ gitlearn git:(master) ✗ vim test-package-file➜ gitlearn git:(master) ✗ git add .➜ gitlearn git:(master) ✗ git commit -m "test package file 1"[master (root-commit) 12cbb50] test package file 1Committer: bjhl <bjhl@bjhldeMacBook-Pro.local>1 file changed, 1 insertion(+)create mode 100644 test-package-file# 查看数据对象的id➜ gitlearn git:(master) git cat-file -p master^{tree}100644 blob c2e8644a8379ab0cf4f2bc5a14160d94608502f7 test-package-file# 修改 test ,添加 test package file * 700➜ gitlearn git:(master) vim test-package-file➜ gitlearn git:(master) ✗ vim test-package-file➜ gitlearn git:(master) ✗ git add .➜ gitlearn git:(master) ✗ git commit -m "test package file 2"[master 421177c] test package file 2Committer: bjhl <bjhl@bjhldeMacBook-Pro.local>1 file changed, 740 insertions(+)# 查看第二次提交的数据对象的id➜ gitlearn git:(master) git cat-file -p master^{tree}100644 blob 363980e2f742592594e01648888a661d2d0479b9 test-package-file### 查看两次数据对象的大小➜ gitlearn git:(master) git cat-file -s c2e8644a8379ab0cf4f2bc5a14160d94608502f718➜ gitlearn git:(master) git cat-file -s 363980e2f742592594e01648888a661d2d0479b913338## 查看一下这两次文件存储的文件。➜ gitlearn git:(master) sh /opt/util/dirfile.sh ./.git/objects./.git/objects/12/cbb50359469eb29efe29945a3b18e262f6e862./.git/objects/36/3980e2f742592594e01648888a661d2d0479b9./.git/objects/42/1177c9026e757e7e123b6ee589bcffa067eb96./.git/objects/8e/0703665933215a57787a3117b079d4c8cb1921./.git/objects/c2/e8644a8379ab0cf4f2bc5a14160d94608502f7./.git/objects/c3/1d0ebb18efdfc791a254ab58a01ff2ed0136e4
现在git
中存储了两个文件,分别是我们第一次提交和第二次提交的内容。
如果git
像SVN
一样记录变更的内容的话,是不是更好呢?
git
上本来可以那么做。git
最初向磁盘中存储对象时所使用的格式称为 松散对象格式。但是,git
会时不时的将多个对象打包成一个 称为 "包文件" 的二进制文件。来节省空间和效率。
当仓库中有太多的 松散对象,或者手动执行 git gc
命令,或者向远程服务器执行推送时,git
都会这么做。
# 执行一下 git gc➜ gitlearn git:(master) git gcCounting objects: 6, done.Delta compression using up to 8 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (6/6), done.Total 6 (delta 0), reused 0 (delta 0)➜ gitlearn git:(master) sh /opt/util/dirfile.sh ./.git/objects./.git/objects/info/packs./.git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.idx./.git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.pack
这里生成了一个包文件(.pack
)和一个索引文件(.idx
)。包文件包括了刚才从一个文件系统中移除的所有对象的内容,索引文件包含了文件的偏移信息。我们可以通过偏移文件快速的定位任意一个指定对象。
git
打包对象时,会查找命名以及大小相近的文件,并只保存文件不同版本之间的差异内容。可以使用 git verify-pack
这个底层命令查看已经打包内容:
➜ gitlearn git:(master) git verify-pack -v .git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.idx421177c9026e757e7e123b6ee589bcffa067eb96 commit 238 160 1212cbb50359469eb29efe29945a3b18e262f6e862 commit 190 131 172363980e2f742592594e01648888a661d2d0479b9 blob 13338 80 303c31d0ebb18efdfc791a254ab58a01ff2ed0136e4 tree 45 55 3838e0703665933215a57787a3117b079d4c8cb1921 tree 45 56 438c2e8644a8379ab0cf4f2bc5a14160d94608502f7 blob 18 28 494non delta: 6 objects.git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.pack: ok
其中第三列是在包文件中各个对象的大小,可以看到,树对象是45
字节,数据对象和提交对象,都是原来的大小。
显然这次并没有进行压缩。我们再次修改文件:
# 修改文件:最后一行添加 test package file➜ gitlearn git:(master) vim test-package-file➜ gitlearn git:(master) ✗ git add .➜ gitlearn git:(master) ✗ git commit -m "test package file 3"[master 2ab3a27] test package file 3Committer: bjhl <bjhl@bjhldeMacBook-Pro.local>1 file changed, 1 insertion(+)# 查看目录下的文件➜ gitlearn git:(master) sh /opt/util/dirfile.sh ./.git/objects./.git/objects/2a/b3a275bf16a86f77e632127ccf62bcecf98579./.git/objects/84/003569b2725d79bc209821c9c3dbb5c2ef8a8d./.git/objects/df/e088f704e98a835ed3ca2ce31919947d63f884./.git/objects/info/packs./.git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.idx./.git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.pack# 进行打包➜ gitlearn git:(master) git gcCounting objects: 9, done.Delta compression using up to 8 threads.Compressing objects: 100% (5/5), done.Writing objects: 100% (9/9), done.Total 9 (delta 1), reused 5 (delta 0)➜ gitlearn git:(master) sh /opt/util/dirfile.sh ./.git/objects./.git/objects/info/packs./.git/objects/pack/pack-a30aa1dcd93c0fadc605cf3fb5828e8cf186ef29.idx./.git/objects/pack/pack-a30aa1dcd93c0fadc605cf3fb5828e8cf186ef29.pack# 查看打包的内容➜ gitlearn git:(master) git verify-pack -v .git/objects/pack/pack-a30aa1dcd93c0fadc605cf3fb5828e8cf186ef29.idx2ab3a275bf16a86f77e632127ccf62bcecf98579 commit 238 160 12421177c9026e757e7e123b6ee589bcffa067eb96 commit 238 160 17212cbb50359469eb29efe29945a3b18e262f6e862 commit 190 131 33284003569b2725d79bc209821c9c3dbb5c2ef8a8d tree 45 56 555c31d0ebb18efdfc791a254ab58a01ff2ed0136e4 tree 45 55 6118e0703665933215a57787a3117b079d4c8cb1921 tree 45 56 684dfe088f704e98a835ed3ca2ce31919947d63f884 blob 13356 92 463 # 第三次提交363980e2f742592594e01648888a661d2d0479b9 blob 7 18 666 1 dfe088f704e98a835ed3ca2ce31919947d63f884 # 第二次提交c2e8644a8379ab0cf4f2bc5a14160d94608502f7 blob 18 28 740 # 第一次提交non delta: 8 objectschain length = 1: 1 object.git/objects/pack/pack-a30aa1dcd93c0fadc605cf3fb5828e8cf186ef29.pack: ok
我们可以到看到 第三次提交的内容是 13356
字节,而第二次提交的内容只有 7
个字节。显然,git
这次进行压缩。另外,第三个版本是保存了最新的文件内容,原始版本是以差异方式存在的-- 这是因为大部分情况下,需要快速的访问文件的最新版本。
总结
git
创建仓库两种方式:本地创建,clone
仓库git
仓库的目录结构四种对象: 提交对象,数据对象,树对象,标签对象。 树对象: commit->tree->blob
(文件) !!课后作业:多文件的提交记录!!heads/master
,heads/test/
,tags/
.这几个文件的作用git symbolic
命令,objects
目录 我们通过一个最简单的三次提交来看了一下git
仓库是如何存储数据的。这里,我贴上这张我们操作过程的图,加深一下印象.
refs
: 引用Index
文件. 记录当前哪些文件被修改,新增。包文件:解决git工作目录过大的问题。压缩。
最后
我在【方家小白】等你, 期待与你一起成长!

既然来了,就点个赞再走吧~




