Saturday, February 20, 2010

Private Branches in Subversion

For anyone still learning svn, this is what I've been doing when I create and use private branches (aka "feature" branches).

For me, the main motivation for creating a private branch is to stash some code on which I've been working when I have to switch over to something else before I've finished (or can't check it in yet for some other reason). You could also use a private branch to collaborate on (or just share) some experimental feature with another developer, or to submit a bugfix that needs to be reviewed before checking it into the mainline (and of course you can use the same switching/merging techniques with release branches as you do with private branches).

Here are two other tutorials for using branches (although some of Evan Weaver's "best practices," like deleting the trunk of the repository, are probably not actually best practices in practice):

1. Decide where to put the private branch in source control.

Some projects might just use the standard branches directory for this. Other projects have a sibling of the trunk called experimental or exp or just ex specifically for these kind of experimental branches. I'll use exp in this writeup, and I'll put it in a repository named my-project — although it can be anywhere (outside of the trunk or whatever part of the tree from which you're branching, obviously).

So, assuming you already have the my-project repo set up, first create a temporary working copy for exp in a temp directory (checkout with --depth empty to avoid pulling down the whole repo):

$ mkdir /tmp/my-project $ cd /tmp/my-project $ svn checkout —depth empty http://example.com/svn/my-project .

Then create the exp folder, and check it in:

$ mkdir exp $ svn commit -m 'created an experimental section for my-project'

2. Create the private branch.

Ideally, you've already got the working copy of the trunk locally, and updated it to the latest revision. In that case, you can just branch from the head revision of the trunk. You use the svn copy command to do this, specifying the source url (the trunk) and the destination url (the new branch root: my-new-branch). This is completely a server-side operation, so it doesn't matter from where you invoke it. Also, copying is very cheap in subversion, so it should execute quickly (if you ignore the usual network latency):

$ cd ~/projects/my-project $ svn update $ svn copy http://example.com/svn/my-project/trunk http://example.com/svn/my-project/exp/my-new-branch -m 'branch from trunk at 9269'

In practice, your working copy may be a few days out of date; in that case you should branch from the trunk revision to which you last updated (or update your working copy to the head):

$ svn copy -r 8189 http://example.com/svn/my-project/trunk http://example.com/svn/my-project/exp/my-new-branch -m 'branch from trunk at 8189'

3. Switch your working copy to the private branch.

Now that the branch has been created, you can switch the working copy of the tree you currently have checked out from the trunk to the new branch. If you want to switch your entire working copy, just use the switch command from the working copy root, and specify the url of the branch to which to switch:

$ cd ~/projects/my-project $ svn switch http://example.com/svn/my-project/exp/my-new-branch

You can also switch just part of the tree, like just one specific component on which you want to work:

$ cd ~/projects/my-project/foo/bar $ svn switch http://example.com/svn/my-project/exp/my-new-branch/foo/bar

Note that switch is very similar to update — it will update your working copy (at least the files you haven't modified locally) to match the revision of the branch to which you switched. If I just created my-new-branch from the head revision of the trunk, but I haven't updated my working copy in a while, when I switch from the trunk to my-new-branch, it's going to pull down all the files that have been updated since I last sync'd. If your working copy is messy or out of sync, you're probably better off switching just specific projects/components/folders/files within the branch, rather than the whole thing.

4. Commit to the private branch.

You can commit as often as you want to the private branch. Descendants of directories that have been switched automatically are committed to the branch to which their ancestors have been switched. For example, in my local my-project folder, the root of my working copy is pointed to the trunk, but the bar directory is switched to my-second-branch, and a.html is switched to my-first-branch:

projects my-project: ^/my-project/trunk foo bar: ^/my-project/exp/my-second-branch/foo/bar baz a.html: ^/my-project/exp/my-first-branch/foo/bar/baz/a.html b.html

If I commit a.html, it will be committed to my-first-branch; and if I commit b.html, it will be committed to my-second-branch. I haven't actually tried this out, but if try to commit both at the same time (ie by committing from any ancestor folder), I believe each will be committed to the correct branch (a.html to my-first-branch and b.html to my-second-branch) in the same atomic checkin. The same holds for other svn commands, like update, diff, etc: files will be compared to the branch/revision to which you've switched their nearest ancestor.

You can always tell which branch (and revision) to which you have switched a given file with the svn info command. Among other things, it will print out the URL of the corresponding file in source control, and the revision (of the branch) to which you last updated:

$ cd ~/projects/my-project $ svn info
... URL: http://example.com/svn/my-project/trunk ... Revision: 9269 ...
$ svn info foo/bar | grep URL
URL: http://example.com/svn/my-project/exp/my-second-branch/foo/bar

5. Merge updates from the trunk to the private branch.

If you've been working on your branch for a while, you may want to get the latest updates from the trunk to see how they work with your branch (and then continue working on your branch). You use the merge command for this. If you want to merge the entire trunk to your switched branch, it's pretty straightforward. You used to have to specify the revisions from which you originally branched (or the last revision you merged), but the latest version of svn (starting with 1.6?) can figure it out for you automatically — so you only have to specify the url of the branch from which to merge:

$ cd ~/projects/my-project $ svn merge http://example.com/svn/my-project/trunk

You can also use the merge command to merge just specific revisions or specific files from the trunk:

$ svn merge -r 9269:10035 http://example.com/svn/my-project/trunk/foo/bar/baz/a.html foo/bar/baz/a.html

You can always skip this step if you don't care to get the latest from the trunk while you're working — you can always check that your new code works with the trunk when you merge back from the private branch to the trunk (in the next step), anyway.

6. Merge the private branch back into the trunk.

Finally, when you're ready to merge back to the trunk, make sure you've committed everything you're going to commit to the private branch. Then switch your working copy back to the trunk:

$ cd ~/projects/my-project $ svn switch http://example.com/svn/my-project/trunk

If you've switched individual subdirectories in your working copy, you have to switch them back individually:

$ svn switch http://example.com/svn/my-project/trunk/foo/bar foo/bar $ svn switch http://example.com/svn/my-project/trunk/foo/bar/baz/a.html foo/bar/baz/a.html

Now merge in the changes from your private branch:

$ svn merge http://example.com/svn/my-project/exp/my-new-branch

If you've done any merging from the trunk to the private branch (step 5), you should also use the --reintegrate flag so svn knows it can avoid merging back changes from the branch to the trunk that it previously had merged from the trunk to the branch. Finally, commit the merged changes to the trunk:

$ svn commit -m 'merged my-new-branch into trunk'

7. Delete the private branch.

You don't have to delete the branch, but once svn has merged from the trunk to the branch and back again, it may have problems tracking further merges between the two branches. You can continue developing with the private branch if you really want to — you'll just have to be much more careful when you merge. The easiest thing simply is to create a new private branch.

If you do delete the branch and ever need, for some reason, to get back to it, it will of course still be there — you can just switch back to it with the svn switch command (or check it out as a separate working copy), as long as you know the revision range in which it existed (which you can always dig out of the logs):

$ svn delete http://example.com/svn/my-project/exp/my-new-branch $ mkdir ~/projects/my-project-old $ cd ~/projects/my-project-old $ svn checkout -r 10036 http://example.com/svn/my-project/exp/my-new-branch

Sunday, February 14, 2010

IE VPCs on Ubuntu

I love the fact that Microsoft makes these Internet Explorer Application Compatibility VPC Images available, but I hate the fact that they're so darn hard to setup on linux. Plus the last few times they've rev'd the images, they've packaged them in a way where the licensing breaks if you don't run them in Microsoft's VPC software (obviously available for Windows only). And of course, each of the different VPCs (for ie6, ie7, and ie8) use the same disk uuid, which doesn't play nicely with VirtualBox. So this is what I end up doing:

Initial Setup

1) Download the latest VPCs.

2) Install unrar to extract the images:

sudo apt-get install unrar

3) Extract each image:

unrar e -o+ IE6-XP-SP3.exe

(I also move and rename them to something convenient, like ie6.vhd.)

4) Install VirtualBox:

