Subversion has numerous features, options, bells, and whistles, but on a day-to-day basis, odds are that you will use only 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.
The typical work cycle looks like this:
Update your working copy.
svn update
Make changes.
svn add
svn delete
svn copy
svn move
Examine your changes.
svn status
svn diff
Possibly undo some changes.
svn revert
Resolve conflicts (merge others' changes).
svn update
svn resolve
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 other developers on the project have made since your last update. 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
.
Now you can get to work and make changes in your working copy. It's usually most convenient to decide on a discrete change (or set of changes) to make, such as writing a new feature, fixing a bug, and so on. The Subversion commands that you will use here are svn add, svn delete, svn copy, svn move, and svn mkdir. However, if you are merely editing files that are already in Subversion, you may not need to use any of these commands until you commit.
You can make two kinds of changes 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, it 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.
Here is an overview of the five Subversion subcommands that you'll use most often to make tree changes:
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
Schedule file, directory, or symbolic link
foo
to be deleted from the
repository. If foo
is a file or
link, it is immediately deleted from your working copy.
If foo
is a directory, it is not
deleted, but Subversion schedules it for deletion. When
you commit your changes, foo
will
be entirely removed from your working copy and the
repository.
[3]
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
option.
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
option.
svn mkdir blort
This command is exactly the same as running
mkdir blort; svn add blort
. That is,
a new directory named blort
is
created and scheduled for addition.
Once you've finished making changes, you need to commit them to the repository, but before you do so, it's usually a good idea to take a look at exactly what you've changed. By examining your changes before you commit, you can make a more accurate log message. You may also discover that you've inadvertently changed a file, and this gives you a chance to revert those changes before committing. Additionally, this is a good opportunity to review and scrutinize changes before publishing them. You can see an overview of the changes you've made by using svn status, and dig into the details of those changes by using 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.
To get an overview of your changes, you'll use the svn status command. You'll probably use svn status more than any other Subversion command.
If you run svn status at the top of
your working copy with no arguments, it will detect all file
and tree changes you've made. Here are a few examples of
the most common status codes that svn
status can return. (Note that the text following
#
is not
actually printed by 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
The file, directory, or symbolic link
item
has been scheduled for
addition into the repository.
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
The file, directory, or symbolic link
item
has been scheduled for
deletion from the repository.
M item
The contents of the file item
have been modified.
If you pass a specific path to svn status, you get information about that item alone:
$ 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.
Another way to examine your changes is with the
svn diff command. You can find out
exactly how you've modified things by
running svn diff
with no arguments, which
prints out file changes in unified diff
format:
$ 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
You could, for example, email the patch file to another developer for review or testing prior to a commit.
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 -x "-i"
foo.c
.
Suppose while viewing the output of svn diff you determine that all the changes you made to a particular file are mistakes. Maybe you shouldn't have changed the file at all, or perhaps it would be easier to make different changes starting from scratch.
This is a perfect opportunity to use 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
Note | |
---|---|
|
Or perhaps you mistakenly removed a file from version control:
$ 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
(which stands for
“Updated”) and G
(for “merGed”) codes are no cause for concern;
those files cleanly absorbed changes from the repository. A
file marked with U
contains
no local changes but was updated with changes from the
repository. One marked with
G
had local changes to begin
with, but the changes coming from the repository didn't
overlap with those 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
Let's briefly review each of these options before we go into detail on what each option means.
(p) postpone
Leave the file in a conflicted state for you to resolve after your update is complete.
(df) diff-full
Display the differences between the base revision and the conflicted file itself in unified diff format.
(e) edit
Open the file in conflict with your favorite editor,
as set in the environment variable
EDITOR
.
(r) resolved
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.
(mf) mine-full
Discard the newly received changes from the server and use only your local changes for the file under review.
(tf) theirs-full
Discard your local changes to the file under review and use only the newly received changes from the server.
(l) launch
Launch an external program to perform the conflict resolution. This requires a bit of preparation beforehand.
(h) help
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.
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 the section called “Merging conflicts by hand” 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 the section called “Configuration Options” 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
(for “Conflicted”) 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 whether a
file is capable of contextual, line-based merging. See
the section called “File Content Type”
to learn more.)
For every conflicted file, Subversion places three extra unversioned files in your working copy:
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.
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
, 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.
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 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'
Merging conflicts by hand can be quite intimidating the first time you attempt it, but with a little practice, it can become as easy as falling off a bike.
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 =======
The text between the second and third sets of conflict markers is the text from Sally's commit:
======= 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.
Finally! Your edits are finished, you've merged all changes from the server, and you're ready to commit your changes to the repository.
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.
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.
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
the section called “Config”) for
composing a log message.
Tip | |
---|---|
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 …
(The exact wording of this error message depends on the network protocol and server you're using, but the idea is the same in all cases.)
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] 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 the section called “Resurrecting Deleted Items”.
[4] And you don't have a WLAN card. Thought you got us, huh?
[5] And if you ask them for it, they may very well ride you out of town on a rail.