深入理解 Git: Git 第1版 源码 分析 (概述)

脑图1: README

GIT: 内容追踪器
                                    
                                    1.1 content
                                 /  -> + head: object type tag + content size + content = original object
                                |   -> zlib 压缩 (deflated) = _compressed_ object
                                    -> SHA-1 = 2 + 38 个 hex
                                |   -> object name

                                |—— 1.2 object 可 指向 其他 object 
                                    -> 解引用 其 SHA-1 
                                    -> 可构建 `objects 体系(hierarchy)`
            1. object database  |
               按 content 寻址
         /                      |                           object 的 SHA-1(即 path) 与 object content 的 SHA-1 匹配
        |                                                /
                                |—— 1.3 object 一致性检验
        |                                                \
                                |                           object -> 解压 ( inflates) -> bytes 流 <ascii tag without space> + <space> + <ascii decimal size> + <byte\0> + <binary object data>
        |
                                |                   blob
        |                        \                /       
                                    1.4 3种 object- tree: 按 filename+content 排序 => 2 个 tree 比较, O(n) n 是 diff 的 大小, 而非 tree 的 大小
        |                                         \
                                                    changeset / commit      
Git     | 
                                            2.1 按 name 排序 
        |                                /  
                                        |   2.2 随时 可被 update
        |
         \  2. current directory cache  |
               即 .dircache/index                                
                                        |                可 re-generate 其所 cache 的 full state: current directory cache -> 唯一 tree object
                                         \              /
                                            2.3 2个 属性
                                                        \
                                                         可 快速 找出 cached state 与 current state 的 diff
脑图2:    软件系统 & Git  
        
                            设计思想 (第1版设计: 开山鼻祖)
                                                        
                          /                             
                         /                                          
                    软件                                
                 /       \                              
                |           代码质量 (后续 迭代&维护: 锦上添花)                     
软件系统 & Git  |                                                                    
                                        核心思想: 分布式, 3 种 object, 3个区
                |                    /  
                                    /
                |          第1版          
                                    \                1) `异常处理` 不好, 常出现 `SegmentFault`
                |        /           \             /   
                        /               代码质量  |— 2) `内存泄漏`: 如 write-tree.c -> main -> buffer                                            
                |      /                           \
                 \    /                              3) `读 index` 中 `变更文件信息` 用 `array` 存储 -> 较多 申请释放操作 -> 优化: `链表` 存储
                    Git
                       \
                        \
                          杀手锏: 分支策略 / 远程仓库 / 日志系统 / git hooks 是后续 逐步迭代出来的
脑图3: Git 第1版 设计思想
                                工作区 / workspace / working directory
                              /     
                             /
                1. 3个区   —— 暂存区 / index     / staging area
              /              \  
             /                \
            |                   版本库 / commit history / local repo
                                    已提交 data        
            |
                                             blob object
            |                              /    `文件 snapshot (快照)`
                                          /
            |                   2.1  3 种 —— tree object 
                             /            \     `目录结构` + `blob object 索引`
            |               |              \
设计思想                                     commit object
            |               |                   `所在 tree commit` + `parent commit 指针` + `提交信息`
             
            |—— 2. object   |—— 2.2 `存储目录` 默认为 .dircache/objects (最新版 git 中 是 .git/objects), 也可用 环境变量 SHA1_FILE_DIRECTORY 指定
                                        .dircache/objects 目录结构 查询: linux 命令 $ tree .dircache/objects
            |               \
                                2.3  sha1 值 9a860ccaa5f7d7e207e932ce2d4d1c72489ea83e = 前 2个 hex 值 是 目录 / 其余为 name
            |                         => object path: .dircache/objects/9a/860ccaa5f7d7e207e932ce2d4d1c72489ea83e
            
            |—— 3. 哈希算法: Git 用 `OpenSSL 库` 中 `sha1 算法`
                
            |                                     staging area
             \                                  /   
              \                                /             (1) 32Byte head
                4. .dircache/index (二进制)文件            / 
                                               \         /                             32Byte
                                                \       /                           /   
                                                  结构  ——   (2) 文件1信息 64Byte  |—— 8Byte
                                                        \                          |—— 20Byte(40hex): sha-1 
                                                         \                         |—— 2Byte         : namelen
                                                          \                         \  不定长       : name 
                                                           \                            hexdump -C 的 print 右侧能看出 相应 name (相应 file_name)
                                                            \                           
                                                             (3) 文件2信息 64Byte + ...
