. */ class GitCommand { private static $git = null; private static $counter = 0; private static function command_header() { if(!self::$git) { //find git executable if(GitConfig::GIT_EXEC) self::$git = GitConfig::GIT_EXEC; else self::$git = str_replace(array("\n", "\r"), array("", ""), `which git`); if(!self::$git) { trigger_error("Can not find git executable.", E_USER_ERROR); self::$git = "git"; } } $command = self::$git; return $command; } private static function git_execute($params, $git_path = null) { $result = array(); if($git_path) { $args = array("--git-dir=".$git_path); $args = array_merge($args, $params); } else $args = $params; $command = self::command_header(); foreach($args as $arg) { $command .= ' '.escapeshellarg($arg); } exec($command, $result); self::$counter++; return implode("\n", $result); } public static function core_version() { $args = array("--version"); $version = self::git_execute($args); preg_match("/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/i", $version, $result); return $result[0]; } public static function last_activity($git_path, $ref = null) { $args = array("for-each-ref", "--format=%(committer)", "--sort=-committerdate", "--count=1"); if($ref) $args[] = $ref; $age = self::git_execute($args, $git_path); preg_match("/[0-9]{9,}/i", $age, $result); return $result[0]; } private static function parse_commit($commit_data) { $commit = array(); $rev_lines = explode("\n", str_replace("\r", "", $commit_data)); $commit['id'] = $rev_lines[0]; $commit['parent'] = array(); if(!preg_match("/^[a-f0-9]{40}$/i", $commit['id'])) return null; foreach($rev_lines as $rev_line) { if(substr($rev_line, 0, 4) == " ") { if(array_key_exists('text', $commit)) $commit['text'] .= "\n"; else $commit['text'] = ''; $commit['text'] .= substr($rev_line, 4); } else { $opt = explode(" ", $rev_line, 2); if($opt[0] == "tree") $commit['tree'] = $opt[1]; else if($opt[0] == "parent") $commit['parent'][] = $opt[1]; else if($opt[0] == "author") { preg_match('/(.*) <([^>]*)> ([0-9]*) ([+\-0-9]{5})/i', $opt[1], $matches); $commit['author'] = $matches[1]; $commit['author_mail'] = $matches[2]; $commit['author_time'] = $matches[3]; $commit['author_timezone'] = $matches[4]; } else if($opt[0] == "committer") { preg_match('/(.*) <([^>]*)> ([0-9]*) ([+\-0-9]{5})/i', $opt[1], $matches); $commit['committer'] = $matches[1]; $commit['committer_mail'] = $matches[2]; $commit['committer_time'] = $matches[3]; $commit['committer_timezone'] = $matches[4]; } } } return $commit; } public static function get_commits($git_path, $head, $maxcount, $skip, $file = null) { $args = array("rev-list", "--header", "--max-count=".$maxcount, "--skip=".$skip, ($head ? $head : "--all"), "--"); if($file) $args[] = $file; $commit_list = self::git_execute($args, $git_path); $commits = array(); foreach(explode("\000", $commit_list) as $commit) { if($commit) $commits[] = self::parse_commit($commit); } return $commits; } public static function get_commit($git_path, $commit_id) { $args = array("rev-list", "--header", "--max-count=1", ($commit_id ? $commit_id : "--all"), "--"); $commit_data = self::git_execute($args, $git_path); $commit = self::parse_commit($commit_data); return $commit; } public static function get_hash($git_path, $ref) { $args = array("rev-parse", "--verify", "-q", $ref); $result = self::git_execute($args, $git_path); if(preg_match("#([a-f0-9]{40})#i", $result, $match)) return $match[1]; return null; } private static function parse_difftree($line) { $entry = array(); if(preg_match('/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/i', $line, $matches)) { $entry['parents'] = 1; $entry['from_mode'] = $matches[1]; $entry['to_mode'] = $matches[2]; $entry['from_id'] = $matches[3]; $entry['to_id'] = $matches[4]; $entry['status'] = $matches[5]; $entry['similarity'] = $matches[6]; $entry['file'] = $matches[7]; if($entry['status'] == 'R' || $entry['status'] == 'C') { //renamed or copied $files = explode("\t", $entry['file']); $entry['from_file'] = $files[0]; $entry['to_file'] = $files[1]; } else $entry['from_file'] = $entry['to_file'] = $entry['file']; } else if(preg_match('/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$/i', $line, $matches)) { $entry['parents'] = strlen($matches[1]); $from_modes = explode(" ", $matches[2]); $entry['from_mode'] = array_slice($from_modes, 1, -1); $entry['to_mode'] = $from_modes[0]; $from_ids = explode(" ", $matches[3]); $entry['from_id'] = array_slice($from_ids, 1, -1); $entry['to_id'] = $from_ids[0]; $entry['status'] = str_split($matches[4]); $entry['file'] = $entry['from_file'] = $entry['to_file'] = $matches[5]; } return $entry; } public static function get_commit_changes($git_path, $commit_id, $parents) { $args = array("diff-tree", "-r", "--no-commit-id"); switch(GitConfig::DETECT_RENAME_LEVEL) { case 0: $args[] = "-M"; break; case 1: $args[] = "-C"; break; case 2: $args[] = "-C"; $args[] = "--find-copies-harder"; break; } if(GitConfig::DETECT_REWRITES) $args[] = "-B"; if(is_array($parents) && count($parents) > 1) $args[] = "-c"; else if(is_array($parents) && count($parents) == 1) $args[] = $parents[0]; else $args[] = "--root"; $args[] = $commit_id; $args[] = "--"; $result = self::git_execute($args, $git_path); $tree = array(); foreach(explode("\n", $result) as $line) { if($line == "") continue; $tree[] = self::parse_difftree($line); } return $tree; } public static function get_commit_diff($git_path, $commit_id, $parents) { $args = array("diff-tree", "-r", "--no-commit-id", "--patch-with-raw", "--full-index"); switch(GitConfig::DETECT_RENAME_LEVEL) { case 0: $args[] = "-M"; break; case 1: $args[] = "-C"; break; case 2: $args[] = "-C"; $args[] = "--find-copies-harder"; break; } if(GitConfig::DETECT_REWRITES) $args[] = "-B"; if(is_array($parents) && count($parents) > 1) $args[] = "--cc"; else if(is_array($parents) && count($parents) == 1) $args[] = $parents[0]; else $args[] = "--root"; $args[] = $commit_id; $args[] = "--"; $result = self::git_execute($args, $git_path); $tree = array(); $diffs = array(); $diff = null; $parse_difftree = true; foreach(explode("\n", $result) as $line) { if($line == "" && $parse_difftree) $parse_difftree = false; else if($parse_difftree) $tree[] = self::parse_difftree($line); else { if(preg_match('/^diff/i', $line)) { if($diff) $diffs[] = $diff; $diff = array(); } $diff[] = $line; } } if($diff) $diffs[] = $diff; $diff_data['tree'] = $tree; return array('tree' => $tree, 'diffs' => $diffs); } }