2 /* GitCommand.class.php - phpgitweb
3 * Copyright (C) 2011-2012 Philipp Kreil (pk910)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 private static $git = null;
21 private static $counter = 0;
23 private static function command_header() {
26 if(GitConfig::GIT_EXEC)
27 self::$git = GitConfig::GIT_EXEC;
29 self::$git = str_replace(array("\n", "\r"), array("", ""), `which git`);
31 trigger_error("Can not find git executable.", E_USER_ERROR);
35 $command = self::$git;
40 private static function git_execute($params, $git_path = null) {
44 $args = array("--git-dir=".$git_path);
45 $args = array_merge($args, $params);
49 $command = self::command_header();
50 foreach($args as $arg) {
51 $command .= ' '.escapeshellarg($arg);
54 exec($command, $result);
56 return implode("\n", $result);
59 public static function core_version() {
60 $args = array("--version");
61 $version = self::git_execute($args);
62 preg_match("/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/i", $version, $result);
66 public static function last_activity($git_path, $ref = null) {
67 $args = array("for-each-ref", "--format=%(committer)", "--sort=-committerdate", "--count=1");
70 $age = self::git_execute($args, $git_path);
71 if(preg_match("/[0-9]{9,}/i", $age, $result))
77 private static function parse_commit($commit_data) {
79 $rev_lines = explode("\n", str_replace("\r", "", $commit_data));
80 $commit['id'] = $rev_lines[0];
81 $commit['parent'] = array();
82 if(!preg_match("/^[a-f0-9]{40}$/i", $commit['id']))
84 foreach($rev_lines as $rev_line) {
85 if(substr($rev_line, 0, 4) == " ") {
86 if(array_key_exists('text', $commit))
87 $commit['text'] .= "\n";
90 $commit['text'] .= substr($rev_line, 4);
92 $opt = explode(" ", $rev_line, 2);
94 $commit['tree'] = $opt[1];
95 else if($opt[0] == "parent")
96 $commit['parent'][] = $opt[1];
97 else if($opt[0] == "author") {
98 preg_match('/(.*) <([^>]*)> ([0-9]*) ([+\-0-9]{5})/i', $opt[1], $matches);
99 $commit['author'] = $matches[1];
100 $commit['author_mail'] = $matches[2];
101 $commit['author_time'] = $matches[3];
102 $commit['author_timezone'] = $matches[4];
103 } else if($opt[0] == "committer") {
104 preg_match('/(.*) <([^>]*)> ([0-9]*) ([+\-0-9]{5})/i', $opt[1], $matches);
105 $commit['committer'] = $matches[1];
106 $commit['committer_mail'] = $matches[2];
107 $commit['committer_time'] = $matches[3];
108 $commit['committer_timezone'] = $matches[4];
115 public static function get_commits($git_path, $head, $maxcount, $skip, $file = null) {
116 $args = array("rev-list", "--header", "--max-count=".$maxcount, "--skip=".$skip, ($head ? $head : "--all"), "--");
119 $commit_list = self::git_execute($args, $git_path);
121 foreach(explode("\000", $commit_list) as $commit) {
123 $commits[] = self::parse_commit($commit);
128 public static function get_commit($git_path, $commit_id) {
129 $args = array("rev-list", "--header", "--max-count=1", ($commit_id ? $commit_id : "--all"), "--");
130 $commit_data = self::git_execute($args, $git_path);
131 $commit = self::parse_commit($commit_data);
135 public static function get_hash($git_path, $ref) {
136 $args = array("rev-parse", "--verify", "-q", $ref);
137 $result = self::git_execute($args, $git_path);
138 if(preg_match("#([a-f0-9]{40})#i", $result, $match))
143 private static function parse_difftree($line) {
145 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)) {
146 $entry['parents'] = 1;
147 $entry['from_mode'] = $matches[1];
148 $entry['to_mode'] = $matches[2];
149 $entry['from_id'] = $matches[3];
150 $entry['to_id'] = $matches[4];
151 $entry['status'] = $matches[5];
152 $entry['similarity'] = $matches[6];
153 $entry['file'] = $matches[7];
154 if($entry['status'] == 'R' || $entry['status'] == 'C') { //renamed or copied
155 $files = explode("\t", $entry['file']);
156 $entry['from_file'] = $files[0];
157 $entry['to_file'] = $files[1];
159 $entry['from_file'] = $entry['to_file'] = $entry['file'];
160 } else if(preg_match('/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$/i', $line, $matches)) {
161 $entry['parents'] = strlen($matches[1]);
162 $from_modes = explode(" ", $matches[2]);
163 $entry['from_mode'] = array_slice($from_modes, 1, -1);
164 $entry['to_mode'] = $from_modes[0];
165 $from_ids = explode(" ", $matches[3]);
166 $entry['from_id'] = array_slice($from_ids, 1, -1);
167 $entry['to_id'] = $from_ids[0];
168 $entry['status'] = str_split($matches[4]);
169 $entry['file'] = $entry['from_file'] = $entry['to_file'] = $matches[5];
174 public static function get_commit_changes($git_path, $commit_id, $parents) {
175 $args = array("diff-tree", "-r", "--no-commit-id");
176 switch(GitConfig::DETECT_RENAME_LEVEL) {
185 $args[] = "--find-copies-harder";
188 if(GitConfig::DETECT_REWRITES)
190 if(is_array($parents) && count($parents) > 1)
192 else if(is_array($parents) && count($parents) == 1)
193 $args[] = $parents[0];
196 $args[] = $commit_id;
198 $result = self::git_execute($args, $git_path);
200 foreach(explode("\n", $result) as $line) {
203 $tree[] = self::parse_difftree($line);
208 public static function get_commit_diff($git_path, $commit_id, $parents) {
209 $args = array("diff-tree", "-r", "--no-commit-id", "--patch-with-raw", "--full-index");
210 switch(GitConfig::DETECT_RENAME_LEVEL) {
219 $args[] = "--find-copies-harder";
222 if(GitConfig::DETECT_REWRITES)
224 if(is_array($parents) && count($parents) > 1)
226 else if(is_array($parents) && count($parents) == 1)
227 $args[] = $parents[0];
230 $args[] = $commit_id;
232 $result = self::git_execute($args, $git_path);
236 $parse_difftree = true;
237 foreach(explode("\n", $result) as $line) {
238 if($line == "" && $parse_difftree)
239 $parse_difftree = false;
240 else if($parse_difftree)
241 $tree[] = self::parse_difftree($line);
243 if(preg_match('/^diff/i', $line)) {
253 $diff_data['tree'] = $tree;
254 return array('tree' => $tree, 'diffs' => $diffs);