Subversion

This is a short description of some of the frequently used features of the versioning software subversion as described in the subversion book. The things described here should work with subversion 1.3 and subversion 1.4.

Help

To get help on ‹command› (e.g. diff):

svn help ‹command›

Similarly:

svnadmin help ‹command›
svnlook help ‹command›

Repository creation

Create a repository for the project myproject:

svnadmin create ~/svn/myproject

Repository URLs

Examples:

file:///home/‹user›/svn/myproject
svn+ssh://‹user›@‹machine›/home/‹user›/svn/myproject

No things like ~ or $HOME allowed.

If you have a working copy of some repository and would like to gain general information as e.g. the repository url then, in the directory or your working copy, type:

svn info ./

Revision numbers

Many commands take an argument --revision ‹r› (or equivalently -r ‹r›). The revision number ‹r› is a whole number greater than 0 or HEAD meaning the latest revision or BASE meaning the revision your local copy is based on.

Importing

Create somewhere a temporary file tree containing all the files the repository needs to keep. E.g:

~/temp/myproject/
|--trunk/
|  |--dirA/
|  |--dirB/
|  |  |--anotherDir/
|  |  |--file1.aha
|  |  `--file2.aha
|  |--dirC/
|  |--fileA.ext
|  `--fileB.ext
|--branches/
`--tags/

Then use the import facility:

svn import ~/temp/myproject ‹url› -m 'Initial import'

If message (-m) is not given then $EDITOR or $VISUAL will automatically start for you to compose a message.

Peeking into a repository

Use the svn list command to peek into any sub-directory. E.g:

svn list -v ‹url›
svn list -v ‹url›/trunk/dirB

Use svnlook to gain information on the whole repository. E.g:

svnlook info ~/svn/myproject -r ‹r›

gives information on revision ‹r›.

svnlook tree ~/svn/myproject --show_ids

lists all directories and files in the tree.

Checking out a revision / making a local copy

After the following, ~/somedir will contain the directory trunk:

cd ~/somedir
svn checkout ‹url›/trunk

After the following, ~/someother will contain the directory dirA:

cd ~/someother
svn checkout ‹url›/trunk/dirA

After the following, the contents of trunk will be in the newly created directory ~/adir/projDir:

cd ~/adir
svn checkout ‹url›/trunk projDir

To check out revision number ‹r›:

svn checkout ‹url›/trunk -r ‹r›

Omitting -r checks out the latest revision.

The local copy contains .svn directories in every directory. Inside these a copy of the BASE revision (the one checked out or updated the last time) is kept.

Committing the changes made

(Also called “checking in” your local version.) In your local project copy:

svn commit -m "Information about the chandes made"

When executed in some sub-directory of your local copy this will only send all files and sub-directories.

Usually you should do an update before a commit to merge the changes into your local copy in case somebody else has committed a new revision.

Updating the local copy

In your local project copy:

svn update

In some sub-directory of your local copy this will only update all files and sub-directories.

Short command names:

checkout co
update up
commit ci
status st

Moving a repository (also between different subversion versions)

Dump whole repository (using old version of svnadmin):

svnadmin dump ~/svn/myproject > ~/dumpfile

Load into a new repository (using new version of svnadmin):

svnadmin create --fs-type fsfs ~/svn2/myproject
svnadmin load ~/svn2/myproject < ~/dumpfile

Or load by means of a pipe:

svnadmin create --fs-type fsfs ~/svn2/myproject
svnadmin dump ~/svn/myproject | svnadmin load ~/svn2/myproject

Dump a range of revisions:

svnadmin dump ~/svn/myproject -r 101:200 > dumpfile-101-200

Read all log messages

In your local project copy:

svn log

In some sub-directory this might show only the log messages of those revisions which updated something inside the directory.

Or to show verbosely the log messages concerning a single file (or sub-directory):

svn log -v fileA.ext

Make changes to your local copy

Use svn add, svn delete, svn copy and svn move to let subversion know about changes made concerning files, directories and symbolic links.

Note

svn copy will result in a “cheap” copy (no data is really duplicated) in contrast to copying a file with cp and adding it again with svn add.

Information about your working copy

Display all file and tree changes made since the BASE revision in trunk and all sub-directories:

cd ~/somedir/trunk
svn status

Status of file1.aha:

svn status dirB/file1.aha

List all files and directories and their status:

svn status -v

List all file differences in the current directory and all sub-directories:

svn diff

Reverting to the BASE revision

Undo changes made since you last checked out a revision (your BASE revision) using svn revert. This will not delete any files in your local copy which have not been added with svn add. Also, no deleted directories can be restored.

To reset the status and contents of fileA.txt to the BASE revision:

svn revert dirB/fileA.txt

To reset the status of the directory dirB to the BASE revision, without caring about files and sub-directories inside the directory:

svn revert dirB

To reset the status of dirB and the status and contents of all directories and files in dirB to the BASE revision, without caring about sub-sub-directories:

svn revert dirB/*

To reset the status of dirB and the status and contents of all sub-directories and files (recursively) inside dirB to the BASE revision:

svn revert dirB -R

Conflicts

Before committing a new revision make an svn update. Thereby subversion lists all files in conflict with the changes you made with a ‘C’. For every file in conflict three additional files are created (e.g. ‹file› is in conflict):

‹file› A merged file with conflict markers in it
‹file›.mine The file as it was before the update
‹file›.r‹base› The file as it is in the BASE revision
‹file›.r‹head› The file as downloaded in the newest revision

As long as the three additional files are around, subversion considers ‹file› to be in conflict. Remove them with:

svn resolved ‹file›

Conflict markers (<<<<<< and ====== and >>>>>>) are placed in the merged file:

<<<<<< .mine
text I have written
=======
text somebody else has written in the
meantime and commited for revision 6
>>>>>> .r6

If I cannot resolve the conflict by hand I can svn revert the file (no svn resolved necessary after this) or just copy one of the three versions onto ‹file› and use svn resolved.

Branching

Consider the following possible repository layout for myproject:

~/svn/myproject/
|--trunk/
|  |--dirA/
|  `--A.txt
|--branches/
`--tags/

A branch is created by copying the trunk directory into branches:

svn copy ‹url›/trunc ‹url›/branches/mybranch -m"creating branch"

This makes a “cheap” copy (no data duplicated) in the repository. Using svn copy on a local working copy of the project and committing the changes has exactly the same effect.

Merging

svn merge requires two files (or directories) to compare against each other, similar to svn diff. These can be given as:

‹url›/path1/file1[@‹n›] ‹url›/path2/file2[@‹m›]

or:

~/pathInLocalCopy1/file1[@‹n›] ~/pathInLocalCopy2/file2[@‹m›]

Alternatively revision numbers can be specified by means of the argument -r n:m.

Here ‹n› and ‹m› are revision numbers, [] means optional with default HEAD. path1/file1 and path2/file2 can identify the same files/directories.

The differences are then applied as a patch to a target (which optionally can be omitted).

Examples:

svn merge -r 12:13 ‹url›/trunk/dirA

merges all changes from revision 12 to 13 in dirA into the current directory.:

svn merge -r 12:13 ‹url›/trunk/A.txt

merges the changes from revision 12 to 13 in A.txt into the same file in the current working directory.

Merge can be used to undo changes. E.g. to undo what has been commited in revision 13:

svn merge -r 13:12 ~/pathInLocalCopy/file

or equivalently:

svn merge -c -13 ~/pathInLocalCopy/file

Preview changes by:

svn diff -r 12:13 ‹url›/trunk/A.txt

Or use svn merge with the option --dry-run to print status messages.

Do an svn commit before merging. After merging you have to commit the changes. Mention the revision number(s) in the commit message, there is no other way to tell later what has been merged.

In case of a conflict three files will be created:

‹file›.working My working copy before the merge
‹file›.left The two files whose differences should have been merged with ‹file›.working
‹file›.right

Tags

Tags are created exactly the same way like branches, e.g. a tag called release-1.2 is created by:

svn copy ‹url›/trunk ‹url›/tags/release-1.2

If you have a local working copy myproject which is a mixup from different revisions and would like to create a tag special from it:

svn copy myproject ‹url›/tags/special

The svnserve server

Start svnserve as a daemon publishing all repositories found under ~/svn as follows:

svnserve -d -r ~/svn/

If ‹user› runs the daemon on ‹host›, then the ‹user›s repository in ~/svn/myproject is accessible by other users as follows:

svn checkout svn://‹host›/myproject

Access control for myproject is configured in the ‹user›s files:

~/svn/myproject/conf/svnserve.conf
~/svn/myproject/conf/passwd

If you have ssh access to ‹host› then you might also be able to checkout as follows:

svn checkout svn+ssh://‹user›@‹host›/path_to_svn_repos/myproject

Your access rights are then the same as if you were ‹user›@‹host›.

If a svnserve process is already running and you are not root and want to start your own svnserve server process, then you might have to choose a different port (default is 3690, so maybe use 3680) on which your svnserve listens for requests:

svnserve -d --listen-port ‹port› --listen-host ‹host› -r ~/svn/

Then the corresponding checkout is:

svn checkout svn://‹host›:‹port›/myproject

Keyword substitution

You can tell subversion to substitute certain strings (keywords) in certain documents of the local copy with meaningfull content each time you do a commit. Keywords start and end with $ and are case sensitive:

Keyword Substitute example Keyword aliases
$Date$ $Date: 2006-07-22 21:42:37 -0700 (Sat, 22 Jul 2006)$ $LastChangedDate$
$Revision$ $Revision: 144$ $LastChangedRevision$, $Rev$
$Author$ $Author: harry$ $LastChangedBy$
$HeadURL$ $HeadURL: http://svn.a.net/repos/README$ $URL$
$Id$ $Id: calc.c 148 2006-07-28 21:30:43Z sally$  

For keyword substitution to work, this feature must be enabled on a per-file basis. Subversion handles this by means of file properties. A local modification of the properties is e.g:

svn propset svn:keywords ‹keywords› ‹file›

where ‹keywords› contains in double quotes the keywords for which to enable substitution (e.g. "Date Author"). In the next commit, subversion will perform the substitution for the first time.

To enable keyword substitution in all files under ‹directory›:

svn propset -R svn:keywords ‹keywords› ‹directory›

Ignoring files / directories

There is a mechanism to let subversion ignore certain files or directories. It is still possible to add them with svn add but if not versioned, they will not show up in svn status.

One way to do this is via file properties. E.g. to edit ignore properties of ‹directory›:

svn propedit svn:ignore ‹directory›

An editor will open where you can add lines (e.g. *.tmp or ‹file or directory name›) indicating what files / directories under ‹directory› will be ignored.

To set the svn:ignore property on a directory using a ‹file› containing the ignore patterns (one per line) use:

svn propset svn:ignore --file ‹file› ‹directory›

Refactoring a repository

It can happen that you have (accidentally) commited files that really should not have been commited, e.g., large binary files or files that are secret or unrelated to the project. You can of course “delete” a file with:

svn rm <file>
svn ci -m "removing unwanted file"

But the file will still be in the repository. If you want to get rid of it (almost) completely you can follow this procedure:

  1. Identify affected nodes

    I give two ways of figuring out the exact paths/files we may want to drop, in case you don’t know them upfront.

    First, assume that we are particularly interested in commits that have changed or added large amounts of data. Produce a list of all revisions sorted by their size:

    for r in `svn log -q | grep ^r | cut -d ' ' -f 1 | tr -d r`; do
      echo "revision $r is " `svn diff -c $r | wc -c` " bytes"
    done | sort -k 4 -nr > revsize.txt
    

    After this, we can examine revsize.txt and print files affected by some revision number ‹r› as:

    svn log -v -r ‹r›
    

    Second, assume that we want to get rid of all (or some) files ending with .‹ext›. Find these files in the log of a local copy:

    svn log -v | grep -ie "\.‹ext›" | sed "s/ *[ADM] \///" | sed "s/ (.*)$//" | sort -u > list.txt
    
  2. Filter the repository

    First we have to dump the original repository:

    svnadmin dump ~/path/to/repos > ~/dump
    

    Then we use svndumpfilter produce a filtered_dump in which the file/directory ‹path› has been omitted:

    svndumpfilter exclude "‹path›" < ~/dump > ~/filtered_dump
    

    The ‹path› must be fully specified, e.g., trunc/dir/file.ext or trunc/dir. Several paths to be dropped can be given.

    Alternatively, we can filter the dump by specifying the path(s) to keep:

    svndumpfilter include "‹path›" < ~/dump > ~/filtered_dump
    

    Note

    svndumpfilter is not very strong and may fail for complicated situations in which, e.g., files have been moved. See Svndumpsanitizer for a more sound filter.

  3. Create a new repository and load the filtered dump

    svnadmin create ~/svn/myfilteredrepo
    svnadmin load ~/svn/myfilteredrepo < ~/filtered_dump
    

    Now we can checkout this new repository.