At my work we use git and Bugzilla for source code control and bug tracking respectively. We have a process where bugfixes are commited to a branch off of master with the name of bugfix/bz1234 where 1234 is the id of the issue in our bugzilla system.

After a fix is completed, a merge request is filed in our bugzilla system to merge this branch back into master (developers don't have write privileges to master). We are in the process of writing up a hook to parse commit messages to automate all of this but it's taking some time.

So, in the interest of making my life a bit easier until then, I took advantage of Bugzilla's excellent email_in.pl to parse incoming emails to edit tickets and wrote up a command to file fix and merge tickets for me.

I simply put git-bz on my $PATH and call it thusly:

> git bz fix
> git fix 1.2.3 hotfix | sendmail -t

Note: Our merge tickets require a milestone and a version for the merge ticket, thus the extra arguments for the merge.

Your system is probably radically different than ours but I thought it'd be interesting to post something here showing how powerful git is. Let me know what you think.

#!/usr/bin/env perl
# git-bz -- given a version and target, print out a merge
#           email for sending to bz

use warnings;
use strict;

use Carp;
use Git;

#============================================================================
# map a shorthand version to the one bz expects for merges
#============================================================================
my %versions = (
    'mne'    => 'Merge (M&E/Patch)',
    'rc'     => 'Merge (RC/Major)',
    'hotfix' => 'Merge (Hotfix)'
);

#============================================================================
# get the action and config info
#============================================================================
my $action   = shift || '';
my $bz_email = Git::config('bz.email');

if (! $bz_email) {
    croak 'No bugzilla email found';
}

#============================================================================
# start doing the work
#============================================================================
if ( $action eq 'fix' ) {
    my $user_email = get_user_email();
    my $bz_info = get_bz_info();

    print <<"MAIL";
To: $bz_email
From: $user_email
Subject: Issue $bz_info->{bug} fixed

\@bug_id = $bz_info->{bug}
\@bug_status = RESOLVED
\@resolution = FIXED

Fixed in $bz_info->{branch} pending merge.

MAIL

} elsif ( $action eq 'merge' ) {
    my $target_milestone = shift || '---';
    my $merge_to = shift;
    my $version  = ( $merge_to && $versions{$merge_to} ) ||
                   'unspecified';
    my $bz_info  = get_bz_info();

    my $user_email = get_user_email();

    if ( !defined $bz_info->{bug} ) {
        croak "No bug id found in branch name";
    }

    my $bug_id = $bz_info->{bug};
    my $bz_branch = $bz_info->{branch};

 #============================================================================
 # get the remote branch, url and name
 #============================================================================
    my @remote_info
        = Git::command( 'config', '--get-regexp', '^branch\.'
                        . $bz_branch );
    my ( $remote, $remote_branch );
    for my $line (@remote_info) {
        if ( $line =~ /\.remote\s+(.+)/x ) {
            $remote = $1;
        } elsif ( $line =~ /merge\s+((refs\/)?heads\/)?(.+)/x ) {
            $remote_branch = $3;
        }
    }

    my $remote_url = Git::command_oneline( 'config', '--get-regexp',
        'remote.' . $remote . '.url' );
    if ( $remote_url =~ m/url\s+(.+)/x ) {
        $remote_url = $1;
    }

 #============================================================================
 # get the commits to show
 #============================================================================
    my $commits = Git::command(
        'log',            "$remote/$remote_branch..",
        '--pretty=short', '--name-status'
    );

 #============================================================================
 # finally print out the email message
 #============================================================================
    print <<"MAIL";
To: $bz_email
From: $user_email
Subject: Please merge $bz_branch to $remote_branch

\@blocked = $bug_id
\@product = Application Defects
\@component = Merge Request
\@version = $version
\@target_milestone = $target_milestone

Please merge $bz_branch to the remote branch $remote_branch

Merge: $bz_branch
To: $remote_branch
Repository: $remote_url

$commits
MAIL

} else {
    croak <<"USAGE";
error: Unknon subcommand: $action
usage: git bz fix <target>
   or: git bz merge <merge_to> <target>

USAGE
}

#============================================================================
# get current branch and bug ID
#============================================================================
sub get_bz_info {
    my $bz_branch = Git::command_oneline( 'symbolic-ref', 'HEAD' );
    $bz_branch =~ s{^refs\/heads/}{}x;
    my ($bug_id) = $bz_branch =~ m/bz(\d+)/x;
    return { branch => $bz_branch, bug => $bug_id };
}

sub get_user_email {
    my $email = Git::command_oneline( 'config', '--get', 'user.email' );
    return $email;
}

If you want to use it, you'll need to set up a config to point to your own bz email.

> git config bz.email mybz@somedomain.com

This script is available as a gist on github.