基本的工作周期

Subversion有许多特性、选项和华而不实的高级功能,但日常的工作中� 只使用其中的一小部分,有一些只在特殊情况才会使用,在这一节里,我们会介绍许多� 在日常工作中常见的命令。

典型的工作周期是这� �的:

现在� 可以开始工作并且修改� 的工作拷贝了,� 很容易决定作出一个修改(或者是一组),像写一个新的特性,修正一个错误等等。这时可以使用的Subversion命令包括svn addsvn deletesvn copysvn move。如果� 只是修改版本库中已经存在的文件,在� 提交之前,不必使用上面的任何一个命令。� 可以对工作备份作的修改包括:

修改文件,可以使用文本编辑器、字处理软件、图形程序或任何� 常用的工具,Subverion处理二进制文件像同文本文件一� �—效率也一� �。

这些是常用的可以修改目录� �结构的子命令(我们会在后面包括svn importsvn mkdir)。

svn add foo

预定将文件、目录或者符号链foo添� 到版本库,当� 下次提交后,foo会成为其父目录的一个子对象。注意,如果foo是目录,所有foo中的内容也会预定添� 进去,如果� 只想添� foo本身,使用--non-recursive-N)参数。

svn delete foo

预定将文件、目录或者符号链foo从版本库中� 除掉,如果foo是文件,它马上从工作拷贝中� 除,如果是目录,不会被� 除,但是Subversion准备好� 除了,当� 提交� 的修改,foo就会在� 的工作拷贝和版本库中被� 除。[2]

svn copy foo bar

建立一个新的项目bar作为foo的复制品,当在下次提交时会将bar添� 到版本库,这种拷贝历史会记录下来(按照来自foo的方式记录),svn copy并不建立中介目录。

svn move foo bar

这个命令与与运行svn copy foo bar; svn delete foo完全相同,bar作为foo的拷贝准备添� ,foo已经预定要被� 除,svn move不建立中介的目录。

当� 完成修改,� 需要提交他们到版本库,但是在此之前,检查一下做过什么修改是个好主意,通过提交前的检查,� 可以整理一份精确的日志信息,� 也可以发现� 不小心修改的文件,给了� 一次恢复修改的机会。此外,这是一个审查和仔细察看修改的好机会,� 可通过命令svn statussvn diffsvn revert精确地察看所做的修改。� 可以使用前两个命令察看工作拷贝中的修改,使用第三个来撤销部分(或全部)的修改。

Subversion已经被优化来帮助� 完成这个任务,可以在不与版本库通讯的情况下做许多事情,详细来说,对于每一个文件,� 的的工作拷贝在.svn包含了一个“原始的”拷贝,所以Subversion可以快速的告诉� 那些文件修改了,甚至允许� 在不与版本库通讯的情况下恢复修改。

相对于其他命令,� 会更多地使用这个svn status命令。

