基本的工作周期
基本的工作周期
Subversion有许多特性、选项和华而不实的高级功能,但日常的工作中你只使用其中的一小部分,在这一节里,我们会介绍许多你在日常工作中常用的命令。
典型的工作周期是这样的:
更新你的工作拷贝
svn update
做出修改
svn add
svn delete
svn copy
svn move
检验修改
svn status
svn diff
可能会取消一些修改
svn revert
解决冲突(合并别人的修改)
svn update
svn resolved
提交你的修改
svn commit
更新你的工作拷贝
当你在一个团队的项目里工作时,你希望更新你的工作拷贝得到所有其他人这段时间作出的修改,使用svn update让你的工作拷贝与最新的版本同步。
$ svn update U foo.c U bar.c Updated to revision 2.
这种情况下,其他人在你上次更新之后提交了对foo.c
和bar.c
的修改,因此Subversion更新你的工作拷贝来引入这些更改。
当服务器通过svn update将修改传递到你的工作拷贝时,每一个项目之前会有一个字母,来让你知道Subversion为保持最新对你的工作拷贝作了哪些工作。关于这些字母的详细含义,可以看svn update。
修改你的工作拷贝
现在你可以开始工作并且修改你的工作拷贝了,你很容易决定作出一个修改(或者是一组),像写一个新的特性,修正一个错误等等。这时可以使用的Subversion命令包括svn add、 svn delete、svn copy和svn move。如果你只是修改版本库中已经存在的文件,在你提交之前,不必使用上面的任何一个命令。
你可以对工作拷贝做出两种修改:文件修改和目录树修改。你不需要告诉Subversion你希望修改一个文件,只需要用你的编辑器、字处理器、图形程序或任何工具做出修改,Subversion自动监测到文件的更改,此外,二进制文件的处理方式和文本文件一样—也有同样的效率。对于目录树更改,你可以告诉Subversion将文件和目录预定的删除、添加、拷贝或移动标记,这些动作会在工作拷贝上立刻发生效果,但只有提交后才会在版本库里生效。
下面是Subversion用来修改目录树结构的五个子命令。
版本控制符号连接
在非Windows平台,Subversion可以将特殊类型符号链接(或是“symlink”)版本化,一个符号链接是对文件系统中其他对象的透明引用,可以通过对符合链接操作实现对引用对象的读写操作。
当符号链提交到Subversion版本库,Subversion会记住这个文件实际上是一个符号链,也会知道这个符号链指向的“对象”。当这个符号链检出到另一个支持符号链的操作系统上时,Subversion会重新构建文件系统级的符号链接。当然这样不会影响在Windows这类不支持符号链的系统上,在此类系统上,Subversion只会创建一个包含指向对象路径的文本文件,因为这个文件不能在Windows系统上作为符号链使用,所以它也会防止Windows用户作其他Subversion相关的操作。
- svn add foo
预定将文件、目录或者符号链
foo
添加到版本库,当你下次提交后,foo
会成为其父目录的一个子对象。注意,如果foo
是目录,所有foo中的内容也会预定添加进去,如果你只想添加foo
本身,请使用--non-recursive (-N)
参数。- svn delete foo
预定将文件、目录或者符号链
foo
从版本库中删除,如果foo是文件,它马上从工作拷贝中删除,如果是目录,不会被删除,但是Subversion准备好删除了,当你提交你的修改,foo
就会在你的工作拷贝和版本库中被删除。[4]- svn copy foo bar
建立一个新的项目
bar
作为foo
的复制品,会自动预定将bar
添加,当在下次提交时会将bar
添加到版本库,这种拷贝历史会记录下来(按照来自foo
的方式记录),svn copy并不建立中介目录。- svn move foo bar
这个命令与与运行svn copy foo bar;svn delete foo完全相同,
bar
作为foo
的拷贝准备添加,foo
已经预定被删除,svn move不建立中介的目录。- svn mkdir blort
这个命令同运行 mkdir blort; svn add blort相同,也就是创建一个叫做
blort
的文件,并且预定添加到版本库。
不通过工作拷贝修改版本库
有一些情况下会立刻提交目录树的修改到版本库,这只发生在子命令直接操作URL,而不是工作拷贝路径时。以特定的方式使用svn mkdir、svn copy、svn move和svn delete可以针对URL操作(并且不要忘记svn import只针对URL操作)。
指定URL的操作方式有一些区别,因为在使用工作拷贝的运作方式时,工作拷贝成为一个“集结地”,可以在提交之前整理组织所要做的修改,直接对URL操作就没有这种奢侈,所以当你直接操作URL的时候,所有以上的动作代表一个立即的提交。
检查你的修改
当你完成修改,你需要提交他们到版本库,但是在此之前,检查一下做过什么修改是个好主意,通过提交前的检查,你可以整理一份精确的日志信息,你也可以发现你不小心修改的文件,给了你一次恢复修改的机会。此外,这是一个审查和仔细察看修改的好机会,你可通过命令svn status浏览所做的修改,通过svn diff检查修改的详细信息。
看!没有网络!
这三个命令(svn status、svn diff和svn revert)都可以在没有网络的情况下工作(假定你的版本库是通过网络而不是本地访问的),这让你在没有网络连接时的管理修改过程更加容易,像在飞机上旅行,乘坐火车往返或是在海滩上奋力工作时。[5]
Subversion通过在.svn
管理区域使用原始的版本缓存来做到这一点,这使得报告和恢复本地修改而不必访问网络,这个缓存(叫做“text-base”)也允许Subversion可以根据原始版本生成一个压缩的增量(“区别”) 提交—即使你有个非常快的网络,有这样一个缓存有极大的好处,只向服务器提交修改的部分而不是整个文件。
Subversion已经被优化来帮助你完成这个任务,可以在不与版本库通讯的情况下做许多事情,详细来说,对于每一个文件,你的的工作拷贝在.svn
包含了一个“原始的”拷贝,所以Subversion可以快速的告诉你那些文件修改了,甚至允许你在不与版本库通讯的情况下恢复修改。
查看你的修改概况
为了浏览修改的内容,你会使用这个svn status命令,在所有Subversion命令里,svn status可能会是你用的最多的命令。
CVS 用户:控制另类的更新!
你也许使用cvs update来看你做了哪些修改,svn status会给你所有你做的改变—而不需要访问版本库,并且不会在不知情的情况下与其他用户作的更改比较。
在Subversion,update只是做这件事—将工作拷贝更新到版本库的最新版本,你可以消除使用update察看本地修改的习惯。
如果你在工作拷贝的顶级目录运行不带参数的svn status命令,它会检测你做的所有的文件或目录的修改,以下的例子是来展示svn status可能返回的状态码(注意,#
之后的不是svn status打印的)。
A stuff/loot/bloo.h # file is scheduled for addition C stuff/loot/lump.c # file has textual conflicts from an update D stuff/fish.c # file is scheduled for deletion M bar.c # the content in bar.c has local modifications
在这种格式下,svn status打印6列字符,紧跟一些空格,接着是文件或者目录名。第一列告诉一个文件或目录的状态或它的内容,返回代码如下:
A item
预定加入到版本库的文件、目录或符号链的
item
。C item
文件
item
发生冲突,在从服务器更新时与本地版本发生交迭,在你提交到版本库前,必须手工的解决冲突。D item
文件、目录或是符号链
item
预定从版本库中删除。M item
文件
item
的内容被修改了。
如果你传递一个路径给svn status,它只给你这个项目的信息:
$ svn status stuff/fish.c D stuff/fish.c
svn status也有一个--verbose (-v)
选项,它可以显示工作拷贝中的所有项目,即使没有改变过的:
$ svn status -v M 44 23 sally README 44 30 sally INSTALL M 44 20 harry bar.c 44 18 ira stuff 44 35 harry stuff/trout.c D 44 19 ira stuff/fish.c 44 21 sally stuff/things A 0 ? ? stuff/things/bloo.h 44 36 harry stuff/things/gloo.c
这是svn status的“加长形式”,第一列保持相同,第二列显示一个工作版本号,第三和第四列显示最后一次修改的版本号和修改人(这些列不会与我们刚才提到的字符混淆)。
上面所有的svn status调用并没有联系版本库,只是与.svn
中的原始数据进行比较的结果,最后,是--show-updates (-u)
选项,它将会联系版本库为已经过时的数据添加新信息:
$ svn status -u -v M * 44 23 sally README M 44 20 harry bar.c * 44 35 harry stuff/trout.c D 44 19 ira stuff/fish.c A 0 ? ? stuff/things/bloo.h Status against revision: 46
注意这两个星号:如果你现在执行svn update,你的README
和trout.c
会被更新,这告诉你许多有用的信息—你可以在提交之前,需要使用更新操作得到文件README
的更新,或者说文件已经过时,版本库会拒绝了你的提交。(后面还有更多关于此主题)。
关于文件和目录,svn status可以比我们的展示显示更多的内容,完整的描述可以看svn status。
检查你的本地修改的详情
另一种检查修改的方式是svn diff命令,你可以通过不带参数的svn diff精确的找出你所做的修改,这会输出统一区别格式的区别信息:
$ svn diff Index: bar.c =================================================================== --- bar.c (revision 3) +++ bar.c (working copy) @@ -1,7 +1,12 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <stdio.h> int main(void) { - printf("Sixty-four slices of American Cheese...\n"); + printf("Sixty-five slices of American Cheese...\n"); return 0; } Index: README =================================================================== --- README (revision 3) +++ README (working copy) @@ -193,3 +193,4 @@ +Note to self: pick up laundry. Index: stuff/fish.c =================================================================== --- stuff/fish.c (revision 1) +++ stuff/fish.c (working copy) -Welcome to the file known as 'fish'. -Information on fish will be here soon. Index: stuff/things/bloo.h =================================================================== --- stuff/things/bloo.h (revision 8) +++ stuff/things/bloo.h (working copy) +Here is a new file to describe +things about bloo.
svn diff命令通过比较你的文件和.svn
的“原始”文件来输出信息,预定要增加的文件会显示所有增加的文本,要删除的文件会显示所有要删除的文本。
输出的格式为统一区别格式(unified diff format),删除的行前面加一个-
,添加的行前面有一个+
,svn diff命令也打印文件名和打补丁需要的信息,所以你可以通过重定向一个区别文件来生成“补丁”:
$ svn diff > patchfile
举个例子,你可以把补丁文件发送邮件到其他开发者,在提交之前审核和测试。
Subversion使用内置区别引擎,缺省情况下输出为统一区别格式。如果你期望不同的输出格式,你可以使用--diff-cmd
指定外置的区别程序,并且通过--extensions
传递其他参数,举个例子,察看本地文件foo.c
的区别,同时忽略大小写差异,你可以运行svn diff --diff-cmd /usr/bin/diff --extensions '-bc' foo.c。
取消本地修改
假定我们在看svn diff的输出,你发现对某个文件的所有修改都是错误的,或许你根本不应该修改这个文件,或者是从开头重新修改会更加容易。
这是使用svn revert的好机会:
$ svn revert README Reverted 'README'
Subversion把文件恢复到未修改的状态,叫做.svn
目录的“原始”拷贝,应该知道svn revert可以恢复任何预定要做的操作,举个例子,你不再想添加一个文件:
$ svn status foo ? foo $ svn add foo A foo $ svn revert foo Reverted 'foo' $ svn status foo ? foo
注意
svn revertITEM
的效果与删除ITEM
然后执行svn update -r BASEITEM
完全一样,但是,如果你使用svn revert它不必通知版本库就可以恢复文件。
或许你不小心删除了一个文件:
$ svn status README README $ svn delete README D README $ svn revert README Reverted 'README' $ svn status README README
解决冲突(合并别人的修改)
我们可以使用svn status -u来预测冲突,当你运行svn update一些有趣的事情发生了:
$ svn update U INSTALL G README C bar.c Updated to revision 46.
U
和G
没必要关心,文件干净的接受了版本库的变化,文件标示为U
表明本地没有修改,文件已经根据版本库更新。G
标示合并,标示本地已经修改过,与版本库没有重迭的地方,已经合并。
但是C
表示冲突,说明服务器上的改动同你的改动冲突了,你需要自己手工去解决。
当冲突发生了,有三件事可以帮助你注意到这种情况和解决问题:
Subversion在更新时打印
C
标记,并且标记这个文件已冲突。如果Subversion认为这个文件是可合并的,它会置入冲突标记—特殊的横线分开冲突的“两面”—在文件里可视化的描述重叠的部分(Subversion使用
svn:mime-type
属性来决定一个文件是否可以使用上下文的,以行为基础的合并,更多信息可以看“文件内容类型”一节。)对于每一个冲突的文件,Subversion放置三个额外的未版本化文件到你的工作拷贝:
filename.mine
你更新前的文件,没有冲突标志,只是你最新更改的内容。(如果Subversion认为这个文件不可以合并,
.mine
文件不会创建,因为它和工作文件相同。)filename.rOLDREV
这是你的做更新操作以前的
BASE
版本文件,就是你在上次更新之后未作更改的版本。filename.rNEWREV
这是你的Subversion客户端从服务器刚刚收到的版本,这个文件对应版本库的
HEAD
版本。
这里
OLDREV
是你的.svn
目录中的修订版本号,NEWREV
是版本库中HEAD
的版本号。
举一个例子,Sally修改了sandwich.txt
,Harry刚刚改变了他的本地拷贝中的这个文件并且提交到服务器,Sally在提交之前更新它的工作拷贝得到了冲突:
$ svn update C sandwich.txt Updated to revision 2. $ ls -1 sandwich.txt sandwich.txt.mine sandwich.txt.r1 sandwich.txt.r2
在这种情况下,Subversion不会允许你提交sandwich.txt
,直到你的三个临时文件被删掉。
$ svn commit -m "Add a few more things" svn: Commit failed (details follow): svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict
如果你遇到冲突,三件事你可以选择:
“手动”合并冲突文本(检查和修改文件中的冲突标志)。
用某一个临时文件覆盖你的工作文件。
运行svn revert <filename>来放弃所有的本地修改。
一旦你解决了冲突,你需要通过命令svn resolved让Subversion知道,这样就会删除三个临时文件,Subversion就不会认为这个文件是在冲突状态了。[6]
$ svn resolved sandwich.txt Resolved conflicted state of 'sandwich.txt'
手工合并冲突
第一次尝试解决冲突让人感觉很害怕,但经过一点训练,它简单的像是骑着车子下坡。
这里一个简单的例子,由于不良的交流,你和同事Sally,同时编辑了sandwich.txt
。Sally提交了修改,当你准备更新你的工作拷贝,冲突发生了,我们不得不去修改sandwich.txt
来解决这个问题。首先,看一下这个文件:
$ cat sandwich.txt Top piece of bread Mayonnaise Lettuce Tomato Provolone <<<<<<< .mine Salami Mortadella Prosciutto ======= Sauerkraut Grilled Chicken >>>>>>> .r2 Creole Mustard Bottom piece of bread
小于号、等于号和大于号串是冲突标记,并不是冲突的数据,你一定要确定这些内容在下次提交之前得到删除,前两组标志中间的内容是你在冲突区所做的修改:
<<<<<<< .mine Salami Mortadella Prosciutto =======
后两组之间的是Sally提交的修改冲突:
======= Sauerkraut Grilled Chicken >>>>>>> .r2
通常你并不希望只是删除冲突标志和Sally的修改—当她收到三明治时,会非常的吃惊。所以你应该走到她的办公室或是拿起电话告诉Sally,你没办法从从意大利熟食店得到想要的泡菜。[7]一旦你们确认了提交内容后,修改文件并且删除冲突标志。
Top piece of bread Mayonnaise Lettuce Tomato Provolone Salami Mortadella Prosciutto Creole Mustard Bottom piece of bread
现在运行svn resolved,你已经准备好提交了:
$ svn resolved sandwich.txt $ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
现在我们准备好提交修改了,注意svn resolved不像我们本章学过的其他命令一样需要参数,在任何你认为解决了冲突的时候,只需要小心运行svn resolved,—一旦删除了临时文件,Subversion会让你提交这文件,即使文件中还存在冲突标记。
记住,如果你修改冲突时感到混乱,你可以参考subversion生成的三个文件—包括你未作更新的文件。你也可以使用三方交互合并工具检验这三个文件。
复制文件到你的工作文件
如果你只是希望取消你的修改,你可以仅仅拷贝Subversion为你生成的文件替换你的工作拷贝:
$ svn update C sandwich.txt Updated to revision 2. $ ls sandwich.* sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1 $ cp sandwich.txt.r2 sandwich.txt $ svn resolved sandwich.txt
脚注:使用svn revert
如果你得到冲突,经过检查你决定取消自己的修改并且重新编辑,你可以恢复你的修改:
$ svn revert sandwich.txt Reverted 'sandwich.txt' $ ls sandwich.* sandwich.txt
注意,当你恢复一个冲突的文件时,不需要再运行svn resolved。
提交你的修改
最后!你的修改结束了,你合并了服务器上所有的修改,你准备好提交修改到版本库。
svn commit命令发送所有的修改到版本库,当你提交修改时,你需要提供一些描述修改的日志信息,你的信息会附到这个修订版本上,如果信息很简短,你可以在命令行中使用--message
(或-m
)选项:
$ svn commit -m "Corrected number of cheese slices." Sending sandwich.txt Transmitting file data . Committed revision 3.
然而,如果你把写日志信息当作工作的一部分,你也许会希望告诉Subversion通过一个文件名得到日志信息,使用--file
选项:
$ svn commit -F logmsg Sending sandwich.txt Transmitting file data . Committed revision 4.
如果你没有指定--message
或者--file
选项,Subversion会自动地启动你最喜欢的编辑器(见“配置”一节的editor-cmd
部分)来编辑日志信息。
提示
如果你使用编辑器撰写日志信息时希望取消提交,你可以直接关掉编辑器,不要保存,如果你已经做过保存,只要简单的删掉所有的文本并再次保存,然后退出。
$ svn commit Waiting for Emacs...Done Log message unchanged or not specified a)bort, c)ontinue, e)dit a $
版本库不知道也不关心你的修改作为一个整体是否有意义,它只检查是否有其他人修改了同一个文件,如果别人已经这样做了,你的整个提交会失败,并且提示你一个或多个文件已经过时了:
$ svn commit -m "Add another rule" Sending rules.txt svn: Commit failed (details follow): svn: Your file or directory 'sandwich.txt' is probably out-of-date …
(错误信息的精确措辞依赖于网络协议和你使用的服务器,但对于所有的情况,其思想完全一样。)
此刻,你需要运行svn update来处理所有的合并和冲突,然后再尝试提交。
我们已经覆盖了Subversion基本的工作周期,还有许多其它特性可以管理你得版本库和工作拷贝,但是只使用前面介绍的命令你就可以很进行日常工作了,我们还会覆盖更多用的还算频繁的命令。
[4] 当然没有任何东西是在版本库里被删除了—只是在版本库的HEAD
里消失了,你可以通过检出(或者更新你的工作拷贝)你做出删除操作的前一个修订版本来找回所有的东西,详细请见“找回删除的项目”一节。
[5] 而且你也没有WAN卡,考虑到你得到我们,哈!
[6] 你也可以手工的删除这三个临时文件,但是当Subversion会给你做时你会自己去做吗?我们是这样想的。
[7] 如果你向他们询问,他们非常有理由把你带到城外的铁轨上。