Tips on using Subversion, aka SVN.

Subversion Commands

Here's a quick guide—for my own use as much as anyone else's—to the SVN commands I most often use.

Note: in the explanations below, the term item refers to both files and directories.

Status Commands

svn status

  • Compare local version to the base revision—that is, to the repository version that your changes have modified, which may or may not be the same as what is currently in the repository.
  • Item status is represented by symbols appearing in the left-most column of the output—see the table below.
Symbol Meaning
(empty string) Code not changed.
M Modified item
? New item not in repository
! (exclmation pt.) Repository item missing
A Added item
D Deleted item
I (capital 'I') Ignored item
G Merged item
C Item in conflict with repository change

svn status -u

  • Compare local working copy to what is currently in the repository itself.
  • Tells you which files you have changed locally.
  • Tells you what updates to the repository are not in the local version.
  • Gives you the latest revision number in the repository.

For example, let's suppose you've changed a few files, you run this command, and this is the output.

$ svn status -u
M              144   index.html
        *            Java/enum.html
M       *      140   Java/unitTesting.html
Status against revision:    144

Every time someone commits to the repository, its revision number is increased by one. The "Status against revision" number in the output above indicates the repository's current revision number, which in this case is 144.

In addition, the status check returns information on these three files.

  • One file, index.html, has been changed locally—the "M" in the leftmost column tells you this. It is a working copy of the repository's version of the file which belongs to Revision 144.
  • The second file, enum.html (in the Java directory) has been changed by someone else and this change has been committed to the repository—as indicated by the asterisk in the second column. There is nothing in the first column of the output, which indicates that you have not changed this file locally.
  • The third file, unitTesting.html, has both been changed locally and has had its repository version updated by someone else. The change you made was on the version of the file which belonged to Revision 140.

svn status --no-ignore

  • Run the svn status command, but include in the output the status of ignored items.

Commit Commands

These commands commit not only your modifications to the repository, but also commit the addition or removal of files to the repository. In other words, an svn add, svn remove and svn move must all be followed by a commit.

svn commit -m "your message" <item>

  • Commit a single item, with a message that will go into the file's history.

svn commit -m "your message"

  • Recursively commit your local changes to the repository, that is, commit all changes from the current directory down into the directory tree.
  • The message becomes part of the change history.

Add Commands

This set of commands adds files and directories to the repository. They deal only with items whose status is marked by a question mark ("?"). Note that svn add must be followed by a commit.

svn add <item>

  • Add the file to the repository.
  • If is a directory, all unversioned subfolders and files will also be added—which may or may not be what you actually want.

for i in $(svn status | grep \? | awk '{print $2}'); do svn add $i; done

  • Probably your best bet for a recursive add.
  • This Unix command recursively adds all new files to the repository, whether their directories are versioned or unversioned.
  • The command works by adding all files whose status is "?", so it does not operate on your ignored files, as these do not appear in the status command.

svn add *

  • Recursively add all new items to the repository.
  • This command skips over versioned directories, i.e. it will not add unversioned files in a versioned directory (or even a non-versioned directory in a versioned directory).
  • Warning: note that this command will also add files/folders in the current directory which are in your ignore list!

svn add * --force

  • Recursively add all new items to the repository, whether their directories are versioned or unversioned.
  • Note that this command will also add files/folders in the current directory which are in your ignore list!

Move Commands

This set of commands moves or renames a file or directory. Moving or renaming a file through SVN is better than simply doing it through your operating system, because the file's change history will be maintained. (That is, SVN will copy the history of the old file to the new file's history.) A second advantage of doing the move through SVN, rather than via your file system, is that it spares you the bother of having to do an svn delete on the old file name/path, and and svn add on the new one: SVN does that for you.

Note that an svn move must be followed by a commit. Also note that SVN may not at first let you do this commit. If you get the message "Item ... is out of date," don't panic. Do an svn update , which will seemingly do nothing, but which in fact will let you perform the commit.

svn move <oldItemName> <newItemName>

svn move <oldPath/item> <newPath/item>

  • Rename or move an item.

Remove/Delete Commands

This set of commands removes a specific file or directory from the repository. The following svn commands behave identically: remove, delete, rm, del.

Note that these changes must all be followed by a commit.

svn rm <item>

  • Remove the file/directory from both your filesystem and the repository.

