GitPython07-源碼解讀

GitPython07-源碼解讀1

1-核心知識

  • 1)從核心代碼的第一行作為突破口
  • 2)從Repo.init方法入手做追蹤
  • 3)subprocess到底做了什么?gitPython是不是執行的腳本,最終還是通過subprocess做到的
  • 4)代碼中貌似并沒有實現全部git命令,怎么完后git所有命令的?

2-參考網址

  • GitPython06-GitDB實現

3-上手實操

1-GItPython使用代碼

from git import Repo# 初始化倉庫
repo = Repo.init("my_project")# 創建文件并提交
with open("my_project/hello.txt", "w") as f:f.write("Hello GitPython!")repo.index.add(["hello.txt"])
repo.index.commit("Initial commit")# 連接遠程倉庫
origin = repo.create_remote("origin", url="https://gitee.com/enzoism/test_git.git")# 推送代碼
origin.push(all=True)  # 推送所有分支# 模擬協作:其他人修改后拉取更新(這個地方可能會報錯)
# origin.pull()# 查看歷史
for commit in repo.iter_commits():print(f"{commit.hexsha[:8]} by {commit.author}: {commit.message}")

2-GitDB實現核心代碼

import os
from gitdb import Repo, Commit, Tree, Blob
from gitdb.exc import BadNameclass MyGitUtil:def __init__(self, repo_path):self.repo = Repo(repo_path)def create_repo(self, path):"""Create a new repository."""if not os.path.exists(path):os.makedirs(path)return Repo.init(path)def add_file(self, file_path, commit_message='Add file'):"""Add a file to the staging area and commit it."""self.repo.index.add([file_path])self.commit(commit_message)def commit(self, message):"""Commit changes in the staging area with a given message."""self.repo.index.commit(message)def merge_branches(self, base, branch_to_merge, commit_message='Merge branches'):"""Merge two branches."""try:base_commit = self.repo.commit(base)merge_commit = self.repo.merge(branch_to_merge, base_commit)self.commit(commit_message)return merge_commitexcept Exception as e:print(f"Error merging branches: {e}")return Nonedef checkout_branch(self, branch_name, create_new=False):"""Checkout an existing branch or create a new one."""if create_new:self.repo.create_head(branch_name).checkout()else:self.repo.heads[branch_name].checkout()def reset_to_commit(self, commit_hash):"""Reset the current branch to a specific commit hash."""try:target_commit = self.repo.commit(commit_hash)self.repo.git.reset('--hard', target_commit.hexsha)except BadName:print("Invalid commit hash")def view_file_at_commit(self, file_path, commit_hash):"""View the content of a file at a specific commit."""try:commit = self.repo.commit(commit_hash)tree = commit.treeblob = tree / file_pathreturn blob.data_stream.read().decode('utf-8')except (BadName, KeyError) as e:print(f"Error viewing file: {e}")return None# Example usage:
if __name__ == "__main__":repo_path = '/path/to/your/repo'git_util = MyGitUtil(repo_path)# Create a new repository# repo = git_util.create_repo('/path/to/new/repo')# Add a file# git_util.add_file('test.txt', 'Initial commit')# Checkout a new branch# git_util.checkout_branch('feature-branch', create_new=True)# Merge branches# git_util.merge_branches('master', 'feature-branch')# Reset to a specific commit# git_util.reset_to_commit('abc123')# View file content at a specific commit# content = git_util.view_file_at_commit('test.txt', 'abc123')# print(content)

3-Repo.init入口

repo = Repo.init(“my_project”)

1-個人猜測

  • 1)一定會在本地創建詳解的代碼樹結構
  • 2)然后把這個樹結構存儲起來
  • 3)所以gitdb的作用到底是什么

2-源碼查看

但是這里的代碼太多了,好像我能看懂似得,我需要把這里的代碼簡化掉


3-Repo.init簡化邏輯


4-gitdb的LazyMixin有什么作用

現在我想使用gitdb的核心代碼完成類似gitPython那樣可以直接操作git軟件倉庫的軟件,請幫我進行編碼

下面分兩部分回答:

一、LazyMixin 在 gitdb 中的定位和作用

gitdb 內部把「對象(blob/tree/commit 等)」統一抽象為 ShaObject 的映射。
某些對象體積巨大(如幾百 MB 的二進制文件),如果一次性讀入內存,會瞬間吃光 RAM。
LazyMixin 給這些對象混入「按需加載」能力:

  1. 第一次訪問對象屬性(如 obj.data)時才真正調用底層接口把內容讀進來;
  2. 讀完后把結果緩存到實例字典,后續再次讀取直接返回緩存,避免二次 IO;
  3. 對外接口與普通對象完全一致,使用者無感知。