脑图4: Git 第 1版 7 个底层 可行性文件

可执行文件           git 命令                      说明
init-db             git init .                  初始化仓库
update-cache        git update-index --add ...  添加 文件 或 目录
show-diff           git diff                    查 diff 内容
cat-file            git cat-file -t             读 object 文件
read-tree           git cat-file -p             读 tree
write-tree          git write-tree              写到 tree
commit-tree         git commit -tree [-p]       提交 tree
脑图5: Git 第 1版涉及 Linux 命令

                1. cat ./< original object 文件名 >    => 查 original object 的 content
             /  
            |—— 2. 查看目录结构
                    $ tree -h
            |       $ tree . -a
     
linux 命令    |—— 3. Ubuntu 下 怎么 `显示隐藏文件 (夹)`
                    Ctrl+H 
             
            |   4. 查 二进制 文件 内容
                    hexdump
            |       hexdump -C .dircache/index
             \  
                5. echo 将 file content(如 commit 的 comment)写入 file
                    $ echo "second commit" > changelog
脑图6: Git 第1版源码 框架: 底层7条命令 & 机制
                
                    1. $ ./init-db                          1.1 功能 
                 /                                              初始化仓库
                |                   
                                                            1.2 实现
                |                                               创建目录
                                                                    .dircache        // Git 工作目录, 最新为.git/
                |                                                       objects
                                                                            00 ~ ff  // 256个目录
                |                                           1.3 调用
                                                                $ init-db
                |

                |—— 2. $ ./update-cache <file> ...          2.1 功能
                                                                提交 `工作区` 修改文件 到 `暂存区`
                |                                           
                                                            2.2 实现
                |                                               遍历
                                                                    (1) `变更 文件` -> 压缩 -> blob object -> sha1
                |                                               
                                                                    (2) ctime mtime dev info mode uid gid size sha-1 
                |                                                       构成 `变更 文件信息` 
                                                                        -> 写入 index 文件: .dircache/index
                |
                                                                初始化 repo 时, 自动创建 index 文件
                |                                           
                                                            2.3 调用
                |                                               (1) 新增 文件 README.md + 文件夹及文件
                                                                    $ echo "hello git" > README.md
                |                                                   $ echo "folder1 file1" > folder1/file.c 
                                                                
                |                                               (2) 提交 到 staging area
                                                                    $ ./update-cache README.md
                |
                                                                (3) ubuntu 下 显示隐藏文件 index
                |                                                   Ctrl+H
                                                                    
                |                                               (4) 查看 二进制文件 index
                                                                    $ hexdump -C .dircache/index
                |                                               

                |—— 3. $ ./cat-file <sha1>                  3.1 功能: 
                                                                据 sha1 -> object 的 有效 content  -> 写到 临时文件 temp_*** 
                |                                               -> print <临时文件名>: <object type>
                                                                
                |                                           3.2 实现
                                                                sha1 -> object 文件 -> 解压 
                |                                               -> original object = object type tag + content size + content 
                                                                -> 解析 content  -> 写到 临时文件 temp_*** -> print tempFileName
                |                                               -> 解析 object type -> print
                                                                
                |                                           3.3 调用
                                                                (1)cat-file <sha1>
                |                                                   $ ./cat-file <sha1>
                                                                    temp_git_file_***: blob
                |
                                                                (2) 查 tempFile 内容
                |                                                   $ cat ./temp_git_file_***
                                                                    hello git!                                                                              
