Planning a Subversion import
svn import
is a bit of a landmine. It's not obvious from the
command format, nor from its (almost nonexistent) documentation exactly what
shape the imported repository will actually take once it's imported —
and it's difficult to impossible to rectify a mistake once you've made one,
since Subversion also doesn't offer a way to preview an import or delete an
imported project that you created wrong by accident.
For example, let's say you have this structure:
/home/jdavies/development/game /home/jdavies/development/game/pom.xml /home/jdavies/development/game/src/main/java/com/jdavies/game/Game.javaYou'd like to put this under version control.
svn import
is
the tool for that... but how, exactly? What you really want is for
svn checkout
to yield:
$ cd /tmp $ ls $ svn checkoutRight? Or, to be more inline with Subversion standards, you really want this to be:/game $ ls game
$ cd /tmp $ ls $ svn checkoutInvoking the exact proper incantation to make/game/trunk game $ ls game
svn import
work
this way is
- Not documented
- Easy to get wrong
- Impossible to fix if you do get it wrong
So, before you import anything into Subversion, I strongly recommend you make a local repository, test the import there, check it out from the test repository, make sure it's right, and only then actually import it into the main production repository. You can create a local repository easily:
$ cd /var $ sudo mkdir svn $ sudo chown jdavies svn $ svnadmin create /var/svn/reposIf you take a look at the created directory, you'll see there's quite a bit in there:
$ ls /var/svn/repos/ README.txt conf db format hooks locksAlthough the repository itself is actually empty:
$ svn list file:///var/svn/repos $So, the first step in getting my new game imported under version control is to reformat its directory structure to match Subversion's preferred "trunk", "tags", "branches" structure. Surprisingly, you have to do this manually; the subversion import process doesn't help with this.
$ cd ~/development/game $ mkdir trunk $ mkdir branches $ mkdir tags $ mv pom.xml src/ trunk/Now you have the directory structure:
/home/jdavies/development/game /home/jdavies/development/game/branches /home/jdavies/development/game/tags /home/jdavies/development/game/trunk/pom.xml /home/jdavies/development/game/trunk/src/main/java/com/jdavies/game/Game.java
branches
and tags
are empty — that's OK.
You'll use them when you create some actual branches or tags.
Now it's time to import. Checking the documentation for the import
command, you see:
$ svn help import import: Commit an unversioned file or tree into the repository. usage: import [PATH] URL Recursively commit a copy of PATH to URL. If PATH is omitted '.' is assumed. Parent directories are created as necessary in the repository. If PATH is a directory, the contents of the directory are added directly under URL.
So... the URL is the URL of my repository, and the PATH is the directory that I want to put under version control, right?
$ cd game $ svn import . file:///var/svn/repos -m "Initial import"Now I've messed up my repository quite a bit:
$ svn list file:///var/svn/repos branches/ tags/ trunk/Now the
trunk
, branches
and tags
directories are at the top of your repository. And because subversion is
a revision control system, evidence of your mistake will be preserved in the
repository until the end of time:
$ svn log file:///var/svn/repos ------------------------------------------------------------------------ r2 | joshuadavies | 2013-10-24 09:06:16 -0500 (Thu, 24 Oct 2013) | 1 line Delete accidental import ------------------------------------------------------------------------ r1 | joshuadavies | 2013-10-24 09:02:12 -0500 (Thu, 24 Oct 2013) | 1 line Initial import ------------------------------------------------------------------------
Some people actually manage their repositories this way deliberately; if
you look under trunk
, you'll see a list of projects.
I don't think that this is a particularly good idea, because projects in a
repository have divergent lifespans — even if you're not managing a
group of disparate projects now, chances are you will be in the near future.
You'll be much better off in the long run maintaining the top level as a list
of projects, each with its own trunk
, branches
and
tags
subdirectories and subsequently their own revision
strategy.
But this problem may strike you as obvious — after all, I never said
anything about my game
directory when I did the import, right?
However, this invocation, which does reference my game
directory
actually does the same thing:
$ cd development $ svn import game file:///var/svn/repos -m "Initial import"The takeaway here is that the first argument to
import
is not
used to form the path that's imported into the repository; the contents
of the directory named by the first argument are appended to the second.
Subversion starts by navigating to the directory named as the URL; if a
subdirectory component isn't found, it will be created. Then it adds every
file or directory found underneath the PATH argument to that directory.
This means that the following will produce the results you intend:
$ cd development $ svn import . file:///var/svn/repos/game -m "Initial import"If
game
is the only directory in development
.
Most likely, it isn't, so what you need to do is to repeat it:
$ cd development $ svn import game file:///var/svn/repos/game -m "Initial import" Adding game/trunk Adding game/trunk/src Adding game/trunk/src/main Adding game/trunk/src/main/java Adding game/trunk/src/main/java/com Adding game/trunk/src/main/java/com/jdavies Adding game/trunk/src/main/java/com/jdavies/game Adding game/trunk/src/main/java/com/jdavies/game/Game.java Adding game/trunk/pom.xml Adding game/branches Adding game/tags Committed revision 1.