26 Jun 2009, 8:20 p.m.

Command-Line Subversion Tutorial, Part 3: Subversion Properties

In Part 1 of this series we looked at the basics of command-line Subversion, and Part 2 covered importing, branching and merging. In this third part I'd like to introduce a useful and very powerful Subversion feature, which isn't always terribly well understood by developers: that of Subversion Properties.

Subversion Properties

So what is a "property" within the context of Subversion? Well, you can think of SVN properties as "metadata" associated with a file or directory, some information above and beyond the actual contents of the file - the stuff that you're usually concerned with. A property is structured as a simple key-value pair. The key is a string, and the content of the value can be absolutely anything you like, including binary data.

It isn't hard to find potential uses. Image files can be tagged with a brief description of their subject; source code files can be tagged with, say, the date at which the code was last reviewed, and by whom; or the text of a magazine article in progress could be tagged with a list of topics still to be covered. The list goes on, and there really is no limit.

So SVN properties are inherently fairly useful, but where the feature really shines is when it's combined with a number of special, reserved property names, to provide some very useful functionality indeed. We'll have a look at a handful of those special properties in due course, but first let's find out how to create, modify and remove properties.

Adding, Editing and Removing Properties

To add a property to a file, you need to head back to the command line and issue the svn propset command. The syntax for that is as follows:

[simon@vps02 hellosite]$ svn propset <property-name> <property-value> <path>

Continuing with the "code review" example, let's say I've just reviewed Bart's changes to index.html:

[simon@vps02 hellosite]$ svn propset "Reviewed-By" "Simon" index.html
property 'Reviewed-By' set on 'index.html'
[simon@vps02 hellosite]$ svn propset "Reviewed-Date" "2009-06-26" index.html
property 'Reviewed-Date' set on 'index.html'
[simon@vps02 hellosite]$ svn status
 M     index.html
[simon@vps02 hellosite]$ svn commit -m 'Added code review properties'
Sending        index.html

Committed revision 497.

That's done the job. You can see there that properties have to be committed, just like any changes to the content of the file. In fact, properties respond to the use of things like svn commit, svn update and svn revert in much the same way as the contents of the file do. For a refresher course on those commands, check out Part 1.

The next thing we might wish to do is find out which properties are set on the file, using svn proplist:

[simon@vps02 hellosite]$ svn proplist index.html
Properties on 'index.html':
  Reviewed-By
  Reviewed-Date

That's the two properties we added, but what were the values again? svn propget will tell us:

[simon@vps02 hellosite]$ svn propget Reviewed-By index.html
Simon
[simon@vps02 hellosite]$ svn propget Reviewed-Date index.html
2009-06-26

To edit a property, simply use svn propset once again, this time supplying the new value for the property. You can also use svn propedit <filename> to edit the property value using an external editor, typically vi, should you wish.

Finally, to delete a property, use svn propdel:

[simon@vps02 hellosite]$ svn propdel Reviewed-By index.html
property 'Reviewed-By' deleted from 'index.html'.
[simon@vps02 hellosite]$ svn propdel Reviewed-Date index.html
property 'Reviewed-Date' deleted from 'index.html'.
[simon@vps02 hellosite]$ svn status
 M     index.html
[simon@vps02 hellosite]$ svn commit -m 'Delete properties' index.html
Sending        index.html

Committed revision 498.
[simon@vps02 hellosite]$ svn status
[simon@vps02 hellosite]$ svn proplist index.html
[simon@vps02 hellosite]$

You can see that the properties have now been deleted. That's pretty much all there is to the mechanics of managing properties within Subversion, so it's time to look at some far more useful applications of the feature, based on the built-in svn: properties.

svn:ignore

Properties whose names begin with svn: are special, in that they are reserved for use by Subversion itself, and allow it to implement some pretty cool features. One of the most useful of these is svn:ignore.

The svn:ignore property is designed for situations where you have a directory within your project that contains generated artefacts - for example log files, cache files, or phpDocumentor output. You certainly don't want those committed to your repository, and you also don't want to have to work around many lines of output marked "?" every time you run svn status.

The solution is to use svn:ignore. The value of this property is typically a pattern, and when this is added to a directory, all files within that directory that match the pattern will be completely and utterly ignored by Subversion. Let's see that in action. Imagine that we've added some logging code to our simple website, and it has been happily logging away while we've been developing:

[simon@vps02 hellosite]$ svn status
?      logs/log.20090623
?      logs/log.20090624
?      logs/log.20090625

We don't want that stuff committed. svn:ignore to the rescue:

[simon@vps02 hellosite]$ svn  propset svn:ignore "log.*" logs/
property 'svn:ignore' set on 'logs'
[simon@vps02 hellosite]$ svn status
 M     logs
[simon@vps02 hellosite]$ svn commit -m 'Use svn:ignore to ignore files in log/'
Sending        logs

Committed revision 496.
[simon@vps02 hellosite]$ svn status
[simon@vps02 hellosite]$ ls logs/
log.20090623  log.20090624  log.20090625

That's pretty cool. Any file that matches the pattern "log.*" will not show up under svn:status, and more to the point, will not accidentally be committed to your repository.

svn:externals

The majority of non-trivial development projects make use of a range of third-party or other external libraries. In the PHP world, that is often something like Zend Framework or Simpletest, but the problem is much the same in other programming languages.

When you check out a project, before you can start work you need to resolve the dependencies, and make sure that your working copy of the code has access to all the libraries it needs. I've seen all sorts of clumsy mechanisms in my time, such as manually sourcing a copy of the library, or symlinking files all over the place. However, as is often the case, there is a standard tool designed exactly for the job, and that tool is the svn:externals property.

It's common practice to keep these sorts of things under a separate lib/ directory, so I'll do just that. The svn:externals property can then be set on that directory. The value of the property is a specially formatted string in two parts: the path under which you want the library to reside, and a Subversion URL from where the library should be fetched. I'll add the Zend Framework to my project like so:

[simon@vps02 hellosite]$ svn propset svn:externals \
            "zendframework svn://svnrepo/zendframework/" \
            lib/
property 'svn:externals' set on 'lib'
[simon@vps02 hellosite]$ svn commit -m 'Add svn:externals for ZF'
Sending        lib

Committed revision 501.
[simon@vps02 hellosite]$ svn update

Fetching external item into 'lib/zendframework'
A    lib/zendframework/Zend/Service/Amazon/S3.php
A    lib/zendframework/Zend/Pdf/Filter/AsciiHex.php
A    lib/zendframework/Zend/Pdf/Filter/Interface.php

I've snipped the output there, since Zend Framework is enormous, but you can see that Subversion is happily fetching the library for you, as hoped. Note that while this example assumes that I have a copy of the library in my own repository, externals can equally be fetched from another repository altogether, such as Zend's own repository.

From now on, all a developer need do is simply check the project out of the respository, and Subversion will fetch the libraries automatically. Similarly, when the libraries need to be updated, the changes can be made in one place, and anyone checking out a project, or running svn update, will seamlessly receive the latest version.

Conclusion

Subversion properties are clearly awesome, therefore. There are of course more built-in svn: properties: for example svn:eol-style can be used to standardise line breaks (think \n versus \r\n) when files need to be shared across multiple platforms, and svn:executable will automatically set the executable bit on relevant files when you check them out on Unix systems.

I think you'll agree that Subversion properties are a powerful feature, and they can be used in far more ways than I've illustrated. In fact the only limits are your imagination, and common sense.

This last point bears a moment's thought. For the most part, properties are invisible to the naked eye: you either have to know that they're there, or go looking for them. Documentation can help, but even so, it's best not to stuff too much vital information in a property and assume that all other developers will pick up on it.

This is part of a general theme which you'll hear me bang on about a fair bit: use of Subversion (and indeed all other tools and technologies) has to be appropriate. This is one reason why I really like the Pragmatic Version Control using... books, and it's a reason why I'll return with a fourth - and probably final - part to this series, where we'll take a look at some best-practices and common-sense advice for using Subversion simply, effectively and above all appropriately.

Previous Posts in this Series:

Related Reading

Posted by Simon in Programming and Version Control
1 Aug 2009, 8:10 a.m.

Philippe

Hello Simon,
Really nice tuto. Concise and straight-forward. A good asset for those how started using svn a while ago and never had enough time to explore some suspected usefull functionalities like you described.
Thanks and hopping your part4 will raise your blog soon :)
Philippe

4 Dec 2009, 5:24 p.m.

Hao

Simon: Very nice tutorial. Concise and useful. Waiting for the part4 to learn more~

Hao

4 Dec 2009, 5:37 p.m.

Simon Harris

Thanks for the comments. You've reminded me that I really should get that final part finished.