Subversion 1.5 - 解决冲突

Filed Under (Subversion Client, Subversion in the Enterprise) by rocksun on 23-07-2007

如果你是这个blog的常规读者,你一定不会怀疑我们会在今年的晚些时候发布Subversion 1.5,包含合并跟踪这个大特性。技术上讲,合并跟踪是一项很大的内部特性,唯一例外就是对于UI中合并过程的简化。实现这个特性导致我们花费大量时间认真考虑整个合并过程和随之而来的技术上不属于合并跟踪的改进,这让我们写下这篇blog,来描述Subversion 1.5是如何让解决冲突的更简单。

Subversion 1.5带来了两项对于解决冲突的改进,第一个是svn resolved增加新的–accept选项,你可以通过指定一个文件的版本来解决冲突,另一个特性是交互冲突解决。

svn resolved

当Subversion在一个文件生成冲突时,你需要做出一些动作来解决冲突,然后运行svn resolved命令告诉Subversion你做的事情,就像前面说的,这个命令现在包含了一个–accept选项,允许你在确定冲突已经解决的同时选择文件一个特定的版本,这个特性最常见的用法就是对付二进制文件。

一个我会使用这个特性的例子是当我有一个发布分支使用了与trunk不同版本的二进制文件,例如JAR文件,例如,我的分支也许使用某个库的1.x版本,而trunk使用2.x版本,当我将修改从trunk转移到分支时,会包含这个库的文件,我会在分支上得到冲突,如果没有这个命令,我需要在工作拷贝手工的拷贝/改名正确版本的文件,然后运行svn resolved命令,通过这个改进,我可以告诉Subversion这样做,例如:

svn resolved –accept=left my-library.jar

这样会代替branch中使用的文件来解决冲突,除了二进制文件,其他用户会在脚本中实现自动合并到某个分支类型,显然这个特性不是用来在你使用三方区别/合并工具用来解决冲突时使用,然而,下个特性就是解决这中场景。


交互式冲突解决

svn resolved特性的开发确实使得开发者思考这种特性是如何集成到合并过程本身的(顺便说一下:所有这些特性可以应用到update和switch命令,因为他们也会产生冲突),这导致了交互冲突解决特性的产生,下面是这个特性的例子:

    svn up
U contrib/client-side/svnmerge/svnmerge_test.py
Conflict discovered in ‘contrib/client-side/svnmerge/svnmerge.py’.
Select: (p)ostpone, (d)iff, (e)dit, (h)elp : h
(p)ostpone - mark the conflict to be resolved later
(d)iff - show all changes made to merged file
(e)dit - change merged file in an editor
(r)esolved - accept merged version of file
(m)ine - accept my version of file
(t)heirs - accept repository’s version of file
(l)aunch - use third-party tool to resolve conflict
(h)elp - show this list

Select: (p)ostpone, (d)iff, (e)dit, (h)elp : t
G contrib/client-side/svnmerge/svnmerge.py
Updated to revision 25685.

在这个例子里,我运行了update命令,且得到了冲突,这是因为我本地修改了文件。这个update过程会提示我解决这个冲突,也提供了许多选项来辅助我。在这个例子里,我使用了(h)elp选项来显示更多的选购向,例如在svn resolved特性里,我可以选择某个文件的版本(对于二进制文件类型很有效),但是现在我也可以选择看一下diff,或者打开一个编辑器或甚至启动一个3方区别工具,后面的两个选项需要设置EDITOR或SVNMERGE的环境变量来启动相应的工具。

如果你采用这个选项来编辑文件,当你结束时,你可以告诉Subversion你已经解决了冲突,如果你只是希望Subversion将文件标记为conflicted,你也可以使用(p)ostpone选项,这样就可以在以后解决冲突,就像你现在的方式。

你有很多方法可以关闭这个特性,第一是添加–non-interactive选项,有这个选项时,Subversion会仅仅创建冲突,而不会在过程中提示你。对于自动脚本或你不希望提示时非常有用,你也可以通过运行配置文件关闭这个特性:

     [miscellany]
interactive-conflicts = no

通过设置这个配置文件,你就从本质上回到1.5之前的行为方式。

结论

还有一点我没有谈到的是这Subversion的这些特性都有Subversion API,所以图形化客户端可以很容易的实现同样的特性,例如我们已经将交互冲突解决集成到了CollabNet Desktop - Eclipse Edition,随着Subversion 1.5版本发布日期的临近,我可能会在另一个帖子里介绍其他Subversion图形客户端的这个特性,类似的,我肯定TortoiseSVN会很快集成这些特性,做出伟大的事情来。

