24

I'm looking for a way to read a Git commit message with PHP. I suspect I need to use a Git hook, but I've never worked with them before, so I need a push in the right direction. Specifically, I'd like to implement the following process:

  • A PHP script is executed automatically after every commit
  • The script captures the Git username, the time of the commit, and the commit content

If at all possible, I'd like to stick with pure PHP. If there are tutorials or references that you could point out, that would be a huge help.

VirtuosiMedia
  • 52,016
  • 21
  • 93
  • 140
  • 1
    Also, [this](https://github.com/kore/php-commit-hooks) looks pretty cool. – cambraca Jan 31 '12 at 22:26
  • I just answered a similar question but it was related to github http://stackoverflow.com/a/9150437/706466 – Pawel Dubiel Feb 05 '12 at 23:32
  • Thanks @Pawel. That would be great for a public-facing site and assuming GitHub, but I'm actually looking to do it on a localhost. – VirtuosiMedia Feb 06 '12 at 00:39
  • @VirtuosiMedia On localhost you can navigate to /.git/hooks and rename post-commit.sample to post-commit and then put inside #!/usr/bin/php There are also other hooks that may be more suitable for you. However I didn't succeed with reading anything from php://stdin inside those scripts. If you find out how to do this please let me know :-). Here is explained which hook is run when http://book.git-scm.com/5_git_hooks.html. – Pawel Dubiel Feb 06 '12 at 01:05
  • Are you using a remote repository or just all local? – Nic Feb 06 '12 at 06:58
  • @melee - Both, but the priority is to detect it on the localhost as there isn't necessarily going to be a public address to use with GitHub's hooks. – VirtuosiMedia Feb 06 '12 at 15:07

4 Answers4

26

To get the commit hash, you can use

git rev-parse --verify HEAD 2> /dev/null

From within php:

exec('git rev-parse --verify HEAD 2> /dev/null', $output);
$hash = $output[0];

You can get the commit message, author and time (though - the time will simply be "now" if it's run as part of a post-commit hook) with:

exec("git show $hash", $output);

If it's not obvious, whatever you do with php is simply going to be a wrapper around the things you'd do with git on the cli - I.e. any "how can I do x with git from php" is just exec('the git answer', $output)

AD7six
  • 63,116
  • 12
  • 91
  • 123
  • Does this need some special library, configuration or other effort? I am getting an empty array as a content of `$output` and an error (undefined index) when trying to examine `$output[0]`. – trejder Jul 23 '23 at 14:33
7

As far as using PHP to extract the correct commit:

Indefero

There is a project called Indefero that is a PHP forge tool that has an SCM connector for git. You could easily use their git class as an API for yourself. You can just grab the git class and the SCM class.

I have, for example, pulled out two methods from the class below, which I think are the most relevant to you so you can see how they work.

Get a changelog list: getChangeLog()

/**
 * Get latest changes.
 *
 * @param string Commit ('HEAD').
 * @param int Number of changes (10).
 * @return array Changes.
 */
public function getChangeLog($commit='HEAD', $n=10)
{
    if ($n === null) $n = '';
    else $n = ' -'.$n;
    $cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log%s --date=iso --pretty=format:\'%s\' %s',
                   escapeshellarg($this->repo), $n, $this->mediumtree_fmt,
                   escapeshellarg($commit));
    $out = array();
    $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
    self::exec('IDF_Scm_Git::getChangeLog', $cmd, $out);
    return self::parseLog($out);
}

Get a particular commit: getCommit()

/**
 * Get commit details.
 *
 * @param string Commit
 * @param bool Get commit diff (false)
 * @return array Changes
 */
public function getCommit($commit, $getdiff=false)
{
    if ($getdiff) {
        $cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' show --date=iso --pretty=format:%s %s',
                       escapeshellarg($this->repo),
                       "'".$this->mediumtree_fmt."'",
                       escapeshellarg($commit));
    } else {
        $cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log -1 --date=iso --pretty=format:%s %s',
                       escapeshellarg($this->repo),
                       "'".$this->mediumtree_fmt."'",
                       escapeshellarg($commit));
    }
    $out = array();
    $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
    self::exec('IDF_Scm_Git::getCommit', $cmd, $out, $ret);
    if ($ret != 0 or count($out) == 0) {
        return false;
    }
    if ($getdiff) {
        $log = array();
        $change = array();
        $inchange = false;
        foreach ($out as $line) {
            if (!$inchange and 0 === strpos($line, 'diff --git a')) {
                $inchange = true;
            }
            if ($inchange) {
                $change[] = $line;
            } else {
                $log[] = $line;
            }
        }
        $out = self::parseLog($log);
        $out[0]->diff = implode("\n", $change);
    } else {
        $out = self::parseLog($out);
        $out[0]->diff = '';
    }

    $out[0]->branch = implode(', ', $this->inBranches($commit, null));
    return $out[0];
}

VersionControl_Git from PEAR

There is also a library in PEAR called VersionControl_Git that would be helpful in this situation and is documented.

Treffynnon
  • 21,365
  • 6
  • 65
  • 98
4

As @Pawel mentioned, you're going to want to work with hooks on this:

On localhost you can navigate to /.git/hooks and rename post-commit.sample to post-commit and then put inside #!/usr/bin/php There are also other hooks that may be more suitable for you.

Git will look for the post-commit hook after you've commit and automatically run anything inside.

What you're going to want to do here depends on the task, but I'd suggest curling the script - here's where things get interesting.

In order to extract the information you're looking for, you're going to want to use git log -1 - that should pull back the latest commit.

More specifically, you'll want to build your commit using the --pretty=format toggle, which can output the latest commit with the info you need. Check out this string:

git log -1 --pretty=format:'%h - %cn (%ce) - %s (%ci)'

This would return most of the things you are looking for. Check out the git-log page to see all of the different % variables that you can use with --pretty=format:. Once you've made the string that you'd like, you can either POST those via cURL to a PHP script, or run a PHP script and use shell_exec(git log -1 --pretty=format:'%h - %cn (%ce) - %s (%ci)') to work with the commit message inline.

Nic
  • 13,287
  • 7
  • 40
  • 42
2

I was digging in the same question and found out a way to do it faster and easier.

To get just the commit message you could use

git rev-list --format=%B --max-count=1 HEAD

Obviously HEAD may be replaced with any commit hash.

It will output something like

commit 4152601a42270440ad52680ac7c66ba87a506174
Improved migrations and models relations

Second line is what you need.

phaberest
  • 3,140
  • 3
  • 32
  • 40