当前位置: 首页 > news >正文

GitPython08-源码解读

GitPython08-源码解读

1-核心知识

  • 1)gitPython核心代码很多都是对git命令返回的结果进行解析,在此补充git命令的返回内容
  • 2)git ls-tree -> 查看某个提交或分支所对应的目录树
  • 3)源码中Tree对应的业务逻辑 -> 获取git ls-tree返回到contents中返回(比较绕的一点就是懒加载,借助烘焙标签)
  • 4)git rev-list ->按拓扑顺序(或时间顺序)列出提交对象的 SHA-1 哈希值,用来展示用户的commit信息
  • 5)源码中Commit对应的业务逻辑 -> 从git rev-list中获取提交的信息【提交人等信息】+【Diff文件变动】
  • 6)git diff -> 获取git文件变动内容
  • 7)源码中Diff对应的业务逻辑 -> Commit使用git diff命令结果转化为Diff对象
  • 8) LazyMixin是为了属性的懒读取,只要同一个对象已经读取过了就直接返回,没有才读取
  • 9)LazyMixin有两个实现类:Tree和Commit,Diff不是LazyMixin的实现类

2-参考网址

  • Git的使用教程

3-上手实操

1-git ls-tree命令

git ls-tree 是一个底层(plumbing)命令,用来查看某个 Git 对象(通常是树对象 tree object)的内容。它最常见的用途是查看某个提交或分支所对应的目录树(即文件和子目录的列表及其对应的 blob/tree 对象哈希)。


📌 基本语法

git ls-tree [-d] [-r] [-t] [-l] [--name-only] <tree-ish> [path...]
  • <tree-ish>:可以是分支名、tag、commit SHA、HEAD 等。
  • [path...]:可选,限制输出的路径。

🔍 常用选项

选项说明
-r递归列出所有子目录的内容
-d只显示目录(tree 对象)
-l显示对象大小
--name-only只显示文件名(不显示模式、类型、哈希)

✅ 实际案例

假设你有一个 Git 仓库,结构如下:

my-project/
├── README.md
├── src/
│   └── main.py
└── docs/└── guide.txt

1. 查看当前分支根目录的内容(非递归)

git ls-tree HEAD

输出示例:

100644 blob 3b18e51...    README.md
040000 tree 9f3e2a1...    docs
040000 tree 1a2b3c4...    src

说明:

  • 100644 是普通文件模式;
  • 040000 是目录;
  • blob 是文件对象,tree 是目录对象;
  • 后面是对象哈希和文件名。

2. 递归查看整个目录树(包括子目录)

git ls-tree -r HEAD

输出示例:

100644 blob 3b18e51...    README.md
100644 blob 5f6a7b8...    docs/guide.txt
100644 blob 2c3d4e5...    src/main.py

3. 只查看目录(不显示文件)

git ls-tree -d HEAD

输出示例:

040000 tree 9f3e2a1...    docs
040000 tree 1a2b3c4...    src

4. 查看某个子目录的内容

git ls-tree HEAD src

输出示例:

100644 blob 2c3d4e5...    src/main.py

5. 只显示文件名(常用于脚本处理)

git ls-tree --name-only HEAD

输出示例:

README.md
docs
src

6. 查看某个特定提交(非 HEAD)的目录结构

git ls-tree -r a1b2c3d

其中 a1b2c3d 是某个 commit 的哈希。


🧠 补充说明

  • git ls-tree 不会显示 .gitignore 中忽略的文件,因为它只显示已提交的对象。
  • 如果你想查看某个路径的历史变化,应该使用 git log -- path 而不是 ls-tree

📌 小结速查表

命令说明
git ls-tree HEAD查看当前分支根目录的内容(非递归)
git ls-tree -r HEAD递归查看整个目录树(包括子目录)
git ls-tree -d HEAD只查看目录(不显示文件)
git ls-tree HEAD src查看某个子目录的内容
git ls-tree --name-only HEAD只显示文件名(常用于脚本处理)
git ls-tree -r a1b2c3dg查看某个特定提交(非 HEAD)的目录结构

如需进一步处理这些文件(比如导出或打包),可以结合 git archivegit cat-file 使用。


2-源码中Tree对应的业务逻辑

100644 blob 6a9d72ad2b4e8b7288973a8bb1ead8a529f84190    .gitignore
040000 tree 27f560f217fe016818401537ed55a5c7430e9588    test
100644 blob acf4a637c96293ceea1f7e78fd1dafbfb517cc2c    tree.py
100644 blob a10a7b41e4314ea4d87557af65aa303337c225d3    utils.py
160000 commit d35b34c6e931b9da8f6941007a92c9c9a9b0141a  bar

