Using the Subversion Ruby Bindings
September 19, 2007
I work with a distributed team of Ruby (on Rails) developers, and we use Subversion as our repository of choice. Just recently, I wanted to find out which files were recently changed or added to a particular directory of our project.
Doing it manually would’ve been tedious. I also wanted to do it with code so that maybe sometime in the future we might be able to script some behavior depending on the last changed date of a file or group of files in our repository.
I could’ve resorted to merely calling svn info and parsing its output, but I thought maybe this was a good excuse to sharpen my Ruby chops and figure out the Subversion Ruby bindings.
Obtaining the Subversion Ruby Bindings
Subversion has shipped with Ruby bindings since 1.3. You can build Subversion from scratch, and then build Swig then the Ruby bindings like so.
Or, if you’re on the Mac and use MacPorts like me then it’s all much, much simpler. If you don’t have Subversion yet, you can install it using
sudo port install subversion +no_bdb
The +no_bdb variant instructs MacPorts to build Subversion without the Berkley DB back-end. This is necessary because as of this writing I kept running into issues when MacPorts attempts to build db44 , which apr-util depends on. If you have to use the BDB filesystem with Subversion, then see my previous entry to build db44 first, then you can build Subversion normally with just sudo port install subversion.
Once you have Subversion installed, it’s just another simple matter to get the ruby bindings:
sudo port install subversion-rubybindings +no_bdb
Again, note the +no_bdb variant. Now, maybe there’s a way to instruct MacPorts to build Subversion with the Ruby bindings in one command that I don’t know of. In any case, the above command alone works if you already have Subversion installed.
Poking around…
At this point, I thought I was all set to use the Subversion Ruby bindings. Firing up irb and going require 'svn/core' returned true so I thought I was well on my way.
Unfortunately, I had to do a little more poking around to actually figure out how to get it to work. First off, I couldn’t find any handy reference to the Subversion Ruby API. For this, your best bet is the actual source which you can view here.
Diving into the source can be intimidating (and a put off) for some people, but fortunately Ruby code is quite easy to read and comprehend (one of the reasons why I like it). Unfortunately, the Ruby code itself at times just drops down to the actual Subversion C APIs. Fortunately, we can find a reference for those APIs via the Subversion doxygen-generated documents here.
Displaying Subversion file information using Ruby
Armed with the above, I was able to put the pieces together. The following is a simple script that mimicks the output of svn info on a Subversion working copy:
#!/usr/bin/env ruby
require 'svn/client'
# Get the first argument from the command line
target = $*[0]
ctx = Svn::Client::Context.new
receiver = Proc.new do |path, info|
puts "Path: #{path}"
puts "URL: #{info.url}"
puts "Repository Root: #{info.repos_root_url}"
puts "Repostiory UUID: #{info.repos_UUID}"
puts "Revision: #{info.rev}"
puts "Node Kind: #{info.kind}"
puts "Schedule: #{info.schedule}"
puts "Last Changed Author: #{info.last_changed_author}"
puts "Last Changed Rev: #{info.last_changed_rev}"
last_changed_time = Time.from_apr_time(info.last_changed_date)
puts "Last Changed Date: #{last_changed_time}"
end
Svn::Client.info(target, nil, nil, receiver, false, ctx)
A little note, at first I was simply calling ctx.info(target) {|path, info| ...} like what might be inferred from the test code. This works, but will prompt for the Subversion authentication (SSH, actually, since we use “svn+ssh:” repository URLs). I figured out from the API documentation that you have to pass NULL (or svn_opt_revision_unspecified, which I couldn’t find the Ruby constant for) to force the Subversion client to pull the information solely from the working copy.
Now that we can at least view the working copy information, the rest of the bindings should be useable with the source, the unit tests and the API docs as references.
Happy hacking!
March 12, 2008 at 5:13 am
Interesting post. Have you played around with the bindings anymore ?