svnexternals

How does one setup correlated subversion externals? Should one?


We have a couple of Zend_Framework applications that we'd like to keep in separate subversion repositories. However, these applications share the same database abstraction layer, and several of the same common components.

We'd like to share the common bits between the apps somehow. The current idea we have looks like

svn://foo/itg-common/trunk
svn://foo/itg-common/branches/foo
svn://foo/itg-common/branches/production

svn://foo/itg-app/trunk
svn://foo/itg-app/branches/foo
svn://foo/itg-app/branches/production

Now, we'd like the itg-app repository to have an externals reference to the itg-common repository. The problem is we want e.g. itg-app/trunk/common to be linked to itg-common/trunk, itg-app/branches/foo/common to be linked to itg-common/branches/foo, etc. That is, the general pattern is itg-app/$BRANCH/common -> itg-common/$BRANCH.

Now, in principle we could create these externals, but problems arise whenever we would try to merge. E.g. merging from $/trunk to $/branches/production would overwrite the svn:externals property to make $/branches/production/common to point to itg-common/trunk.

Does this make sense? If it does, is there some way around this problem? If it does not, why not, and what should we do instead?


Solution

  • Does this make sense? If it does, is there some way around this problem? If it does not, why not, and what should we do instead?

    As prodigitalson said already, an external in SVN basically is considered a totally different piece of software, with its own release cycles, branches, tags, etc. According to this model, you should not have unpinned externals into some trunk at all, but have them all pinned to either a tag or a revision. That's a pretty restricted model of using code from external sources, but that's what SVN supports. Stray from this, and you are on your own. (See below for my personal war story about that.)

    Another thing I would reconsider if I was you is referring to a different repository. IME referring to an external relatively to the current project or repository root is much better than using absolute paths. Just consider the possibility that you might ever change the protocol from svn: to, say, https:. (I have seen this happening to a company, and despite heavily relying on scripting to change all the externals, this transition was a mess everyone involved keeps having nightmares about.) Relative paths to externals simply are more robust, but they are only available within a single repository.


    The company I am working for has just faced a similar problem. We have a lot of code that is shared between different products. This is done via externals, and those externally referenced projects have externals themselves. At some spots we're down to three layers of recursive externals, and we already know that there will be more in the future. All that code is worked on constantly, and we like to let externals reference the trunk of the projects they refer to, because automatic testing takes the edge off that, and managing releases of all those recursive externals would require efforts we simply cannot afford.

    But branching a project in this setting is a real pain in the, erm, neck, because it's not enough to pin a project's externals, when those themselves then refer to unpinned externals. Whenever you need to branch project foo to foo', which, via an external, refers to project X, which in turn, via an external, refers to project XX, you would need an foo' branch of XX which is an external in an foo' branch of X, which is an external of foo' itself. Imagine half a dozen externals in foo, half of which have their own externals, some recursing three or even more layers, and you reach the point where project XXX is littered with branches created for projects it shouldn't even know about.

    Our solution is to recursively either A) replace all externals with a branch of what they referred to or B) setup a branch of external X in foo/branches/externals/foo'/X that's referenced by <project>/branches/foo' (with foo/branches/externals/foo'/X itself referring to foo/branches/externals/foo'/XX). However, setting this up when creating foo' is impossibly complex and offers more than enough chances for making silly mistakes, so we employ scripting to recursively descent the project and its externals and do all that.