# 从创建仓库开始
这篇文章分享一下我们在使用 git 进行版本管理时候的常用的操作!我们从创建仓库开始~
获取代码仓库的方式有两种,一种是从零开始初始化一个 git 仓库,另一种是 clone 他人的仓库。
首先我们创建一个空目录,然后初始化一个仓库。
1 2 3 4 5 6
| / cd # 创建空目录 ~ mkdir gitlearn ~ cd gitlearn # 初始化仓库 ~ git init .
|
就这样我们创建一个了本地仓库, 团队合作的时候这样肯定不行的,不行,后面我们后介绍到。
或者 我们还可以克隆一个远程仓库
1
| git clone https://gitee.com/fangjiaxiaobai/gitlearn.git
|
我们来看一下新建的仓库目录下都有什么文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ➜ gitlearn git:(master) l total 0 drwxr-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) l total 24 drwxr-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
目录下的所有文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| # 查看objects下的文件 ~ l .git/objects total 0 drwxr-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 info drwxr-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/objects total 0 drwxr-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 info drwxr-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/objects total 0 drwxr-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 info drwxr-xr-x 2 root wheel 64B 7 14 15:10 pack ➜ gitlearn git:(master) ✗ git add . ➜ gitlearn git:(master) ✗ l .git/objects total 0 drwxrwxrwx 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 37 drwxrwxrwx 2 root wheel 64B 7 14 15:10 info drwxrwxrwx 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 3717929ecb3efabae427dbe3725654b3de1a114b test - objects - files - 第一行
# 我们进行commit ➜ gitlearn git:(master) ✗ git commit -m "test-object-files add" [master (root-commit) 1883fd5] test-object-files add Committer: 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.txt 100755 blob 3717929ecb3efabae427dbe3725654b3de1a114b test-objects-files-01.txt
➜ gitlearn git:(master) git cat-file -p 1883fd5aefeb430cb25150e531e92e38f0176f0d # 存储的是 树对象(后面有介绍) tree 54500d5d74f828d1a3193fb685084b87aaf419c7 author bjhl <bjhl@bjhldeMacBook-Pro.local> 1594712728 +0800 committer bjhl <bjhl@bjhldeMacBook-Pro.local> 1594712728 +0800
test-object-files add
|
git cat-file
这个命令可以实现从 git
仓库中取回数据。-p 参数可以自动判断内容的类型。还有一个写入的命令。 git hash-object
. 下面简单演示一下。
1 2 3 4 5 6 7 8 9
| ➜ gitlearn git:(master) echo 'test hash-object function' | git hash-object -w --stdin 11a5f11388c345846bbaa060a98d8e93a1114e99 ➜ gitlearn git:(master) git cat-file -p 11a5f11388c345846bbaa060a98d8e93a1114e99 test 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
下的校验和。
1 2 3 4 5 6 7 8 9 10
| ➜ gitlearn git:(master) git cat-file -p 1883fd5aefeb430cb25150e531e92e38f0176f0d tree 54500d5d74f828d1a3193fb685084b87aaf419c7 test-object-files add
➜ gitlearn git:(master) git cat-file -p 54500d5d74f828d1a3193fb685084b87aaf419c7 100755 blob 3717929ecb3efabae427dbe3725654b3de1a114b test-objects-files-01.txt
➜ gitlearn git:(master) git cat-file -p 3717929ecb3efabae427dbe3725654b3de1a114b test - objects - files - 第一行
|
我根据 数对象 1883fd
找到了 54500d
(表示对应的 txt 文件),然后可以根据 txt 文件,看到这次提交的内容 371792
. 如下图。
这时,我们在尝试从修改一下这个文件,进行提交。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ➜ gitlearn git:(master) sudo vim test-objects-files-01.txt Password: ➜ 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 updated 1 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
命令来查看日志。可以发现。
1 2 3 4 5 6 7 8 9 10 11
| commit 200e059f0c0b178378aecd6736f384a74d28df94 (HEAD -> master) Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 16:24:02 2020 +0800
test-object-files updated
commit 1883fd5aefeb430cb25150e531e92e38f0176f0d Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 15:45:28 2020 +0800
test-object-files add
|
为了便于大家理解,咱们继续修改这个文件,加上第三行数据: test - objects - files - 第三行
:
然后执行
1 2
| git add . git commit -m "test-object-files updated 02"
|
然后查看 objects
目录下的文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ➜ 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 a62a79d99b37bc5c44ca083cd40f9e4c00aae0bd tree c43622f1e8e728f88eb11adfee1ea08d41c792a0 parent 200e059f0c0b178378aecd6736f384a74d28df94 test-object-files updated 02
➜ gitlearn git:(master) git cat-file -p c43622f1e8e728f88eb11adfee1ea08d41c792a0 100755 blob baf6b0843a522f8e77f326d2615cb37ed290d06e test-objects-files-01.txt ➜ gitlearn git:(master) git cat-file -p baf6b0843a522f8e77f326d2615cb37ed290d06e test - 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 目录!
1 2 3
| ➜ gitlearn git:(master) l ./.git/refs/ drwxrwxrwx 3 root wheel 96B 7 14 17:02 heads drwxrwxrwx 2 root wheel 64B 7 14 15:10 tags
|
- heads 目录,存储各个分支的 HEAD 指针指向的版本。
- tags 目录,git tag 命令产生的结果。
# heads 目录
我们还是根据 objects 部门的操作来演示这部分的内容。
如下图。
通过查看我们目前是在 a62a79d99b37bc5c44ca083cd40f9e4c00aae0bd
这个版本上。
1 2
| ➜ gitlearn git:(master) cat ./.git/refs/heads/master a62a79d99b37bc5c44ca083cd40f9e4c00aae0bd
|
我们可以通过 git update-ref
命令来实现切换版本 (回退到制定的版本上)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
commit a62a79d99b37bc5c44ca083cd40f9e4c00aae0bd (HEAD -> master) Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 17:02:46 2020 +0800
test-object-files updated 02
commit 200e059f0c0b178378aecd6736f384a74d28df94 Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 16:24:02 2020 +0800
test-object-files updated
commit 1883fd5aefeb430cb25150e531e92e38f0176f0d Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 15:45:28 2020 +0800
test-object-files add
➜ .git git:(master) git update-ref refs/heads/master 200e059f0c0b178378aecd6736f384a74d28df94
➜ gitlearn git:(master) ✗ cat ./.git/refs/heads/master 200e059f0c0b178378aecd6736f384a74d28df94
commit 200e059f0c0b178378aecd6736f384a74d28df94 (HEAD -> master) Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 16:24:02 2020 +0800
test-object-files updated
commit 1883fd5aefeb430cb25150e531e92e38f0176f0d Author: bjhl <bjhl@bjhldeMacBook-Pro.local> Date: Tue Jul 14 15:45:28 2020 +0800
test-object-files add
➜ gitlearn git:(master) ✗ cat test-objects-files-01.txt test - objects - files - 第一行 test - objects - files - 第二行 test - objects - files - 第三行
➜ gitlearn git:(master) ✗ cd .git ➜ .git git:(master) git update-ref refs/heads/test 200e059f0c0b178378aecd6736f384a74d28df94
➜ .git git:(master) git log --pretty=oneline test 200e059f0c0b178378aecd6736f384a74d28df94 (HEAD -> master, test) test-object-files updated 1883fd5aefeb430cb25150e531e92e38f0176f0d test-object-files add
➜ .git git:(master) git branch * master test
|
上面的演示中,我们发现了,虽然只是修改的记录的文件,但是并没有对我们工作区的文件进行更新。怎么解决呢?
# HEAD 文件
HEAD
文件通常是一个符号引用,指向目前所在的分支。所谓符号引用,表示它是一个指向其他引用的指针。
我们查看一下 HEAD
文件中的内容
1 2
| ➜ gitlearn git:(master) ✗ cat .git/HEAD ref: refs/heads/master
|
这里呢,有一个命令可以帮助我们实现修改 HEAD
文件的内容
git symbolic-ref
1 2 3 4 5 6 7 8 9 10 11 12
| # 查看当前所在的分支 ➜ gitlearn git:(master) ✗ git symbolic-ref HEAD refs/heads/master # 设置当前所在的分支 ➜ .git git:(master) git symbolic-ref HEAD refs/heads/test # 已经可以看到我们已经把分支切换到了test ➜ .git git:(test) # 我们去看 工作区里的文件,发现并没有变化! ➜ .git git:(test) cat ../test-objects-files-01.txt test - objects - files - 第一行 test - objects - files - 第二行 test - objects - files - 第三行
|
文件的内容的,修改就必须使用 git checkout
/ git reset
了。
我们回来接着看 refs 目录的另一个目录:tags.
tags 是 git tag 命令的产物。
然后,在 git 中,tags 其实是标签对象,包含了一个标签创建者信息,一个日期,一段注释信息,以及一个指针。标签对象通常指向一个提交对象,而不是一个树对象。永远都会指向同一个提交对象。
标签分为 附注标签 和 轻量标签。
使用命令
1 2
| # 常见一个轻量标签 -> 一个固定的引用 ➜ .git git:(test) git update-ref refs/tags/v1.0 1883fd5aefeb430cb25150e531e92e38f0176f0d
|
创建一个附注标签的时候,git 会创建一个标签对象,并记录一个引用来指向该标签对象,而不是直接指向要提交的对象。
1 2 3 4 5 6 7 8 9 10 11 12
| ➜ .git git:(test) git tag -a v1.1 1883fd5aefeb430cb25150e531e92e38f0176f0d -m '附注标签' # 查看标签对象。 ➜ .git git:(test) cat refs/tags/v1.1 3f224b87d02aa196b0b8331aa28389a172e3f723 # 查看附注标签详细的内容 ➜ .git git:(test) git cat-file -p 3f224b87d02aa196b0b8331aa28389a172e3f723 object 1883fd5aefeb430cb25150e531e92e38f0176f0d type commit tag v1.1 tagger bjhl <bjhl@bjhldeMacBook-Pro.local> 1594727545 +0800
附注标签
|
# 远程引用
在 git 的引用中,还有一个远程引用。
这个目录会在我们配置远程仓库的时候出现,我们一起配置一下远程仓库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| # 配置远程仓库 ➜ gitlearn git:(master) git remote add origin https://gitee.com/fangjiaxiaobai/gitlearn.git
# push 到远程分支。 ➜ gitlearn git:(master) git push origin master Counting 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 8 drwxrwxrwx 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 heads drwxr-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/master 200e059f0c0b178378aecd6736f384a74d28df94
|
这个校验码表示 ,远程版本库中和本地的版本库中最新一次交互的 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 会不会占用很大的存储空间?
首先,我们新建一个仓库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| # 创建仓库 ➜ 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 1 Committer: 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 2 Committer: 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 c2e8644a8379ab0cf4f2bc5a14160d94608502f7 18 ➜ gitlearn git:(master) git cat-file -s 363980e2f742592594e01648888a661d2d0479b9 13338
## 查看一下这两次文件存储的文件。 ➜ 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
都会这么做。
1 2 3 4 5 6 7 8 9 10 11
| # 执行一下 git gc ➜ gitlearn git:(master) git gc Counting 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
这个底层命令查看已经打包内容:
1 2 3 4 5 6 7 8 9
| ➜ gitlearn git:(master) git verify-pack -v .git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.idx 421177c9026e757e7e123b6ee589bcffa067eb96 commit 238 160 12 12cbb50359469eb29efe29945a3b18e262f6e862 commit 190 131 172 363980e2f742592594e01648888a661d2d0479b9 blob 13338 80 303 c31d0ebb18efdfc791a254ab58a01ff2ed0136e4 tree 45 55 383 8e0703665933215a57787a3117b079d4c8cb1921 tree 45 56 438 c2e8644a8379ab0cf4f2bc5a14160d94608502f7 blob 18 28 494 non delta: 6 objects .git/objects/pack/pack-275c7b3d69b1acacc114c081d6ffa61d6683e8a1.pack: ok
|
其中第三列是在包文件中各个对象的大小,可以看到,树对象是 45
字节,数据对象和提交对象,都是原来的大小。
显然这次并没有进行压缩。 我们再次修改文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| # 修改文件: 最后一行添加 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 3 Committer: 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 gc Counting 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.idx 2ab3a275bf16a86f77e632127ccf62bcecf98579 commit 238 160 12 421177c9026e757e7e123b6ee589bcffa067eb96 commit 238 160 172 12cbb50359469eb29efe29945a3b18e262f6e862 commit 190 131 332 84003569b2725d79bc209821c9c3dbb5c2ef8a8d tree 45 56 555 c31d0ebb18efdfc791a254ab58a01ff2ed0136e4 tree 45 55 611 8e0703665933215a57787a3117b079d4c8cb1921 tree 45 56 684 dfe088f704e98a835ed3ca2ce31919947d63f884 blob 13356 92 463 # 第三次提交 363980e2f742592594e01648888a661d2d0479b9 blob 7 18 666 1 dfe088f704e98a835ed3ca2ce31919947d63f884 # 第二次提交 c2e8644a8379ab0cf4f2bc5a14160d94608502f7 blob 18 28 740 # 第一次提交 non delta: 8 objects chain length = 1: 1 object .git/objects/pack/pack-a30aa1dcd93c0fadc605cf3fb5828e8cf186ef29.pack: ok
|
我们可以到看到 第三次提交的内容是 13356
字节,而第二次提交的内容只有 7
个字节。显然, git
这次进行压缩。另外,第三个版本是保存了最新的文件内容,原始版本是以差异方式存在的 -- 这是因为大部分情况下,需要快速的访问文件的最新版本。
# 总结
git
创建仓库两种方式:本地创建, clone
仓库
git
仓库的目录结构
objects
目录
我们通过一个最简单的三次提交来看了一下 git
仓库是如何存储数据的。
这里,我贴上这张我们操作过程的图,加深一下印象.
refs
: 引用
- 四种对象:提交对象,数据对象,树对象,标签对象。
- 树对象:
commit->tree->blob
(文件) 课后作业:多文件的提交记录
heads/master
, heads/test/
, tags/
. 这几个文件的作用
git symbolic
命令,
Index
文件。记录当前哪些文件被修改,新增。
- 包文件: 解决 git 工作目录过大的问题。压缩。
# 最后
期望与你一起遇见更好的自己
扫码或搜索:方家小白
发送 290992
即可立即永久解锁本站全部文章