sudo apt-get install virtualbox-ose virtualbox-guest-additions

Rinse and Repeat

After 3 days or reboots of the image, these VPC images will stop working in VirtualBox, so if you're not careful you'll have to repeat these steps fairly often (fortunately, VirtualBox's snapshotting capabilities can help here). Keep massaging the scalp until you've produced a thick, luxurious lather:

5) Clone the vhds, to give them universally unique uuids (apparently Microsoft doesn't quite get the meaning of uuid). Also, this'll give you a pristine image when you need to repeat the process:

VBoxManage clonehd ie6.vhd ie6cloned.vhd

6) Fire up the VirtualBox ui:

virtualbox

(I actually usually do this via the desktop menubar: Applications > Accessories > VirtualBox OSE.)

7) Add each of the vhds (you can also do this via VBoxManage commands, but I'm too lazy to figure them out):

  1. Click the New toolbar item. Click Next.
  2. Enter a name (like ie6) and click Next.
  3. Select the Base Memory Size (256 MB for ie6/7; 512 MB for ie8), click Next.
  4. Select Use existing hard disk, click the little Folder icon.
  5. Click the Add toolbar item.
  6. Browse to the (cloned) .vhd file, select it, and click Open.
  7. Select the vhd you just added, and click Select.
  8. Click Next, then click Finish.

8) Run each image (select the image, click the Start toolbar item).

