Subversion的“拷贝-修改-合并”模型是以行为基础(如程序源代码 )的项目最佳的协调方式,然而就像什么时候锁定是必需的里说过的,有时候有些人需要“锁定-修改-解锁”模型来代替Subversion的标准模型,当一个文件由二进制数据组成,合并两个修改通常是困难的,Subversion 1.2和以后的版本提供了锁定特性,也就是别的版本控制系统常说的“保留检出”。
Subversion的锁定特性有两个目标:
顺序访问资源。允许用户得到一个排他的修改文件权,如果Harry保留了修改foo.jpg
的权利,Sally将不可以提交这个文件的修改。
辅助交流。防止用户进行不可合并的修改,如果Harry已经保留了修改foo.jpg
的权利,然后Sally会很容易注意到这个事实而不会去修改这个文件。
Subversion的锁定特性当前限制到文件—还没有可能限制整个目录树的访问。
在Subversion的版本库,一个锁是一份元数据,可以排它赋予某个用户修改权,这个用户被称作锁的拥有者。每个锁都有一个唯一标识,通常是一长串字符,叫做锁令牌。版本库在一个单独的表里管理锁,提交时强制锁定检查,如果操作会修改或删除文件(或者是删除文件父目录),版本库会要求两份信息:
用户认证。执行提交的客户端必须认证是锁的拥有者。
软件授权。用户的工作拷贝必须将锁令牌提交,证明它清楚地知道使用那个锁。
一个例子是按照顺序。为了描述方便,假定Harry决定修改一个JPEG图像,为了防止其他用户提交这个文件的修改,他使用svn lock命令锁定了版本库的这个文件:
$ svn lock banana.jpg --message "Editing file for tomorrow's release." 'banana.jpg' locked by user 'harry'. $ 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: 2005-03-15 12:43:04 -0600 (Tue, 15 Mar 2005) Text Last Updated: 2005-06-08 19:23:07 -0500 (Wed, 08 Jun 2005) Properties Last Updated: 2005-06-08 19:23:07 -0500 (Wed, 08 Jun 2005) Checksum: 3b110d3b10638f5d1f4fe0f436a5a2a5 Lock Token: opaquelocktoken:0c0f600b-88f9-0310-9e48-355b44d4a58e Lock Owner: harry Lock Created: 2005-06-14 17:20:31 -0500 (Tue, 14 Jun 2005) Lock Comment (1 line): Editing file for tomorrow's release.
前一个例子描述了许多新事物,第一,注意Harry在svn
lock中使用了--message
选项,类似于svn
commit,svn lock命令可以有描述锁定原因的注释(通过--message
(-m)
或--file (-F)
)。然而不像svn
commit,svn
lock不会自动强制启动你喜欢的编辑器,锁定注释是可选的,但是为了方便交流我们还是推荐使用。
第二,锁定成功了。这意味着文件已经锁定了,Harry有了文件最新的版本,如果Harry的工作拷贝文件不是最新的,版本库会拒绝请求,强制Harry执行svn update并重新运行锁定命令。
也要注意到在创建版本库的锁定之后,工作拷贝也缓存了锁定的信息—最重要的是锁定令牌。有锁定令牌是非常重要的,这给了工作拷贝权利利用这个锁的能力。svn status会在文件后面显示一个K
(locKed的缩写),表明了拥有锁定令牌。
现在Harry已经锁定了banana.jpg
,Sally不能修改或删除这个文件:
$ whoami 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)
但是,当完成了香蕉的黄色渐变,就可以提交文件的修改,因为认证为锁定的拥有者,也因为他的工作拷贝有正确的锁定令牌:
$ whoami harry $ 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:
$ whoami sally $ svn status --show-updates 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: 2005-01-25 12:43:04 -0600 (Tue, 25 Jan 2005) Lock Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b Lock Owner: harry Lock Created: 2005-02-16 13:29:18 -0500 (Wed, 16 Feb 2005) 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去询问他应该释放这个锁定,如果他不再,她就要自己强制解除这个锁定或者是找管理员去做。
版本库锁定并不是神圣不可侵犯的,不只是创建者可以释放锁定,任何人都可以。当有其他人期望消灭锁定时,我们称之为打破锁定。
从管理员的位子上很容易打破锁定,svnlook和svnadmin程序都有能力从版本库直接显示和删除锁定。(关于这些工具的信息可以看“管理员的工具箱”一节。)
$ svnadmin lslocks /usr/local/svn/repos Path: /project2/images/banana.jpg UUID Token: opaquelocktoken:c32b4d88-e8fb-2310-abb3-153ff1236923 Owner: frank Created: 2005-06-15 13:29:18 -0500 (Wed, 15 Jun 2005) Expires: Comment (1 line): Still improving the yellow color. Path: /project/raisin.jpg UUID Token: opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b Owner: harry Created: 2005-02-16 13:29:18 -0500 (Wed, 16 Feb 2005) 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'.
更有趣的选项是允许用户互相打破锁定,为此只需要使用unlock命令的--force
选项:
$ whoami sally $ svn status --show-updates 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询问版本库:
$ whoami harry $ svn status K raisin.jpg $ svn status --show-updates 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
。如果这个属性附加到一个文件上(属性值无关紧要),这个文件将只读,当用户锁定了文件,并且接受了锁定令牌,文件成了可读写。当锁定被释放—明确的解锁或提交后的释放—文件重新回到只读状态。
根据这个原理,如果一个图像文件有这个属性,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: 2005-06-08 07:29:18 -0500 (Thu, 08 June 2005) Lock Comment (1 line): Making some tweaks. Locking for the next two hours.
作为一个“最佳实践”,用户和管理员都应该给不能根据上下文的文件添加svn:needs-lock
属性,这是鼓励好的锁定习惯和防止浪费的主要技术手段。
需要注意到这个属性是依赖于锁定系统的交流工具,不管是否有这个属性,文件都可以锁定。相反的,无论有没有这个属性,并不会要求提交需要首先锁定文件。
这个系统并不是毫无瑕疵,即使有这个属性,只读提醒也有可能失效。有些程序“偷偷的篡改了”文件的只读属性,悄无声息的允许用户编辑和保存文件,不幸的是,Subversion对此无能为力。