Subversion的複製-修改-合併版本控制模型的關鍵是其合併算法,也就是如何處理多個用戶修改同時修改一個文件產生衝突時的算法。Subversion本身只提供了一個這樣的算法,其三方區別算法可以足夠聰明的的行粒度的資料處理,Subversion也支持使用外部(External)比較工具(「外部(External) diff3」一節中有描述),有一些可以做得非常好,或許可以提供以單詞或字母粒度的算法。但是,這些工具的共同點是基於文字的,當你討論非文字文件格式時,這看起來有一點殘酷。如果你無法找到一個工具支持這種類型的合併,你的複製-修改-合併模型就會遇到麻煩。
讓我們看一個使用這個模型的真實例子,Harry和Sally是同一個項目的圖形設計師,汽車技工的間接營銷。海報的設計一個小車,需要一些主要部分的工作,使用PNG文件格式。海報的部署幾乎完成,Harry和Sally都看上了一個從損壞小車得到的特別照片—一個1967的淡藍色的Ford Mustang,擋泥板有一些濺跡。
現在,作為圖像設計的慣例,計劃的改變導致車的顏色很重要,所以Sally將工作副本更新到HEAD,啟動圖形編輯軟體,修改圖像將車的顏色修改為櫻桃紅,同時Harry那一天特別有靈感,所以決定如果這個車受到更大的撞擊可能會有更好的效果。他也更新到HEAD,然後在車擋風玻璃上製作了一些裂痕,他設法在Sally完成前結束修改,因為受到自己不可阻擋天賦的鼓舞,提交了圖像。沒過多久,Sally結束了她的工作,嘗試提交。但是如我們所料,Subversion提交失敗,告訴Sally她的圖像已經過期了。
這裡就是麻煩的地方,如果Harry和Sally修改的是文字文件,她只需要簡單得更新工作副本,接收Harry的修改。在最壞的情況下,他們會修改文件的同一部分,Sally需要人工解決衝突。但是現在不是文字文件—而是二進製圖像,沒法估計合併的結果會是什麼樣子的,已存的軟體不可能從基線圖像分離出Harry和Sally的工作,並組合出一個擋風玻璃壞掉的紅色Mustang。
很顯然,如果能夠將Harry和Sally的工作串行話事情會變得平滑,也就是說Harry可以等到Sally的紅車然後再畫上破壞的擋風玻璃,或者Sally在破壞之後改變顏色。就像在「「複製-修改-合併」方案」一節討論的,如果Harry和Sally之間有完美的交流,就不會有這種問題發生。[15]但是作為一種版本控制系統,實際上是一種交流的形式,使得軟體遵循非並行編輯的串行化也不是一件壞事,這裡Subversion實現了鎖定-修改-解鎖模型,這裡我們要討論Subversion的鎖定特性,與其他版本控制系統的「保留檢出」機制類似。
Subversion 的鎖定特性為兩個主要目的服務:
順序訪問資源。允許用戶得到一個排他的修改文件權,這個用戶可以確定不可合併的修改不會被浪費—他對這個修改的提交會成功。
輔助交流。通過要求用戶對某個版本化對像串行工作,用戶可以知道對像正在被別人修改,這樣可以防止浪費精力和時間去修改一個不可合併和提交的對象。
當我們引用Subversion鎖定特性時,這是在討論一個處理版本化文件的行為特性[16](聲明對一個文件排他性修改特權),包括對文件的鎖定和解鎖(釋放排他性修改權限),察看包括文件被誰鎖定的報告,以及提醒企圖修改鎖定文件的用戶。在本小節,我們會覆蓋鎖定特性的大部分內容。
在Subversion的版本庫,一個鎖是一份Meta資料,可以排它賦予某個用戶修改權,這個用戶被稱作鎖的擁有者。每個鎖都有一個唯一標識,通常是一長串字元,叫做鎖令牌。版本庫管理鎖,控制著鎖的建立,權限控制和刪除。如果提交包含了修改或者刪除鎖
為了描述鎖的產生,我們回到前面那個關於多個圖形設計師共同工作的例子,Harry決定修改一個JPEG圖像,為了防止其他用戶此時提交這個文件的修改(也是警告別人他正在修改它),他使用svn lock命令鎖定了版本庫中的這個文件:
$ svn lock banana.jpg -m "Editing file for tomorrow's release." 'banana.jpg' locked by user 'harry'. $
前一個例子描述了許多新事物,第一,注意Harry在svn lock中使用了--message (-m)選項,類似於svn commit,svn lock命令可以有描述鎖定原因的註釋(通過--message (-m)或--file (-F))。然而不像svn commit,svn lock不會自動強制啟動你喜歡的編輯器,鎖定註釋是可選的,但是為了方便交流我們還是推薦使用。
第二,鎖定成功了,這意味著文件沒有被別人鎖定,Harry的文件是最新的版本。如果Harry的工作副本文件不是最新的,版本庫會拒絕請求,強制Harry執行svn update並重新運行鎖定命令,同樣,如果此文件已經被別的用戶鎖定了,鎖定命令也會失敗。
就像你看到的,svn lock打印了鎖定成功的確認訊息。此時,通過svn status和svn info的輸出我們可以看到文件已經鎖定。
$ svn status
K banana.jpg
$ svn info banana.jpg
Path: banana.jpg
Name: banana.jpg
URL: http://svn.example.com/repos/project/banana.jpg
Repository UUID: edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 2198
Node Kind: file
Schedule: normal
Last Changed Author: frank
Last Changed Rev: 1950
Last Changed Date: 2006-03-15 12:43:04 -0600 (Wed, 15 Mar 2006)
Text Last Updated: 2006-06-08 19:23:07 -0500 (Thu, 08 Jun 2006)
Properties Last Updated: 2006-06-08 19:23:07 -0500 (Thu, 08 Jun 2006)
Checksum: 3b110d3b10638f5d1f4fe0f436a5a2a5
Lock Token: opaquelocktoken:0c0f600b-88f9-0310-9e48-355b44d4a58e
Lock Owner: harry
Lock Created: 2006-06-14 17:20:31 -0500 (Wed, 14 Jun 2006)
Lock Comment (1 line):
Editing file for tomorrow's release.
$
svn info命令不會聯繫版本庫,當對工作副本路徑應用svn info命令時,可以揭示令牌的一個重要事實—它們快取在工作副本。有鎖定令牌是非常重要的,這給了工作副本權利利用這個鎖的能力。svn status會在文件後面顯示一個K(locKed的縮寫),表明了擁有鎖定令牌。
現在Harry已經鎖定了banana.jpg,Sally不能修改或刪除這個文件:
$ svn delete banana.jpg D banana.jpg $ svn commit -m "Delete useless file." Deleting banana.jpg svn: Commit failed (details follow): svn: DELETE of '/repos/project/!svn/wrk/64bad3a9-96f9-0310-818a-df4224ddc35d/banana.jpg': 423 Locked (http://svn.example.com) $
但是,當完成了香蕉的黃色漸變,就可以提交文件的修改,因為認證為鎖定的擁有者,也因為他的工作副本有正確的鎖定令牌:
$ svn status M K banana.jpg $ svn commit -m "Make banana more yellow" Sending banana.jpg Transmitting file data . Committed revision 2201. $ svn status $
需要注意到提交之後,svn status顯示工作副本已經沒有鎖定令牌了,這是svn commit的標準行為方式—它會遍歷工作副本(或者從目標列表,如果有列表的話),並且作為提交的一部分發送所有遇到的鎖定令牌到伺服器。當提交完全成功,前面用到的所有版本庫鎖定都會被釋放—即使是沒有提交的文件。這樣的原因是不鼓勵用戶濫用鎖定,或者是長時間的保持鎖定。例如,假定Harry不小心鎖定了images目錄的30個文件,因為他不確定要修改什麼文件,他最後只修改了四個文件,當他運行svn commit images,會釋放所有的30個鎖定。
自動釋放鎖定的特性可以通過svn commit的--no-unlock選項關閉,當你要提交文件,同時期望繼續修改而必須保留鎖定時非常有用。這個特性也可以半永久性的設定,方法是設定運行中config文件(見「運行設定區」一節)的no-unlock = yes。
當然,鎖定一個文件不會強制一個人要提交修改,任何時候都可以通過運行svn unlock命令釋放鎖定:
$ svn unlock banana.c 'banana.c' unlocked.
最明顯的方式就是因為鎖定而不能提交一個文件,最簡單的方式是svn status --show-updates:
$ svn status -u
M 23 bar.c
M O 32 raisin.jpg
* 72 foo.h
Status against revision: 105
$
在這個例子裡,Sally可以見到不僅她的foo.h是過期的,而且發現兩個計劃要提交的文件被鎖定了。O符號表示其他人所訂了文件。如果她嘗試提交,raisin.jpg的鎖定會阻止她,Sally會納悶誰鎖定了文件,什麼時候,為什麼。再一次,svn info擁有答案:
$ svn info http://svn.example.com/repos/project/raisin.jpg Path: raisin.jpg Name: raisin.jpg URL: http://svn.example.com/repos/project/raisin.jpg Repository UUID: edb2f264-5ef2-0310-a47a-87b0ce17a8ec Revision: 105 Node Kind: file Last Changed Author: sally Last Changed Rev: 32 Last Changed Date: 2006-01-25 12:43:04 -0600 (Sun, 25 Jan 2006) Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b Lock Owner: harry Lock Created: 2006-02-16 13:29:18 -0500 (Thu, 16 Feb 2006) Lock Comment (1 line): Need to make a quick tweak to this image. $
就像svn info可以檢驗工作副本的對象,它也可以檢驗版本庫的對象,如果svn info的主要參數是工作副本路徑,所有工作副本的快取訊息都會顯示,發現了鎖定就意味著工作副本擁有鎖定令牌(如果一個文件被另一個用戶在另一個工作副本鎖定,工作副本路徑上運行svn info不會顯示鎖定訊息)。如果svn info的主參數是URL,就會反映版本庫中最新版本的對象訊息,任何對鎖定的提及描述了當前對象的鎖定。
所以在這個特定的例子裡,Sally可以看到Harry在二月十六日為了「做修改」而鎖定了這個文件,現在已經六月了,她懷疑他可能是忘記了這個鎖定,她會打電話給Harry去詢問他應該釋放這個鎖定,如果他不再,她就要自己強制解除這個鎖定或者是找管理員去做。
版本庫鎖定並不是神聖不可侵犯的,在Subversion的預設設定狀態,不只是建立者可以釋放鎖定,任何人都可以。當有其他人期望消滅鎖定時,我們稱之為打破鎖定。
從管理員的位子上很容易打破鎖定,svnlook和svnadmin程序都有能力從版本庫直接顯示和刪除鎖定。(關於這些工具的訊息可以看「管理員的工具箱」一節。)
$ svnadmin lslocks /usr/local/svn/repos Path: /project2/images/banana.jpg UUID Token: opaquelocktoken:c32b4d88-e8fb-2310-abb3-153ff1236923 Owner: frank Created: 2006-06-15 13:29:18 -0500 (Thu, 15 Jun 2006) Expires: Comment (1 line): Still improving the yellow color. Path: /project/raisin.jpg UUID Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b Owner: harry Created: 2006-02-16 13:29:18 -0500 (Thu, 16 Feb 2006) Expires: Comment (1 line): Need to make a quick tweak to this image. $ svnadmin rmlocks /usr/local/svn/repos /project/raisin.jpg Removed lock on '/project/raisin.jpg'. $
更有趣的選項是允許用戶互相打破鎖定,為此,Sally只需要使用unlock命令的--force選項:
$ svn status -u
M 23 bar.c
M O 32 raisin.jpg
* 72 foo.h
Status against revision: 105
$ svn unlock raisin.jpg
svn: 'raisin.jpg' is not locked in this working copy
$ svn info raisin.jpg | grep URL
URL: http://svn.example.com/repos/project/raisin.jpg
$ svn unlock http://svn.example.com/repos/project/raisin.jpg
svn: Unlock request failed: 403 Forbidden (http://svn.example.com)
$ svn unlock --force http://svn.example.com/repos/project/raisin.jpg
'raisin.jpg' unlocked.
$
Sally初始的unlock命令失敗了,因為她直接在自己的工作副本上運行了svn unlock,而這裡沒有鎖定令牌。為了直接從版本庫刪除鎖定,她需要給svn unlock傳遞URL參數,她的這一次嘗試又失敗了,因為她不是鎖定的擁有者(也沒有鎖定令牌)。當她使用了--force選項後,認證和授權的要求被忽略了,遠程的鎖定被打破了。
當然,簡單的打破鎖定也許還不夠,在這個例子裡,Sally不僅想要打破Harry遺忘的鎖定,她也希望自己重新鎖定。她可以通過運行svn unlock --force緊接著svn lock,但是有可能有人在這兩次命令之間鎖定了文件,最簡單的方式是竊取這個鎖定,將打破和重新鎖定變成一種原子操作,為此需要運行svn lock加--force選項:
$ svn lock raisin.jpg svn: Lock request failed: 423 Locked (http://svn.example.com) $ svn lock --force raisin.jpg 'raisin.jpg' locked by user 'sally'. $
在任何情況下,無論鎖定被打破還是竊取,Harry都會感到驚訝。Harry的工作副本還保留有原來的鎖定令牌,但是鎖定已經不存在了,鎖定令牌可以說已經死掉了。鎖定令牌指代的鎖定被打破(版本庫中不再存在)或者是竊取了(被另一個鎖定代替了),任何一種情況下,Harry都可以使用svn status詢問版本庫:
$ svn status
K raisin.jpg
$ svn status -u
B 32 raisin.jpg
$ svn update
B raisin.jpg
$ svn status
$
如果版本庫鎖定被打破了,svn status --show-updates會在文件旁邊顯示一個B (Broken)。如果有一個新的鎖,就會顯示一個T (sTolen)符號。最終,svn update會注意到所有死掉的鎖定並且把它們從工作副本中刪除掉。
我們已經見到了如何利用svn lock和svn unlock來建立、釋放、打破和竊取鎖定,這就滿足了順序訪問文件的要求,但是浪費時間這個大問題該如何呢?
例如,假定Harry鎖定了一個圖片,並開始編輯。同時,幾英里之外的Sally希望做同樣的工作,她沒想到運行svn status --show-updates,她不知道Harry已經鎖定了文件。她花費了數小時來修改文件,當她真被提交時發現文件已經被鎖定或者是她的文件已經過期了。她的修改不能和Harry的合併,他們中的一人需要拋棄自己的工作,許多時間被浪費了。
Subversion針對此問題的解決方案是提供一種機制,提醒用戶在開始編輯以前必須鎖定這個文件,這個機制就是提供一種特別的屬性--svn:needs-lock。當有這個值時,除非用戶鎖定這個文件,否則文件一直是只讀的。當得到一個鎖定令牌(運行svn lock的結果),文件變成可讀寫,當釋放這個鎖後,文件又變成只讀。
根據這個原理,如果一個圖像文件有這個屬性,Sally打開編輯文件就會立刻注意到有些特別,大多數程序會在打開只讀文件時立刻警告,至少所有的程序會防止她保存修改,這提醒了她編輯之前需要鎖定文件,這樣她就發現了原來存在的鎖定:
$ /usr/local/bin/gimp raisin.jpg gimp: error: file is read-only! $ ls -l raisin.jpg -r--r--r-- 1 sally sally 215589 Jun 8 19:23 raisin.jpg $ svn lock raisin.jpg svn: Lock request failed: 423 Locked (http://svn.example.com) $ svn info http://svn.example.com/repos/project/raisin.jpg | grep Lock Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b Lock Owner: harry Lock Created: 2006-06-08 07:29:18 -0500 (Thu, 08 June 2006) Lock Comment (1 line): Making some tweaks. Locking for the next two hours. $
我們鼓勵用戶和管理員都應該給不能根據上下文的文件新增svn:needs-lock屬性,這是鼓勵好的鎖定習慣和防止浪費的主要技術手段。
需要注意到這個屬性是依賴於鎖定系統的交流工具,不管是否有這個屬性,文件都可以鎖定。相反的,無論有沒有這個屬性,並不會要求提交需要首先鎖定文件。
這個系統並不是毫無瑕疵,即使有這個屬性,只讀提醒也有可能失效。有些程序「偷偷的篡改了」文件的只讀屬性,悄無聲息的允許用戶編輯和保存文件,不幸的是,Subversion對此無能為力—即使到了現今,還是沒有任何工具能夠代替人與人的良好交流。[17]