目的是:直接new Tree(repo)先不烘焙,调用构造方法才烘焙(烘焙就是把属性set进去)

    def construct_initialize(self, repo, id, text):self.repo = repo  # 保存仓库self.id = id  # 保存 SHA-1self.contents = []  # 子对象列表self.__baked__ = False  # 标记还未烘焙(延迟加载用)# 逐行解析(烘焙:就是遍历text把文件的名称全部设置到self.contents中)for line in text.splitlines():self.contents.append(self.content_from_string(self.repo, line))# 过滤掉解析失败的 Noneself.contents = [c for c in self.contents if c is not None]# 标记为已烘焙(防止重复解析)self.__bake_it__()return self

3-git rev-list命令

git rev-list 是一个非常底层且强大的 Git 命令,用于按拓扑顺序(或时间顺序)列出提交对象的 SHA-1 哈希值。它是许多高级命令(如 git log, git bisect, git rebase)的底层实现基础。


📌 基本语法

git rev-list [options] <commit-ish>... [--] [<path>...]
  • <commit-ish>:可以是分支名、tag、commit SHA、HEAD 等。
  • [-- <path>...]:可选,限制只列出影响指定路径的提交。

✅ 常见用途与案例

1️⃣ 查看某个分支的所有提交(按时间倒序)

git rev-list main

输出示例:

a1b2c3d4e5f6...
7a8b9c0d1e2f...
...

2️⃣ 查看某个分支的提交数量

git rev-list --count main

输出示例:

42

3️⃣ 查看两个分支之间的差异提交(main 有但 dev 没有的)

git rev-list main ^dev

或等价:

git rev-list dev..main

输出示例:

a1b2c3d4...
7a8b9c0d...

4️⃣ 查看从某个时间点以来的提交(比如某个 tag 之后)

git rev-list v1.0..HEAD

5️⃣ 查看某个文件的所有历史提交(影响该文件的)

git rev-list HEAD -- src/main.py

6️⃣ 查看某个作者的提交

git rev-list --author="Alice" main

7️⃣ 查看某段时间内的提交

git rev-list --since="2024-01-01" --until="2024-06-01" main

8️⃣ 查看合并提交(merge commits)

git rev-list --merges main

9️⃣ 查看非合并提交(普通提交)

git rev-list --no-merges main

🔟 限制输出数量(比如只看最近的 5 个)

git rev-list -n 5 main

🧠 进阶案例:找出某个 bug 的引入提交(结合 bisect)

git rev-list --bisect main

这会输出一个中间提交,用于二分查找 bug。


✅ 实际应用:配合 xargs 批量处理提交

git rev-list main | head -5 | xargs -I {} git show --oneline {}

输出示例:

a1b2c3d Fix typo in README
7a8b9c0 Add new feature
...

📌 小结速查表

命令说明
git rev-list main列出 main 分支的所有提交
git rev-list --count main统计提交数量
git rev-list dev..mainmain 有但 dev 没有的提交
git rev-list --author="Alice"某作者的所有提交
git rev-list --since="2024-01-01"某时间之后的提交
git rev-list --merges只列出合并提交
git rev-list HEAD -- file.txt列出影响 file.txt 的提交

如需进一步分析这些提交(如查看 diff、统计变更行数),可以结合 git log, git show, git diff 使用。


4-源码中Commit用法

Commit的作用就是从git rev-list中获取提交的信息【提交人等信息】+【Diff文件变动】

commit 4c8124ffcf4039d292442eeccabdeca5af5c5017
tree 672eca9b7f9e09c22dcb128c283e8c3c8d7697a4
parent 634396b2f541a9f2d58b00be1a07f0c358b999b3
author Tom Preston-Werner <tom@mojombo.com> 1191999972 -0700
committer Tom Preston-Werner <tom@mojombo.com> 1191999972 -0700implement Grit#headscommit 634396b2f541a9f2d58b00be1a07f0c358b999b3
tree b35b4bf642d667fdd613eebcfe4e17efd420fb8a
author Tom Preston-Werner <tom@mojombo.com> 1191997100 -0700
committer Tom Preston-Werner <tom@mojombo.com> 1191997100 -0700initial grit setupcommit ab25fd8483882c3bda8a458ad2965d2248654335
tree c20b5ec543bde1e43a931449b196052c06ed8acc
parent 6e64c55896aabb9a7d8e9f8f296f426d21a78c2c
parent 7f874954efb9ba35210445be456c74e037ba6af2
author Tom Preston-Werner <tom@mojombo.com> 1182645538 -0700
committer Tom Preston-Werner <tom@mojombo.com> 1182645538 -0700Merge branch 'site'Some other stuff
  • Commiti对象属性