svn rm --keep-local <item>

  • Remove the file/directory from the repository, but not locally.
  • Since this will generally be an important file or directory, e.g. .classpath, ordinarily you will also want to ignore this file as well.

for i in $(svn status | grep ! | awk '{print $2}'); do svn rm $i; done

  • Unix command to recursively delete all missing files from the repository, whether their directories are versioned or unversioned.
  • The command works by deleting all files whose status is "!".
  • Since, unlike svn add, there is no recursive version of delete, this command can sometimes be very useful—for example, if you've deleted many files in several directories via your operating system.

Revert Commands

Use svn revert to undo local changes and revert your code back to the repository's version.

svn revert <file>

  • Reverts the file.
  • Using a directory instead of a file does not lead the command to recurse directories. See svn revert -R below.

svn revert -R .

  • Recursively reverts all the modified files to their original repository versions.
  • If a file is in an Add ("A") state, this command removes the add, changing the file's state to "?".
  • No items in the "?" state will be deleted from the file system. To recursively remove these, see the next command below.

for i in $(svn status | grep \? | awk '{print $2}'); do rm $i; done

  • Recursively deletes all the new but unadded files—in other words, those whose status is "?".
  • Though this is not technically a revert command, I've put it in this group because it's a revert command in spirit: it lets you undo a previous decision to add a bunch of files to the repository.

Less-often-used SVN Commands

svn list

  • Lists the items in the current directory which exist in the repository.

svn info

  • Gives basic info about this working copy.
  • Most useful when you want the URL of the repository location.

svn checkout <URL> <fullPath>

  • Creates a local version of the repository by linking the fullPath directory to the repository and copying the files at the repository location to that location in your file system.
  • It's very easy to unintentionally add an extra directory level—or remove a desired level. Don't be surprised if it takes a couple of tries. For me, it helps if I remember to: (1) use an explicit path rather than, for example, "."; and (2) let the checkout command create the destination directory for me. This helps me avoid the mistake of creating the directory myself and then cd-ing into it before I run the command.
  • If a password is required you will be prompted with your computer user name as the default logon user name; if that is not correct, the password will fail and you'll be given a chance to input both the repository user name and the repository password.

svn diff <item>

  • Does a Unix-style diff of the file and the base revision, showing all of your changes to the file.

svn log --limit <n>

  • Shows the last n commits to the repository.
  • svn log without the limit argument shows an unreadable amount of history.
  • Use svn update before svn log, or very likely your most recent commits won't be displayed.

svn merge -c -<n>

  • Updates your local working copy by undoing all the changes related to Revision n.
  • For example, if the commit of Revision n involved a modification of two files, Revision n - 1's version of those two files will be downloaded into your working copy.
  • Note that this local change will itself have to be committed if you decide to finalize the roll-back.

Ignoring a File or Directory

Every once in a while you don't want a file committed to the repository; for example, an IDE project file. If the settings in this file are not applicable to those of other developers, you don't want this information stored in the repository.

Or when it comes to Jupyter notebooks, you probably don't want any of the files in the .ipynb_checkpoints in your repository.

Ignoring Files

In the steps below we:

  • Verify that the file we want to ignore, .project, is in the present working directory.
  • Try to edit the svn:ignore properties, but find that we need to tell SVN which text editor we want to use.
  • We indicate we want to use vim as our text editor.
  • Then we try to open the editor again—successfully this time.
$ ls -alt 
total 16
drwxr-xr-x  5 someuser  staff  160 Oct 15  2017 ..
drwxr-xr-x  3 someuser  staff   96 Oct 15  2017 bin
drwxr-xr-x  3 someuser  staff   96 Oct 15  2017 src
drwxr-xr-x  7 someuser  staff  224 Oct 15  2017 .
-rw-r--r--  1 someuser  staff  295 Oct 15  2017 .classpath
-rw-r--r--  1 someuser  staff  367 Oct 15  2017 .project
drwxr-xr-x  3 someuser  staff   96 Oct 15  2017 .settings

$ svn propedit svn:ignore .
svn: E205007: None of the environment variables SVN_EDITOR, VISUAL or EDITOR are set, and no 'editor-cmd' run-time configuration option was found

$ export SVN_EDITOR=vim

$ svn propedit svn:ignore .

At this point vim will open the properties file. You want to insert the following line into the editor.

.project

In vim, you insert this line with the following key sequence.

i
.project
<esc-key>
ZZ

