[hg]研究筆記 working directory 以及 commit

  • 6194
  • 0

[hg]研究筆記 working directory 以及 commit

在 Bryan O'Sullivan 寫的 Mercurial: The Definitive Guide [1] 有提到

When you explicitly hg add, hg remove, hg rename or hg copy files, Mercurial updates the dirstate so that it knows what to do with those files when you commit.

也就是每當使用者明確的執行 hg add, hg remove, hg rename, hg copy 這幾個指令的時候,Mercurial 會更新 dirstate,如此在執行 commit 時,可以用 dirstate 與 working directory 裡的檔案做比對。

在做 commit 的時候,就是 commit 一個新的 changeset。這個動作會更新每個被更改過的檔案的 Revlog,還有現在版本的 Manifest,以及 Changelog。commit 是一個兩階段的過程,首先由上而下,從 changelog 到 menifest 再到 files。第二階段由下而上,從 files 到 manifest 再到 chnagelog。

以下是翻話官網的內容:

第一階段(由上而下)

在這個階段的第一步是得到父版本的 changelog。changelog 是個虛擬的檔案,是概念上的存在,而不一定要在實體上存在於 repository。其實,它以 revlog 的型態存在。就像所有你所追蹤的檔案一樣。當有需要的時候,從 revlog 可以組回任何版本的 changelog。

對於每一個 repository 的版本 Changelog 有一個 version (one entry in its revlog)。每一個version 的 changelog 有存一個 meta infomation,例如 commit 的 timestamp、commit 的 username 以及 commit log。最重要的是裡面還存了一個 nodeid,它指示了一個 manifest 的特定版本。

跟 changelog 一樣,manifest 也是一個有版本的、虛擬的檔案。它有自己的 revlog,在 changelog 裡所指定的 nodeid 會在 manifest 的 revlog 裡對應到一個唯一的 entry(換句話說就是特定版本的 manifest)。所以第二步就是找到 changelog 所指定的 nodeid,然後把那個版本的 manifest 取回來。在這個步驟中,現在的 manifest 的版本還是使用父版本。

每一個版本的 manifest 就像是 repository 的某個時間點的檔案們的快照(換句話說就是 repository 的特定版本)。Manifest 並不直接儲存檔案的內容,而是儲存每個被追蹤的檔案的 nodeid。就像 manifest 的 nodeid 存在於 changelog 一樣,每一個在 manifest 存的 nodeid 都指定一個檔案的某個特定版本(也就是在檔案的 revlog 的某個特定的 entry)。

第一階段的最後一個步驟,就是把追蹤的檔案中有被更改的檔案們,依照 manifest 裡所指定的特定版本取回來,這些都是父版本的檔案。

要注意的是這個階段只是把 repository 更新至父版本,但這個更新只是在記憶體裡做,而不是更新到磁碟或檔案系統裡。

第二階段(由下而上)

當父版本的 changelog、manifest、檔案們都重建回來,第二階段可以開始為所有受影響的revlog 建立新的 entry。建立新的 revlog 的 entry 的第一步是決定新的 nodeid,這在 revlog 裡會唯一地標示出那個 entry。Nodeid 是由 hash 兩個父版本的 nodeid 以及完整的新版本的檔案內容。要記得的是那兩個父 nodeid 與父 ChangeSetId 不同。父 nodeid 是標識,為了在同一個 revlog 裡標定其他 entry。ChangeSetId 只是 changelog 的一個父 id,不是給檔案的 manifest 使用。對於 manifest,它的父 nodeid 是被指定於父版本的 changelog 裡。對於檔案們,它們的父 nodeid 被指定於父版本的 manifest 裡。

因為 nodeid 需要新版本檔案的完整內容,所以第二階段需要由下而上。受追蹤的檔案的新版本的 nodeid 需要先被計算,然後 manifest 裡的值使用新版本的 nodeid 就建立了新的 manitest。

當新版本的 manifest 準備好,新的 manifest 的 nodeid 才能被計算。這樣才能產生新的 changelog,然後是產生 changelog 的新 nodeid,這才是新版本的 repositoy 的 ChangeSetId。

其實 commit 還有最後一個階段是真正地更新 changelog、manifest 以及所有被更動過的檔案的 revlog。這動作放最後的原因是因為每個 revlog 的 entry 有包含對應的 ChangeSetId for repository,那個 ChangeSetId 不到最後沒辦法知道。

 

[1] http://hgbook.red-bean.com/read/behind-the-scenes.html

[2] http://mercurial.selenic.com/wiki/ChangeSet

 

 

 

分享