🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

我使用 Git 已經很多年了。提交、推播、拉取,偶爾遇到問題也會感到恐慌。但如果你問我執行git commit時到底發生了什麼,我會給你一個模糊的答案,例如“保存更改”,並希望你不要再問更多問題。

這讓我很困擾。所以我用 Rust 建構了自己的版本控制系統Veridian 。不是因為世界需要另一個 Git,而是因為我需要理解我們已經擁有的 Git。

事實證明,Git 比我想像的要簡單得多。

為什麼每個人都覺得 Git 令人困惑

我們學習 Git 的方式是倒著學的。我們記住命令,卻不理解它們的作用。 git git add暫存檔案。好吧,但暫存到底是什麼意思呢? git commit儲存你的工作。很酷,但保存在哪裡,又是如何保存的呢?

我花了好幾年時間只是遵循命令。我會用 Git,但不懂它。建立 Veridian 改變了這一切。

Git 到底是什麼

撇開所有指令和功能,Git 只是一個內容可尋址的儲存系統。聽起來很複雜,但其實不然。

你有一個儲存系統,它根據內容而不是名稱來保存資料。輸入兩次相同的內容?儲存位置相同。更改一個字元?儲存位置不同。

這就是 Git。 “位置”是 SHA-1 雜湊值。 「儲存」是.git/objects資料夾。

三種類型的物件

Blob是文件內容。取出你的文件,加入類似blob <size>\0頭,對其進行哈希處理、壓縮、儲存。搞定。 Git 不關心檔名,只關心內容。

是目錄列表。它們透過列出檔案和資料夾及其雜湊值來表示「這個資料夾是這樣的」。樹指向 blob 和其他樹。

提交是帶有上下文的快照。每個提交都指向一棵樹(專案的樣子),指向父親提交(先前的提交),並包含作者、時間和訊息等元資料。

三種物件類型。這就是整個系統。

為什麼哈希系統如此智能

同一個文件在多個提交中?只保存一次。在一個大文件中更改了一行?只儲存新版本。想檢查兩個檔案是否相同?比較哈希值,立即獲得答案。

Git 不會一遍又一遍地複製你的專案。它只會儲存獨特的片段,並根據這些片段建立快照。這就是為什麼包含數百個提交的倉庫並不會變得龐大。

分支只是文件

分支實際上是一個帶有提交哈希的文件。

檔案.git/refs/heads/main包含 40 個字符,這是你最新提交的哈希值。當你建立分支時,Git 會使用目前提交的雜湊值寫入一個新檔案。當你提交時,Git 會使用新的雜湊值更新該檔案。

無需複製。只需更新一個小文字檔案。這就是分支“輕量級”的原因。

壓縮部分很酷

Git 使用 zlib 在儲存所有內容之前進行壓縮。因此,你的目標檔案不僅僅是原始內容,而是經過壓縮的。當我建立 Veridian 時,每次讀寫都必須處理這種壓縮和解壓縮。

具體過程如下:Git 取得你的 blob(包含檔案頭),壓縮後儲存在.git/objects/ab/cdef123...中,其中ab是哈希值的前兩個字符, cdef123...是剩餘部分。拆分兩個字元只是為了避免在一個目錄中存放數千個文件,因為這會降低檔案系統的執行速度。

讀取文件意味著找到文件,用 zlib 解壓縮,解析文件頭檢查物件類型和大小,然後返回內容。 Rust 的標準函式庫沒有內建 zlib,所以我使用了flate2 crate 來實作。大概用了 5 行程式碼。

建造 Veridian 教會了我什麼

我以為建置版本控制系統會很難。但事實並非如此。

Veridian 建築

使用 Rust 進行建置非常有趣,因為 Rust 會讓你思考記憶體和所有權。當你對檔案進行哈希處理並建立樹時,你需要妥善處理錯誤(如果檔案不存在怎麼辦?)並謹慎管理緩衝區(你不能直接將一個 5GB 的檔案載入到記憶體中)。

但說實話,版本控制邏輯本身就很簡單。我的大部分程式碼只是讀取檔案、計算 SHA-1 雜湊值以及寫入壓縮資料。難點不在於演算法,而是理解 Git 到底在做什麼。