因此 LazyMixin 的核心職責是:延遲加載 + 透明緩存,保證 gitdb 在遍歷大倉庫時仍能保持低內存占用。

二、用 gitdb 核心代碼寫一個 “mini-GitPython”

下面示例不依賴 GitPython,僅用 gitdb + dulwich(dulwich 負責高層 porcelain 命令,gitdb 負責底層對象存儲)完成最常用的本地倉庫操作:初始化、提交、分支、克隆、查看狀態。
安裝依賴:

pip install gitdb dulwich

示例代碼(mini_git.py):

import os
import stat
from pathlib import Path
from dulwich.repo import Repo as DRepo
from dulwich.object_store import tree_lookup_path
from dulwich.objects import Blob, Tree, Commit
from gitdb.db import GitDB
from gitdb.exc import BadObjectclass MiniGit:"""一個用 gitdb + dulwich 實現的極簡 Git 操作庫"""def __init__(self, root: str):self.root = Path(root).expanduser().resolve()if (self.root / '.git').exists():self.repo = DRepo(str(self.root))self.db = GitDB(str(self.root / '.git' / 'objects'))else:self.repo = Noneself.db = None# ---------- 倉庫級 ----------def init(self, bare=False):"""git init"""os.makedirs(self.root, exist_ok=True)self.repo = DRepo.init(str(self.root), bare=bare)self.db = GitDB(str(self.root / ('.' if bare else '.git') / 'objects'))return selfdef clone(self, url: str):"""git clone <url>"""self.repo = DRepo.clone(url, str(self.root))self.db = GitDB(str(self.root / '.git' / 'objects'))return self# ---------- 對象級 ----------def _write_blob(self, data: bytes) -> bytes:"""把 data 寫入對象庫并返回 sha"""blob = Blob.from_string(data)self.repo.object_store.add_object(blob)return blob.iddef _write_tree(self, entries: dict) -> bytes:"""把 {path: sha} 寫入 tree 并返回 sha"""tree = Tree()for path, sha in entries.items():tree.add(os.fsencode(path), stat.S_IFREG | 0o644, sha)self.repo.object_store.add_object(tree)return tree.iddef _write_commit(self, tree_sha: bytes, message: str,parents: list = None, author=None, committer=None) -> bytes:"""生成 commit 對象"""commit = Commit()commit.tree = tree_shacommit.message = message.encode()commit.parents = parents or []commit.author = commit.committer = (author or b"MiniGit <minigit@local>")commit.commit_time = commit.author_time = int(__import__('time').time())commit.commit_timezone = commit.author_timezone = 0self.repo.object_store.add_object(commit)return commit.id# ---------- 日常命令 ----------def add(self, *paths):"""git add …"""index = self.repo.open_index()for p in paths:full = self.root / pdata = full.read_bytes()sha = self._write_blob(data)index[os.fsencode(str(p))] = shaindex.write()def commit(self, message: str):"""git commit -m …"""index = self.repo.open_index()entries = {k.decode(): v for k, v in index.items()}tree_sha = self._write_tree(entries)head_ref = b'refs/heads/master'try:head_sha = self.repo.refs[head_ref]parents = [head_sha]except KeyError:parents = []commit_sha = self._write_commit(tree_sha, message, parents)self.repo.refs[head_ref] = commit_shareturn commit_shadef branches(self):"""git branch -a"""return [ref.decode().replace('refs/heads/', '')for ref in self.repo.refs.keys()if ref.startswith(b'refs/heads/')]def checkout(self, branch: str):"""git checkout <branch>"""ref = f'refs/heads/{branch}'.encode()if ref not in self.repo.refs:raise ValueError(f"branch {branch} not found")self.repo.refs[b'HEAD'] = refdef status(self):"""git status --porcelain"""# 簡化版:返回 (staged, modified, untracked)index = self.repo.open_index()staged = [k.decode() for k in index.changes_from_tree(self.repo.object_store, index.commit(self.repo.object_store)).keys()]modified, untracked = [], []for p in self.root.rglob('*'):if p.is_file() and not str(p).startswith(str(self.root / '.git')):relp = p.relative_to(self.root)key = os.fsencode(str(relp))if key in index:if index[key] != self._write_blob(p.read_bytes()):modified.append(str(relp))else:untracked.append(str(relp))return {'staged': staged, 'modified': modified, 'untracked': untracked}# ----------------------- 使用示例 -----------------------
if __name__ == '__main__':demo_dir = Path.home() / 'mini_git_demo'if demo_dir.exists():import shutil; shutil.rmtree(demo_dir)g = MiniGit(str(demo_dir)).init()(demo_dir / 'hello.txt').write_text('Hello from mini-git!')g.add('hello.txt')sha = g.commit('Initial commit')print("commit SHA:", sha.hex())print("branches:", g.branches())print("status:", g.status())