Here's a crash course in vim, with regard to the four commands given in the box above:

  • "i" allows for the insertion of text.
  • Then you can type the text you want to insert.
  • You get out of insert mode with the Escape key.
  • Then you save and exit with "ZZ".

At this point you can verify that you've modified the ignore property by seeing that "." (meaning the present working directory) has been changed.

$ svn status
 M      .

Ignoring All the Files in a Directory

Suppose you want a Jupyter .ipynb_checkpoints directory in your repository, but you don't want any of the files.

One solution would be to follow the steps above to edit a directory's ignore property, and place this single line in the file:

*.*

An easier solution would be to add this line to the properties with a single command. Follow these steps.

$ cd .ipynb_checkpoints/

$ svn propset svn:ignore '*.*' .

$ svn commit -m "Ignoring contents of .ipynb_checkpoints."

You can optionally verify your change to the directory's properties with an svn proplist.

$ svn proplist -v -R .
Properties on '.':
  svn:ignore
    *.*

Troubleshooting

Here I offer solutions to commonly-occurring SVN problems, grouped by error message.

'table is out of date'

svn: E155011: Commit failed (details follow):
svn: E155011: Directory 'CodeExamples/src/table' is out of date
svn: E170004: Item '/CodeExamples/src/table' is out of date

Sometimes SVN doesn't let you perform a commit when you think it should. I've noticed this can happen after I move or delete a file or a directory. What often fixes the problem is an SVN update that, it would seem, does nothing.

$ svn commit -m "Begin Multiset page."
Deleting       CodeExamples/src/table
svn: E155011: Commit failed (details follow):
svn: E155011: Directory 'CodeExamples/src/table' is out of date
svn: E170004: Item '/CodeExamples/src/table' is out of date

$ svn update
Updating '.':
At revision 103.

$ svn commit -m "Begin Multiset page."
Deleting       CodeExamples/src/table
Transmitting file data ........
Committed revision 104.

'too many arguments to (...) command'

svn: E205000: Too many arguments to import command

For this error message, the command name depends on the command you are trying to run.

$ svn commit -m “Begin Multiset page.”
svn: E205000: Too many arguments to commit command

To resolve this issue, of course you should first verify that you do not have too many arguments for the command you're using. Then see if your command unintentionally includes what are called "smart quotes" (high-ascii left and right single or double quotation marks). This can happen when you cut and paste the command from an editor like TextEdit on a Mac, or Word on a Windows machine—or maybe even from a web page.

'local unversioned, incoming add upon update'

In my experience, you get this error message when you try to perform an svn checkout into a directory that already contains files.

// Don't do this!

$ svn checkout https://somePath/ProjectB
C ProjectB
A ProjectB/site.yaml
(... more output ...)
Checked out revision 936923.

$ svn status
D     C ProjectB
      >   local unversioned, incoming add upon update
D       ProjectB/site.yaml
(... more output ...)
Summary of conflicts:
  Tree conflicts: 1

If this is what is happening to you, chances are that you are trying to create a new Subversion project with existing code. Now, you could google "local unversioned, incoming add upon update" and follow the advice you find. But you might be better off to save your code, delete the checkout directory, and follow my advice on Creating a New Project below.

SVN Conflicts

A conflict occurs when we, usually unintentionally, ask Subversion to merge two different versions of the same file. This normally happens when two different people are working on the same file, and after one of them commits their work. The second person encounters the confilict either when:

  • They try to update their modified local working copy with the latest version from the repository.
  • Or, they try to commit their modified version of the file to the repository.

Avoiding Conflicts

The best strategy is to avoid conflicts altogether. You avoid conflicts by following these three guidelines:

  1. Never do an svn update without first doing an svn status -u.

  2. Never commit any file without first doing an svn status -u.

  3. Never let SVN merge conflicting files. That is, always perform merges manually, with a good diff tool; for example, one provided by your IDE.

Now for the details. Suppose you've changed a few files and you're ready to commit. Here's the output of your svn status -u.

$ svn status -u
M              144   index.html
        *            Java/enum.html
M       *      140   Java/unitTesting.html
Status against revision:    144

(For an explanation of how to read this output, see Status Commands, above.)

Here are the two ways you can avoid this potential conflict, in my order of preference.

  • Within your IDE, perform the equivalent of svn status -u, and use the IDE's diff tools to merge the repo version of the file with your local working copy.

  • Make a copy of the conflicting file. Revert your changes to the file in question. Use svn update Java/unitTesting.html to get the latest version of the file from the repository. Finally, use the diff tool of your choice to merge your work into the file. From the command line, these steps look like this:

$ cp Java/unitTesting.html Java/new_unitTesting.html

$ svn revert Java/unitTesting.html
Reverted 'Java/unitTesting.html'

$ svn update Java/unitTesting.html
(...)

$ svn status -u
?                 Java/new_unitTesting.html

You can see that new_unitTesting.html is not recognized as belonging to the repository. Now it's time to use your diff tool to merge this copy with the repository's version of unitTesting.html which you just downloaded.

Dealing with a Conflict

This section explains what happens when you try to let Subversion merge two different versions of the same file.

Let's say you do an svn status -u, and see that a file needs to be merged.

$ svn status -u
M       *      155   utils/DialogUtility.java

Then you perform an update, and see that—zut alors!—SVN can't do an automatic merge. Hit "p" to postpone the merge and deal with it later.

$ svn update

Conflict discovered in 'utils/DialogUtility.java'.
Select: (p) postpone, (df) diff-full, (e) edit,
        (mc) mine-conflict, (tc) theirs-conflict,
        (s) show all options: p
C    utils/DialogUtility.java
Updated to revision 165.
Summary of conflicts:
Text conflicts: 1

In this scenario's next step, you do an "svn status" to see what the situation is, and discover new files that you never created.

$ svn status
C       utils/DialogUtility.java
?       utils/DialogUtility.java.mine
?       utils/DialogUtility.java.r155
?       utils/DialogUtility.java.r165
Summary of conflicts:
  Text conflicts: 1

The final step of the scenaro has you wishing that you'd taken my advice (see above), and performed the merge manually.

In the listing above, note the "C," which of course stands for "confilct." SVN has left you with three new files, and a new version of DialogUtility.java.

  • DialogUtility.java.r155 is the version of the file that you did your work in.
  • DialogUtility.java.r165 is the latest version in the repository
  • DialogUtility.java.mine is the version of the file you had before you did the ill-fated update. In other words, this is the file that contains your work.

So if DialogUtility.java.mine has all your recent modifications to the file, then what is in DialogUtility.java?

The answer is, nothing pretty. And nothing that will compile. Here's the kind of thing you'll see there.

<<<<<<< .mine
=======
    public static List<String> COMMON_VERBS = ImmutableList.of(
    "ask", "be", "call", "come", "do", "find", "get", "give", "go", "have", "know", "let", "look", "make", "mean", "need", "put", "say", "take", "tell", "think", "want");

>>>>>>> .r165

You have a couple of options. You could decide which of the three new files you want to keep, and rename it to "DialogUtility.java." In this case you would delete the two remaining files. But probably what you want is to keep both your changes and the changes of the last person who committed the file to the repository. This means going through DialogUtility.java, searching for "=====", and for each diff figuring out which version you want. (Once finished, you'll need to use the svn resolve command.)

Creating a New Project

Few things seem to be as much of a time-killer as creating a new project in an existing Subversion repository. I think I've finally got it figured out.

When There Is No Code to Check-in

This is the preferred way to create a new project in an existing repository. However, it requires you to think about the repository before you write any code—which is not the way most of us tend to think.

Let's suppose:

  • Your new project is called ProjectX.

  • It's repository location will be http://subversion.company.com/svn.

  • You want your working directory to be /home/me/workspace/ProjectX.

$ cd /home/me/workspace

$ svn mkdir http://subversion.company.com/svn/ProjectX

# Let the checkout create the new ProjectX directory for you.
$ svn checkout http://subversion.company.com/svn/ProjectX /home/me/workspace/ProjectX

$ cd ProjectX

$ svn info

At this point, the final command, svn info, should succeed and show the path to your repository location.

When You Already Have Some Code to Check-in

I expect this is the usual case: you're hot on a new project and you start creating directories, empty classes and maybe even a few method stubs, and then suddenly you realize you should commit your stuff to the repository. Only, there's no project currently set up for it.

I've attempted to do this many times, and usually find myself in conflict hell. My advice is, don't even try to add the project and the code to the repository at the same time. Instead:

  • Temporarily rename the directory containing your new code.

  • Follow the steps above for adding a new project when there is no code to check in.

  • Copy the code from the temporary directory to the directory created by the svn checkout command.

  • Perform the necessary svn add and svn commit steps, as per usual.