id: 提交的 SHA1 ID
parents: 父提交的 ID 列表(将转换为 Commit 实例)
tree: 对应的树对象 ID(将转换为 Tree 实例)
author: 作者信息字符串
authored_date: 作者提交时间
committer: 提交者信息字符串
committed_date: 提交时间
message: 提交消息的第一行

    def __init__(self, repo, **kwargs):LazyMixin.__init__(self)self.repo = repo  # 所属的 Git 仓库对象self.id = None  # 提交 IDself.tree = None  # 树对象self.author = None  # 作者self.authored_date = None  # 作者提交时间self.committer = None  # 提交者self.committed_date = None  # 提交时间self.message = None  # 提交消息self.parents = None  # 父提交列表# 动态设置传入的参数for k, v in kwargs.items():setattr(self, k, v)# 如果提供了 ID,则进一步解析 parents 和 treeif self.id:if 'parents' in kwargs:self.parents = map(lambda p: Commit(repo, **{'id': p}), kwargs['parents'])if 'tree' in kwargs:self.tree = tree.Tree(repo, **{'id': kwargs['tree']})

5-git diff命令

git diff 是日常使用频率最高的 Git 命令之一,用来查看工作区、暂存区、分支之间的差异。它本质上是调用 git diff-filesgit diff-indexgit diff-tree 等底层命令的封装。


📌 基本语法

git diff [<options>] [<commit>] [--] [<path>...]
  • <commit>:可以是 commit SHA、分支名、tag 等。
  • [-- <path>...]:可选,限制只查看某些文件或目录的差异。

✅ 常见场景与案例

1️⃣ 查看工作区暂存区的差异(默认行为)

git diff

输出示例(简写):

diff --git a/README.md b/README.md
index 3b18e51..7a8b9c0 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@# My Project
+This line is added in working directory

2️⃣ 查看暂存区最新提交的差异(即准备提交的改动)

git diff --cached        # 或 --staged

输出示例:

diff --git a/src/main.py b/src/main.py
index 2c3d4e5..5f6a7b8 100644
--- a/src/main.py
+++ b/src/main.py
@@ -10,6 +10,7 @@ def main():print("Hello")
+    print("World")

3️⃣ 查看两个分支之间的差异

git diff main..dev

或等价:

git diff main dev

输出示例:

diff --git a/src/utils.py b/src/utils.py
new file mode 100644
index 0000000..9f3e2a1
--- /dev/null
+++ b/src/utils.py
@@ -0,0 +1,3 @@
+def helper():
+    pass

4️⃣ 查看某个文件在两个提交之间的差异

git diff HEAD~2 HEAD -- src/main.py

5️⃣ 查看某个文件在工作区与最新提交之间的差异

git diff HEAD -- src/main.py

6️⃣ 查看两个提交之间的所有差异(压缩为一个 diff)

git diff a1b2c3d..7a8b9c0

7️⃣ 查看某次提交引入了哪些改动(与父提交对比)

git diff 7a8b9c0^ 7a8b9c0

8️⃣ 查看合并提交的差异(与两个父提交对比)

git diff -m -c MERGE_COMMIT_SHA

9️⃣ 只看文件名(不显示具体内容)

git diff --name-only

🔟 统计变更行数(增删行数)

git diff --stat

输出示例:

 README.md    | 3 ++-src/main.py  | 1 +2 files changed, 3 insertions(+), 1 deletion(-)

🎯 进阶技巧

✅ 高亮单词级差异(更细粒度)

git diff --word-diff

✅ 生成补丁文件(用于邮件发送或 review)

git diff main..dev > changes.patch

✅ 查看已暂存但未提交的差异(GUI 风格)

git diff --cached --name-only | xargs git diff --cached

📌 小结速查表

命令作用
git diff工作区 vs 暂存区
git diff --cached暂存区 vs HEAD
git diff HEAD工作区 vs HEAD
git diff main..dev分支 dev vs 分支 main
git diff HEAD~2 HEAD最近两次提交的差异
git diff --name-only只列出变更文件名
git diff --stat统计变更行数
git diff --word-diff单词级差异高亮

如需将差异导出为补丁、用于代码审查或 CI 检查,git diff 是最直接可靠的工具。


