成为git砖家(10): 根据文件内容生成SHA-1

文章目录

    • [1. `.git/objects` 目录](#1. .git/objects 目录)
    • [2. `git cat-file` 命令](#2. git cat-file 命令)
    • [3. 根据文件内容生成 sha-1](#3. 根据文件内容生成 sha-1)
    • [4. 结语](#4. 结语)
    • [5. References](#5. References)

1. .git/objects 目录

git 是一个根据文件内容进行检索的系统。 当创建 hello.py, 填入

python 复制代码
print("hello, world")

的内容, 并执行

bash 复制代码
git add hello.py
git commit -m "init"

会在 .git/objects 目录生成子目录和文件。 子目录是2位,文件则是38位, 子目录和文件名字拼接起来的到的40位哈希码, 就是 SHA-1:

比较新版本的 git, 当执行上述 git 操作后, 会在 .git/objects 里存储多个子目录, 旧版本的 git 则只生成一个子目录。我用的 git 2.45.2, 目录结构为:

2. git cat-file 命令

git cat-file 命令能查看 sha-1 的情况, 这里暂时未查阅文档, 仅做基本介绍。

git cat-file -t <sha-1> 查看的是 sha-1 的类型。 其中 sha-1 是子目录和文件拼接起来的。例如

bash 复制代码
➜  test git:(main) ✗ git cat-file -t 8cde7829c178ede96040e03f17c416d15bdacd01
blob

git cat-file -p <sha-1> 则是查看 blob 类型的内容:

bash 复制代码
➜  test git:(main) ✗ git cat-file -p 8cde7829c178ede96040e03f17c416d15bdacd01
print("hello world")

3. 根据文件内容生成 sha-1

git 其实已经帮我们计算了 sha-1, 这是它存储文件时最基本的计算。 当我们有两个内容完全一样的文件被 git addgit commit, 对应的 blob 对象是相同的。

作为验证,我们拷贝 hello.py 内容并提交:

bash 复制代码
➜  test git:(main) ✗ cp hello.py world.py
➜  test git:(main) ✗ git add world.py
➜  test git:(main) ✗ git commit -m "add world.py"
[main f72f05d] add world.py
 1 file changed, 1 insertion(+)
 create mode 100644 world.py

发现 .git/objects 目录新增的两个子目录,分别是 tree 和 commit 类型,并不是 blob 类型。 换言之, world.pyhello.py 对应的 blob 都是 8cde7829c17.

作为验证, 可以使用 Python 的 hashlib模块, 基于如下格式算出 sha-1:

bash 复制代码
blob {文件内容长度}\0 {file_content}

其中 {file_content} 是文件内容.

的到的结果是:

bash 复制代码
➜  test git:(main) ✗ python githash.py hello.py
8cde7829c178ede96040e03f17c416d15bdacd01
➜  test git:(main) ✗ python githash.py world.py
8cde7829c178ede96040e03f17c416d15bdacd01

具体的 githash.py 实现如下:

python 复制代码
#!/usr/bin/env python3

from sys import argv
from hashlib import sha1
from io import StringIO

class Githash(object):
    def __init__(self):
        self.buf = StringIO()

    def update(self, data):
        self.buf.write(data)

    def hexdigest(self):
        data = self.buf.getvalue().encode('utf-8')
        h = sha1()
        h.update(f"blob {len(data)}\0".encode('utf-8'))
        h.update(data)

        return h.hexdigest()

def githash_data(data):
    h = Githash()
    h.update(data)
    return h.hexdigest()

def githash_fileobj(fileobj):
    return githash_data(fileobj.read())

if __name__ == '__main__':
    for filename in argv[1:]:
        with open(filename, 'r', encoding='utf-8') as fileobj:
            print(githash_fileobj(fileobj))

4. 结语

.git/objects 目录存放的子目录中, 有些子目录是 blob 类型的对象, 表示了文件内容。 当两个文件内容一致时, git 对它们生成相同的 SHA-1。 在了解 blob 类型对象的 sha-1 计算过程的前提下,基于 Python 的 hashlib 写了一个工具, 能根据文件内容算出 sha-1, 这既可以作为理解 git 对象存储的初步, 也可以作为后续自行实现一个 mini-git 的基础。

5. References

相关推荐
Franklin9 小时前
VS 版本更新git安全保护问题的解决
git
我是一只代码狗12 小时前
idea中合并git分支
git
我是一只代码狗12 小时前
idea中使用git
git·gitee·github
恋喵大鲤鱼13 小时前
git restore
git·git restore
李少兄13 小时前
Git Commit Message写错后如何修改?已Push的提交如何安全修复?
git·安全
Fireworkitte13 小时前
git stash
git
pe7er1 天前
git submodule简易指南
git
xiaocainiao8811 天前
Python 实战:构建 Git 自动化助手
git·python·自动化
Casia_Dominic2 天前
【三维重建工具】NeRFStudio、3D GaussianSplatting、Colmap安装与使用指南
git·3d·github·点云