tags:

views:

518

answers:

6

I have multiple projects in SVN. Each of these project sits in it's own trunk and branched for releases.

And there is a shared code which is used in each project. The question is what is the best way to handle the code.

Let me give couple of scenarios and the issues related to them

a) Put the shared code in separate trunk (or repository) and use svn:external.

In the case if we branched some of projects, there will be two problems:

  • Any modification of shared code which are made in trunk will be propagated to branch, because svn:external will pick up the changes
  • In the case if we will need at some moment go back and build exactly the code which was build for release, it will be hard for us to get exact code, because snv:external will again pick up latest copy of shared code, instead of code at the moment when project was brached.

As I understand there is one work around. As soon as we branch, we can modify svn:external to pick up exact revision of shared code. However, there are again two pitfalls:

  • You need remember to do this each time when you branch. (I hate such things, which is easy to forget).
  • You can't modify shared code, if you need to do a hotfix for the branched/released project.

b) Another solution is to branch shared code when the project is branched and change external to point to bracnhed copy of shared code.

  • Again, one of the problem is manual step, which is easy to forget
  • Another problem is merge problems. SVN will skip externals when you will try to merge changes in the project to the trunk. So, again, developer needs to remember to merge shared code manually.

Am I missing anything? Is there any reasonable way to handle this?

+5  A: 

Personally i keep a separate repository then use the [http://svnbook.red-bean.com/en/1.0/ch07s03.html svn:externals] attribute.

SVN Externals allow for you to link to other repositories (even ones you dont run e.g. the smarty subversion repo) when you run an svn update both your project and the external repo will be updated.

with SVN externals you can also link to specific revisions using somethign like http://path-to-project.com/svn/thing -r1234 for releases and other things that you need to keep static

Best practice IMHO is to always specify a revision then update the revision number as you make changes to the shared library so that you can keep track of WHY you updated this data. also keeps everything sane when you tag or branch the main project.

Mike Valstar
+1 for reminder to refer to a specific revision of the externals
Michael Hackner
That's indeed a good practice! Or whenever possible, point to the tags of the other repository in svn:external links (which should guarantee immutability and is easier to interpret in terms of which version is used).
RedGlyph
+1  A: 

You have two correct answers, with two sets of drawbacks.
Here's what I would recommend

Put your shared code into another repository, tag the code with the release version Create an svn externals in your trunk directory that points to your tag.

When you change your library, retag it, and update your trunk application. When you take a branch, your branch will still point to the correct tagged version of your library.

Alternatively try building a separate release of your library, and reference the library as a jar or lib/so.

Michael Brunton-Spall
Sounds like a very good methodology :-)
RedGlyph
A: 

My recommendation for handling shared code (particularly with Java and .NET or a C/C++ library) is to use two sets of repositories: one for source code, and another for versioning released images. When you make changes to the 'common' project, you commit your source changes to the source tree, then build it and then publish the binaries by committing them as a new revision on the release tree. The projects that use the 'common' project then use the svn:externals property to bring in the released binaries. There's no temptation to modify the local images of the shared code. If someone wants to modify the 'common' code, then they do that through the normal development practices for that project.

For more details, see this answer to a similar question.

Craig Trader
It's somewhat misusing the versioning tool, something we tend to frown upon in our team but which, I admit, is convenient. Preferable is to store the binaries in a shared directory structure (which includes the version), so long as it is possible to re-create them from the versioned sources (if debug is necessary and so on). The main issue with storing binaries in the repository was the unnecessary surcharge of the server.
RedGlyph
I keep the sources versioned as well (of course). Using a repository is preferable to a shared file system when you're working with a distributed development team -- all you need to provide is subversion access (via http/https) instead of having to do CIFS over a VPN.
Craig Trader
A: 

We use a single repository structure like this:

/trunk/project1
/trunk/classlibrary (shared code)
/trunk/project2