6-源码中Diff用法

获取git diff命令的返回结果进行打印,README.md文件在return self代码之后,添加了两行文本

git diffdiff --git a/README.md b/README.md
index 43c9850..4bcfc73 100644
--- a/README.md
+++ b/README.md
@@ -140,3 +140,6 @@ git ls-tree HEADself.__bake_it__()return self
+----
+## 5-Diff
+> 获取git diff命令的返回结果进行打印
  • 返回Diff对象的属性说明如下
Diff对象属性说明
--------
repo        : 仓库对象,用于后续在 Commit 构造时传入
a_path      : 旧版本文件路径(a/ 前缀)
b_path      : 新版本文件路径(b/ 前缀)
a_commit    : 旧版本对应的 Commit 对象;若为新增文件则为 None
b_commit    : 新版本对应的 Commit 对象;若为删除文件则为 None
a_mode      : 旧版本文件模式(八进制字符串,如 100644)
b_mode      : 新版本文件模式(八进制字符串,如 100755)
new_file    : True 表示该文件在本次 diff 中被新增
deleted_file: True 表示该文件在本次 diff 中被删除
diff        : 该行以下的所有 diff 文本(包括 ---、+++、@@ 等)

4-特殊情况补充

1-git rev-list main执行报错

这个报错的意思是:Git 无法识别你提供的参数 master,因为它在当前仓库里既找不到叫 master 的分支,也找不到叫 master 的文件或路径。


原因

  1. 你的仓库默认分支可能不叫 master
    现在很多仓库默认分支是 main,而不是 master
  2. 你当前目录下可能根本没有 master 分支
    你可以用 git branch -agit branch -r 看看有哪些分支。

解决方法

✅ 1. 查看实际存在的分支
git branch -a

输出示例:

* mainremotes/origin/HEAD -> origin/mainremotes/origin/main

如果你看到的是 main 而不是 master,那就用 main 代替 master


✅ 2. 替换命令

把原来的命令:

git rev-list --branches master

改成:

git rev-list --branches main

或者如果你想列出所有分支的 commit,也可以直接:

git rev-list --branches

✅ 3. 如果你确实需要 master 分支

你可以从远程拉取或创建它:

git checkout -b master origin/master

前提是远程仓库确实有一个叫 master 的分支。


总结

你看到的报错是因为 master 这个分支在当前仓库中不存在。先确认分支名,再替换即可。


2-区分两种git diff两种方式

1-git diff --numstat

# 1-统计文件的改动数量-方式1
git diff --numstat
1       0       README.md
1       0       repo.py

2-git diff --stat

# 2-统计文件的改动数量-方式2
git diff --statREADME.md | 1 +repo.py   | 1 +2 files changed, 2 insertions(+)

5-知识总结

1-LazyMixin的实现类

1-LazyMixin类

这个类就提供了一个能力:延迟初始化的属性的加载

  • 1)属性写入是【bake】方法,需要每个具体的子类去实现
  • 2)属性读取是【getattribute】方法,这里会判断有没有加载过,加载过直接返回,没有重新加载
class LazyMixin(object):lazy_properties = []def __init__(self):self.__baked__ = Falsedef __getattribute__(self, attr):val = object.__getattribute__(self, attr)if val is not None:return valelse:self.__prebake__()return object.__getattribute__(self, attr)def __bake__(self):raise NotImplementedError(" '__bake__' method has not been implemented.")def __prebake__(self):if self.__baked__:returnself.__bake__()self.__baked__ = Truedef __bake_it__(self):self.__baked__ = True


2-Tree实现类

    def __bake__(self):# 调用类方法 construct 重新构造 Tree 对象temp = Tree.construct(self.repo, self.id)# 把解析出来的内容列表赋给自身self.contents = temp.contents


3-Commit实现类

    def __bake__(self):"""延迟加载:从 Git 仓库中加载完整的提交信息。"""temp = Commit.find_all(self.repo, self.id, **{'max_count': 1})[0]self.parents = temp.parentsself.tree = temp.treeself.author = temp.authorself.authored_date = temp.authored_dateself.committer = temp.committerself.committed_date = temp.committed_dateself.message = temp.message


2-Diff类

Diff类的目标是:把Commit获取的信息转化为Diff对象,并没有直接使用git diff命令来获取仓库的变动内容

  • 1)作者认为:展示的diff信息其实是commit的对象信息
  • 2)所以git diff原生的命令调用是在Commit中进行实现的