Git 底层命令    |
                                                
                |—— 4. show-diff                            4.1 功能
                                                                查 `工作区` 和 `暂存区` 中 `文件差异`
                |                                           
                                                            4.2 实现
                |                                               循环遍历 
                                                                    `变更 文件信息` 
                |                                                   -> 与 `index 文件` 中相应 `文件信息`
                                                                    -> 有差异 
                |                                                   -> 输出 file content diff
                                                                    
                |                                                   先比 file info (=> fast) -> 有差异 -> 才比 file content => fast
                                                                
                |                                           4.3 调用
                                                                (1) 改 README.md
                |                                                   $ echo "hello" > README.md
                
                |                                               (2) 查 diff
                                                                    $ ./show-diff
                |                                               
                                                                
                |—— 5. write-tree                           5.1 功能
                                                                解析 index 文件, 生成 tree object
                |                                               
                                                            5.2 实现
                |                                               index 文件 -> 提取 各变更文件/目录 (即 blob / tree object ) info 中的 mode/name/sha-1 -> tree original object -> 压缩 -> tree object 

                |                                           5.3 调用
                                                                (1) 
                |                                                   $ write-tree
                                                                    treeSha1
                |                                               
                                                                (2)
                |                                                   $ cat-file <treeSha1>
                                                                    tempFile
                |                                               
                                                                (3)
                |                                                   $ cat ./<tempFile>
                                                                    tree object 有效 content
                                                            
                |—— 6. read-tree <treeSha1>                 6.1 功能
                                                                解析 tree object, 输出 tree object 所含 blob/tree object 的 mode + name (即 path) + sha1
                |                                           
                                                            6.2 实现
                |                                               treeSha1 -> tree object -> 解压 -> 所含 blob/tree object 的 mode+name+sha-1  

                |                                           6.3 调用
                                                                (1)
                |                                                   $ write-tree
                                                                    treeSha1
                |
                                                                (2)
                |                                                   $ read-tree  <treeSha1>
                                                                    tree 有效 content 
                |                                               
                 \                                              
                    7. commit-tree <treeSha1> < changelog   7.1 功能 
                                                                基于 tree sha1, 生成 commit object  

                                                            7.2 实现
                                                                建 array -> 写入 tree:<tree_sha1> + 获取 & 写入 parent:<parent_sha1> + author/commiter 信息 + comment 
                                                                -> 填 head -> 压缩 -> commit object
                                                            
                                                            7.3 调用 $ commit-tree <sha1> [-p <sha1>]* < changelog
                                                                (1) write-tree
                                                                    $ write-tree 
                                                                        treeSha1
                                                                
                                                                (2) first commit-tree
                                                                    $ echo "first commit" > changelog
                                                                    $ commit-tree <treeSha1> < changelog
                                                                        commitSha1/commitId
                                                                    $ cat-file <cmmoitSha1>
                                                                        tempFileName
                                                                    $ cat <tempFileName>
                                                                        commit object 有效 content
                                                                
                                                                (3) 更改 README.md
                                                                    $ echo "hello second commit" > README.md
                                                                
                                                                (4) 提交 到 staging area
                                                                    $ ./update-cache README.md
                                                                
                                                                (5) write-tree
                                                                    $ write-tree
                                                                        treeSha1
                                                                
                                                                (6) second commit-tree
                                                                    $ echo "second commit" > changelog
                                                                    $ commit-tree <treeSha1> < changelog
                                                                        commitSha1/commitId
Git 底层命令关系.png
blob object.png
tree object.png
commit object.png
index 文件.png

1 获取 git 源码

Git 第1版 源码 仅约 1000 行

Git 是世界上使用最广泛的 VCS
https://github.com/git/git.git

从自己的 gitee 账户导入, 再从 gitee 仓库 clone
# clone git 源码
$ git clone git@gitee.com:liyu_mypassion/git_first_ver.git

# 查看 第1个提交
$ git log --date-order --reverse
commit e83c5163316f89bfbde7d9ab23ca2e25604af290
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Thu Apr 7 15:13:13 2005 -0700

    Initial revision of "git", the information manager from hell

# 变为第1个提交, 用 commit-id
$ git reset --hard e83c5163316f89bfbde7d9ab23ca2e25604af290

2 文件结构

image.png
手动删 隐藏文件夹 .git
image.png
# linux 下
$ tree -h
.
├── [2.4K]  cache.h
├── [ 503]  cat-file.c
├── [4.0K]  commit-tree.c
├── [1.2K]  init-db.c
├── [ 957]  Makefile
├── [5.5K]  read-cache.c
├── [8.2K]  README
├── [ 986]  read-tree.c
├── [2.0K]  show-diff.c
├── [5.3K]  update-cache.c
└── [1.4K]  write-tree.c

