基本的工作周期

Subversion has numerous features, options, bells, and whistles, but on a day-to-day basis, odds are that you will only use a few of them. In this section, we'll run through the most common things that you might find yourself doing with Subversion in the course of a day's work.

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

  1. 更新你的工作拷贝。

    • svn update

  2. Make changes.

    • svn add

    • svn delete

    • svn copy

    • svn move

  3. Examine your changes.

    • svn status

    • svn diff

  4. Possibly undo some changes.

    • svn revert

  5. Resolve conflicts (merge others' changes).

    • svn update

    • svn resolve

  6. Commit your changes.

    • svn commit

更新你的工作拷贝

When working on a project with a team, you'll want to update your working copy to receive any changes made since your last update by other developers on the project. Use svn update to bring your working copy into sync with the latest revision in the repository:

$ svn update
U  foo.c
U  bar.c
Updated to revision 2.

In this case, it appears that someone checked in modifications to both foo.c and bar.c since the last time you updated, and Subversion has updated your working copy to include those changes.

When the server sends changes to your working copy via svn update, a letter code is displayed next to each item to let you know what actions Subversion performed to bring your working copy up-to-date. To find out what these letters mean, run svn help update.

修改你的工作拷贝

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

There are two kinds of changes you can make to your working copy: file changes and tree changes. You don't need to tell Subversion that you intend to change a file; just make your changes using your text editor, word processor, graphics program, or whatever tool you would normally use. Subversion automatically detects which files have been changed, and in addition, handles binary files just as easily as it handles text files—and just as efficiently too. For tree changes, you can ask Subversion to “mark” files and directories for scheduled removal, addition, copying, or moving. These changes may take place immediately in your working copy, but no additions or removals will happen in the repository until you commit them.

下面是Subversion用来修改目录树结构的五个子命令。

svn add foo

Schedule file, directory, or symbolic link foo to be added to the repository. When you next commit, foo will become a child of its parent directory. Note that if foo is a directory, everything underneath foo will be scheduled for addition. If you want only to add foo itself, pass the --depth empty option.

svn delete foo

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

svn copy foo bar

Create a new item bar as a duplicate of foo and automatically schedule bar for addition. When bar is added to the repository on the next commit, its copy history is recorded (as having originally come from foo). svn copy does not create intermediate directories unless you pass the --parents.

svn move foo bar

This command is exactly the same as running svn copy foo bar; svn delete foo. That is, bar is scheduled for addition as a copy of foo, and foo is scheduled for removal. svn move does not create intermediate directories unless you pass the --parents.

svn mkdir blort

这个命令同运行 mkdir blort; svn add blort相同,也就是创建一个叫做blort的文件,并且预定添加到版本库。

检查你的修改

当你完成修改,你需要提交他们到版本库,但是在此之前,检查一下做过什么修改是个好主意,通过提交前的检查,你可以整理一份精确的日志信息,你也可以发现你不小心修改的文件,给了你一次恢复修改的机会。此外,这是一个审查和仔细察看修改的好机会,你可通过命令svn status浏览所做的修改,通过svn diff检查修改的详细信息。

Subversion has been optimized to help you with this task, and it is able to do many things without communicating with the repository. In particular, your working copy contains a hidden cached “pristine” copy of each version controlled file within the .svn area. Because of this, Subversion can quickly show you how your working files have changed or even allow you to undo your changes without contacting the repository.

查看你的修改概况

为了浏览修改的内容,你会使用这个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

In this output format, svn status prints six columns of characters, followed by several whitespace characters, followed by a file or directory name. The first column tells the status of a file or directory and/or its contents. The codes we listed are:

A item

预定加入到版本库的文件、目录或符号链的item

C item

The file item is in a state of conflict. That is, changes received from the server during an update overlap with local changes that you have in your working copy (and weren't resolved during the update). You must resolve this conflict before committing your changes to the repository.

D item

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

M item

文件item的内容被修改了。

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

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

svn status also has a --verbose (-v) option, which will show you the status of every item in your working copy, even if it has not been changed:

$ 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

This is the “long form” output of svn status. The letters in the first column mean the same as before, but the second column shows the working revision of the item. The third and fourth columns show the revision in which the item last changed, and who changed it.

None of the prior invocations to svn status contact the repository—instead, they compare the metadata in the .svn directory with the working copy. Finally, there is the --show-updates (-u) option, which contacts the repository and adds information about things that are out of date:

$ 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

Notice the two asterisks: if you were to run svn update at this point, you would receive changes to README and trout.c. This tells you some very useful information—you'll need to update and get the server changes on README before you commit, or the repository will reject your commit for being out of date (more on this subject later).

svn status can display much more information about the files and directories in your working copy than we've shown here—for an exhaustive description of svn status and its output, see 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.

The svn diff command produces this output by comparing your working files against the cached “pristine” copies within the .svn area. Files scheduled for addition are displayed as all added text, and files scheduled for deletion are displayed as all deleted text.

Output is displayed in unified diff format. That is, removed lines are prefaced with -, and added lines are prefaced with +. svn diff also prints filename and offset information useful to the patch program, so you can generate “patches” by redirecting the diff output to a file:

$ svn diff > patchfile

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

Subversion uses its internal diff engine, which produces unified diff format, by default. If you want diff output in a different format, specify an external diff program using --diff-cmd and pass any flags you'd like to it using the --extensions (-x) option. For example, to see local differences in file foo.c in context output format while ignoring case differences, you might run svn diff --diff-cmd /usr/bin/diff --extensions '-i' foo.c.

取消本地修改

假定我们在看svn diff的输出,你发现对某个文件的所有修改都是错误的,或许你根本不应该修改这个文件,或者是从开头重新修改会更加容易。

这是使用svn revert的好机会:

$ svn revert README
Reverted 'README'

Subversion reverts the file to its premodified state by overwriting it with the cached “pristine” copy from the .svn area. But also note that svn revert can undo any scheduled operations—for example, you might decide that you don't want to add a new file after all:

$ 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

$ svn delete README
D         README

$ svn revert README
Reverted 'README'

$ svn status README

解决冲突(合并别人的修改)

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

$ svn update
U  INSTALL
G  README
Conflict discovered in 'bar.c'.
Select: (p) postpone, (df) diff-full, (e) edit,
        (h) help for more options:

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

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

Let's briefly review each of these options before we go into detail on what each option means.

(p)ostpone

Leave the file in a conflicted state for you to resolve after your update is complete.

(d)iff

Display the differences between the base revision and the conflicted file itself in unified diff format.

(e)dit

Open the file in conflict with your favorite editor, as set in the environment variable 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

Discard the newly received changes from the server and use only your local changes for the file under review.

(t)heirs-(f)ull

Discard your local changes to the file under review and use only the newly received changes from the server.

(l)aunch

Launch an external program to perform the conflict resolution. This requires a bit of preparation beforehand.

(h)elp

Show the list of all possible commands you can use in interactive conflict resolution.

We'll cover these commands in more detail now, grouping them together by related functionality.

Viewing conflict differences interactively

Before deciding how to attack a conflict interactively, odds are that you'd like to see what exactly is in conflict, and the diff command (d) is what you'll use for this:

…
Select: (p) postpone, (df) diff-full, (e) edit,
        (h)elp for more options : d
--- .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.

Resolving conflict differences interactively

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 “Merging conflicts by hand”一节 for a walkthrough), so some people like to use fancy graphical merge tools instead.

In order 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 “配置选项”一节 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 lastly, 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 (aka “mine”) by using the “mine-full” command (mf) or choose theirs by using the “theirs-full” command (tf).

Postponing conflict resolution

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 prints a C during the update and remembers that the file is in a state of conflict.

  • 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 if a file is capable of contextual, line-based merging. See “文件内容类型”一节 to learn more.)

  • 对于每一个冲突的文件,Subversion放置三个额外的未版本化文件到你的工作拷贝:

    filename.mine

    你更新前的文件,没有冲突标志,只是你最新更改的内容。(如果Subversion认为这个文件不可以合并,.mine文件不会创建,因为它和工作文件相同。)

    filename.rOLDREV

    这是你的做更新操作以前的BASE版本文件,就是你在上次更新之后未作更改的版本。

    filename.rNEWREV

    这是你的Subversion客户端从服务器刚刚收到的版本,这个文件对应版本库的HEAD版本。

    Here OLDREV is the revision number of the file in your .svn directory, and NEWREV is the revision number of the repository HEAD.