class Diff(object):def __init__(self, repo, a_path, b_path, a_commit, b_commit,a_mode, b_mode, new_file, deleted_file, diff):self.repo = repoself.a_path = a_pathself.b_path = b_path# 如果旧版本为空(全 0 SHA)或传入空值,则置为 Noneif not a_commit or re.search(r'^0{40}$', a_commit):self.a_commit = Noneelse:# 通过 commit.Commit 构造旧版本 Commit 对象self.a_commit = commit.Commit(repo, **{'id': a_commit})# 同理处理新版本if not b_commit or re.search(r'^0{40}$', b_commit):self.b_commit = Noneelse:self.b_commit = commit.Commit(repo, **{'id': b_commit})self.a_mode = a_modeself.b_mode = b_modeself.new_file = new_fileself.deleted_file = deleted_fileself.diff = diff@classmethoddef list_from_string(cls, repo, text):"""将 Git 输出的 diff 字符串解析为 Diff 对象列表。参数----repo : 仓库对象text : Git diff 原始文本(包含一个或多个文件的 diff)返回----diffs : list[Diff]按文件顺序解析得到的 Diff 对象列表"""lines = text.splitlines()  # 将 diff 文本按行切分a_mode = Noneb_mode = Nonea_path = Noneb_path = Nonea_commit = Noneb_commit = Nonediffs = []  # 结果列表while lines:# 匹配 diff --git a/xxx b/xxx 行,提取旧/新文件路径m = re.search(r'^diff --git a/(\S+) b/(\S+)$', lines.pop(0))if m:a_path, b_path = m.groups()# 处理纯 mode 变更(无内容变更)if lines and re.search(r'^old mode', lines[0]):m = re.search(r'^old mode (\d+)', lines.pop(0))if m:a_mode, = m.groups()m = re.search(r'^new mode (\d+)', lines.pop(0))if m:b_mode, = m.groups()# 如果下一行还是 diff --git,说明只有 mode 改变if lines and re.search(r'^diff --git', lines[0]):diffs.append(Diff(repo, a_path, b_path,None, None,a_mode, b_mode,False, False, None))continue# 初始化文件状态标志new_file = Falsedeleted_file = False# 处理新增文件if lines and re.search(r'^new file', lines[0]):m = re.search(r'^new file mode (.+)', lines.pop(0))if m:b_mode, = m.groups()a_mode = Nonenew_file = True# 处理删除文件elif lines and re.search(r'^deleted file', lines[0]):m = re.search(r'^deleted file mode (.+)$', lines.pop(0))if m:a_mode, = m.groups()b_mode = Nonedeleted_file = True# 解析 index 行,提取旧/新 blob SHA 与可选的新模式if lines:m = re.search(r'^index ([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+) ?(.+)?$',lines.pop(0))if m:a_commit, b_commit, b_mode = m.groups()# 去掉可能存在的多余空格if b_mode:b_mode = b_mode.strip()# 收集该文件剩余所有 diff 行,直到遇到下一个 diff --git 或文件结束diff_lines = []while lines and not re.search(r'^diff', lines[0]):diff_lines.append(lines.pop(0))diff = "\n".join(diff_lines)# 将解析结果封装为 Diff 对象并加入列表diffs.append(Diff(repo, a_path, b_path,a_commit, b_commit,a_mode, b_mode,new_file, deleted_file, diff))return diffs
  • Commit类中关于git diff的命令调用
    @classmethoddef diff(cls, repo, a, b = None, paths = []):if isinstance(b, list):paths = bb = Noneif paths:paths.insert(0, "--")if b:paths.insert(0, b)paths.insert(0, a)text = repo.git.diff(*paths, **{'full_index': True})return diff.Diff.list_from_string(repo, text)

3-Tree类

先思考一个事情,这个Tree返回的数据结构是一个层级很深的树还是一个列表?