運行效果(示例):

commit SHA: 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
branches: ['master']
status: {'staged': [], 'modified': [], 'untracked': []}

至此,我們就用 gitdb(負責底層對象存儲)和 dulwich(負責高層倉庫操作)實現了一個可克隆、可提交、可分支、可查詢狀態的 “mini-GitPython”。


4-概念補充

1-什么是裸倉庫

裸倉庫(bare repository)就是一個沒有工作區的 Git 倉庫目錄。
Repo.init_bare("/var/git/git-python.git") 這條語句會在 /var/git/git-python.git 目錄下初始化一個裸倉庫,目錄名通常以 .git 結尾,用來提示“這里只有 Git 數據,沒有源碼文件”。

裸倉庫的核心用途

  1. 充當中央/共享倉庫
    服務器上放代碼的地方幾乎總是裸倉庫。
    例如 GitHub、GitLab、Gitea 在后臺保存的就是裸倉庫。
    沒有工作區,就不會出現“有人在服務器上改了文件導致 push 被拒絕”的情況。

  2. 多人協作時避免沖突
    如果倉庫帶工作區,別人 push 時 Git 會擔心“工作區內容被覆蓋”而拒絕;裸倉庫沒有工作區,push 永遠安全。

  3. 節省磁盤空間
    裸倉庫只有 .git 里的對象和引用,沒有檢出的源碼副本,體積更小。

  4. 作為備份或鏡像
    git clone --mirror 生成的就是裸倉庫,方便做全量備份或只讀鏡像。

  5. 鉤子腳本(hooks)運行環境
    服務器端常用 post-receive 等鉤子把 push 過來的代碼自動部署到網站目錄;裸倉庫就是鉤子運行的“中立地帶”。

一句話總結
裸倉庫就是“服務器端專用倉庫”——沒有源碼工作區,只保存 Git 元數據,用來接收 push、提供 pull/clone,確保多人協作安全高效。


2.git文件夾中都有什么東西


3-def div(self, file)有什么作用

在 Python 3 里,__div__ 方法已經廢棄,它原本是 Python 2 中用來定義“除法運算符 /”的行為的。


? 正確用法(Python 3)

Python 3 中,除法運算符 / 對應的是:

def __truediv__(self, other):...

而整除運算符 // 對應的是:

def __floordiv__(self, other):...

? __div__ 的問題

如果你在 Python 3 中寫了:

def __div__(self, file):...

不會被調用,因為 Python 3 中 / 運算符只會查找 __truediv__,而不是 __div__


? 示例(Python 3)

