03 Sep, 2009
VendorBranches & the svn_load_dirs.py manual
Okay, well maybe it’s not a manual, but at least it’s a description that I could understand about what svn_load_dirs does, why it is useful, when you want to use it etc. Maybe it’s useful for someone, at least I hope it’s a reference for myself in the future, as I will no doubt quickly forget what I’m about to write.
Many people use vendor branches in their subversion repository. When you depend on some external project, and you want to store its sourcecode in your own repository, you generally just commit it somewhere in your repository, and you call that a vendor branch. Instead of directly committing into your “main tree”, you generally place it somewhere outside. Let’s say /External/Mono. I’ll use mono as an example, because that’s the external dependency I actually deal with frequently.
It turns out it’s comfortable to actually place it in /External/Mono/current. that way when you put a new drop of the mono sourcecode in that place in the repo, you can make a tag at /External/Mono/mono2.6-beta3. Tags are cheap anyway, and it gives you an idea of what the hell you were doing.
Now, in most cases you’ll actually want to modify the vendor’s sourcecode. (If not, there’s not that much point in versioning it). This is where it gets a bit tricky. What I do is actually also have a copy of the mono tree in the main svn tree. This copy is initially made using svn copy. Whenever /External/Mono/current gets updated with a new drop, I svn merge those changes into the actual “real tree”. In order for this merging to work, you actually need to make sure that the svn:mergeinfo property gets properly committed, after merging. (if in the ‘real tree’, you placed the copy at myrealtree/Sources/Mono you need to have the svn:mergeinfo on that mono folder).
So now when you want to make modifications, you just change whatever you want in myrealtree/Sources/Mono. The actual vendor branch at /External/Mono/current should always contain a clean version of the vendors sourcecode.
Now, let’s say we want to upgrade our version of Mono. That means we want /External/Mono/current to now contain updated code. This is where the tricky part starts. What are our options of making this happen? We could just make a checkout of /External/Mono/current, and just copy over the new sourcecode, and commit. This has a problem however:
What happens to deleted files? files that used to be there, but now no longer should be. If you just copy over the new sourcecode, those files will still be there from the old sourcecode.
What happens with renames? Ideally, if the mono sourcecode renamed GZipCompression.cs to GZip.cs, ideally you want that showing up as a rename in our own repo, so our own changes to that file will get carried over.
In practice, I’ve found I can deal with the renames, but the deletes are really quite a problem.
This is where svn_load_dirs.py comes into play. It’s job is to fix this problem. You tell it which repo url you want to be changed, and you tell it which local folder contains the new sourcecode that it should be changed to. You can also ask it to make a tag for you automatically. Let’s take a look at the commandline:
svn_load_dirs.pl -t trunk-r141102 -wc vendorbranchatunity svn://mycompany.com/svn/External/Mono current monoprojecttrunk
this says a few things: the main “vendor branch area” is svn://mycompany.com/svn/External/Mono
please note that this is the parent folder, of /External/Mono/current. we give it the parent folder, because we are specifying two folders that are actually relative to this. the folder where the actual new drop should go, (current), and the folder where the tag should go. (trunk-r141102). So after this process, the svn repo will look like:
/External/Mono/current (with new stuff)
/External/Mono/trunk-r141002 (same stuff, but tagged so it’s easy to find later)
/External/Mono/trunk-r120000 (you might have an older tag from a previous run)
Okay, about the other arguments. the last one is “monoprojecttrunk”. This is a local directory containing the actual sourcedrop that should be placed into ‘current’. In most svn_load_dirs stuff I found online, people suggest this should be an svnexport of wherever this code originated from. For me it has worked just fine to have this be an actual working copy of where the code came from. My code comes from the mono project’s svn. That way it saves me an svn export, in what already is a lenghtier process than you’d want. I do make a few modifications to the local mono checkout though, as it contains a lot of heavy stuff that I don’t use. moon/test for instance contains a lot of testmedia that I don’t care about, and there’s some more cruft (cruft from my pov, useful stuff for others
) that I cut at this point.
These are all the essential arguments that you need to do the real work. There’s one argument that is merely an optimization. it’s the “-wc vendorbranchatunity” one. If you don’t provide this, the script will just make a checkout of the svn url you gave it. Since in practice it takes at least a few tries to get the script to do what you want, making those checkouts all the time takes a lot of time. So I always just make a checkout myself, and tell the script to use that. I make a backup of that checkout, so that when the script blows up on me, I just restore my backup, fix the reason why it broke, and try again. The actual svn url that should be in the checkout is the ‘current’ directory. so not the base url that you pass on the commandline, but the base url + current. (there’s no rule that says you have to call it current, it’s just what I use, and I think the svn book also uses it as an example).
Okay, so we got the whole commandline arguments thing figured out, let’s actually run this thing.
The script will first do some sanity checking, make sure what you say is an svn url actually works, yadayada. Then it will analyze the files currently in “current”, and check them against the files in “monoprojecttrunk”. it makes a list of files that should be added, and files that should be deleted.
It then presents both of these lists to you. You can inspect them for a sanity check. It then asks you if you want to mark any combination of those as a rename. So you can go in and say “Yeah, actually that GZipCompression.cs that you are deleting, and that GZip.cs that you are adding, that is actually a rename”. that way, it will be applied to your svn repo like a rename, which then makes it easier to merge that change into your real tree, especially if you happened to have modifications to that file.
Usually I don’t bother to actually go in and tell it about the renames, because I have checked that the files that I have actually modified are not renamed, so then I really don’t care that much.
Okay, so once you go that out of the way, the script is going to svn-add all over the place, and svn-rm all over the place, and then it’s going to try to commit it all, commit the tag, and then be done.
Unless…
unless some of the changes are actually renames in casing only. It turns out that the mono folks have been cleaning up their shop a bit, and fixed some casing issues. stuff like renaming ChangeLog to Changelog. It turns out that svn_load_dirs is not capable of dealing with these cases gracefully. It will give you this error:
svn: Commit failed (details follow):
svn: ‘/Users/lucas/mono/vendorbranchatunity/mcs/class/Mono.Simd/ChangeLog’ is scheduled for addition, but is missing
When you get this error, the casing problem is probably what’s hitting you. The script will just bail out, and you need to fix the problem manually. I fix this by going into the checkout of ‘current’, and just deleting the file there. commit the delete, and then start the script from the beginning again. (This is where the -wc optimization comes in handy).
When this has succeeded, you should be happy. And then move on to the following step:
merge the changes that just got applied to ‘current’, to the version in your realtree, which actually contains your modifications. I like to use tortoisesvn for this, as it’s recently improved merge gui is actually quite decent, and by this point I’ve usually had my share of commandline argument struggling for the day. so, merge the changed revision into your real tree. (and make sure that when you commit that, you also commit the realtree/Sources/Mono folder itself, because it’s svn:mergeinfo will have changed, and if you do it right, it means the next merge is going to go a lot easier than when you decide to forget about this for now.

As I was looking at the web traffic for this blog, I noticed that the post on Visual Studio integration for Unity was getting most traffic by far. Too bad the post was from a 2.1-mac era, and it actually doesn’t work anymore for 2.5-windows, which I suspect most people are looking for these days.





