Best practice use of svn:externals
Asked Answered
B

2

7

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).

Barclay answered 3/10, 2013 at 10:10 Comment(0)
U
8

I agree with you that one should avoid revision numbers without a peg. I know of no reason not to specify an explicit peg revision (if you're using explicit revisions at all, anyway), and it makes sure you can always roll back to that revision without ambiguity. Where I work we always specify our externals in the 2nd format you list, where the peg is both the peg AND the operating revision. OR, we point to a tag. Because tags are not ever supposed to change after commit (and maybe adjusting externals and such), and at such a high level in the project repository paths are unlikely to change, this should accomplish the same goal as using explicit revisions in a more meaningful (to humans) format.

Note, you CAN combine formats 2 and 3 but that's not necessary, for example -r 123 http://example.com/svn/proj@123 proj.

Underclassman answered 3/10, 2013 at 22:27 Comment(1)
I'm accepting this answer because it addresses/confirms the use of peg revisions versus operative revisions. However, I also like the "Don't do it!" answer that was provided by David W.Barclay
P
8

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.

Preform answered 4/10, 2013 at 2:53 Comment(3)
I agree very much with the using tags idea! I'm adding that to my answer. But sometimes, when you're actively developing a library or something, there not yet BE a tag to point to. In this case, explicit revisions should be used.Underclassman
The problem is that the constantly changing revisions make it very, very difficult to keep things up to date. You branch the project, and suddenly your svn:externals URL changes. As I said in my second answer, I use svn:externals to integrate with Ivy. We have a special properties file (ivy.version.properties) that contain the actual jar version ID. It's set to SNAPSHOT while base jar development is taking place, and switched to release when we finalize our base jars for the release.Preform
(con't) I find that Ivy/Maven integration works better with this type of library releasing than attempting to use svn:externals for it. Of course, this only works for Java and not C, but you could take a similar concept. C libraries are stored on a separate server, and the Makefiles use wget or curl to pull down the proper versions.Preform

© 2022 - 2024 — McMap. All rights reserved.