Rake Task to Display SVN Commits by User

At my current job I’m being asked to send a list of trac tickets I’ve been working on in the previous 2 weeks. After reading this post I figured out that it can be easily done with Rake (well, as everything else you can dream about).

Since we’re using an svn hook that adds notes to the related trac tickets on commit, each revision associated with a ticket has its number in the comments. So all I (Rake task) have to do is to collect all my commit commets, pull ticket numbers from them (typical commit comment can look like this: “refs #786”) and display a list, ordered by date.

I didn’t play with command line svn client much previously, but when I’ve started to write the task I’ve figured out that we can get the svn log in xml using the “—xml” option. Even better!

So I’ve ended up with this:

namespace :svn do
  namespace :log do
    desc 'Displays commints by user in the specified period of time. Usage: rake svn:log:by_user USER=username PASSWORD=pass [URL=repo_url] PERIOD=period_option. Where valid period_options: m - current month, 2w - previous 2 working weeks (monday - friday)'
    task :by_user => :environment do
            
      url = ENV['URL'].to_s
      password = ENV['PASSWORD'].to_s
      period = ['m', '2w'].include?(ENV['PERIOD']) ? ENV['PERIOD'] : '2w'
      #USER parameter is mandatory
      raise 'use rake:svn:log:by_user USER=svn_username' unless user = ENV['USER']
      

      case period
        when 'm'
          start_date = Time.now.beginning_of_month.gmtime
          end_date = Time.now.gmtime
        when '2w'
          start_date = (Time.now.beginning_of_week - 2.weeks).gmtime
          end_date = Time.now.beginning_of_week.gmtime
      end

      #datetime format used by svn log
      TF = '%Y-%m-%d %H:%M:%S +0000'
      #we'll use this var to store commits for each day
      days = {}
      
      require 'rexml/document' 
      
      log = REXML::Document.new(`svn log #{url} --xml -r "{#{start_date.strftime(TF)}}:{#{end_date.strftime(TF)}}" --username #{user} --password #{password}`)

      log.root.each_element do |log_entry|

      #save each revision entry to a hash
      revision = {:number => log_entry.attributes['revision']}
      log_entry.each_element { |log_detail| revision[log_detail.name.to_sym] = log_detail.text } 
      
      #is it my revision?
      if revision[:author] == user
        day = Time.parse(revision[:date]).strftime('%y.%m.%d')
        #for each day we want a list of trac tickets affected or revision number
        #trac tickets numbers are included in the comment like this: #687
        #so we'll try to find such numbers using regexp
        #if there are no tickets numbers we'll simply use revision number like this: [1235]
        tickets = revision[:msg].to_s.scan(/#\d+/)
        tickets << "[#{revision[:number]}]" if tickets.empty?
        (days[day] ||= []) << tickets.join(', ')
        end
        
      end
      
      #and we simply output all commits day by day
      days.keys.sort.each do |day|
        puts "#{day} | #{days[day].join(', ')}"
      end
      
    end
  end
end

The task takes 4 parameters: USER (mandatory) – well, obvious, PASSWORD (mandatory), URL (optional) – can be provided if rails app is not a working copy of repository you’d like to get a log from and PERIOD – “m” to use the revisions made in this month only and “2w” (default) to use revisions made in the previous 2 working weeks. So I can run it like this:

rake svn:log:by_user USER=eugene.bolshakov PASSWORD=secret PERIOD=m

and get something like this:

...
07.07.23 | [2256], [2264], [2265]
07.07.24 | #787, #801, #783
07.07.25 | #786, #823
07.07.26 | [2346]
...

The numbers with sharp are ticket numbers and those in square brackets are the revision numbers. They are displayed if the revision is not related to any trac ticket. Yeah, I know it could be done easier with xpath, but my knowledge of it is poor :(