started diff parser
[phpgitweb.git] / htdocs / lib / GitCommand.class.php
index 4e20281778e1d6c1ab58642f70a0e4beda3d1c4a..86174b5e6e29551eba91a7757378208d913fe9eb 100644 (file)
@@ -20,7 +20,7 @@ class GitCommand {
        private static $git = null;
        private static $counter = 0;
        
-       private static function command_header($git_path = null) {
+       private static function command_header() {
                if(!self::$git) {
                        //find git executable
                        if(GitConfig::GIT_EXEC)
@@ -33,36 +33,41 @@ class GitCommand {
                        }
                }
                $command = self::$git;
-               if($git_path)
-                       $command .= " --git-dir=".$git_path;
+               
                return $command;
        }
        
-       private static function execute($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() {
-               $command = self::command_header();
-               if(!$command)
-                       return null;
-               $command .= " --version";
-               $version = self::execute($command);
+               $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) {
-               $command = self::command_header($git_path);
-               if(!$command)
-                       return null;
-               $command .= " for-each-ref --format=%\(committer\) --sort=-committerdate --count=1";
+               $args = array("for-each-ref", "--format=%(committer)", "--sort=-committerdate", "--count=1");
                if($ref)
-                       $command .= " ".$ref;
-               $age = self::execute($command);
+                       $args[] = $ref;
+               $age = self::git_execute($args, $git_path);
                preg_match("/[0-9]{9,}/i", $age, $result);
                return $result[0];
        }
@@ -71,6 +76,9 @@ class GitCommand {
                $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))
@@ -85,15 +93,17 @@ class GitCommand {
                                else if($opt[0] == "parent")
                                        $commit['parent'][] = $opt[1];
                                else if($opt[0] == "author") {
-                                       preg_match('/(.*) <([^>]*)> ([0-9]*) [+-0-9]*/i', $opt[1], $matches);
+                                       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]*/i', $opt[1], $matches);
+                                       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];
                                }
                        }
                }
@@ -101,13 +111,10 @@ class GitCommand {
        }
        
        public static function get_commits($git_path, $head, $maxcount, $skip, $file = null) {
-               $command = self::command_header($git_path);
-               if(!$command)
-                       return null;
-               $command .= " rev-list --header --max-count=".$maxcount." --skip=".$skip." ".($head ? $head : "--all")." --";
+               $args = array("rev-list", "--header", "--max-count=".$maxcount, "--skip=".$skip, ($head ? $head : "--all"), "--");
                if($file)
-                       $command .= " ".$file;
-               $commit_list = self::execute($command);
+                       $args[] = $file;
+               $commit_list = self::git_execute($args, $git_path);
                $commits = array();
                foreach(explode("\000", $commit_list) as $commit) {
                        if($commit)
@@ -116,4 +123,132 @@ class GitCommand {
                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);
+       }
 }