初始化命令

建立一個.veridian資料夾。新增用於存放物件和引用的子資料夾。建立一個 HEAD 檔案。完成,你就有了一個倉庫。

哈希物件指令

讀取文件,加入文件頭,用 SHA-1 演算法計算雜湊值,用 zlib 壓縮,然後寫入.veridian/objects/ 。返回哈希值。這就是檔案進入系統的過程。

寫樹命令

遍歷一個目錄。對每個檔案進行哈希處理(產生 blob)。將所有檔案的名稱和哈希值放入一個樹形物件中。對這棵樹進行哈希處理。現在你就得到了目錄的快照。

我學到了一件事:樹狀結構條目需要按檔案名稱排序。如果不排序,相同的目錄結構會根據檔案處理順序產生不同的雜湊值。 Git 會對其進行排序以保持雜湊值的一致性。雖然細節不多,但很重要。

提交樹命令

取一個樹形哈希。如果有父提交哈希,則加入。新增作者資訊和時間。新增訊息。對所有內容進行哈希處理。寫入物件。更新分支指針。更新 HEAD。這就是一次提交。

該實現出奇的小。它的運作方式與 Git 類似,因為 Git 實際上就是這麼簡單。

有趣的是:Git 將時間戳記儲存為 Unix 時間戳記(自 1970 年以來的秒數),並附帶時區資訊。因此,提交物件包含類似1760211794 +0530的內容,這是時間戳記和時區偏移。當你git commit ,它會取得你的系統時間和時區。我使用了 Rust 的chrono crate 來實現這一點,但你可以使用任何語言來實現。

最終成功的事情

Git 速度快的原因:它比較的是雜湊值,而不是檔案內容。 40 個字元的字串。超快。

為什麼會出現 HEAD 分離的情況: HEAD 通常指向一個分支文件,而該文件又指向一個提交。直接檢出提交? HEAD 會跳過分支,直接指向提交。 HEAD 分離是因為你不在分支上,而是在特定的提交上。

為什麼可以恢復已刪除的提交:它們仍然在.git/objects中,只是沒有被引用而已。使用git reflog ,找到哈希值,然後恢復。只有垃圾回收才能真正刪除它們。

合併衝突的原因:兩個提交具有相同的父級,但對同一文件進行了不同的更改。 Git 無法決定哪一個提交勝出。它需要您來決定。

我學到了什麼

Git 之所以難,不是因為它複雜,而是因為我們學錯了。

一旦你明白了 Git 是一個鍵值儲存系統,其中鍵是內容哈希,值有三種類型(blob、tree、commit),那麼一切都說得通了。分支是指針。合併是將樹合併在一起。變基是將提交重播到不同的父級。

我使用 Git 很多年,卻一直搞不懂。後來我花了一週時間搭建了 Veridian,突然間 Git 就變得有意義了。不是因為搭建過程很神奇,而是因為它迫使你去理解正在發生的事情。

為什麼你應該嘗試一下

你不需要打造完美的東西。只要開始就好。

建立一個倉庫。將檔案儲存為 blob。建構一棵樹。進行一次提交。做好這四件事,你會比大多數開發人員更了解 Git。

建造 Veridian 大概花了一周時間。現在我用 Git 的時候,我其實知道發生了什麼事。只是一些資料結構和文件操作。沒什麼複雜的。

Veridian 並不完美。它缺少一些功能,可能還有一些 bug。但它教會了我 Git 的工作原理,這才是重點。

如果你想真正學習 Git,而不僅僅是使用它,那就去建立一些東西。即使它很小。即使它出了問題。你花一周時間建立它,比花幾個月閱讀文件學到的更多。

在 GitHub 上查看 Veridian 。打破它,修復它,從中學習。這就是它的工作原理。


原文出處:https://dev.to/kayleecodez/i-built-git-from-scratch-to-finally-understand-what-ive-been-using-for-years-37a9


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝22   💬9   ❤️6
601
🥈
我愛JS
📝4   💬14   ❤️7
271
🥉
御魂
💬1  
3
#4
2
#5
1
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付