Search code examples
gittrac

How can I allow Git commit messages like “fixes #3” to update Trac tickets?


I have just installed Trac 1.0.1 on a server that also holds a Git repository. I’d like to be able to close Trac tickets by including things like “fixes #3” in the Git commit messages. This should pretty easy—by including a post-receive hook in my repository, I can execute some piece of code (e.g. a Python script) after each git push to the server. But what piece of code to use?


Solution

  • After running around for a while and following a couple of dead ends (including the Trac Git page, which is vague on this topic, and the Git plugin, which won’t actually work (?!) until bug #7301 is fixed), I found the solution.

    1. Connect your Git repository to Trac through the steps at “setting up a Trac environment”.

    2. Enable the Commit Ticket Updater plugin, either through the “Admin” section of Trac or by editing trac.ini.

    3. Create a file called post-receive in the hooks directory of your Git repository, with the following content:

      #!/usr/bin/ruby
      
      ARGF.lines do |line|
        fields = line.split
        oldrev = fields[0]
        newrev = fields[1]
        refname = fields[2].chomp
      
        if oldrev =~ /^0+$/
          revspec = newrev
        else
          revspec = oldrev + '..' + newrev
        end
      
        other_branches = `git for-each-ref --format='%(refname)' refs/heads/ | grep -F -v "#{refname}"`
        other_branches = other_branches.chomp.gsub /[\r\n]+/, ' '
      
        commits = `git rev-parse --not #{other_branches} | git rev-list --stdin #{revspec}`
      
        commits.each_line do |commit|
          system "trac-admin .../trac changeset added '(default)' #{commit.chomp}"
        end
      end
      

      Of course, replace “.../trac” with the absolute path to your Trac installation.

      I’m actually using Trac through Virtualenv. If you are too, add this to the top of the file:

      require 'tempfile'
      
      def virtualenv_system(cmd)
        script = Tempfile.new('post_receive')
        script.write 'source .../virtualenvs/trac/bin/activate'
        script.write "\n"
        script.write cmd
        script.close
        system "bash #{script.path}"
        script.unlink
      end
      

      and replace the system call with virtualenv_system.

    4. Make this post-receive file executable.

    This is inspired by the approach given on the Repository Administration page, combined with this SO answer about getting all of the new commits in a post-receive script. I believe that this script, while long, behaves correctly when you are pushing multiple commits and/or pushing commits to branches other than the currently-checked-out one. (The script given on the Repository Administration page does not behave correctly in these situations—it only looks at the most recent commit message from HEAD.)

    After this setup process, any Git commits that contain strings like “fixes #7” will close the corresponding tickets in Trac. You can configure this a little with the options listed on the Commit Ticket Updater page. Specifically, you might want to change the value of commit_ticket_update_envelope; it’s not completely clear, but I think the default is set so that you have to include your commands in square brackets, like “Refactored MyAwesomeClass [fixes #42]”.