Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice use of svn:externals

Let's take three cases:

  1. This statement is good. 999 is a peg. This is the so-called "old format":

    foo -r999 http://myrepo/foo

  2. Alternatively, the peg can also be specified like this (in the "new format"), and is also good:

    http://myrepo/foo@999 foo

  3. But here is a complication in the "new format": -r999 can be put in front, which changes its meaning entirely from being a peg to being an "operative version". And the actual peg becomes HEAD (i.e. no longer deterministic), according to the to SVN Book:

    -r999 http://myrepo/foo foo

    Here's why I think this format may be a very bad idea: If a rename of "bar" to "foo" happens after rev 999, what content will actually be checked out? The renamed version, bar@999. Is this really useful? And then later, another rename happens, naming "baz" to "foo". Now users will be checking out baz@999. Therefore, as time goes on, the user will never be certain that 999 refers to the same code.

Is #3 a loophole waiting to cause trouble? Should users be warned with a bright red flag to avoid using the format of #3? Or is it actually useful, despite its non-determinism? This is almost too easy to misunderstand and to get wrong, and so it seems like a best practice needs to be specified.

From the SVN Book, if you really dig into it, it subtly hints users to be careful:

be careful to avoid naively relocating the -rNNN portion of the definition—the older format uses that revision as a peg revision, but the newer format uses it as an operative revision (with a peg revision of HEAD unless otherwise specified; see the section called “Peg and Operative Revisions” for a full explanation of the distinction here).

like image 291
David Baird Avatar asked Oct 03 '13 10:10

David Baird


1 Answers

Using externals? Best practices?

That's easy! Don't do it!

The svn:externals is one of those things that seemed like such a good idea, but ended up causing all sorts of issues. I have found there are better ways to do this than using svn:externals. For example, you can use Maven or Ivy to do dependency management for you.

However, if I do have to use svn:externals, I will use tags instead of absolute revisions. This works better in newer versions of Subversion, and I don't usually have to worry about using a particular revision, but then the files' URLs all change as files get renamed and moved around. To me, tags are immutable, and once created, they never change.

This also encourages you to think of your svn:externals as completely separate projects. For example, you can talk about Release 4.3 of Foundation. Your svn:externals command would look something like this:

$ svn propset svn:externals `^/tags/4.3/foundation foundation` .

Note I don't put the URL scheme in the tag, or the server name. I almost always use relative paths if the externals is in the same repository as the project. Imagine if you use to use http://, and you move over to using https://, or the name of your Subversion repository machine changes. The externals would still work with this scheme, but break if I had done this:

$ svn propset svn:externals "http://server.com/svn//tags/foundation foundation" .

I am a Pathetic Liar

I am currently working in a project where I did not do what I mentioned above. I don't use tags for my svn:externals, and all of our projects must have them.

In this company, they had a terrible problem with jar dependency management. They produce about a dozen base jars that are used in multiple projects. Each of these projects had different ways of specifying these jars

To get around this, I set up a special Ivy project called ivy.dir. Developers were told to incorporate this project into their current project by using the following command:

$ svn propset svn:externals "../ivy.dir ivy.dir" .

Inside ivy.dir was a complete setup for integrating your project into Ivy. Ivy was preconfigured to use our new corporate Maven repository. All of our various build tools are here.

This approach made incorporating Ivy and good dependency management into our projects a breeze. You merely <import> the ivy.dir/ivy.tasks.xml file into your current build.xml, and use <ivy:cachepath> to create your classpath. A good, well formed build.xml only needs minor modifications to run.

I even created a special <jar.macro> macro definition. It is almost 100% compatible with the <jar> macro, but it will automatically create a pom.xml file from the ivy.xml and embed it into the built Jar (and will also include the Jenkins project name, build date, and build number.)

By using relative URLs, I make it easy to branch everything or to tag it (and the ivy.dir project also gets tagged).

It's a strange way of using svn:externals, but it allows us to get away from the awful use they had before where many of the projects used svn:externals to pull in jars stored inside the repository.

like image 164
David W. Avatar answered Sep 28 '22 20:09

David W.