Store git activity in MySQL with PHP

Git hooks are saving me so much time and providing me with interesting solutions to problems I didn’t even know I had. I can’t be the only person who this would be useful to, so give it a go.

As I said, I work on loads of sites, and keeping track of what’s been done and where is sometimes a bit of a pain. I keep a todo list, but if I get an emergency email from someone, chances are that won’t go through my todos. It will, however, be put into version control.

So this morning I had the bright idea to write a git hook that pushes relevant information to MySQL so that I can run activity reports later. All my bare git repositories are stored in a directory on our dedi, so it’s just a matter of making sure each repository has the post-receive hook in. I do this by keeping the actual hook in the same directory as all my repositories, then symlinking the hook into the appropriate place with the following little script. Obviously, this assumes that your post-receive hook is in the same place as your repositories, and that you want this hook everywhere. But that’s all true, so we’re all good. Once you’ve run the linked script, you’ll only have one hook to maintain and every time you create a new repository, you can just run the script again and everything will all be up-to-date.

Now for the hook. It’s not beautiful PHP, but little scripts like this rarely are, in my experience.

Create this table:

CREATE TABLE `log` (
`id` int(10) unsigned NOT NULL auto_increment,
`repo` varchar(255) NOT NULL,
`commit` varchar(40) NOT NULL,
`date` datetime NOT NULL,
`message` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `commit` (`commit`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Here’s your script. chmod +x it.

#!/usr/bin/env php
<?php
date_default_timezone_set('Europe/London');
exec('pwd',$pwd);
$repo = rtrim(array_shift($pwd),'/');
$repo = substr($repo,strrpos($repo,'/') + 1);
$db = new PDO('mysql:dbname=DB;host=127.0.0.1','USERNAME','PASSWORD');
exec('git log --all --pretty=format:"%H%n%ct%n%s%n%b%n<><><>"',$capture,$log);
if ($capture){
    // preprocess the log
    $commits = array();
    $current = array();
    foreach ($capture as $row){
        if (trim($row) === '<><><>') {
            $commits[] = $current;
            $current = array();
        } else {
            $current[] = $row;
        }
    }
    $v = array();
    $b = array();
    foreach ($commits as $commit){
        $sha = $commit[0];
        $m = $commit[2] . (trim($commit[3]) === '' ? '' : "\n\n. implode("\n",array_slice($commit,3)));
        $d = date('Y-m-d H:i:s',$commit[1]);
        $v[] = '(?,?,?,?)';
        $b[] = $repo;
        $b[] = $sha;
        $b[] = $m;
        $b[] = $d;
    }
    $stmt = $db->prepare('insert ignore into log (repo,commit,message,`date`) values. implode(',',$v));
    try {
        if ($stmt) {
            if (!$stmt->execute($b)) throw new PDOException;;
        } else Throw new PDOException;
    } catch (PDOException $e) {
        mail('EMAIL','Commit did not reach db',$e->getMessage());
    }
}
?>

So basically we’re extracting the log data we need, doing some funky stuff to handle multi-line commit messages (I like to store lots of details as my subject messages tend to be a bit vague!). Other than that, if you’re familiar with PHP, the above should be pretty self-explanatory. If it’s not, hit the comments and I’ll explain things.

I’ve only been using this a little while, but it seems to work very well. If you use it and stumble across any bugs, I’d love to know about them!

Update: I’ve today realised that git log only logs the currently-selected branch, or master on a bare repo so I’ve added the –all switch to git log so I can get the logs for every branch. Most of it’s just “Merged blah” but that means it can be filtered easily and I’d rather have everything and need to filter than be missing something important.

PHP 5.3, iconv, OSX, Symbols Missing _libiconv

I’m not sure how many people will have this problem, but I did so this will remind me.

PHP 5.3 release, for some reason, does not like my default install of iconv. Firstly, I made the grave mistake of failing to point PHP properly at an install in /usr/local/, so replaced my OS installation of iconv with a new one. This was all well and good until I started opening apps that needed specific parts of OSX’s default iconv. So, an OSX restore later, we’re at square one. PHP keeps telling me various crap when I’m trying to compile that it didn’t work with iconv because it’s the wrong version, but would fail during make because it couldn’t find the correct symbols.

Die, spaghetti-code, die.

3610270537

I never really got what spaghetti code was until today. That might sound dumb, but it’s a term that is quite widely-used in different contexts so it can be an odd one.

Today, I have been working with code that is devoid of any design, regard for future developers or useful documentation. Like so much PHP, the end result works, so it’s great. Except it’s not.

Imagine each line above is a function or method call, which has its own integral part to play in the execution of a web page (in this case). The entry and exit points are single entities, but what goes on in between is nothing short of scary!

3610270663

Ideally, I like to see code the main vein of which is there at the start and the end of the app’s execution. Logic can branch from this as far as it wants to get what it needs, but it’s ultimately serving a purpose for the main line. The more I delve into OOP in many languages, the more I realise that procedural code is quite liable to spaghetti, whereas OO is not. That’s not to say it’s impossible, but it’s easier to have one method that sets everything in motion, configures your program and then receives and redirects input in OO than it is procedurally.

Since I started properly getting into OO, I started to liken the execution cycle of a procedural program to a glass of water falling. Everything starts from the same point, but over time everything splays out and does its own thing. Debugging procedural code, therefore, is analogous to blindly cleaning the aforementioned spillage – there is a good chance that the part of the spillage you’re working on has nothing to do with what you’re actually trying to achieve – no matter how relevant it looks!

Unfortunately, I blame PHP for the natural progression of spaghetti code from procedural to half-assed-OO. It’s so easy to get things done without really knowing what you’re doing that it’s actually borderline dangerous. A mishmash of procedural and OO is the absolute worst thing to debug, as it normally combines the horrible execution cycle of procedural apps with the potentially confusing trail of inheritance. Let me tell those who don’t know – it is VERY obvious when you’re working with code written by someone who does not understand inheritance (and I don’t mean what it is, I mean *why* it is) and it almost always makes their half-baked bastardisation of OO impossible to work with.

Have I had a difficult day? You could say that.

299: mitb: razor

3391178172

I’m really enjoying playing with this Raynox. You need a steady hand, though, and after 3 hours of trying to get the PHP 5.3 RC to play nice with MySQL (lucky for me all of my local databases are for development only and I have all the create scripts!) my composure is a little, well, I have crappy composure anyway.

This weekend will hopefully be fun. I want to go and see Hsiao Ning, but I bet the weather will suck.

Working days

I’m all for reusable code. Whenever I’m coding, I’m constantly wondering if there’s a way that I can further abstract what I’ve done so I can use it somewhere else. Because of this, I came across a problem yesterday that got the better of me a little.

I’m working on a project at the moment that requires me to have an awareness of “working” days. Specifically, when a product is sold, the customer has three working days to cancel their order. This would be all well and good if there was 365 working days in the year, and all companies were the same. I started out with this assumption, but was quickly confounded.

Before I explain how I solved (well, only partially – it would require a calendar app to fully solve it to my satisfaction!) it, I’ll explain a little about how some classes come to be in my workflow.