class MyNumber:def __init__(self, value):self.value = valuedef __truediv__(self, other):return MyNumber(self.value / other.value)def __floordiv__(self, other):return MyNumber(self.value // other.value)a = MyNumber(10)
b = MyNumber(3)print((a / b).value)   # 輸出 3.333...
print((a // b).value)  # 輸出 3

? 總結

方法名運算符Python 2Python 3
__div__/? 有效? 廢棄
__truediv__/? 無? 有效
__floordiv__//? 有效? 有效

? 建議

如果你在維護舊代碼或從 Python 2 遷移到 Python 3,請:

  • __div__ 改名為 __truediv__
  • 檢查是否有 from __future__ import division 的影響

4-什么是備選方案

這段代碼來自一個 Git 倉庫對象(repo)的實現,用來讀取該倉庫的 “alternates” 列表。
“alternates” 是 Git 的一個機制:當倉庫 A 把對象(objects)存放在倉庫 B 的 objects 目錄里時,會在倉庫 A 的 .git/objects/info/alternates 文件中記錄倉庫 B 的 objects 目錄路徑,從而避免重復存儲大對象。

逐行解釋:

  1. alternates_path = os.path.join(self.path, *['objects', 'info', 'alternates'])
    拼出 .git/objects/info/alternates 文件的完整路徑。
    self.path 通常是 .git 目錄本身。

  2. if os.path.exists(alternates_path):
    判斷 alternates 文件是否存在;不存在就說明該倉庫沒有使用 alternates 機制。

  3. 若文件存在,則:

    • 打開文件讀取全部內容到 alts
    • 使用 try/finally 保證無論是否拋異常都會關閉文件句柄。
    • alts.strip().splitlines() 去掉首尾空白并按行切分,得到 “備用對象目錄” 的列表(每個元素是一個絕對或相對路徑)。
    • 返回這個列表。
  4. 若文件不存在,直接返回空列表 []

最終效果:
get_alternates() 返回一個字符串列表,里面是此倉庫通過 alternates 機制引用的其他倉庫的 objects 目錄路徑;如果沒有使用 alternates,則返回空列表。


5-def list_from_string(cls, repo, text)方法

代碼中作者,用到了很多關于list_from_string這個方法

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/91439.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91439.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91439.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java繼承機制詳解:從原理到實戰應用

一、繼承的本質&#xff1a;消除冗余&#xff0c;構建邏輯關系想象一個公司管理系統&#xff1a;普通銷售員工&#xff08;CommissionEmployee&#xff09;和帶底薪銷售員工&#xff08;BasePlusCommissionEmployee&#xff09;共享大部分屬性&#xff08;姓名、工號、銷售額、…

工業數采引擎-DTU

DTU配置注冊包及心跳包(對應設備配置->設備SN)&#xff0c;模塊工作方式&#xff1a;TcpClient&#xff0c;首次連接成功后&#xff0c;DTU發送上來的注冊包作為鏈路SessionId1. ModbusRtu設備 -> Dtu -> Server2. DLT645設備 -> Dtu -> Server3. 自定義設備 -&…

AttributeError: ChatGLMTokenizer has no attribute vocab_size

請問運行下面語句tokenizer AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_codeTrue) 出現“AttributeError: ChatGLMTokenizer has no attribute vocab_size”是版本不一致&#xff0c;需要舊的版本卸載conda uninstall transformers或者pip un…

14.串口更新FLASH字庫

一、簡介 在使用STM32等單片機驅動顯示屏時&#xff0c;為了顯示中文字體&#xff0c;常用FLASH保存字庫信息。但是字庫的更新通常只能使用SD卡更新&#xff0c;在一些小型單片機系統(如STM32F103C8T6、STC89C52)上&#xff0c;沒有增加SD卡支持的必要。為解決此問題&#xff0…

Lombok常用注解及功能詳解

Lombok常用注解及功能詳解一、Lombok簡介與環境配置1.1 什么是Lombok&#xff1f;1.2 環境配置1.2.1 Maven項目1.2.2 Gradle項目1.2.3 IDE配置&#xff08;關鍵&#xff09;二、Lombok常用注解詳解2.1 Data&#xff1a;一站式生成核心方法2.2 Getter/Setter&#xff1a;單獨生成…

應用分層

應用分層是?種軟件開發設計思想&#xff0c;它將應用程序分成N個層次&#xff0c;這N個層次分別負責各自的職責&#xff0c; 多個層次之間協同提供完整的功能。根據項目的復雜度&#xff0c;把項目分成三層&#xff0c;四層或者更多層。常見的MVC設計模式&#xff0c;就是應用…

[特殊字符] 【JAVA進階】StringBuilder全方位解析:從使用到源碼,一文搞定!

&#x1f525; 掌握StringBuilder&#xff0c;讓你的Java字符串操作性能飆升&#xff01;&#x1f9e9; StringBuilder是什么&#xff1f; StringBuilder是Java中用于動態構建字符串的可變字符序列類&#xff0c;位于java.lang包中。與不可變的String類不同&#xff0c;StringB…

Redis 數據結構全景解析

Redis 不是簡單的 key-value 緩存&#xff0c;它更像一把“瑞士軍刀”。 只要掌握數據結構&#xff0c;就能把同一份內存用出 10 倍效率。0. 開場白&#xff1a;為什么聊數據結構&#xff1f; 面試常問“Redis 有幾種數據類型&#xff1f;”——很多人答 5 種&#xff08;Strin…

ansible.cfg 配置文件的常見配置項及其說明

配置項說明默認值defaults默認配置部分inventory指定清單文件的位置&#xff0c;可以是文件路徑、目錄或動態清單腳本。/etc/ansible/hostsremote_user默認的遠程用戶roothost_key_checking是否啟用主機密鑰檢查。設置為 False 跳過 SSH 主機密鑰驗證。Trueask_pass是否在執行時…

Effective C++ 條款15:在資源管理類中提供對原始資源的訪問

Effective C 條款15&#xff1a;在資源管理類中提供對原始資源的訪問核心思想&#xff1a;RAII類需要提供訪問其封裝原始資源的顯式或隱式接口&#xff0c;以兼容需要直接操作資源的API&#xff0c;同時維持資源的安全管理。 ?? 1. 原始資源訪問的必要性 使用場景示例&#x…

Linux 進程管理與計劃任務設置

Linux 進程管理與計劃任務設置一、進程管理進程管理用于監控、控制系統中運行的程序&#xff08;進程&#xff09;&#xff0c;包括查看進程狀態、調整優先級、終止異常進程等。以下是核心命令及操作說明&#xff1a;1. 常用進程查看命令&#xff08;1&#xff09;ps&#xff1…

MYSQL數據庫之索引

1、引入索引的問題在圖書館查找一本書的過程&#xff0c;可類比數據庫查詢場景。在一般軟件系統中&#xff0c;對數據庫操作以查詢為主&#xff0c;數據量較大時&#xff0c;優化查詢是關鍵&#xff0c;索引便是優化查詢的重要手段 。2、索引是什么索引是一種特殊文件&#xff…

ArcGIS以及ArcGIS Pro如何去除在線地圖制作者名單

問題&#xff1a;ArcGIS和ArcGIS Pro提供了許多在線地圖服務&#xff0c;但是這些地圖會自動生成制作者名單&#xff0c;如下圖所示&#xff1a; 在線地圖加載方式可參考&#xff1a;如何在ArcGIS和ArcGIS Pro中添加在線底圖 這在出圖時有時會造成圖的部分信息遮擋或出圖不美觀…

InfluxDB 與 Golang 框架集成:Gin 實戰指南(二)

四、實際應用案例4.1 案例背景某智能工廠部署了大量的物聯網設備&#xff0c;如傳感器、智能儀表等&#xff0c;用于實時監測生產線上設備的運行狀態、環境參數&#xff08;如溫度、濕度&#xff09;以及生產過程中的各項指標&#xff08;如產量、次品率&#xff09;。這些設備…

Linux系統磁盤未分配的空間釋放并分配給 / 根目錄的詳細操作【openEuler系統】

選擇 Fix 修正 GPT 表 輸入 Fix 并按回車&#xff0c;parted 會自動&#xff1a; 擴展 GPT 表的 結束位置 到磁盤末尾。釋放未被使用的空間&#xff08;1048576000 個 512B 塊&#xff0c;約 500GB&#xff09;。 驗證修正結果 修正后&#xff0c;再次運行&#xff1a; parted …

王道考研-數據結構-01

數據結構-01視頻鏈接&#xff1a;https://www.bilibili.com/video/BV1b7411N798?spm_id_from333.788.videopod.sections&vd_source940d88d085dc79e5d2d1c6c13ec7caf7&p2 數據結構到底在學什么? 數據結構這門課他要學習的就是怎么用程序代碼把現實世界的問題給信息化&…

k8s云原生rook-ceph pvc快照與恢復(上)

#作者&#xff1a;Unstopabler 文章目錄前言部署rook-ceph on kubernets條件Ceph快照概述什么是PVC安裝快照控制器和CRD1.安裝crds資源2.安裝控制器3.安裝快照類前言 Rook 是一個開源的云原生存儲編排器&#xff0c;為各種存儲解決方案提供平臺、框架和支持&#xff0c;以便與…

springcloud04——網關gateway、熔斷器 sentinel

目錄 注冊中心 nacos | eurekaServer |zookeeper(dubbo) 配置中心 nacos | config Server 遠程服務調用 httpClient | RestTemplate | OpenFeign 負載均衡服務 ribbon | loadbalancer 網關 zuul | gateway 熔斷器 hystrix | sentinel 網關 sentinel 流控 壓測工具 1…

XSS跨站腳本攻擊詳解

一、XSS攻擊簡介跨站腳本攻擊的英文全稱是Cross-Site Scripting&#xff0c;為了與CSS有所區別&#xff0c;因此縮寫為“XSS”由于同源策略的存在&#xff0c;攻擊者或者惡意網站的JavaScript代碼沒有辦法直接獲取用戶在其它網站的信息&#xff0c;但是如果攻擊者有辦法把惡意的…

Linux /proc/目錄詳解

文章目錄前言文件說明注意事項前言 在 Linux 系統中&#xff0c;/proc 目錄是一個特殊的虛擬文件系統&#xff0c;它提供了對系統內核和進程的訪問。/proc 目錄中的文件和目錄不是真實存在的&#xff0c;它們是在運行時由內核動態生成的&#xff0c;用于提供系統和進程的相關信…