根据代码分析,当前的 [Tree](file:///Applications/0ENZO/WorkSpace/Python/20250731_2_GitPython_manual/tree.py#L8-L112) 类实现是一个层级很深的树状结构,而不仅仅是一个列表。让我详细解释:

树状结构分析

1. 树的层次结构

# Tree 类包含一个 contents 属性,这是一个列表
self.contents = []  # 子对象列表# 但这个列表中的每个元素又可能是另一个 Tree 对象
def content_from_string(self, repo, text):# ...if typ == "tree":# 递归创建新的 Tree 对象return Tree(repo, **{'id': id, 'mode': mode, 'name': name})elif typ == "blob":# 或者是文件对象(叶节点)return None  # 当前实现返回 None,但正常应该返回 Blob 对象

2. 树的递归特性

  • 每个 [Tree](file:///Applications/0ENZO/WorkSpace/Python/20250731_2_GitPython_manual/tree.py#L8-L112) 对象包含一个 [contents](file:///Applications/0ENZO/WorkSpace/Python/20250731_2_GitPython_manual/tree.py#L0-L0) 列表
  • [contents](file:///Applications/0ENZO/WorkSpace/Python/20250731_2_GitPython_manual/tree.py#L0-L0) 中的元素可以是:
    • 其他 [Tree](file:///Applications/0ENZO/WorkSpace/Python/20250731_2_GitPython_manual/tree.py#L8-L112) 对象(子目录)
    • [Blob](file:///Applications/0ENZO/WorkSpace/Python/20250731_2_GitPython_manual/blob.py#L1-L58) 对象(文件)
  • 这样就形成了一个递归的树状结构

3. 延迟加载机制

class Tree(LazyMixin):def __bake__(self):# 延迟加载:只有在真正访问时才解析内容temp = Tree.construct(self.repo, self.id)self.contents = temp.contents

这意味着树的结构是按需构建的,只有访问到某个节点时才会加载其子节点。

4. 访问方式

# 可以通过 / 操作符逐层访问
root_tree = Tree.construct(repo, 'master')
sub_tree = root_tree / 'src'  # 访问 src 目录
file_obj = sub_tree / 'main.py'  # 访问 src/main.py 文件

当前实现的问题

从代码中可以看到一些问题:

  1. Blob 对象未实现

    elif typ == "blob":# return blob.Blob(repo, **{'id': id, 'mode': mode, 'name': name})return None  # 当前返回 None
    
  2. basename 属性问题

    @property
    def basename(self):return self.repo.path  # 这里返回的是仓库路径,不是当前目录名
    

结论

当前 Tree 类返回的是一个真正的树状数据结构,具有以下特点:

  1. 层次性:每个节点可以包含子节点,形成层级结构
  2. 递归性:目录节点可以包含其他目录节点
  3. 延迟加载:按需加载节点内容
  4. 导航性:支持通过 / 操作符进行路径式访问

虽然表面上 contents 是一个列表,但整个数据结构是一个完整的树,因为列表中的元素本身又可以是 Tree 对象。

这与文件系统中的目录结构非常相似:一个目录包含文件和子目录,子目录又可以包含更多文件和子目录,形成一个深层的树状结构。


http://www.dtcms.com/a/310575.html

相关文章:

  • 进阶08:C#与SQL Server通信
  • 高效连接,3针M12航空插头助您畅行无阻
  • PSA 制氧装置和VPSA 制氧装置技术特点有什么不同
  • [VL|RIS] ReferSAM
  • windows电脑开机或重启,server不能自启动
  • 关税战火中的技术方舟:新西兰证券交易所的破局之道 ——从15%关税冲击到跨塔斯曼结算联盟,解码下一代交易基础设施
  • 开发后台管理系统的注意事项
  • 26考研|数学分析:曲线(面)积分·三大公式
  • Windows系统优化命令-记录
  • 全国增值税发票查验流程-批量核验-接口集成简便高效
  • 四、基于SpringBoot,MVC后端开发笔记
  • opencv-python的GPU调用
  • 在线免费的AI文本转语音工具TTSMaker介绍
  • U-Mail邮件系统-全面适配信创环境的国产邮件系统
  • 什么是大端?什么是小端?如何验证?
  • MySQL相关概念和易错知识点(3)(表内容的CURD、内置函数)
  • 基于CNN卷积神经网络图像识别28个识别合集-视频介绍下自取
  • Tushare 行情数据完整性同步算法
  • 三轴云台之热成像伪彩模式篇
  • 【Lua】题目小练7
  • Nestjs框架: 请求生命周期与应用生命周期
  • Vue模板语法详解:从基础到进阶的响应式绑定指南1
  • 工业数采引擎-DTU
  • CSS属性值计算规则:从声明到渲染的精确过程
  • 《C++》STL--list容器详解
  • 【读文献】Capacitor-drop AC-DC
  • 移除 Excel 文件(.xlsx)的工作表保护
  • Ubuntu 系统下使用 lsusb 命令识别 USB 设备及端口类型详解
  • 从“多、老、旧”到“4i焕新”:品牌官方商城(小程序/官网/APP···)的范式跃迁与增长再想象
  • 数据结构与算法——字典(前缀)树的实现