2013年4月9日 星期二

筆記:版本控制Git

distributed even if your workflow isnt
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概念

四個基礎物件:
  1. Blobs:所有資料都是Blob,用SHA1演算法來產生唯一的資料代碼,用來識別資料,比對檔案是否相同,與對資料定址。
  2. 樹(Tree):代表與處理目錄資訊
  3. 送交(Commit):代表與處理送交,容器內的歷史就是由送交所組成。
  4. 標籤(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有三個命名空間:
  1. refs/head/ref:本地名稱
  2. refs/remotes/ref:遠端名稱
  3. 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雜湊代碼就可以知道是否為相同送交。

退化的合併:
  1. 已是最新:我有動,其他人沒動,當作是合併的終止條件。
  2. 快轉:我沒動,其他人有動,把所有外來的送交加到我的HEAD上,然後修改HEAD指到最新的送交。

CH10 修改送交
未發布的送交記錄可以隨意修改,已發布的送交記錄請勿修改。這也是設計分散式系統應該要遵守的原則,各自獨立,一旦有互動就要遵守約定。
我目前最理解可以使用Rebase的點,就是當自己在分支上進行開發,產生了一連串的送交,然後從中央容器

CH11 遠端容器
Git記錄容器內所有分支的歷史記錄,完全不用向Server查詢,用磁碟空間來取得容器的獨立性
Git複製出來的容器與原始容器完全對稱,所以可以獨立自主。-->分散式系統的一個點應該與其他點完全對稱,可以獨立處理所有的事情,但也可以互相協調讓輸出量更大。
Git的本地分支可以設定遠端追蹤分支,只要送交物件相同,就可以作合併與rebase,提供使用者彈性。
Git用email地址識別唯一使用者,認證的部份交由外部系統來作,使用者管理系統每個環境都用,Git不需再作一個。而且因為email地址是全球唯一,所以在沒有中央Server的Git環境中,也能辨識出單一使用者。
裸容器的概念是為了要整合最終結果,一個專案可以不只有一個裸容器,可以因為政治,技術,地理,任務等等方式,幾個容器的擁有人共同協商出一個裸容器。

CH12 管理容器
Git的分散式開發模型可以使用中央管理容器,每個開發者仍然可以擁有私人的容器拷貝並能夠獨立作業,工作是分散的。
Git沒有唯一真實的歷史記錄,只要有測試過,push/patch進來,結果正確就可以。所以送交物件,只會有先後順序,時間是第二順位。換言之,時間以及空間的操作都不是好選項,所以Git必定考慮到量子物理的影響。-->分散式系統地點分散,作業流程分散,要求時間同步當然也不可能,Git操作的是資料,所以一切以資料的順序為主,這也是可以應用在分散式系統設計的觀念。


CH15 結合專案

https://projects.kde.org/ KDE專案列表說明
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裡面有以下解法:

  1. 使用拷貝來匯入子專案:如果是外部函式庫,且不會在開發過程中有任何改變,或是很少的更新,外部函式庫的歷史對容器並不重要。
  2. 使用git pull -s subtree來匯入子專案:會從子專案中合併整個歷史記錄,好處是當子專案在外部被更新,本地容器也可以透過git pull -s subtree來更新,還可以保留子專案的歷史記錄。但是如果要把對子專案的修改上傳,邏輯上就會破壞子專案本身自己的歷史記錄,這個結果子專案的開發者沒有辦法接受。
  3. 使用客制化腳本程式來取出子專案:在容器內git clone子專案內容,然後忽略子專案在容器中的內容,讓專案可以編譯,如果想改子專案內容,進到子專案目錄,就是另一個容器。如果需要修改子專案內容且push回去,也是可以的,但是遇到分支,就又需要手動處理一次。有人已經寫出自動管理在容器內的子專案容器的Script。
  4. 原生的解法,使用gitlinks與git submodule:


CH16 在Subversion容器上使用Git

需要把SVN當作是Git的中央容器,有點違背Git分散式的原則,但沒差。
一個容器可以下載所有分支,也可以一個容器一個分支

使用Git svn,可以離線工作,查詢記錄的速度也更快。
使用Git svn,可以減低SVN Server負擔,加快整體開發速度。
使用Git svn,可以作到在SVN的環境下,沒有人知道你在使用Git。

2013年4月6日 星期六

筆記:Perforce The Flow of Change

描述軟體開發的理想世界

在理想世界我們只有一個版本,沒有bug,有足夠的時間開發,開發不會延遲。但是現實世界並非如此,所以我們有了軟體版本控制系統,我們有了分支。

限制分支的數量,把分支看成一個一個stage(development、QA、Beta test、Live),來處理短周期Release,避免分支過多,不管Release再怎麼短,Release Branch只會有三個(所以並不會限定development branch),這個作法稱為staging codeline。

從mainline中分支出development分支,來處理不同開發者,不同團隊開發延遲或是錯誤的程式碼造成無法Release的問題

除非開發者的成果可以正常編譯且功能正常,否則不會進mainline,達成mainline可以維持在理想狀態不斷前進

用Map(地圖)、Protocols(協定)、Convention(公約)、Etiquette(禮讓)來讓Branch可以被管理,
Ex:道路

codeline=觀念
branch=實作
codeline分支出來的起源稱為baseline
沒有baseline就稱為mainline
aka=also known as


tofu scale:越上面越硬(可測試,接近Release,也就是穩定),越下面越軟(不穩定,離Release遠),這個等級代表改變codeline的風險程度。mainline在中間的位置,每個codeline的tofu scale相對於mainline,注意:親子codeline可以表示相對硬度,兄弟codeline只能跟baseline比較

staging codeline與tofu scale在mainline上合體,形成可以兼顧Release與development的分支策略

以map方式表達分支狀態,處理兄弟codeline表示的問題,表達Change該如何流動

使用Change Flow的策略,完成把所有Change Flow全部匯流到mainline的理想,所以mainline只會穩定的成長

Change Flow只能在codeline與它的baseline之間流動,所以一個codeline的改變只能流到baseline,基本上不能跳著流動

只接受穩定的改變,決不強加不穩定的改變

一但relase line被改變,即刻把change整合進baseline,因為release line的改變就是bug fixes and patches,所以會讓baseline更穩定。

絕對不能從baseline把change送進release codeline,因為release line有被更多的測試,所以更加的穩定,而baseline的change會造成release line的穩定度下降,這個原則經常被違反。

change應該被持續整合到development codelines,可以帶給development更多的穩定度,而且可以反映最新的狀況。

development codeline不是持續整合到baseline,而是分批整合到baseline,要整合到baseline,必須滿足以下條件:
     要實作的功能都已實作到一個好的點(Point of completion),實際上可能還有bug,只要不打亂baseline就OK,
     通過development line自己的test
     baseline要是正常狀態(不能有build is broken)
     通過baseline的test

module:一個包含檔案的目錄架構,檔案的內容就是軟體的一部份,可稱為source tree、component、subsystem、folder,module裡面的相對路徑是該元件能夠正確運作不可或缺。

codeline就是module的集合,通常mainline會包含所有的module,分支出去的codeline,會包含所有或是部份的module。

module分成幾個種類:
     Active:就是要被開發的module,就是branch出codeline的理由,隨著工作進行不斷改變,會流向baseline或是從baseline流過來,例如正在開發的物品使用功能。而流向的規則就是比較與baseline的軟硬。
     Inactive:拿來作編譯、測試、除錯,並不會隨著工作進行而改變,流向也只會從baseline流過來,例如公司內部開發的數學運算函式庫,從mainline分支過來某遊戲的development codeline之後,就只是叫用功能而已,不會作改變,當這個函式庫開發者作了大更新到mainline上面,而這個更新的功能codeline有需要,就會從mainlien merge過來。如果是外部的第三方函式庫,可能連更新都不會更新。
     Private:是baseline的縮減版,在開發時期會持續改變,但是不會與baseline有任何流動,例如bin/,內容都是編譯出來的,或許包含設定檔,但這些設定檔也是給debug或測試用,不會回到baseline。

根據以上定義,不同的Module與baseline的互動,可以歸納出如下的結果:
Active:只需要merge
Inactive Module:只需要copy
Private Moduel:什麼都不用作

雖然coding&mering會破壞穩定性,但這是一定要作的事情,不然就不會有產品。但是可以把風險集中在某些地方,因此coding&merging應該在soft codeline。

所以要"Merge down, copy up",從Release line到mainline作merge,從mainline到development line作merge,然後development要作好編譯與測試,然後copy回去mainline。此外,在copy回去mainline的這段時間,mainline必須是被鎖定的,這樣會中斷其他開發者的開發?所以流程必須要建立在development line常常從mainline merge過來,而且有畫分好Active module與inactive module,測試也要很快就能測試結束的狀態下。

解決分支複雜度的方式,在本文中就是以下四種觀念:
Map(地圖):就是流動的圖,描述軟硬關係,流向
Protocols(協定):就是baseline protocol,Release line到baseline持續Merge,baseline不能流向Release line。baseline持續流向development line,development line分批流向baseline。
Convention(公約):分成Active,InActive,Private三個Module,"Merge down,Copy up"。
Etiquette(禮讓):進baseline要切出空檔時間,讓不同Team&Member copy回去,其他人禮貌地等待。


心得
公司裡所有的專案與元件全部都會在mainline上面,也就是直接在Perforce的depot下。
只要一分支出codeline,擁有者必須要持續維護,還有作測試,雖然不管是perforce,Git,或是Subversion都可以分支,但是如果是牽扯到共同開發的分支,就必須遵守規則,換言之就是維護成本提高,但是如果可以讓事情變得更好,就算成本提高也是值得。

baseline必須有一套測試機制,最好是自動,如果還要動用到QA,那QA會太多工作,讓QA只處理Release line就好。

development line也必須要有一套測試機制,通常可以把baseline的拿來用。

要使用module來表達軟體,就要先把軟體切分好元件,或許一開始沒辦法一步到位,但是可以透過重構的方式來改善。

問題
用copy或是merge在perforce的codeline會有什麼不同的呈現方式?還是用想法管理?
實際將private copy到mainline,可以看到copy的選項與merge不同,基本上copy就是將branch mapping整個蓋過去。所以為防止蓋到不想蓋到的地方,ㄧ定要先作merge,保證沒改動的地方與mainline完全相同之後再copy上去。完成之後在reversion graph看到的與merge一樣,方向也相同,不過可以看到chang list裡面描述的不同,有copy from。那copy到底有什麼意義,經過思考之後,我的理解是:
  1. Merge可選擇來源codeline的change list要不要進目標codeline,可以看到額外的選擇很多,所以是用在從hard to soft,Ex:從Release到mainline
  2. Copy就是從來源codeline覆蓋到目標codeline,所有changelist照單全收,所以適合將新功能進baseline,也就是從soft到hard,Ex:從private到mainline,如果弄了新功能,卻又不想進mainline,那merge也沒有用。真的發生這種狀況,就是codeline的作用定義不清楚。


怎麼在Perforce上分Active、Private、InActive Module?好像也是用想法分?

DevOps Lessons Learned at Microsoft Engineering 筆記

原文: https://www.infoq.com/articles/devops-lessons-microsoft 筆記 組織 講Microsoft裡面的DevOps 故事描述的是Cloud & Enterprise and the Bing ...