In C# (which may not be your language of choice), project1 and project2 include a reference to classlibrary. When we build (here's a great advantage of the compiled .NET model), any inconsistencies between the project being built and class_library are found. These inconsistencies are resolved before committing changes. Using a server-based build tool (we use CruiseControl.NET) we can build all of the projects simultaneously which will tell us of any problems.

Unless your project1 and project2 need to reference a specific version of classlibrary (which we avoid, trying to make project1 and project2 always use the latest rev of class_library), this works incredibly smoothly.

I tend to avoid making separate repositories for related applications. Since the class_library in the above example ties all of these things together, there is little advantage or logic in maintaining it in separate versions since the real reason that it is separated at the project level is that it is shared code. Production apps that share code do so for a reason - they should be using the same version of the shared code, which implies that they should be part of the same source control branch.

David Lively
I really dislike this structure. Everything's mashed together and it gets too complicated. Changes to the classlibrary are immediately picked up by both projects, meaning that it becomes fragile and untouchable at scale. You don't get the opportunity to manage your library as it's own entity.Basically, if you're releasing project1 and project2 as separate products, then this layout causes a lot of trouble.
Jim T
I guess there are different points of view here. I prefer this because it keeps us from having multiple versions of a class library that may introduce incompatibilities, or inconsistencies between apps if the interface to a class in the library are modified. Also, this would depend on what you mean by "in the field". In this example, the two projects are an admin site and a public site for the same project - they're separate web application projects, but very tightly coupled for good reason.
David Lively
+2  A: 

We use a system similar to CWT's: Shared code tend to be separate projects in their own right, and as such exist separately in the repository (or a separate repository). Within the project that uses the external projects (the upstream project), we include the compiled/packaged binaries for the shared/downstream project.

This way the upstream project won't be sniped by unexpected changes in downstream projects.

We've written some scripts to automatically update the major upstream projects with newer versions of these binaries when necessary (otherwise it's a manual process to copy the package into the appropriate projects, but that's not that big of a deal anyway).

The drawback we've see so far in this madnes^W method is that downstream projects undergoing very active development (say, in the early stages of a new library) require what may seem like an excessive number of updates to the upstream project. Testing the upstream project can require updating the shared lib, compiling, copying that binary upstream, and the compiling/deploying/whatever the upstream project. However, once an initial frenzy of development on a library slows and the lib becomes somewhat stable, this "problem" evaporates.

As with CWT, the upstream project can't modify the shared code in any meaningful way. Changes downstream must be made within that project explicitly, and propagated upstream where necessary.

supermagic
This is exactly what we're planning right now, with the same justifications.
Jim T
I agree with supermagic's approach. Think in terms of shared libraries rather than shared codefiles/projects. Add the compiled binary for the shared library to your project. This approach has the same effect as including externals to a tag, but none of the maintenance headaches due to managing externals.Some problems I've experienced are creating a tag with externals to the trunk and a trunk and branch pointing to the same external. In my opinion the risks of externals outweigh their benefits.
mcdon
This works fine as long as you only need the binaries, but if you run across a bug in the future in one of the shared libraries, you'll be stuck hunting down the library version that was included in the tag or branch.
David Lively
A: 

The answer that's missing from your list is:

c) Always point externals to a specific version of the shared code.

This works remarkably well:

  1. You can always pull down a specific version of your project and guarantee the build.
  2. Branching and tagging is an operation on the project path alone.
  3. Updates to the shared code can be made for 1 project, but the second project won't pick them up until it's ready to.
  4. When the second project does pick up the changes, you get an event recorded in your trunk saying that it was picked up and why.
  5. You get the opportunity for releasing a specific version of the library with specific features for a project. This can get you out of holes if a project isn't ready for an API change but needs certain bug-fixes, etc.

If required, libraries can be checked out in a separate working copy and modified alongside your main project. If the project is temporarily changed to pick up code from the working copy on disk, you can then still work on libraries and projects in parallel, if needed.

Jim T