9) After XP boots, click No to the Windows Product Activation prompt, then ignore all the other whiny messages (resist the temptation to freaking close them already!), and select the Devices > Install Guest Additions... menu item from the image's menubar (the linux window's menubar, not XP's — to get out of XP, press the right-control key). Click the Continue Anyway when Windows tells you that installing this software will usher in the End Times. Don't reboot when it finishes — fix a few other things first.

10) Because I use the Dvorak keyboard layout, the first thing I do in XP is change the keyboard layout, via the Start > Control Panel > Regional and Language Options control panel; flip to the Languages tab, click Details...; then click Add..., select Dvorak, click OK, make it the Default, OK, OK, OK already!

11) Next I get rid of XP's screensaver, via the Start > Control Panel > Display Properties control panel. Flip to the Screen Saver tab, and select (None) from the Screen saver drop down (and click OK when you're done with the display settings). Also choose a larger screen size (Settings tab) and try to find a desktop background that doesn't make you want to puke.

12) Still ignoring XP's whimperings, open a command prompt (Start > All Programs > Accessories > Command Prompt), and enter:

D:\VBoxWindowsAdditions-x86.exe /extract /D=C:\Drivers

13) Now find one of those Hardware Update Wizard dialogs that XP keeps popping up. If you couldn't take it and closed them all, open Start > Administrative Tools > Computer Management, then click Device Manager, then double-click Ethernet Controller, and finally click the Reinstall Driver... button. Follow these steps:

  1. Select the No, not this time radio-button, click Next.
  2. Select the Install from a list or specific location (Advanced) radio buton, click Next.
  3. Select the Search for the best driver in these locations radio-button, unselect the Search removable media check-box, select the Include this location in the search check-box, and type in C:\Drivers\x86\Network\AMD (or browse to it, if you're visually inclined).
  4. Click Next, click Finish.

Now we're finished with non-ie stuff! Ignore any other Hardware Wizards or other Forces of Darkness that XP sends your way.

14) For ie7, run the Install the IE Explorer 7 Readiness Toolkit item on your desktop.

15) Finally, run ie! You should see some crappy Microsoft home page — this means you've got the networking stuff hooked up right. Select Tools > Internet Options, and click the Use blank button so you never have to see this again. Then flip to the Advanced tab, and unselect the Disable script debugging (Internet Explorer) check-box, so the debugger will come up whenever you run across a javascript error.

OK, now reboot Windows. You've only got one more boot, so don't ever restart again! Once you boot up, save a snapshot with VirtualBox so you can always go back to this happy place.

See Also

Lots of other people have already gone down this road; especially helpful are: