Subversion 有许多特性, 选项和华而不实的高级功能,但日常的工作中你只使用其中的一小部分,在这一节里,我们会介绍许多你在日常工作中常用的命令。
典型的工作周期是这样的:
更新你的工作副本。
svn update
做出修改
svn add
svn delete
svn copy
svn move
检验修改
svn status
svn diff
可能会取消一些修改
svn revert
解决冲突(合并别人的修改)
svn update
svn resolve
提交你的修改
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 help
update。
现在你可以开始工作,并且修改你的工作副本了。你很容易决定作出一个修改(或者是一组),像写一个新的特性,修正一个错误等等。这时可以使用的 Subversion 命令包括 svn add, svn delete, svn copy, svn move 和 svn mkdir。如果你只是修改版本库中已经存在的文件,在你提交之前,不必使用上面的任何一个命令。
你可以对工作副本做出两种修改:文件修改和目录树修改。你不需要告诉 Subversion 你希望修改一个文件,只需要用你的编辑器, 字处理器, 图形程序,或任何工具做出修改,Subversion 会自动检测到文件的修改。此外,二进制文件的处理方式和文本文件一样,也有同样的效率。对于目录树更改,你可以告诉 Subversion 将文件和目录“标记”为调度删除, 添加, 拷贝或移动。这些动作会在工作副本上立刻发生效果,但只有提交后才会在版本库里生效。
下面是 Subversion 用来修改目录树结构的五个最常用的子命令。
svn add foo
调度将文件, 目录或者符号链接 foo
添加到版本库。当你下次提交后,foo 会成为其父目录的一个子对象。注意,如果
foo 是目录,所有 foo 中的内容也会调度增加。如果你只想添加
foo 本身,请使用 --non-recursive (-N) 参数。
svn delete foo
调度将文件, 目录或者符号链接 foo 从版本库中删除,如果 foo
是文件或符号链接,它会马上从工作副本中删除。如果 foo 是目录,不会被删除,但是 Subversion
调度删除它。当你提交修改后,foo 就会在你的工作副本和版本库中被删除。[3]
svn copy foo bar
建立一个新条目 bar 作为 foo 的复制品,并且自动调度增加
bar,当在下次提交时会将 bar
添加到版本库,这种复制会记录下来历史(按照来自 foo 的方式记录)。如果不传递
--parents,svn copy 并不建立中介目录。
svn move foo bar
这个命令与与运行 svn copy foo bar; svn delete foo
完全相同,bar 作为 foo
的拷贝调度添加,foo 已经调度删除。如果不传递
--parents,svn move 不建立中介的目录。
svn mkdir blort
这个命令同运行 mkdir blort; svn add blort 相同,也就是创建一个叫做
blort 的文件,并且调度增加到版本库。
当你完成修改,你需要提交它们到版本库,但是在此之前,检查一下做过什么修改是个好主意。通过提交前的检查,你可以整理一份精确的日志信息。你也可以发现你不小心修改的文件,给了你一次撤销修改的机会。此外,这是一个在发布之前,复审和检查的好机会。你可通过命令 svn status 浏览所做的修改,通过命令 svn diff 检查修改的详细信息。
Subversion 已经被优化来帮助你完成这个任务,可以在不与版本库通讯的情况下做许多事情。详细来说,对于每一个文件,你的的工作副本在
.svn 包含了一个“原始的”副本,所以 Subversion
可以快速的告诉你哪些文件修改了,甚至允许你在不与版本库通讯的情况下恢复修改。
为了浏览你的修改,可以使用 svn status 命令。在所有的 Subversion 命令中,svn status 可能会是你使用最多的命令。
如果你在工作副本的顶级目录运行不带参数的 svn status
命令,它会检测你对所有文件或目录作出的修改。以下的例子是来展示 svn status 可能返回的状态代码(注意
# 之后的内容不是 svn status 打印的信息)。
? scratch.c # file is not under version control 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 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
的“原始”文件来输出信息,预定要增加的文件会显示所有增加的文本,预定要删除的文件会显示所有要删除的文本。
输出的格式为统一差异格式。删除的行前面加一个 -,增加的行前面有一个
+。svn diff
命令也打印文件名和补丁程序需要的位置信息,所以你可以通过重定向一个差异文件来生成“补丁”:
$ svn diff > patchfile
举个例子,你可以通过邮件把补丁文件发送到其他开发者,在提交之前审核或测试。
Subversion 使用内置差异引擎,默认输出统一差异格式。如果你期望不同的输出格式,你可以使用
--diff-cmd 指定外置的比较程序,并且通过选项 --extensions
(-x) 来传递其它参数。例如,察看本地文件 foo.c
的本地修改,同时忽略大小写差异,你可以运行 svn diff --diff-cmd /usr/bin/diff -x "-i"
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 status README $ svn delete README D README $ svn revert README Reverted 'README' $ svn status README
We've already seen how svn status -u can predict
conflicts. Suppose you run svn update and some
interesting things occur:
$ svn update
U INSTALL
G README
Conflict discovered in 'bar.c'.
Select: (p) postpone, (df) diff-full, (e) edit,
(h) help for more options:
The U and
G codes are no cause for concern; those
files cleanly absorbed changes from the repository. The files marked with
U contained no local changes but were
Updated with changes from the repository.
The G stands for
merGed, which means that the file had local
changes to begin with, but the changes coming from the repository didn't
overlap with the local changes.
But the next two lines are part of a feature (new in Subversion 1.5) called
interactive conflict resolution. This means that the
changes from the server overlapped with your own, and you have the
opportunity to resolve this conflict. The most commonly used options are
displayed, but you can see all of the options by typing
h:
… (p) postpone - mark the conflict to be resolved later (df) diff-full - show all changes made to merged file (e) edit - change merged file in an editor (r) resolved - accept merged version of file (mf) mine-full - accept my version of entire file (ignore their changes) (tf) theirs-full - accept their version of entire file (lose my changes) (l) launch - launch external tool to resolve conflict (h) help - show this list
在我们详细查看每个选项含义之前,让我们简短的回顾一下所有这些选项。
p)ostpone
让文件在更新完成之后保持冲突状态。
d)iff-(f)ull
使用标准区别格式显示base修订版本和冲突文件本身的区别。
e)dit
用你喜欢的编辑器打开冲突的文件,编辑器是环境变量EDITOR设置的。
r)esolved
After editing a file, tell svn that you've resolved the conflicts in the file and that it should accept the current contents—basically that you've “resolved” the conflict.
m)ine-(f)ull
丢弃新从服务器接收的变更,并只使用你查看文件的本地修改。
t)heirs-(f)ull
丢弃你对查看文件的本地修改,只使用从服务器新接收的变更。
l)aunch
启动外部程序来执行冲突解决,这需要一些预先的准备。
h)elp
显示所有在冲突解决时可能使用的命令。
我们现在会更详细的覆盖这些命令,根据关联功能对其进行分组。
Before deciding how to attack a conflict interactively, odds are that you'd
like to see exactly what is in conflict, and the
diff-full command (df) is what
you'll use for this:
…
Select: (p) postpone, (df) diff-full, (e) edit,
(h)elp for more options : df
--- .svn/text-base/sandwich.txt.svn-base Tue Dec 11 21:33:57 2007
+++ .svn/tmp/tempfile.32.tmp Tue Dec 11 21:34:33 2007
@@ -1 +1,5 @@
-Just buy a sandwich.
+<<<<<<< .mine
+Go pick up a cheesesteak.
+=======
+Bring me a taco!
+>>>>>>> .r32
…
The first line of the diff content shows the previous contents of the
working copy (the BASE revision), the next content line
is your change, and the last content line is the change that was just
received from the server (usually the
HEAD revision). With this information in hand, you're
ready to move on to the next action.
There are four different ways to resolve conflicts interactively—two of which allow you to selectively merge and edit changes, and two of which allow you to simply pick a version of the file and move along.
If you wish to choose some combination of your local changes, you can use
the “edit” command (e) to manually edit
the file with conflict markers in a text editor (determined by the
EDITOR environment variable). Editing the file by hand
in your favorite text editor is a somewhat low-tech way of remedying
conflicts (see 第 4.5.4 节 “手工合并冲突” for a
walkthrough), so some people like to use fancy graphical merge tools
instead.
To use a merge tool, you need to either set the SVN_MERGE
environment variable or define the merge-tool-cmd option
in your Subversion configuration file (see 第 1.3 节 “配置选项” for more details). Subversion will
pass four arguments to the merge tool: the BASE revision
of the file, the revision of the file received from the server as part of
the update, the copy of the file containing your local edits, and the merged
copy of the file (which contains conflict markers). If your merge tool is
expecting arguments in a different order or format, you'll need to write a
wrapper script for Subversion to invoke. After you've edited the file, if
you're satisfied with the changes you've made, you can tell Subversion that
the edited file is no longer in conflict by using the “resolve”
command (r).
If you decide that you don't need to merge any changes, but just want to
accept one version of the file or the other, you can either choose your
changes (a.k.a. “mine”) by using the “mine-full”
command (mf) or choose theirs by using the
“theirs-full” command (tf).
This may sound like an appropriate section for avoiding marital
disagreements, but it's actually still about Subversion, so read on. If
you're doing an update and encounter a conflict that you're not prepared to
review or resolve, you can type p to postpone
resolving a conflict on a file-by-file basis when you run svn
update. If you're running an update and don't want to resolve
any conflicts, you can pass the --non-interactive option to
svn update, and any file in conflict will be marked with
a C automatically.
The C stands for
conflict. This means that the changes from
the server overlapped with your own, and now you have to manually choose
between them after the update has completed. When you postpone a conflict
resolution, svn typically does three things to assist you
in noticing and resolving that conflict:
Subversion在更新时打印C标记,并且标记这个文件已冲突。
If Subversion considers the file to be mergeable, it places
conflict markers—special strings of text that
delimit the “sides” of the conflict—into the file to
visibly demonstrate the overlapping areas. (Subversion uses the
svn:mime-type property to decide whether a file is
capable of contextual, line-based merging. See 第 3.1 节 “文件内容类型” to learn more.)
对于每一个冲突的文件,Subversion放置三个额外的未版本化文件到你的工作副本:
filename.mine
This is your file as it existed in your working copy before you updated your
working copy—that is, without conflict markers. This file has only
your latest changes in it. (If Subversion considers the file to be
unmergeable, the .mine file isn't created, since it
would be identical to the working file.)
filename.rOLDREV
This is the file that was the BASE revision before you
updated your working copy. That is, the file that you checked out before
you made your latest edits.
filename.rNEWREV
This is the file that your Subversion client just received from the server
when you updated your working copy. This file corresponds to the
HEAD revision of the repository.
这里OLDREV是你的.svn目录中的修订版本号,NEWREV是版本库中HEAD的版本号。
For example, Sally makes changes to the file
sandwich.txt, but does not yet commit those changes.
Meanwhile, Harry commits changes to that same file. Sally updates her
working copy before committing and she gets a conflict, which she postpones:
$ svn update
Conflict discovered in 'sandwich.txt'.
Select: (p) postpone, (df) diff-full, (e) edit,
(h)elp for more options : p
C sandwich.txt
Updated to revision 2.
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2
At this point, Subversion will not allow Sally to
commit the file sandwich.txt until the three temporary
files are removed:
$ 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
If you've postponed a conflict, you need to resolve the conflict before
Subversion will allow you to commit your changes. You'll do this with the
svn resolve command and one of several arguments to the
--accept option.
如果你希望选择上次检出后修改之前的文件版本,选择base参数。
如果你希望选择只包含你修改的版本,选择mine-full参数。
如果你希望选择最近从服务器更新的版本(因此会丢弃你的所以编辑),选择theirs-full参数。
However, if you want to pick and choose from your changes and the changes
that your update fetched from the server, merge the conflicted text
“by hand” (by examining and editing the conflict markers within
the file) and then choose the working argument.
svn resolve removes the three temporary files and accepts
the version of the file that you specified with the
--accept option, and Subversion no longer considers the
file to be in a state of conflict:
$ svn resolve --accept working sandwich.txt Resolved conflicted state of 'sandwich.txt'
第一次尝试解决冲突让人感觉很害怕,但经过一点训练,它简单的像是骑着车子下坡。
Here's an example. Due to a miscommunication, you and Sally, your
collaborator, both edit the file sandwich.txt at the
same time. Sally commits her changes, and when you go to update your
working copy, you get a conflict and you're going to have to edit
sandwich.txt to resolve the conflict. First, let's
take a look at the file:
$ 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
The strings of less-than signs, equals signs, and greater-than signs are conflict markers and are not part of the actual data in conflict. You generally want to ensure that those are removed from the file before your next commit. The text between the first two sets of markers is composed of the changes you made in the conflicting area:
<<<<<<< .mine Salami Mortadella Prosciutto =======
后两组之间的是Sally提交的修改冲突:
======= Sauerkraut Grilled Chicken >>>>>>> .r2
Usually you won't want to just delete the conflict markers and Sally's changes—she's going to be awfully surprised when the sandwich arrives and it's not what she wanted. This is where you pick up the phone or walk across the office and explain to Sally that you can't get sauerkraut from an Italian deli. [5] Once you've agreed on the changes you will commit, edit your file and remove the conflict markers:
Top piece of bread Mayonnaise Lettuce Tomato Provolone Salami Mortadella Prosciutto Creole Mustard Bottom piece of bread
Now use svn resolve, and you're ready to commit your changes:
$ svn resolve --accept working sandwich.txt Resolved conflicted state of 'sandwich.txt' $ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
Note that svn resolve, unlike most of the other commands we deal with in this chapter, requires that you explicitly list any filenames that you wish to resolve. In any case, you want to be careful and use svn resolve only when you're certain that you've fixed the conflict in your file—once the temporary files are removed, Subversion will let you commit the file even if it still contains conflict markers.
If you ever get confused while editing the conflicted file, you can always consult the three files that Subversion creates for you in your working copy—including your file as it was before you updated. You can even use a third-party interactive merging tool to examine those three files.
If you get a conflict and decide that you want to throw out your changes,
you can run svn resolve --accept theirs-full
and Subversion will
discard your edits and remove the temporary files:CONFLICTED-PATH
$ svn update
Conflict discovered in 'sandwich.txt'.
Select: (p) postpone, (df) diff-full, (e) edit,
(h) help for more options: p
C sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1
$ svn resolve --accept theirs-full sandwich.txt
Resolved conflicted state of 'sandwich.txt'
If you decide that you want to throw out your changes and start your edits again (whether this occurs after a conflict or anytime), just revert your changes:
$ svn revert sandwich.txt Reverted 'sandwich.txt' $ ls sandwich.* sandwich.txt
Note that when you revert a conflicted file, you don't have to use svn resolve.
最后!你的修改结束了,你合并了服务器上所有的修改,你准备好提交修改到版本库。
The svn commit command sends all of your changes to the
repository. When you commit a change, you need to supply a log
message describing your change. Your log message will be
attached to the new revision you create. If your log message is brief, you
may wish to supply it on the command line using the
--message (-m) option:
$ svn commit -m "Corrected number of cheese slices." Sending sandwich.txt Transmitting file data . Committed revision 3.
然而,如果你把写日志信息当作工作的一部分,你也许会希望告诉Subversion通过一个文件名得到日志信息,使用--file(-F)选项:
$ svn commit -F logmsg Sending sandwich.txt Transmitting file data . Committed revision 4.
If you fail to specify either the --message
(-m) or --file (-F)
option, Subversion will automatically launch your favorite editor (see the
information on editor-cmd in 第 1.3.2 节 “配置”) for composing a log message.
|
提示 |
|---|---|
|
If you're in your editor writing a commit message and decide that you want to cancel your commit, you can just quit your editor without saving changes. If you've already saved your commit message, simply delete the text, save again, and then abort: $ svn commit Waiting for Emacs...Done Log message unchanged or not specified (a)bort, (c)ontinue, (e)dit a $ |
The repository doesn't know or care whether your changes make any sense as a whole; it checks only to make sure nobody else has changed any of the same files that you did when you weren't looking. If somebody has done that, the entire commit will fail with a message informing you that one or more of your files are out of date:
$ svn commit -m "Add another rule" Sending rules.txt svn: Commit failed (details follow): svn: File '/sandwich.txt' is out of date …
(错误信息的精确措辞依赖于网络协议和你使用的服务器,但对于所有的情况,其思想完全一样。)
At this point, you need to run svn update, deal with
any merges or conflicts that result, and attempt your commit again.
That covers the basic work cycle for using Subversion. Subversion offers many other features that you can use to manage your repository and working copy, but most of your day-to-day use of Subversion will involve only the commands that we've discussed so far in this chapter. We will, however, cover a few more commands that you'll use fairly often.
[3] 当然没有任何东西是在版本库里被删除了—只是在版本库的 HEAD
里消失了。你可以通过检出(或者调整你的工作副本到)你做出删除操作的前一个修订版本,来找回所有的东西,详情参见第 3.5 节 “找回删除的项目”。
[4] 而且你也没有无线上网卡。不要妄想连接到我们,哈!
[5] 如果你向他们询问,他们非常有理由把你带到城外的铁轨上。