svn 基本使用
https://help.ubuntu.com/community/Subversion
svn import
svn import是将未版本化文件导入版本库的最快方法,会根据需要创建中介目录。svn import不需要一个工作拷贝,你的文件会直接提交到版本库,这通常用在你希望将一组文件加入到Subversion版本库时,例如:
$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/some/project /
-m "Initial import"
Adding mytree/foo.c
Adding mytree/bar.c
Adding mytree/subdir
Adding mytree/subdir/quux.h
Committed revision 1.
在上一个例子里,将会拷贝目录mytree
到版本库的some/project
下:
$ svn list file:///usr/local/svn/newrepos/some/project
bar.c
foo.c
subdir/
注意,在导入之后,原来的目录树并没有转化成工作拷贝,为了开始工作,你还是需要运行svn checkout导出一个工作拷贝。
推荐的版本库布局
尽管Subversion的灵活性允许你自由布局版本库,但我们有一套推荐的方式,创建一个trunk
目录来保存开发的“主线”,一个branches
目录存放分支拷贝,tags
目录保存标签拷贝,例如:
$ svn list file:///usr/local/svn/repos
/trunk
/branches
/tags
初始化检出
大多数时候,你会使用checkout从版本库取出一个新拷贝开始使用Subversion,这样会在本机创建一个项目的“本地拷贝”,这个拷贝包括了命令行指定版本库中的HEAD
(最新的)版本:
$ svn checkout http://svn.collab.net/repos/svn/trunk
A trunk/Makefile.in
A trunk/ac-helpers
A trunk/ac-helpers/install.sh
A trunk/ac-helpers/install-sh
A trunk/build.conf
…
Checked out revision 8810.
管上面的例子取出了trunk目录,你也完全可以通过输入特定URL取出任意深度的子目录:
$ svn checkout /
http://svn.collab.net/repos/svn/trunk/subversion/tests/cmdline/
A cmdline/revert_tests.py
A cmdline/diff_tests.py
A cmdline/autoprop_tests.py
A cmdline/xmltests
A cmdline/xmltests/svn-test.sh
…
Checked out revision 8810.
因为Subversion使用“拷贝-修改-合并”模型而不是“锁定-修改-解锁”模型(见“版本模型”一节),你可以在工作拷贝中开始修改的目录和文件,你的工作拷贝和你的系统中的其它文件和目录完全一样,你可以编辑并改变它,移动它,也可以完全的删掉它,把它忘了。
svn
目录包含什么?
工作拷贝中的任何一个目录包括一个名为.svn
管理区域,通常列表操作不显示这个目录,但它仍然是一个非常重要的目录,无论你做什么?不要删除或是更改这个管理区域的任何东西,Subversion使用它来管理工作拷贝。
如果你不小心删除了子目录.svn
,最简单的解决办法是删除包含的目录(普通的文件系统删除,而不是svn delete),然后在父目录运行svn update,Subversion客户端会重新下载你删除的目录,并包含新的.svn
。
因为你可以使用版本库的URL作为唯一参数取出一个工作拷贝,你也可以在版本库URL之后指定一个目录,这样会将你的工作目录放到你的新目录,举个例子:
$ svn checkout http://svn.collab.net/repos/svn/trunk subv
A subv/Makefile.in
A subv/ac-helpers
A subv/ac-helpers/install.sh
A subv/ac-helpers/install-sh
A subv/build.conf
…
Checked out revision 8810.
这样将把你的工作拷贝放到subv
而不是和前面那样放到trunk
,如果subv
不存在,将会自动创建。
基本的工作周期
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列字符,紧跟一些空格,接着是文件或者目录名。第一列告诉一个文件或目录的状态或它的内容,返回代码如下:
-
预定加入到版本库的文件、目录或符号链的
item
。 -
文件
item
发生冲突,在从服务器更新时与本地版本发生交迭,在你提交到版本库前,必须手工的解决冲突。 -
文件、目录或是符号链
item
预定从版本库中删除。 -
文件
item
的内容被修改了。
A item
C item
D item
M 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放置三个额外的未版本化文件到你的工作拷贝:
-
你更新前的文件,没有冲突标志,只是你最新更改的内容。(如果Subversion认为这个文件不可以合并,
.mine
文件不会创建,因为它和工作文件相同。) -
这是你的做更新操作以前的
BASE
版本文件,就是你在上次更新之后未作更改的版本。 -
这是你的Subversion客户端从服务器刚刚收到的版本,这个文件对应版本库的
HEAD
版本。
filename.mine
filename.rOLDREV
filename.rNEWREV
这里
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] 如果你向他们询问,他们非常有理由把你带到城外的铁轨上。