原始链接

Mark Phippard is Director, Subversion Engineering at CollabNet. He works on the CollabNet Subversion team and is a project owner for the Subclipse project as well as a partial committer for Subversion. Permalink


Technorati : , , , , , , , , , , , ,

第二次机会和Subversion

Filed Under (Subversion Client) by rocksun on 20-07-2007

我们一直在犯错误,如果生活总会给你第二次机会,或者改正错误的机会,那不是很棒吗?幸运的是你使用了Subversion,大多数情况下,你可以轻松的修改错误。

假设我们的Subversion不能做的很简单,你犯了一些错误,但是删除了所有发生过的轨迹,Subversion记录在某个时间点的版本库,而且历史不可变,也就是不可以修改。很不幸,这意味着一旦你不小心提交了包含密码的配置文件,你可以编辑文件并再次提交,但是Subversion永久保留最初的提交,包含密码。删除这些东西的唯一办法就是把版本库dump到一个文件,从dump文件中小心的删除那些内容,然后重新加载版本库。这不适合胆小者,但是可以做。大多数Subversion安装使用提交钩子在每次提交时发送修改细节邮件,这很有价值,即使Subversion让删除轨迹变得容易,也无法避免邮件发送到全世界。

一个Subversion可以简单修正的常见错误就是对于删除的恢复,我恨这一点,但是几个月以前我不小心将Subclipse版本库的整个branches目录删除了,你可以在这里看到使用ViewVC看到血淋淋的细节,我知道我可以简单将它找回来。如果你查看以前的链接,你可以看到我在修订版本2981提交了删除,我需要做的就是将其从2980拷贝出来。

svn copy -r2980 http://subclipse.tigris.org/svn/subclipse/branches@2980 \
http://subclipse.tigris.org/svn/subclipse/branches

这个命令的意思是,”将2980修订版本的branches目录拷贝到branches(目前不存在的目录)”,你需要在第一个URL中添加@2980,它告诉Subversion的历史跟踪规则找到这个目录。如果你省略这个参数,Subversion会查看版本库的HEAD修订版本,你可以在ViewVC的 这里看到提交的结果,请注意你可以看到项目是从修订版本2980拷贝而来。

所以就是这样将删除的东西恢复,如果事情更加细致该如何?你修改了一组文件,但是你不希望有这些修改。你希望从本质上”回退提交”,这对于Subverison非常简单,你可以通过所谓的”反向合并实现”。本质上讲,就是使用svn merge命令将修订版本号反转过来,对于命令行用户,这种情况已经在Subversion book中详细论述,请见回退修改部分,这一小节也有一些其他的例子,很值得一看。

Subclipse和TortoiseSVN在其图形界面中提供了更简单的方法,1月份我在我的个人blog里写了一个类似的例子,它介绍了使用Subclipse GUI完成这个功能的方法,如果你希望知道如何使用GUI完成这个功能可以看这个帖子

如果说我希望这个帖子能带来什么,我希望是增加Subversion对于数据安全的信心,继续修改并提交,那就是Subversion的工作,如果你犯了错误,不必担心,Subversion一直会给你第二次机会。

原始链接

Mark Phippard is Director, Subversion Engineering at CollabNet. He works on the CollabNet Subversion team and is a project owner for the Subclipse project as well as a partial committer for Subversion.


Technorati : , , , , , , , , , ,

来自问题盒:Subversion锁定

Filed Under (Subversion Client, Subversion in the Enterprise) by rocksun on 12-07-2007

在CollabNet,我们经常会在许多不同途径(webinar,培训等)得到很多Subversion的问题,我们尝试尽快地回答问题,但是有时候我们需要记录未回答的问题,所以就像以前在这个blog里所做的,我们尝试回答这些排队的问题,主要关注于Subversion的锁定特性

首先,允许我首先从高级别总结一下锁定特性,Subversion采用的缺省版本控制模型是”拷贝-修改-合并“模型,用户会从版本化目录中检出个人工作拷贝,然后提交修改,只需要担心偶尔发生的对于同一个文件的修改。锁定(有时候我们称为”保留检出”)允许Subversion用户跳出缺省模型,而采用”锁定-修改-解锁“模型,这样多个用户顺序提交给定文件,所以冲突不会发生。后一种模型是Visual SourceSafe中经常使用的,Subversion的锁定特性通过允许用户声明对一个文件的修改排他锁来完成这个功能(然后之后放弃这个权利)。

