started diff parser
[phpgitweb.git] / htdocs / lib / PageClasses.diff.class.php
1 <?php
2 /* PageClasses.diff.class.php - phpgitweb
3  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
4  * 
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.
9  * 
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.
14  * 
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/>. 
17  */
18
19 class diff {
20         private $commit, $tree, $diff, $data = array();
21         
22         public function __construct($commit, $tree, $diff) {
23                 $this->commit = $commit;
24                 $this->tree = $tree;
25                 $this->diff = $diff;
26         }
27         
28         public static function generate_html($commit, $diff_data) {
29                 $diff_id = 0;
30                 $patch_id = 1;
31                 $html = array();
32                 foreach($diff_data['tree'] as $tree) {
33                         do {
34                                 if(array_key_exists($diff_id, $diff_data['diffs']))
35                                         $diff = $diff_data['diffs'][$diff_id];
36                                 else
37                                         $diff = null;
38                                 if($diff && preg_match('/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/i', $diff[0], $matches))
39                                         $to_file = $matches[2];
40                                 else if($diff && preg_match('/^diff --(cc|combined) ("?.*"?)$/i', $diff[0], $matches))
41                                         $to_file = $matches[2];
42                                 else
43                                         $to_file = null;
44                                 if(!$to_file || strtolower($to_file) != $tree['to_file']) {
45                                         if($tree['parents'] > 1) {
46                                                 //jump over missing diffs
47                                                 $diffobj = new diff($commit, $tree, null);
48                                                 $html[] = $diffobj->gen_contentprovider($patch_id);
49                                                 $patch_id++;
50                                                 break;
51                                         }
52                                         if(!$to_file)
53                                                 break;
54                                 }
55                                 $diffobj = new diff($commit, $tree, $diff);
56                                 $html[] = $diffobj->gen_contentprovider($patch_id);
57                                 $diff_id++;
58                                 do {
59                                         if(!array_key_exists($diff_id, $diff_data['diffs']))
60                                                 break;
61                                         $diff = $diff_data['diffs'][$diff_id];
62                                         if(preg_match('/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/i', $diff[0], $matches))
63                                                 $to_file = $matches[2];
64                                         else if(preg_match('/^diff --(cc|combined) ("?.*"?)$/i', $diff[0], $matches))
65                                                 $to_file = $matches[2];
66                                         else
67                                                 break;
68                                         if(!array_key_exists('to_file', $tree))
69                                                 $tree['to_file'] = $tree['file'];
70                                         if(strtolower($to_file) != $tree['to_file']) 
71                                                 break;
72                                         $diffobj = new diff($commit, $tree, $diff);
73                                         $html[] = $diffobj->gen_contentprovider(null);
74                                         $diff_id++;
75                                 } while(true);
76                                 $patch_id++;
77                                 break;
78                         } while(true);
79                 }
80                 return $html;
81         }
82         
83         private function gen_diffcmd_header() {
84                 $header = array();
85                 if(!$this->diff) {
86                         $header[] = "diff --cc ";
87                         $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
88                 } else if($this->tree['parents'] > 1) {
89                         preg_match('!^(diff (.*?) )"?.*$!', $this->diff[0], $match);
90                         $header[] = $match[1];
91                         if(preg_match('/^[0]{40}$/', $this->tree['to_id']))
92                                 $header[] = $this->tree['to_file'];
93                         else
94                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
95                 } else {
96                         preg_match('!^(diff (.*?) )"?a/.*$!', $this->diff[0], $match);
97                         $header[] = $match[1];
98                         if(preg_match('/^[0]{40}$/', $this->tree['from_id']))
99                                 $header[] = 'a/'.$this->tree['file'];
100                         else
101                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][0], 'file' => $this->tree['from_file'], 'path' => 'a/'));
102                         $header[] = ' ';
103                         if(preg_match('/^[0]{40}$/', $this->tree['to_id']))
104                                 $header[] = 'b/'.$this->tree['file'];
105                         else
106                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => 'b/'));
107                 }
108                 return $header;
109         }
110         
111         private function gen_index_header($line) {
112                 $header = array();
113                 if(preg_match('/^((copy|rename) from ).*$/', $line, $matches)) {
114                         $header[] = $matches[1];
115                         if(array_key_exists('copy', $this->data))
116                                 $this->data['copy']++;
117                         else
118                                 $this->data['copy'] = 0;
119                         $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][$this->data['copy']], 'file' => $this->tree['from_file'], 'path' => ''));
120                 } else if(preg_match('/^((copy|rename) to ).*$/', $line, $matches)) {
121                         $header[] = $matches[1];
122                         $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
123                 } else if(preg_match('/^(index )[0-9a-fA-F]{40},[0-9a-fA-F]{40}/', $line, $matches)) { //combined diff
124                         $header[] = $matches[1];
125                         $parent_id = 0;
126                         foreach($this->tree['from_id'] as $from_id) {
127                                 if($parent_id)
128                                         $header[] = ',';
129                                 if(preg_match('/^[0]{40}$/', $from_id))
130                                         $header[] = '0000000';
131                                 else
132                                         $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)));
133                                 $parent_id++;
134                         }
135                 } else if(preg_match('/^(index )([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/', $line, $matches)) {
136                         $header[] = $matches[1];
137                         if(preg_match('/^[0]{40}$/', $matches[2]))
138                                 $header[] = '0000000';
139                         else
140                                 $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)));
141                         $header[] = '..';
142                         if(preg_match('/^[0]{40}$/', $matches[3]))
143                                 $header[] = '0000000';
144                         else
145                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'id' => substr($matches[3], 0, 7)));
146                 } else
147                         $header[] = $line;
148                 if(preg_match('/([0-7]{6})$/', $line, $matches)) {
149                         $ftype = Tools::get_filetype($matches[1], true);
150                         $header[] = new ContentProvider('commitdiff', 'patch_fileinfo', array('info' => $ftype));
151                 }
152                 return $header;
153         }
154         
155         private function gen_contentprovider($patch_id) {
156                 if(!$this->diff)
157                         $pageclass = 'patch_nodiff';
158                 else if(!$patch_id)
159                         $pageclass = 'patch_continued';
160                 else
161                         $pageclass = 'patch_normal';
162                 $page = new ContentProvider('commitdiff', $pageclass);
163                 $page->set('diffcmd', $this->gen_diffcmd_header());
164                 if(!$this->diff)
165                         return $page;
166                 $is_header = true;
167                 $first_header = true;
168                 for($i = 1; $i < count($this->diff); $i++) {
169                         $line = $this->diff[$i];
170                         if($is_header) {
171                                 if(preg_match('/^--- |^diff /', $line))
172                                         $is_header = false;
173                                 else {
174                                         if(!$first_header)
175                                                 $page->append('header', '<br />');
176                                         $page->append('header', $this->gen_index_header($line));
177                                         $first_header = false;
178                                         continue;
179                                 }
180                         }
181                         $lineobj = new ContentProvider('commitdiff', 'patch_diffline');
182                         //if(preg_match('', $line))
183                 }
184                 if($is_header)
185                         $page->set('patch', '');
186                 
187                 return $page;
188         }
189         
190 }
191
192 ?>