For example, Sally makes changes to the file sandwich.txt in the repository. Harry has just changed the file in his working copy and checked it in. Sally updates her working copy before checking in 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.

If you want to choose the version of the file that you last checked out before making your edits, choose the base argument.

If you want to choose the version that contains only your edits, choose the mine-full argument.

If you want to choose the version that your most recent update pulled from the server (and thus discarding your edits entirely), choose the theirs-full argument.

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, 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'

Merging conflicts by hand

第一次尝试解决冲突让人感觉很害怕,但经过一点训练,它简单的像是骑着车子下坡。

这里一个简单的例子,由于不良的交流,你和同事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

The strings of less-than signs, equal 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. So 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. [6] Once you've agreed on the changes you will check in, 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 run 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 run 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.

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

Discarding your changes in favor of a newly fetched revision

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:

$ 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'

Punting: using svn revert

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 run svn resolve.

提交你的修改

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

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

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

However, if you've been composing your log message as you work, you may want to tell Subversion to get the message from a file by passing the filename with the --file (-F) option:

$ 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
$

The repository doesn't know or care if your changes make any sense as a whole; it checks only to make sure that 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 is 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
…

(错误信息的精确措辞依赖于网络协议和你使用的服务器,但对于所有的情况,其思想完全一样。)

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

我们已经覆盖了Subversion基本的工作周期,还有许多其它特性可以管理你得版本库和工作拷贝,但是只使用前面介绍的命令你就可以很进行日常工作了,我们还会覆盖更多用的还算频繁的命令。



[4] Of course, nothing is ever totally deleted from the repository—just from the HEAD of the repository. You can get back anything you delete by checking out (or updating your working copy to) a revision earlier than the one in which you deleted it. Also see “找回删除的项目”一节.

[5] 而且你也没有WAN卡,考虑到你得到我们,哈!

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