推荐使用锁定吗?

Subversion开发者不会真的开发新特性来违反社区自己的推崇和最佳实践,而锁定特性(Subversion 1.2添加)是有意开发的,问题可以这样描述:”何时推荐锁定?”为了回答,我们来看一下两种控制模型赞成和反对的理由。

拷贝-修改-合并具有较少管理和流程的特性,所有的人可以自由修改文件,只是在发生冲突时解决。不幸的是,当冲突发生时,解决冲突的代价会有很大的不同,从不费任何脑到我浪费了一周的工作,这取决于冲突文件的格式。

当使用锁定-修改-解锁时,每一个人需要付出流程代价,如在需要编辑时明确锁定文件的,而在结束修改时解锁文件。如果用户不小心,这可能意味着一个用户可能保留不必要的时间来编辑文件,这会妨碍项目组的生产力,也会需要管理员的干涉。但是在正面,你不必在解决冲突上花费力气,因为冲突(只会发生在多个文件提交同一个文件的修改)不可能发生。

就像生活中的很多事情,两种模型的平衡会是最好的结果。Subversion的内置上下文修改合并算法可以处理大多数多个用户同时修改同一个文件的情况,当然文件是文本文件或其他人可读格式。对这些文件,使用拷贝-修改-合并(不锁定文件)可以节省用户投入到流程的步骤,鼓励协作并防止管理投入。但是对于处理冲突代价很高的文件,例如不可读文件格式,锁定和解锁会比解决可能发生的冲突轻松许多。


Subversion支持保留检出吗?

就像前面提到的,很多人将Subversion的文件锁定特性当作”保留检出”,但是这非常不准确的。在具备保留检出功能的版本控制系统,术语”checkout”不仅仅是在你的计算机得到一个版本化的文件,也是获得修改文件的权利,一个保留检出是同样的,除了你保护的修改权限是排他的 - 文件对其他用户是只读的。Subversion使用这个术语很古怪,缺省情况下,如果你从Subversion版本库检出一个文件,你可以编辑它,你可能无法提交它,一方面因为你没有授权,也有可能是你的本地工作拷贝已经过时了,或者是本文相关的 - 因为其他人锁定了它。

但是等一下,如果有其他人锁定了文件,为什么Subversion允许我们花时间修改它?

你可以运行’svn status –update’或对文件或版本库URL运行’svn info’来查看被其他人锁定的文件,但是Subversion不会坚持这些信息到工作拷贝,在你最后询问服务器锁定和提交修改之间一定有一个时间差,锁定文件节省了处理冲突或浪费编辑的时间,但是不能帮助协作者来解决你以前面对的情形。

处理这种场景的最佳方法是使用Subversion的文件属性-svn:needs-lock。通过在文件上设置这个属性,你可以告诉Subversion这个文件会导致你和你的协作者需要在修改之前获得这个文件的锁,设置svn:needs-lock属性的文件在工作拷贝(在OS的文件系统意义上的)首先会是只读的,这样足以让行为良好的编辑软件不会修改这个文件,当你将文件加载到软件时,软件会提示文件只读,你会记起你需要使用”svn lock”锁定文件,通过这样做,Subversion会改变工作拷贝中的文件系统访问控制,它不再是只读得了,你可以修改这个文件了。当你提交并释放了文件的锁定,Subversion重新让它在工作拷贝回到只读状态。

作为最佳实践,我推荐你小心的评估svn:needs-lock属性的有无,如果一个文件没有包含这个属性,你确实需要锁定它吗?如果你确实需要锁定文件,有其他人会从工作拷贝的只读属性中获益吗?


请注意,svn:needs-lock属性不会导致Subversion要求文件提交时是锁定状态,它只会作用于客户端文件系统的只读问题,前面我也说了svn:needs-lock发挥作用需要你使用行为良好的软件。但是如果你的软件忽略只读文件系统位-或者你手工修改文件许可位 - 你或许不会发现在修改之前获取锁会失败,Subversion会很高兴得允许你提交修改。(如果你的软件这样运行,请考虑告诉它的开发者!)

可以锁定模块/目录,还是只能是文件?

很不幸,此时在Subversion里你只能锁定文件。