如果� 在工作拷贝的顶级目录运行不带参数的svn status命令,它会检测� 做的所有的文件或目录的修改,以下的例子是来展示svn status可能返回的状态� �(注意,#之后的不是svn status打印的)。

  L    abc.c               # svn已经在.svn目录锁定了abc.c
M      bar.c               # bar.c的内容已经在本地修改过了
 M     baz.c               # baz.c属性有修改,但没有内容修改
X      3rd_party           # 这个目录是外部定义的一部分
?      foo.o               # svn并没有管理foo.o
!      some_dir            # svn管理这个,但它可能丢失或者不完整
~      qux                 # 作为file/dir/link进行了版本控制,但类型已经改变
I      .screenrc           # svn不管理这个,配置确定要忽略它
A  +   moved_dir           # 包含历史的添加,历史记录了它的来历
M  +   moved_dir/README    # 包含历史的添加,并有了本地修改
D      stuff/fish.c        # 这个文件预定要删除
A      stuff/loot/bloo.h   # 这个文件预定要添加
C      stuff/loot/lump.c   # 这个文件在更新时发生冲突
R      xyz.c               # 这个文件预定要被替换
    S  stuff/squawk        # 这个文件已经跳转到了分支

在这种� �式下,svn status打印五列字符,紧跟一些空� �,接着是文件或者目录名。第一列告诉一个文件的状态或它的内容,返回代� �解释如下:

A item

文件、目录或是符号链item预定� 入到版本库。

C item

文件item发生冲突,在从服务器更新时与本地版本发生交迭,在� 提交到版本库前,必须手工的解决冲突。

D item

文件、目录或是符号链item预定从版本库中� 除。

M item

文件item的内容被修改了。

R item

文件、目录或是符号链item预定将要替换版本库中的item,这意味着这个对象首先要被� 除,另外一个同名的对象将要被添� ,所有的操作发生在一个修订版本。

X item

目录没有版本化,但是与Subversion的外部定义关联,关于外部定义,可以看“外部定义”一节

? item

文件、目录或是符号链item不在版本控制之下,� 可以通过使用svn status--quiet-q)参数或父目录的svn:ignore属性忽略这个问题,关于忽略文件的使用,见svn:ignore”一节

! item

文件、目录或是符号链item在版本控制之下,但是已经丢失或者不完整,这可能� 为使用非Subversion命令� 除� 成的,如果是一个目录,有可能是检出或是更新时的中断� 成的,使用svn update可以重新从版本库获得文件或者目录,也可以使用svn revert file恢复原来的文件。

~ item

文件、目录或是符号链item在版本库已经存在,但� 的工作拷贝中的是另一个。举一个例子,� � 除了一个版本库的文件,新建了一个在原来的位置,而且整个过程中没有使用svn delete或是svn add

I item

文件、目录或是符号链item不在版本控制下,Subversion已经配置好了会在svn addsvn importsvn status命令忽略这个文件,关于忽略文件,见svn:ignore”一节。注意,这个符号只会在使用svn status的参数--no-ignore时才会出现—否则这个文件会被忽略且不会显示!

第二列说明文件或目录的属性的状态(更多细节可以看“属性”一节),如果一个M出现在第二列,说明属性被修改了,否则显示空白。

第三列只显示空白或者LL表示Subversion已经在.svn工作区域锁定了这个项目,当� 的svn commit正在运行的时候—也许正在输入log信息,运行svn status� 可以看到L� �记,如果这时候Subversion并没有运行,可以推测Subversion发生中断并且已经锁定,� 必须运行svn cleanup来清除锁定(本节后面将有更多论述)。

第四列只会显示空白或++的意思是一个有附� 历史信息的文件或目录预定添� 或者修改到版本库,通常出现在svn move或是svn copy时,如果是看到A� � +就是说要包含历史的增� ,它可以是一个文件或是拷贝的� �目录。+表示它是即将包含历史增� 到版本库的目录的一部分,也就是说他的父目录要拷贝,它只是跟着一起的。 M� � +表示将要包含历史的增� ,并且已经更改了。当� 提交时,首先会随父目录进行包含历史的增� ,然后本地的修改提交到更改后的版本。

第五列只显示空白或是S,表示这个目录或文件已经转到了一个分支下了(使用svn switch)。

如果� � 递一个路径给svn status,它只给� 这个项目的信息:

$ svn status stuff/fish.c
D      stuff/fish.c

svn status也有一个--verbose-v)选项,它可以显示工作拷贝中的所有项目,即使没有改变过:

$ svn status --verbose
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 --show-updates --verbose
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,� 的READMEtrout.c会被更新,这告诉� 许多有用的信息—� 可以在提交之前,需要使用更新操作得到文件README的更新,或者说文件已经过时,版本库会拒绝了� 的提交。(后面还有更多关于此主题)。