0 directories, 11 files
# 统计 代码行数: 共 1076 
$ find . "(" -name "*.c" -or -name "*.h" -or -name "Makefile" ")" -print | xargs wc -l
   23 ./cat-file.c
   51 ./init-db.c
  248 ./update-cache.c
   43 ./read-tree.c
   66 ./write-tree.c
   93 ./cache.h
   81 ./show-diff.c
   40 ./Makefile
  172 ./commit-tree.c
  259 ./read-cache.c
 1076 total

3 编译

#1 直接编译, 找不到 <openssl/sha.h>
$ make
gcc -g   -c -o update-cache.o update-cache.c
In file included from update-cache.c:1:
cache.h:13:10: fatal error: openssl/sha.h: No such file or directory
   13 | #include <openssl/sha.h>
      |          ^~~~~~~~~~~~~~~
compilation terminated.
make: *** [<builtin>: update-cache.o] Error 1
#2 安装ssl开发库, ubuntu下
sudo apt-get install libssl-dev -y
$ make
gcc -g   -c -o update-cache.o update-cache.c
In file included from update-cache.c:1:
cache.h:14:10: fatal error: zlib.h: No such file or directory
   14 | #include <zlib.h>
      |          ^~~~~~~~
compilation terminated.
make: *** [<builtin>: update-cache.o] Error 1
#3 安装 zlib
apt-get install zlib1g-dev
#4 修改 Makefile,生成 可执行文件

LIBS=-lssl
改为
LIBS=-lcrypto -lz
#5 make
编译生成 `7 个 可执行文件`

见 脑图 4
image.png

4 Git 第1版 设计思想

Write programs to work together.
—— Unix philosophy
Git 是 `分布式`: 每个 developer `本地工作区` 都是 `完整 的 版本库`
见脑图 3
image.png
// .dircache/objects 目录结构:
$ tree .dircache/objects
.dircache/objects
├── 5d  # tree
│   └── 323ceef3152979ef4b50bf20e4a72468b7abee
├── 82  # blob (README.md 第 1 次 提交到 暂存区)
│   └── f8604c3652fa5762899b5ff73eb37bef2da795
├── 9a  # commit
│   └── 860ccaa5f7d7e207e932ce2d4d1c72489ea83e
├── d1  # blob (README.md 第 2 次 提交到 暂存区)
│   └── 411423ff8cd2481a52ee2a96a999bf676d6242 

5 Git 第1版 源码分析(框架): 底层7条命令 & 机制

Write programs that do one thing and do it well.
——Unix philosophy

1. init-db

$ ./init-db
image.png
# 查看初始化后的目录结构
$ tree . -a
.
├── cache.h
├── cat-file
├── cat-file.c
├── cat-file.o
├── commit-tree
├── commit-tree.c
├── commit-tree.o
├── .dircache    # git 工作目录
│   └── objects   # objects 文件 目录
│       ├── 00
│       ├── 01
│       ├── 02
│       ├── 03
...
│       └── ff
├── init-db
├── init-db.c
├── init-db.o
├── Makefile
├── read-cache.c
├── read-cache.o
├── README
├── read-tree
├── read-tree.c
├── read-tree.o
├── show-diff
├── show-diff.c
├── show-diff.o
├── update-cache
├── update-cache.c
├── update-cache.o
├── write-tree
├── write-tree.c
└── write-tree.o

258 directories, 26 files

2. update-cache

$ ./update-cache <file> ...
# (1) 新增 README.md 文件
$ echo "hello git" > README.md

