成为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

相关推荐
闲云一鹤21 小时前
Git LFS 扫盲教程 - 你不会还在用 Git 管理大文件吧?
前端·git·前端工程化
vibecoding日记4 天前
为什么我就想要「线性历史 + Signed Commits」,GitHub 却把我当猴耍 🤬🎙️
git·编程工具
程序员小崔日记4 天前
如何将代码轻松上传到 Gitee?Git 使用全攻略!
git·gitee·上传
Bigger5 天前
为什么你的 Git 提交需要签名?—— Git Commit Signing 完全指南
git·开源·github
DianSan_ERP6 天前
电商API接口全链路监控:构建坚不可摧的线上运维防线
大数据·运维·网络·人工智能·git·servlet
红豆子不相思6 天前
Tomcat 环境搭建与集群实战
服务器·git·tomcat
杰哥技术分享6 天前
Git 仓库迁移技术文档:从 CODING.net 迁移至腾讯云 CNB
git
梅孔立6 天前
Ansible 100 台服务器一键管控实战 进阶版
服务器·git·ansible
qq_426003967 天前
git切换当前分支到远程分支
git