finished commitdiff page
[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                 if($patch_id == 1) {
81                         if(count($commit['parent']) > 1)
82                                 $html[] = new ContentProvider('commitdiff', 'patch_nopatch_merge');
83                         else
84                                 $html[] = new ContentProvider('commitdiff', 'patch_nopatch_simple');
85                 }
86                 return $html;
87         }
88         
89         private function gen_diffcmd_header() {
90                 $header = array();
91                 if(!$this->diff) {
92                         $header[] = "diff --cc ";
93                         $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
94                 } else if($this->tree['parents'] > 1) {
95                         preg_match('!^(diff (.*?) )"?.*$!', $this->diff[0], $match);
96                         $header[] = $match[1];
97                         if(preg_match('/^[0]{40}$/', $this->tree['to_id']))
98                                 $header[] = $this->tree['to_file'];
99                         else
100                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
101                 } else {
102                         preg_match('!^(diff (.*?) )"?a/.*$!', $this->diff[0], $match);
103                         $header[] = $match[1];
104                         if(preg_match('/^[0]{40}$/', $this->tree['from_id']))
105                                 $header[] = 'a/'.$this->tree['file'];
106                         else
107                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][0], 'file' => $this->tree['from_file'], 'path' => 'a/'));
108                         $header[] = ' ';
109                         if(preg_match('/^[0]{40}$/', $this->tree['to_id']))
110                                 $header[] = 'b/'.$this->tree['file'];
111                         else
112                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => 'b/'));
113                 }
114                 return $header;
115         }
116         
117         private function gen_index_header($line) {
118                 $header = array();
119                 if(preg_match('/^((copy|rename) from ).*$/', $line, $matches)) {
120                         $header[] = $matches[1];
121                         if(array_key_exists('copy', $this->data))
122                                 $this->data['copy']++;
123                         else
124                                 $this->data['copy'] = 0;
125                         $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['parent'][$this->data['copy']], 'file' => $this->tree['from_file'], 'path' => ''));
126                 } else if(preg_match('/^((copy|rename) to ).*$/', $line, $matches)) {
127                         $header[] = $matches[1];
128                         $header[] = new ContentProvider('commitdiff', 'patch_link_blob', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'path' => ''));
129                 } else if(preg_match('/^(index )[0-9a-fA-F]{40},[0-9a-fA-F]{40}/', $line, $matches)) { //combined diff
130                         $header[] = $matches[1];
131                         $parent_id = 0;
132                         foreach($this->tree['from_id'] as $from_id) {
133                                 if($parent_id)
134                                         $header[] = ',';
135                                 if(preg_match('/^[0]{40}$/', $from_id))
136                                         $header[] = '0000000';
137                                 else
138                                         $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)));
139                                 $parent_id++;
140                         }
141                 } else if(preg_match('/^(index )([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/', $line, $matches)) {
142                         $header[] = $matches[1];
143                         if(preg_match('/^[0]{40}$/', $matches[2]))
144                                 $header[] = '0000000';
145                         else
146                                 $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)));
147                         $header[] = '..';
148                         if(preg_match('/^[0]{40}$/', $matches[3]))
149                                 $header[] = '0000000';
150                         else
151                                 $header[] = new ContentProvider('commitdiff', 'patch_link_blob_id', array('hash' => $this->commit['id'], 'file' => $this->tree['to_file'], 'id' => substr($matches[3], 0, 7)));
152                 } else
153                         $header[] = $line;
154                 if(preg_match('/([0-7]{6})$/', $line, $matches)) {
155                         $ftype = Tools::get_filetype($matches[1], true);
156                         $header[] = new ContentProvider('commitdiff', 'patch_fileinfo', array('info' => $ftype));
157                 }
158                 return $header;
159         }
160         
161         private function format_line($line) {
162                 $lineobj = new ContentProvider('commitdiff', 'patch_diffline');
163                 $class = "";
164                 if($this->tree['parents'] > 1) {
165                         $prefix = substr($line, 0, $this->tree['parents']);
166                         if(preg_match('/@/', $prefix))
167                                 $class = " chunk_header";
168                         else if(preg_match('/\\/', $prefix))
169                                 $class = " incomplete";
170                         else if(preg_match('/+/', $prefix))
171                                 $class = " add";
172                         else if(preg_match('/-/', $prefix))
173                                 $class = " rem";
174                 } else {
175                         $prefix = substr($line, 0, 1);
176                         if($prefix == '@')
177                                 $class = " chunk_header";
178                         else if($prefix == '\\')
179                                 $class = " incomplete";
180                         else if($prefix == '+')
181                                 $class = " add";
182                         else if($prefix == '-')
183                                 $class = " rem";
184                 }
185                 $lineobj->set('class', $class);
186                 $line = Tools::replaceTabs($line);
187                 $lineobj->set('line', htmlentities($line));
188                 
189                 return $lineobj;
190         }
191         
192         private function gen_contentprovider($patch_id) {
193                 if(!$this->diff)
194                         $pageclass = 'patch_nodiff';
195                 else if(!$patch_id)
196                         $pageclass = 'patch_continued';
197                 else
198                         $pageclass = 'patch_normal';
199                 $page = new ContentProvider('commitdiff', $pageclass);
200                 $page->set('diffcmd', $this->gen_diffcmd_header());
201                 $page->set('patch_id', $patch_id);
202                 if(!$this->diff)
203                         return $page;
204                 $is_header = true;
205                 $first_header = true;
206                 for($i = 1; $i < count($this->diff); $i++) {
207                         $line = $this->diff[$i];
208                         if($is_header) {
209                                 if(preg_match('/^--- |^diff /', $line))
210                                         $is_header = false;
211                                 else {
212                                         if(!$first_header)
213                                                 $page->append('header', '<br />');
214                                         $page->append('header', $this->gen_index_header($line));
215                                         $first_header = false;
216                                         continue;
217                                 }
218                         }
219                         $page->append('patch', $this->format_line($line));
220                         
221                 }
222                 if($is_header)
223                         $page->set('patch', '');
224                 
225                 return $page;
226         }
227         
228 }
229
230 ?>