这种不幸的原因很清楚,因为你不能锁定目录,许多人会这样工作 - 锁定目录下的所有文件,不管对于特定文件集是否必要,这样在Subversion中会有一些性能问题。现在Subversion还不支持多条锁定路径操作的原子化- 如果200个文件中有126个锁定因为一些原因(很可能是被别人锁定了)失败了,你会得到部分锁定。对于经常这样做的人来说,应该考虑”svn lock”不接受recursive子命令的事实,这不是Subversion开发者不小心,如果你希望整个目录的访问限制,请使用路径为基础的访问控制方法

你能告诉Subversion你希望锁定所有文件吗?

是,你可以,但是决定这样做之前请读一下前一个问题的答案。如果你觉得你确实需要,你可以将所有的文件设置svn:needs-lock属性。(你可以参考使用客户端的自动化属性功能。)读一下前面的纪录,为什么Subversion那些文件需要锁定。作为选择,你可以编写一个pre-commit钩子脚本可以检查提交文件是否已经被锁定。(你可以相信Subversion来检查提交者确实是锁定的拥有者。)

可以锁定特定文件类型(例如所有的*.doc文件)吗?

当然,看前一个答案,只需要限制自动化属性的范围,或使用钩子基础的提交验证来确认感兴趣的文件类型。

C. Michael (Mike) Pilato has been on the Subversion project as a committer since 2000. Mike is one of the co-authors of “Version Control with Subversion” and he is on the board of the non-profit Subversion Corporation.


Technorati : , , , , , , , , ,

Subversion 1.5的特性:Changelists

Filed Under (Subversion Client, Subversion in the Enterprise) by rocksun on 05-07-2007

典型工程师的一个特质就是会对潜在问题区域高度敏感,对于软件工程师来说,这通常意味着当在代码块中工作时会在间接视觉中定位可能的问题,这是祝福也是诅咒。你希望bug被修正,但是我们经常会在追求完美的过程中脱离了主要任务。

另一个开发者生活的事实就是他们总有许多事情无法按时完成,而且每一件事都对某些人有最高的优先级,因此,我们发现自己经常在同一时间作许多事情,结果所有的事情进展都会很缓慢,至少你可以凭良心和强迫的微笑:”正在处理,我正在为此事工作。”

我的管理读者,我必须承认我对那些问题绝对没有办法,毕竟这是一个技术blog。但是我可以(也会)告诉你一个即将到来的Subversion特性,可以减少这类开发者的环境转换:changelists

无论什么原因,开发者经常会在任何给定时间修改代码中显著不同的东西,经常是每个修改没有精细的保存在一些模块中,没有安全的与其它修改分离。修改的组会重叠,修改同一个模块的不同文件,甚至是同一文件的不同行。开发者有许多工作方法可以采用来保持任务的组织性,一些用户使用同一个版本库的不同工作拷贝来保存每个单独的修改过程,另外一些人会在版本库创建一个特性分支,使用单独的工作拷贝经常会指向某个分支,也有一些人使用diff和patch来备份和恢复未提交的修改,使用补丁文件来关联每一次修改。这些方法都有其利弊,更大程度上,要做的修改的细节制定了区分它们的方法学。

但是在Subversion 1.5我们有了一个新特性叫做”changelists”,另一种处理混合的方法。changelists基本上是以关联多个文件一起为目的的,应用到工作拷贝文件上的任意标签,Gmail用户对于这个概念已经非常熟悉,Gmail没有提供传统的目录为基础的邮件组织机制,在Gmail里,你可以为邮件附加任意的标签,所以多个共享同一个标签的邮件成为同一个组的一部分,所以察看具备相同标签的邮件成为一个简单的用户技巧。许多Web 2.0的站点具备类似的机制 - 考虑YouTubeFlickr等站点使用的”标签(tags)”,blog条目的”分类(categories)”等等。现在的人们知道组织数据的困难,但是数据的组织应该是一个灵活的概念,以前的文件目录组织方式对于大多数应用过于严格了。

所以Subversion的changelist支持允许你:

  • 给需要与changelist关联的文件应用标签(labels),
  • 删除标签,而且
  • 限制子命令操作的对象仅限于特定标签。

例如,作为我今天要完成任务的一部分,我需要修正一些ViewVC的diff察看相关功能的问题,我的工作需要修改一组文件。

$ cd projects/viewvc
$ svn status
M lib/vclib/ccvs/__init__.py
M lib/vclib/__init__.py
$

但是当我测试这些修改的时候,我发现一些注释支持的代码不能正常工作。因此,就像前面提到的,此刻为了转换上下文环境并修改次要问题我可以做很多事情,为了描述我们将会使用changelists。

