started diff parser
[phpgitweb.git] / htdocs / lib / PageClasses.diff.class.php
diff --git a/htdocs/lib/PageClasses.diff.class.php b/htdocs/lib/PageClasses.diff.class.php
new file mode 100644 (file)
index 0000000..ff53a20
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+/* PageClasses.diff.class.php - phpgitweb
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+
+class diff {
+       private $commit, $tree, $diff, $data = array();
+       
+       public function __construct($commit, $tree, $diff) {
+               $this->commit = $commit;
+               $this->tree = $tree;
+               $this->diff = $diff;
+       }
+       
+       public static function generate_html($commit, $diff_data) {
+               $diff_id = 0;
+               $patch_id = 1;
+               $html = array();
+               foreach($diff_data['tree'] as $tree) {
+                       do {
+                               if(array_key_exists($diff_id, $diff_data['diffs']))
+                                       $diff = $diff_data['diffs'][$diff_id];
+                               else
+                                       $diff = null;
+                               if($diff && preg_match('/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/i', $diff[0], $matches))
+                                       $to_file = $matches[2];
+                               else if($diff && preg_match('/^diff --(cc|combined) ("?.*"?)$/i', $diff[0], $matches))
+                                       $to_file = $matches[2];
+                               else
+                                       $to_file = null;
+                               if(!$to_file || strtolower($to_file) != $tree['to_file']) {
+                                       if($tree['parents'] > 1) {
+                                               //jump over missing diffs
+                                               $diffobj = new diff($commit, $tree, null);
+                                               $html[] = $diffobj->gen_contentprovider($patch_id);
+                                               $patch_id++;
+                                               break;
+                                       }
+                                       if(!$to_file)
+                                               break;
+                               }
+                               $diffobj = new diff($commit, $tree, $diff);
+                               $html[] = $diffobj->gen_contentprovider($patch_id);
+                               $diff_id++;
+                               do {
+                                       if(!array_key_exists($diff_id, $diff_data['diffs']))
+                                               break;
+                                       $diff = $diff_data['diffs'][$diff_id];
+                                       if(preg_match('/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/i', $diff[0], $matches))
+                                               $to_file = $matches[2];
+                                       else if(preg_match('/^diff --(cc|combined) ("?.*"?)$/i', $diff[0], $matches))
+                                               $to_file = $matches[2];
+                                       else
+                                               break;
+                                       if(!array_key_exists('to_file', $tree))
+                                               $tree['to_file'] = $tree['file'];
+                                       if(strtolower($to_file) != $tree['to_file']) 
+                                               break;
+                                       $diffobj = new diff($commit, $tree, $diff);
+                                       $html[] = $diffobj->gen_contentprovider(null);
+                                       $diff_id++;
+                               } while(true);
+                               $patch_id++;
+                               break;
+                       } while(true);
+               }
+               return $html;
+       }
+       
+       private function gen_diffcmd_header() {
+               $header = array();
+               if(!$this->diff) {
+                       $header[] = "diff --cc ";
+                       $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
+               } else if($this->tree['parents'] > 1) {
+                       preg_match('!^(diff (.*?) )"?.*$!', $this->diff[0], $match);
+                       $header[] = $match[1];
+                       if(preg_match('/^[0]{40}$/', $this->tree['to_id']))
+                               $header[] = $this->tree['to_file'];
+                       else
+                               $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
+               } else {
+                       preg_match('!^(diff (.*?) )"?a/.*$!', $this->diff[0], $match);
+                       $header[] = $match[1];
+                       if(preg_match('/^[0]{40}$/', $this->tree['from_id']))
+                               $header[] = 'a/'.$this->tree['file'];
+                       else
+                               $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][0], 'file' => $this->tree['from_file'], 'path' => 'a/'));
+                       $header[] = ' ';
+                       if(preg_match('/^[0]{40}$/', $this->tree['to_id']))
+                               $header[] = 'b/'.$this->tree['file'];
+                       else
+                               $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => 'b/'));
+               }
+               return $header;
+       }
+       
+       private function gen_index_header($line) {
+               $header = array();
+               if(preg_match('/^((copy|rename) from ).*$/', $line, $matches)) {
+                       $header[] = $matches[1];
+                       if(array_key_exists('copy', $this->data))
+                               $this->data['copy']++;
+                       else
+                               $this->data['copy'] = 0;
+                       $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][$this->data['copy']], 'file' => $this->tree['from_file'], 'path' => ''));
+               } else if(preg_match('/^((copy|rename) to ).*$/', $line, $matches)) {
+                       $header[] = $matches[1];
+                       $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
+               } else if(preg_match('/^(index )[0-9a-fA-F]{40},[0-9a-fA-F]{40}/', $line, $matches)) { //combined diff
+                       $header[] = $matches[1];
+                       $parent_id = 0;
+                       foreach($this->tree['from_id'] as $from_id) {
+                               if($parent_id)
+                                       $header[] = ',';
+                               if(preg_match('/^[0]{40}$/', $from_id))
+                                       $header[] = '0000000';
+                               else
+                                       $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['parent'][$parent_id], 'file' => $this->tree['from_file'], 'id' => substr($from_id, 0, 7)));
+                               $parent_id++;
+                       }
+               } else if(preg_match('/^(index )([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/', $line, $matches)) {
+                       $header[] = $matches[1];
+                       if(preg_match('/^[0]{40}$/', $matches[2]))
+                               $header[] = '0000000';
+                       else
+                               $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['parent'][0], 'file' => $this->tree['from_file'], 'id' => substr($matches[2], 0, 7)));
+                       $header[] = '..';
+                       if(preg_match('/^[0]{40}$/', $matches[3]))
+                               $header[] = '0000000';
+                       else
+                               $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'id' => substr($matches[3], 0, 7)));
+               } else
+                       $header[] = $line;
+               if(preg_match('/([0-7]{6})$/', $line, $matches)) {
+                       $ftype = Tools::get_filetype($matches[1], true);
+                       $header[] = new ContentProvider('commitdiff', 'patch_fileinfo', array('info' => $ftype));
+               }
+               return $header;
+       }
+       
+       private function gen_contentprovider($patch_id) {
+               if(!$this->diff)
+                       $pageclass = 'patch_nodiff';
+               else if(!$patch_id)
+                       $pageclass = 'patch_continued';
+               else
+                       $pageclass = 'patch_normal';
+               $page = new ContentProvider('commitdiff', $pageclass);
+               $page->set('diffcmd', $this->gen_diffcmd_header());
+               if(!$this->diff)
+                       return $page;
+               $is_header = true;
+               $first_header = true;
+               for($i = 1; $i < count($this->diff); $i++) {
+                       $line = $this->diff[$i];
+                       if($is_header) {
+                               if(preg_match('/^--- |^diff /', $line))
+                                       $is_header = false;
+                               else {
+                                       if(!$first_header)
+                                               $page->append('header', '<br />');
+                                       $page->append('header', $this->gen_index_header($line));
+                                       $first_header = false;
+                                       continue;
+                               }
+                       }
+                       $lineobj = new ContentProvider('commitdiff', 'patch_diffline');
+                       //if(preg_match('', $line))
+               }
+               if($is_header)
+                       $page->set('patch', '');
+               
+               return $page;
+       }
+       
+}
+
+?>
\ No newline at end of file