# (2) 提交
$ ./update-cache README.md
(3) Ubuntu 下 怎么 `显示隐藏文件 (夹)`
Ctrl+H 
image.png
image.png
#(4) 查看 index 文件
$ hexdump -C .dircache/index
00000000  43 52 49 44 01 00 00 00  01 00 00 00 60 31 dd b9  |CRID........`1..|
00000010  04 4d a6 8d 23 46 14 37  74 d5 97 0a b7 7a ac b4  |.M..#F.7t....z..|
00000020  41 ce f3 60 4c 83 df 35  41 ce f3 60 4c 83 df 35  |A..`L..5A..`L..5|
00000030  05 08 00 00 38 0e 12 00  b4 81 00 00 e8 03 00 00  |....8...........|
00000040  e8 03 00 00 0b 00 00 00  82 f8 60 4c 36 52 fa 57  |..........`L6R.W|
00000050  62 89 9b 5f f7 3e b3 7b  ef 2d a7 95 09 00 52 45  |b.._.>.{.-....RE|
00000060  41 44 4d 45 2e 6d 64 00                           |ADME.md.|
00000068
// => 变更文件 README.md 的 sha1 值
82f8604c3652fa5762899b5ff73eb37bef2da795 

3. cat-file

$ ./cat-file <sha1>
// (1)
$ ./cat-file 82f8604c3652fa5762899b5ff73eb37bef2da795
temp_git_file_1ZeoRy: blob

// (2) 查看 temp_git_file_1ZeoRy 文件内容
$ cat ./temp_git_file_tBTXFM 
hello git!
image.png

4. show-diff

$ show-diff
# (1) 当前无差异
$ ./show-diff
README.md: ok

# (2) 更改 README.md
$ echo "hello" > README.md

# (3) 查看 diff
$ ./show-diff
README.md:  82f8604c3652fa5762899b5ff73eb37bef2da795
--- -   2021-07-18 15:22:01.449806765 +0800
+++ README.md   2021-07-18 15:21:51.971496739 +0800
@@ -1 +1 @@
-hello git!
+hello
// 1)
$ ./update-cache README.md

// 2)
$ hexdump -C .dircache/index

00000000  43 52 49 44 01 00 00 00  01 00 00 00 1a 5c d0 b9  |CRID.........\..|
00000010  88 c3 53 b8 c0 7a a2 71  3f 02 b2 54 f4 e4 a1 0e  |..S..z.q?..T....|
00000020  8f d6 f3 60 23 dd e7 39  8f d6 f3 60 23 dd e7 39  |...`#..9...`#..9|
00000030  05 08 00 00 38 0e 12 00  b4 81 00 00 e8 03 00 00  |....8...........|
00000040  e8 03 00 00 06 00 00 00  d1 41 14 23 ff 8c d2 48  |.........A.#...H|
00000050  1a 52 ee 2a 96 a9 99 bf  67 6d 62 42 09 00 52 45  |.R.*....gmbB..RE|
00000060  41 44 4d 45 2e 6d 64 00                           |ADME.md.|
00000068

// 3) 
$ ./cat-file d1411423ff8cd2481a52ee2a96a999bf676d6242
temp_git_file_q8qN7c: blob

// 4)
$ cat ./temp_git_file_q8qN7c
hello
image.png
前面 提交 到 暂存区 的 `变更文件 / objects 文件 / blob 对象` 仍保存在 原objects 目录下
image.png

5. write-tree

$ ./write-tree
# (1) 提交
$ ./write-tree
5d323ceef3152979ef4b50bf20e4a72468b7abee

# (2) 查看 `objects 文件 内容`
$ ./cat-file 5d323ceef3152979ef4b50bf20e4a72468b7abee
temp_git_file_oUiQV2: tree

$ cat ./temp_git_file_oUiQV2
100664 README.md�A#���H�R�*����gmbB

6. read-tree

$ ./read-tree <sha1>
# 读 tree object
$ ./read-tree  5d323ceef3152979ef4b50bf20e4a72468b7abee
100664 README.md (d1411423ff8cd2481a52ee2a96a999bf676d6242)

7. commit-tree

$ commit-tree <sha1> [-p <sha1>]* < changelog
$ echo "first commit" > changelog

$  ./commit-tree 5d323ceef3152979ef4b50bf20e4a72468b7abee < changelog
Committing initial tree 5d323ceef3152979ef4b50bf20e4a72468b7abee
9a860ccaa5f7d7e207e932ce2d4d1c72489ea83e

# 查看 commit obj 内容
$ ./cat-file 9a860ccaa5f7d7e207e932ce2d4d1c72489ea83e
temp_git_file_hHQULA: commit

$ cat ./temp_git_file_hHQULA
tree 5d323ceef3152979ef4b50bf20e4a72468b7abee
author mypassion2,,, <mypassion2@mypassion2-virtual-machine> Sun Jul 18 15:57:30 2021
committer mypassion2,,, <mypassion2@mypassion2-virtual-machine> Sun Jul 18 15:57:30 2021

first commit
image.png

禁止转载 或 用于其他用途

参考文章

https://developer.aliyun.com/article/772825?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,961评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,444评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,009评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,082评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,101评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,271评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,738评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,395评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,539评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,434评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,481评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,160评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,749评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,816评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,038评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,548评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,140评论 2 341