另一种检查修改的方式是svn diff命令,� 可以通过不带参数的svn diff精确的找出� 所做的修改,这会输出统一区别� �式:[3]

$ 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

举个例子,� 可以把补丁文件发送邮件到其他开发者,在提交之前审� �和测试。

我们可以使用svn status -u来预测冲突,当� 运行svn update一些有趣的事情发生了:

$ svn update
U  INSTALL
G  README
C  bar.c
Updated to revision 46.

UG没必要关心,文件干净的接受了版本库的变化,文件� �示为U表明本地没有修改,文件已经� �据版本库更新。G� �示合并,� �示本地已经修改过,与版本库没有重迭的地方,已经合并。

但是C表示冲突,说明服务器上的改动同� 的改动冲突了,� 需要自己手工去解决。

当冲突发生了,有三件事可以帮助� 注意到这种情况和解决问题:

举一个例子,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 --message "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就不会认为这个文件是在冲突状态了。[4]

$ 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,� 没办法从从意大利熟食店得到想要的泡菜。[5]一旦� 们确认了提交内容后,修改文件并且� 除冲突� �志。

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."

记住,如果� 修改冲突时感到混乱,� 可以参考subversion生成的三个文件—包括� 未作更新的文件。� 也可以使用第三方的合并工具检验这三个文件。

最后!� 的修改结束了,� 合并了服务器上所有的修改,� 准备好提交修改到版本库。

svn commit命令发送所有的修改到版本库,当� 提交修改时,� 需要提供一些描述修改的日志信息,� 的信息会附到这个修订版本上,如果信息很简短,� 可以在命令行中使用--message-m)选项:

$ svn commit --message "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.

然而,如果� 把写日志信息当作工作的一部分,� 也许会希望通过告诉Subversion一个文件名得到日志信息,使用--file选项:

$ svn commit --file logmsg 
Sending        sandwich.txt
Transmitting file data .
Committed revision 4.

如果� 没有指定--message或者--file选项,Subversion会自动地启动� 最喜欢的编辑器(见“config”一节editor-cmd部分)来编辑日志信息。

提示

如果� 使用编辑器撰写日志信息时希望取消提交,� 可以直接关掉编辑器,不要保存,如果� 已经做过保存,只要简单的� 掉所有的文本并再次保存。

$ svn commit
Waiting for Emacs...Done

Log message unchanged or not specified
a)bort, c)ontinue, e)dit
a
$

版本库不知道也不关心� 的修改作为一个整体是否有意义,它只检查是否有其他人修改了同一个文件,如果别人已经这� �做了,� 的整个提交会失败,并且提示� 一个或多个文件已经过时了:

$ svn commit --message "Add another rule"
Sending        rules.txt
svn: Commit failed (details follow):
svn: Out of date: 'rules.txt' in transaction 'g'

此刻,� 需要运行svn update来处理所有的合并和冲突,然后再尝试提交。

我们已经覆盖了Subversion基本的工作周期,还有许多其它特性可以管理� 得版本库和工作拷贝,但是只使用前面介绍的命令� 就可以很轻松的工作了。



[2] 当然没有任何东西是在版本库里被� 除了—只是在版本库的HEAD里消失了,� 可以通过检出(或者更新� 的工作拷贝)� 做出� 除操作的前一个修订版本来找回所有的东西。

[3] Subversion使用内置区别引擎,缺省情况下输出为统一区别� �式。如果� 期望不同的输出� �式,� 可以使用--diff-cmd指定外置的区别程序,并且通过--extensions� 递其他参数,举个例子,察看本地文件foo.c的区别,同时忽略空� �修改,� 可以运行svn diff --diff-cmd /usr/bin/diff --extensions '-bc' foo.c

[4] � 也可以手工的� 除这三个临时文件,但是当Subversion会给� 做时� 会自己去做吗?我们是这� �想的。

[5] 如果� 向他们询问,他们非常有理由把� 带到城外的铁轨上。