function git_read_commit($proj, $head)
{
    $obj = $proj->getObject($head);
    if ($obj->getType() != Git::OBJ_COMMIT) {
        return;
    }
    $commit = array();
    $commit['id'] = sha1_hex($obj->getName());
    $parents = array();
    foreach ($obj->parents as $par) {
        $parents[] = sha1_hex($par);
    }
    $commit['parents'] = $parents;
    if (isset($parents[0])) {
        $commit['parent'] = $parents[0];
    }
    $commit['tree'] = sha1_hex($obj->tree);
    $commit['author'] = sprintf("%s <%s>", $obj->author->name, $obj->author->email);
    $commit['author_epoch'] = $obj->author->time;
    $commit['author_tz'] = sprintf("%+03d%02d", $obj->author->offset / 3600, abs($obj->author->offset % 3600 / 60));
    $commit['author_name'] = $obj->author->name;
    $commit['committer'] = sprintf("%s <%s>", $obj->committer->name, $obj->committer->email);
    $commit['committer_epoch'] = $obj->committer->time;
    $commit['committer_tz'] = sprintf("%+03d%02d", $obj->committer->offset / 3600, abs($obj->committer->offset % 3600 / 60));
    $commit['committer_name'] = $obj->committer->name;
    $commit['title'] = $obj->summary;
    if (strlen($obj->summary) > GITPHP_TRIM_LENGTH) {
        $commit['title_short'] = substr($obj->summary, 0, GITPHP_TRIM_LENGTH) . "...";
    } else {
        $commit['title_short'] = $obj->summary;
    }
    $comment = explode("\n", $obj->summary . "\n" . $obj->detail);
    $commit['comment'] = $comment;
    $age = (int) (time() - $commit['committer_epoch']);
    $commit['age'] = $age;
    $commit['age_string'] = age_string($age);
    date_default_timezone_set("UTC");
    $age_class = $age <= 7200 ? 'age0' : ($age >= 7200 && $age <= 172800 ? 'age1' : ($age >= 172800 ? 'age2' : 'noage'));
    $commit['age_class'] = $age_class;
    if ($age > 60 * 60 * 24 * 7 * 2) {
        $commit['age_string_date'] = date("Y-m-d", $commit['committer_epoch']);
        $commit['age_string_age'] = $commit['age_string'];
    } else {
        $commit['age_string_date'] = $commit['age_string'];
        $commit['age_string_age'] = date("Y-m-d", $commit['committer_epoch']);
    }
    return $commit;
}
function git_read_ref($project, $ref_id, $ref_file)
{
    $hash = $project->revParse(trim($ref_id));
    $type = git_get_type($project, $hash);
    if (!$type) {
        return null;
    }
    $ref_item = array();
    $ref_item['type'] = $type;
    $ref_item['id'] = $ref_id;
    $ref_item['epoch'] = 0;
    $ref_item['age_string'] = "unknown";
    if ($type == "tag") {
        $tag = git_read_tag($project, $hash);
        $ref_item['comment'] = $tag['comment'];
        if ($tag['type'] == "commit") {
            $co = git_read_commit($project, sha1_bin($tag['object']));
            $ref_item['epoch'] = $co['committer_epoch'];
            $ref_item['age_string'] = $co['age_string'];
            $ref_item['age'] = $co['age'];
        } else {
            if (isset($tag['epoch'])) {
                $age = time() - $tag['epoch'];
                $ref_item['epoch'] = $tag['epoch'];
                $ref_item['age_string'] = age_string($age);
                $ref_item['age'] = $age;
            }
        }
        $ref_item['reftype'] = $tag['type'];
        $ref_item['name'] = $tag['name'];
        $ref_item['refid'] = $tag['object'];
    } else {
        if ($type == "commit") {
            $co = git_read_commit($project, $hash);
            $ref_item['reftype'] = "commit";
            $ref_item['name'] = $ref_file;
            $ref_item['title'] = $co['title'];
            $ref_item['refid'] = $ref_id;
            $ref_item['epoch'] = $co['committer_epoch'];
            $ref_item['age_string'] = $co['age_string'];
            $ref_item['age'] = $co['age'];
        }
    }
    return $ref_item;
}
function git_read_commit($proj, $head)
{
    //Purify html output
    $purifier = Codendi_HTMLPurifier::instance();
    $lines = git_read_revlist($proj, $head, 1, NULL, TRUE, TRUE);
    if (!$lines[0] || !preg_match("/^[0-9a-fA-F]{40}/", $lines[0])) {
        return null;
    }
    $commit = array();
    $tok = strtok($lines[0], " ");
    $commit['id'] = $tok;
    $tok = strtok(" ");
    $parents = array();
    while ($tok !== false) {
        $parents[] = $tok;
        $tok = strtok(" ");
    }
    $commit['parents'] = $parents;
    if (isset($parents[0])) {
        $commit['parent'] = $parents[0];
    }
    $comment = array();
    foreach ($lines as $i => $line) {
        if (preg_match("/^tree ([0-9a-fA-F]{40})\$/", $line, $regs)) {
            $commit['tree'] = $regs[1];
        } else {
            if (preg_match("/^author (.*) ([0-9]+) (.*)\$/", $line, $regs)) {
                $commit['author'] = $purifier->purify($regs[1], CODENDI_PURIFIER_BASIC_NOBR, $_REQUEST['group_id']);
                $commit['author_epoch'] = $regs[2];
                $commit['author_tz'] = $regs[3];
                if (preg_match("/^([^<]+) </", $commit['author'], $r)) {
                    $commit['author_name'] = $r[1];
                } else {
                    $commit['author_name'] = $commit['author'];
                }
            } else {
                if (preg_match("/^committer (.*) ([0-9]+) (.*)\$/", $line, $regs)) {
                    $commit['committer'] = $purifier->purify($regs[1], CODENDI_PURIFIER_BASIC_NOBR, $_REQUEST['group_id']);
                    $commit['committer_epoch'] = $regs[2];
                    $commit['committer_tz'] = $regs[3];
                    $commit['committer_name'] = $commit['committer'];
                    $commit['committer_name'] = preg_replace("/ <.*/", "", $commit['committer_name']);
                } else {
                    $trimmed = trim($line);
                    if (strlen($trimmed) > 0 && !preg_match("/^[0-9a-fA-F]{40}/", $trimmed) && !preg_match("/^parent [0-9a-fA-F]{40}/", $trimmed)) {
                        if (!isset($commit['title'])) {
                            $commit['title'] = $trimmed;
                            if (strlen($trimmed) > GITPHP_TRIM_LENGTH) {
                                $commit['title_short'] = substr($trimmed, 0, GITPHP_TRIM_LENGTH) . "...";
                            } else {
                                $commit['title_short'] = $trimmed;
                            }
                        }
                        $trimmed = $purifier->purify($trimmed, CODENDI_PURIFIER_BASIC_NOBR, $_REQUEST['group_id']);
                        $comment[] = $trimmed;
                    }
                }
            }
        }
    }
    $commit['comment'] = $comment;
    $age = time() - $commit['committer_epoch'];
    $commit['age'] = $age;
    $commit['age_string'] = age_string($age);
    date_default_timezone_set("UTC");
    if ($age > 60 * 60 * 24 * 7 * 2) {
        $commit['age_string_date'] = date("Y-m-d", $commit['committer_epoch']);
        $commit['age_string_age'] = $commit['age_string'];
    } else {
        $commit['age_string_date'] = $commit['age_string'];
        $commit['age_string_age'] = date("Y-m-d", $commit['committer_epoch']);
    }
    return $commit;
}