首先,我想创建一个changelists与我已经修改的文件关联,这样可以帮助我将次要问题将要修改的文件区分开来。

$ svn changelist diff-fixes lib/vclib/ccvs/__init__.py
Path ‘lib/vclib/ccvs/__init__.py’ is now a member of changelist ‘diff-fixes’.
$ svn changelist diff-fixes lib/vclib/__init__.py
Path ‘lib/vclib/__init__.py’ is now a member of changelist ‘diff-fixes’.
$ svn status

— Changelist ‘diff-fixes’:
M lib/vclib/ccvs/__init__.py
M lib/vclib/__init__.py
$

就像你看到的,”svn status”的输出反映了新的分组。

现在我开始修正次要问题,需要我修改三个文件,我还没有准备好提交任何文件,所以我对第二次修改也创建一个changelist。

$ svn changelist blame-fix lib/vclib/ccvs/blame.py
Path ‘lib/vclib/ccvs/blame.py’ is now a member of changelist ‘blame-fix’.
$ svn status

— Changelist ‘blame-fix’:
M lib/vclib/ccvs/blame.py

— Changelist ‘diff-fixes’:
M lib/vclib/ccvs/__init__.py
M lib/vclib/__init__.py
$

现在,独一无二的分组看起来很好,但并不是完全有用。如果我希望只是察看diff相关的代码变更,在”svn diff”中,我仍然需要我所修改文件的明确文件名。

$ svn diff lib/vclib/__init__.py lib/vclib/ccvs/__init__.py

这对于两个文件并不太难,但是如果是20个文件呢?

幸运的是,Subversion 1.5的changelist支持更加彻底,我可以使用新的–changelist选项来限定许多子命令只关联给定changelist的文件,结果就是更简单的命令行,以及更简单的来俯瞰我希望检查的文件。

$ svn diff --changelist diff-fixes
Index: lib/vclib/ccvs/__init__.py
===================================================================
— lib/vclib/ccvs/__init__.py (revision 1157)
+++ lib/vclib/ccvs/__init__.py (working copy)
@@ -112,8 +112,8 @@
temp2 = tempfile.mktemp()
open(temp2, ‘wb’).write(self.openfile(path_parts2, rev2)[0].getvalue())

- r1 = self.itemlog(path_parts1, rev1, {})[0]
- r2 = self.itemlog(path_parts2, rev2, {})[0]
+ r1 = self.itemlog(path_parts1, rev1, {})[-1]
+ r2 = self.itemlog(path_parts2, rev2, {})[-1]

info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)
Index: lib/vclib/__init__.py
===================================================================
— lib/vclib/__init__.py (revision 1157)
+++ lib/vclib/__init__.py (working copy)
@@ -240,7 +241,7 @@
def readline(self):
return self.fp.readline()

- def close():
+ def close(self):
try:
if self.fp:
self.fp.close()
$

就像你预期的,我可以在”svn commit”中提供同样的–changelist选项。

$ svn ci -m "Fix some diff-related oopses." --changelist diff-fixes
Sending lib/vclib/ccvs/__init.py
Sending lib/vclib/__init.py
Transmitting file data ..
Committed revision 1158.
$

同时,我的其他changelist依然没有被打扰。

$ svn st

— Changelist ‘blame-fixes’:
M lib/vclib/ccvs/blame.py
$

许多Subversion客户端子命令(commit, diff, info, lock, log, proplist, propget, propset, status, update, …). )出现了新的–changelist选项,而且,当然也有了新的”svn changelist”子命令,用来从changelist添加和删除(通过一个–remove选项)文件。Subversion没有强制你使用changelist,就像大多数事情,Subversion只是提供给你这个工具,让你决定是否使用它。总之,这个特性给了Subversion用户,特别是有Perforce经验的用户 - 同时管理多个进行工作的一个简单方法。

changelist支持也有一些限制,首先,这个特性是路径粒度的特性,如果你发现你有同一个文件的重叠修改,你需要找出不同的方法来处理重叠。同样,changelist只是对于特定工作拷贝私有 - 无法自动与其他用户分享你的changelist定义,但是解决这个特性不是什么大问题 - 如果你需要共享进行中的工作,使用版本化的分支。

原始链接

C. Michael (Mike) Pilato has been on the Subversion project as a committer since 2000. Mike is one of the co-authors of “Version Control with Subversion” and he is on the board of the non-profit Subversion Corporation.


Technorati : , , , , , , , , ,