distributed even if your workflow isnt
distributed is the new centralized.
distributed is the new centralized.
Git靠在檔案裡面記錄的內容來維護自己的狀態,Daemon看使用者的位置在哪個容器,就對哪個容器作事情。
目錄名稱有.git就是裸容器
- 用檔案內容經過SHA1產生唯一ID,長度為160bit,只需比對ID,就可得知檔案是否相同
- 用ID的前16bit做目錄名稱分配,避免同一目錄檔案過多,造成效能低下。
- 160bit的SHA1 ID不只用在內容,Git內部的資料結構,全部都使用SHA1,blob,tree,commit,tag,全部都有自己的SHA1 ID。架構簡潔,容易了解。
- 整個系統的資料結構,只有blob、tree、commit、tag,非常簡單。
- 同一個檔案,如果做了更動,ID就會不同,也會將內容存成一個新的blob,相較於其他SCM是記錄檔案變動,較耗空間。所帶來的好處更多?
- 不以檔案系統的檔名與路徑來決定內部管理資料結構,避免與檔案系統內容綁死,像是改名,追蹤歷史,都以ID來判斷。
- 只要檔案內容不改變,不管是更名,分支,搬移,複製,全部都不需要再多一份檔案。
- 透過維護Tree Object把所有git相關設定放在容器根目錄底下,避免四散設定檔
- 沒有所謂daemon或是Servcie,全部都是有需要才執行功能,功能執行根據容器的內容來處理。這樣省記憶體,省CPU時間。
- 本來想說要是檔案有好幾百萬,那光是開個tree就會很耗記憶體,搜尋與比對檔案內容是否相同,也非常花時間。Git的tree裡面可以有tree,所以tree裡面的內容會因檔案的樹狀結構而分散,除非極端的狀況,同一個目錄就幾百萬個檔案,不然一般的使用狀況,tree檔案的內容應該都很小。
- 是不是多個專案都應該放在一個容器裡面?在Ruckus用Perforce是這麼做,在網龍用Subversion是分開,在Git裡面應該是?因為在Git裡面的branch都是以容器為單位,commit物件也是追蹤容器,所以如果多個專案在同一個repository內,沒有辦法區分出各專案的歷史。而Perforce與Subversion都是以檔案為單位,可以用資料夾來區分歷史,所以可以把多個Project放在同一個Repository裡面
- Git以內容來區分,是否有可能做跨容器的Merge,Merge好之後可以找到某個檔案在兩個不同容器的Branch的所有歷史?
CH4 基本的Git概念
四個基礎物件:
- Blobs:所有資料都是Blob,用SHA1演算法來產生唯一的資料代碼,用來識別資料,比對檔案是否相同,與對資料定址。
- 樹(Tree):代表與處理目錄資訊
- 送交(Commit):代表與處理送交,容器內的歷史就是由送交所組成。
- 標籤(Tag):代表特定的物件,大多是送交物件
Git就用這四個物件,來建構出容器所有的歷史,並且讓重複的內容,只佔用一個空間。
四種物件都會有自己的SHA1雜湊值
索引(Index):Git送交之前的暫存狀態,在送交之前可自由修改。
CH5 檔案管理與索引
Git將檔案分為三個群組:
被追蹤:顧名思義
被忽略:不需管理檔案,*.obj,git管理用檔案.git/,使用者可以自定
不被追蹤:要被處理的檔案。
CH6 送交
每一個送交都會指向前一個送交,經過一段時間,一連串的改變可以用一系列的送交來表示,在送交的時候,必須要跟之前的送交比對有多少改變,這件事不需每一個檔案都作比較,只需比對相對應的Blobs與子樹物件即可,如此樹與樹比對的作法,大大減少遞迴比對到每一個檔案的計算與時間成本。
ref:參照至某個Git物件儲存中物件的SHA1雜湊辨識碼
symref:指向某個Git物件的名稱,也稱作符號參照
dev分支名稱是symref,參照到refs/heads/dev,這是個ref
Git的ref有三個命名空間:
- refs/head/ref:本地名稱
- refs/remotes/ref:遠端名稱
- refs/tags/ref:標籤
可以看到,對於Git而言,只有本地,遠端兩種分別,不管遠端在哪裡,不管遠端扮演什麼角色,沒有其他邏輯,這也是一個分散式系統實作的思維,簡化處理與遠端的互動。
有四個特殊的symref
HEAD:指向目前分支最新的送交
ORIG_HEAD:Merge或Rebase時,目前分支的HEAD
FETCH_HEAD:git fetch下來的分支的HEAD,只有在git fetch之後有效,處理完就沒了。
MERGE_HEAD:Merge時,外部來源分支的HEAD
HEAD~2:HEAD的父親
HEAD^2:HEAD父親的兄弟
Git使用DAG(有向非循環圖)來組織送交
CH9 合併
合併必須發生在單一容器內,也就是說,所有要被合併的分支都必須在相同的容器內。分支如何出現在容器內並不重要。遠端的分支,要作合併也是把分支從遠端下載回來,然後再與本地分支作合併。-->這個作法提供了一個分散式系統實作的思維,單一系統不在遠端作操作,而是把資料從遠端抓回來之後再處理,邏輯就是以本地邏輯,這樣可以簡化系統的複雜度。
合併過來就產生一個新的送交,來表示新的統一狀態。
處理合併的方式,就是沿著送交歷史,比對送交物件,在這邊,SHA1雜湊代碼的功用又出現了,不需一路比對到每一個Blog,只需比對送交物件的SHA1雜湊代碼就可以知道是否為相同送交。
退化的合併:
- 已是最新:我有動,其他人沒動,當作是合併的終止條件。
- 快轉:我沒動,其他人有動,把所有外來的送交加到我的HEAD上,然後修改HEAD指到最新的送交。
CH10 修改送交
未發布的送交記錄可以隨意修改,已發布的送交記錄請勿修改。這也是設計分散式系統應該要遵守的原則,各自獨立,一旦有互動就要遵守約定。
我目前最理解可以使用Rebase的點,就是當自己在分支上進行開發,產生了一連串的送交,然後從中央容器
CH11 遠端容器
Git記錄容器內所有分支的歷史記錄,完全不用向Server查詢,用磁碟空間來取得容器的獨立性
Git複製出來的容器與原始容器完全對稱,所以可以獨立自主。-->分散式系統的一個點應該與其他點完全對稱,可以獨立處理所有的事情,但也可以互相協調讓輸出量更大。
Git的本地分支可以設定遠端追蹤分支,只要送交物件相同,就可以作合併與rebase,提供使用者彈性。
Git用email地址識別唯一使用者,認證的部份交由外部系統來作,使用者管理系統每個環境都用,Git不需再作一個。而且因為email地址是全球唯一,所以在沒有中央Server的Git環境中,也能辨識出單一使用者。
裸容器的概念是為了要整合最終結果,一個專案可以不只有一個裸容器,可以因為政治,技術,地理,任務等等方式,幾個容器的擁有人共同協商出一個裸容器。
CH12 管理容器
Git的分散式開發模型可以使用中央管理容器,每個開發者仍然可以擁有私人的容器拷貝並能夠獨立作業,工作是分散的。
Git沒有唯一真實的歷史記錄,只要有測試過,push/patch進來,結果正確就可以。所以送交物件,只會有先後順序,時間是第二順位。換言之,時間以及空間的操作都不是好選項,所以Git必定考慮到量子物理的影響。-->分散式系統地點分散,作業流程分散,要求時間同步當然也不可能,Git操作的是資料,所以一切以資料的順序為主,這也是可以應用在分散式系統設計的觀念。
CH15 結合專案
http://quickgit.kde.org/ KDE所有專案的容器,可以參考它組織整個KDE產品的架構
http://techbase.kde.org/Development/Git KDE從SVN轉到Git的官方資訊
http://www.omat.nl/2010/10/03/git-kde-org-available/ KDE從SVN轉到Git的相關資訊
要在容器裡面匯入其他專案的函式庫,在Git裡面有以下解法:
- 使用拷貝來匯入子專案:如果是外部函式庫,且不會在開發過程中有任何改變,或是很少的更新,外部函式庫的歷史對容器並不重要。
- 使用git pull -s subtree來匯入子專案:會從子專案中合併整個歷史記錄,好處是當子專案在外部被更新,本地容器也可以透過git pull -s subtree來更新,還可以保留子專案的歷史記錄。但是如果要把對子專案的修改上傳,邏輯上就會破壞子專案本身自己的歷史記錄,這個結果子專案的開發者沒有辦法接受。
- 使用客制化腳本程式來取出子專案:在容器內git clone子專案內容,然後忽略子專案在容器中的內容,讓專案可以編譯,如果想改子專案內容,進到子專案目錄,就是另一個容器。如果需要修改子專案內容且push回去,也是可以的,但是遇到分支,就又需要手動處理一次。有人已經寫出自動管理在容器內的子專案容器的Script。
- 原生的解法,使用gitlinks與git submodule:
CH16 在Subversion容器上使用Git
需要把SVN當作是Git的中央容器,有點違背Git分散式的原則,但沒差。
一個容器可以下載所有分支,也可以一個容器一個分支
使用Git svn,可以離線工作,查詢記錄的速度也更快。
使用Git svn,可以減低SVN Server負擔,加快整體開發速度。
使用Git svn,可以作到在SVN的環境下,沒有人知道你在使用Git。