added hash_base system (base refs gets stored in the _SESSION)
[phpgitweb.git] / htdocs / pages / commit.class.php
1 <?php
2 /* commit.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 difftree {
20         private $difftree_data = null;
21         
22         public function push_difftree_data($data) {
23                 $this->difftree_data = $data;
24         }
25         
26         public function generate_difftree($project, $commit, $patch_link) {
27                 $difftree = new ContentProvider('commit', 'difftree');
28                 if($this->difftree_data)
29                         $tree = $this->difftree_data;
30                 else
31                         $tree = GitCommand::get_commit_changes($project['path'], $commit['id'], $commit['parent']);
32                 $entry_count = 0;
33                 if(count($commit['parent']) > 1)
34                         $difftree->set('class', ' combined');
35                 else
36                         $difftree->set('class', '');
37                 $difftree->set('tree_count', '');
38                 foreach($tree as $entry) {
39                         $entry_count++;
40                         $difftree->append('tree', $this->tree_entry($entry_count, (($entry_count % 2) ? 'dark' : 'light'), $commit, $entry, $patch_link));
41                 }
42                 if($entry_count == 0)
43                         $difftree->set('tree', '');
44                 else if($entry_count > 10)
45                         $difftree->set('tree_count', new ContentProvider('commit', 'tree_count', array('count' => $entry_count)));
46                 return $difftree;
47         }
48         
49         private function tree_entry($diffid, $class, $commit, $entry, $patch_link) {
50                 $tree = new ContentProvider('commit', 'tree');
51                 $tree->set('class', $class);
52                 $tree->set('hash', $commit['id']);
53                 $tree->set('file', $entry['file']);
54                 
55                 if(count($commit['parent']) > 1) {
56                         if(preg_match('/^[0]{40}$/', $entry['to_id'])) //file doesn't exist in the result (child) commit
57                                 $tree->set('file', $entry['file']);
58                         else
59                                 $tree->set('file', new ContentProvider('commit', 'tree_file_link', array('file' => $entry['file'])));
60                         
61                         $tree->set('specials', '');
62                         
63                         if($patch_link)
64                                 $tree->append('merge', new ContentProvider('commit', 'tree_merge_patch_link', array("patch_marker" => "patch".$diffid)));
65                         
66                         $has_history = false;
67                         $not_deleted = false;
68                         
69                         for($i = 0; $i < count($commit['parent']); $i++) {
70                                 $status = $entry['status'][$i];
71                                 $merge = null;
72                                 
73                                 $has_history = ($has_history || ($status != 'A'));
74                                 $not_deleted = ($not_deleted || ($status != 'D'));
75                                 
76                                 switch($status) {
77                                 case 'A': //Added
78                                         $merge = new ContentProvider('commit', 'tree_merge_new');
79                                         break;
80                                 case 'D': //Deleted
81                                         $merge = new ContentProvider('commit', 'tree_merge');
82                                         $merge->set('class', '');
83                                         $blob_link = new ContentProvider('commit', 'tree_merge_blob', array('hash' => $commit['parent'][$i], 'file' => $entry['file']));
84                                         $merge->set('links', array($blob_link, ' | '));
85                                         break;
86                                 default:
87                                         $merge = new ContentProvider('commit', 'tree_merge');
88                                         if($entry['from_id'][$i] == $entry['to_id']) {
89                                                 $merge->set('class', ' nochange');
90                                                 $merge->set('links', ' | ');
91                                         } else {
92                                                 $merge->set('class', '');
93                                                 $merge->set('links', new ContentProvider('commit', 'tree_merge_diff', array('hash' => $commit['id'], 'parent' => $commit['parent'][$i], 'file' => $entry['file'], 'id' => ($i + 1))));
94                                         }
95                                 }
96                                 $tree->append('merge', $merge);
97                         }
98                         
99                         $tree->set('links', '');
100                         if($not_deleted) {
101                                 $tree->append('links', new ContentProvider('commit', 'tree_merge_blob', array('hash' => $commit['id'], 'file' => $entry['file'])));
102                                 if($has_history)
103                                         $tree->append('links', ' | ');
104                         }
105                         if($has_history)
106                                 $tree->append('links', new ContentProvider('commit', 'tree_merge_history', array('hash' => $commit['id'], 'file' => $entry['file'])));
107                         
108                 } else {
109                         $tree->set('file', new ContentProvider('commit', 'tree_file_link', array('file' => $entry['file'])));
110                         $tree->set('merge', '');
111                         
112                         $from_type = Tools::get_filetype($entry['from_mode']);
113                         $to_type = Tools::get_filetype($entry['to_mode']);
114                         
115                         $from_mode = (Tools::is_regular_file($entry['from_mode']) ? Tools::get_file_permissions($entry['from_mode']) : null);
116                         $to_mode = (Tools::is_regular_file($entry['to_mode']) ? Tools::get_file_permissions($entry['to_mode']) : null);
117                         
118                         $link_placeholders = array(
119                                         "hash" => $commit['id'],
120                                         "file" => $entry['file'],
121                                         "parent" => (count($commit['parent']) ? $commit['parent'][0] : ""),
122                                 );
123                         if($patch_link)
124                                 $tree->append('links', new ContentProvider('commit', 'tree_patch_link', array("patch_marker" => "patch".$diffid)));
125                         
126                         switch($entry['status']) {
127                         case 'A': //Added
128                                 $tree->set('specials', new ContentProvider('commit', (Tools::is_regular_file($entry['to_mode']) ? 'tree_new_file' : 'tree_new'), array('type' => $to_type, 'mode' => $to_mode)));
129                                 $tree->append('links', new ContentProvider('commit', 'tree_new_links', $link_placeholders));
130                                 break;
131                         case 'D': //Deleted
132                                 $tree->set('specials', new ContentProvider('commit', 'tree_deleted', array('type' => $from_type)));
133                                 $tree->append('links', new ContentProvider('commit', 'tree_deleted_links', $link_placeholders));
134                                 break;
135                         case 'M': //Modified
136                         case 'T': //Type changed
137                                 if($entry['from_mode'] != $entry['to_mode']) {
138                                         $modified = new ContentProvider('commit', 'tree_changed');
139                                         $tree->set('specials', $modified);
140                                         if($from_type != $to_type)
141                                                 $modified->append('changes', new ContentProvider('commit', 'tree_changed_type', array('from' => $from_type, 'to' => $to_type)));
142                                         if($from_mode != $to_mode && $to_mode) {
143                                                 if($from_mode)
144                                                         $modified->append('changes', new ContentProvider('commit', 'tree_changed_mode', array('from' => $from_mode, 'to' => $to_mode)));
145                                                 else
146                                                         $modified->append('changes', new ContentProvider('commit', 'tree_changed_mode_to', array('to' => $to_mode)));
147                                         }
148                                 } else
149                                         $tree->set('specials', '');
150                                 if(!$patch_link && $entry['from_id'] != $entry['to_id'])
151                                         $tree->append('links', new ContentProvider('commit', 'tree_changed_links_diff', $link_placeholders));
152                                 $tree->append('links', new ContentProvider('commit', 'tree_changed_links', $link_placeholders));
153                                 break;
154                         case 'R': //Renamed
155                         case 'C': //Copied
156                                 $actions = array('R' => 'tree_moved', 'C' => 'tree_copied');
157                                 $move = new ContentProvider('commit', $actions[$entry['status']]);
158                                 $tree->set('specials', $move);
159                                 $tree->set('file', $entry['to_file']);
160                                 $move->set('file', $entry['from_file']);
161                                 $move->set('hash', $commit['parent'][0]);
162                                 $move->set('similarity', $entry['similarity']);
163                                 if($from_mode != $to_mode)
164                                         $move->set('mode', new ContentProvider('commit', 'tree_moved_mode', array('mode' => $to_mode)));
165                                 else
166                                         $move->set('mode', '');
167                                 if($entry['from_id'] != $entry['to_id'])
168                                         $tree->append('links', new ContentProvider('commit', 'tree_moved_links_diff', $link_placeholders));
169                                 $tree->append('links', new ContentProvider('commit', 'tree_moved_links', $link_placeholders));
170                                 break;
171                         default:
172                                 
173                         }
174                         
175                 }
176                 return $tree;
177         }
178         
179 }
180
181 class page_commit {
182     private $page, $phpgitweb;
183         
184     public function main($phpgitweb, $project) {
185                 $this->phpgitweb = $phpgitweb;
186                 if(!$project)
187                         return new ContentProvider('main', 'err400');
188                 $project['refs'] = $phpgitweb->get_project_loader()->getProjectRefs($project);
189                 $phpgitweb->append_header_nav("commit", null, true);
190                 $phpgitweb->append_title("commit");
191                 CommitLoader::parse();
192                 
193                 $commit_loader = new CommitLoader($project);
194                 $commit = $commit_loader->load();
195                 if(!$commit)
196                         return new ContentProvider('main', 'err404_object');
197                 
198                 ContentProvider::overall_set('commit_id', $commit['id']);
199                 
200                 
201                 $this->page = new ContentProvider('commit', 'main');
202                 $this->page->set('hash', $commit['id']);
203                 $this->page->set('author', htmlentities($commit['author']));
204                 $this->page->set('author_mail', htmlentities($commit['author_mail']));
205                 $this->page->set('author_date', gmdate('r', $commit['author_time']));
206                 $author_local_time = $commit['author_time'] + Tools::parseTimeZone($commit['author_timezone']);
207                 if(gmdate('H', $author_local_time) < 6)
208                         $this->page->set('author_local_date', '<span class="atnight">'.gmdate('H:i', $author_local_time).'</span>');
209                 else
210                         $this->page->set('author_local_date', gmdate('H:i', $author_local_time));
211                 $this->page->set('author_timezone', $commit['author_timezone']);
212                 $this->page->set('committer', htmlentities($commit['committer']));
213                 $this->page->set('committer_mail', htmlentities($commit['committer_mail']));
214                 $this->page->set('committer_date', gmdate('r', $commit['committer_time']));
215                 $committer_local_time = $commit['committer_time'] + Tools::parseTimeZone($commit['committer_timezone']);
216                 if(gmdate('H', $committer_local_time) < 6)
217                         $this->page->set('committer_local_date', '<span class="atnight">'.gmdate('H:i', $committer_local_time).'</span>');
218                 else
219                         $this->page->set('committer_local_date', gmdate('H:i', $committer_local_time));
220                 $this->page->set('committer_timezone', $commit['committer_timezone']);
221                 $this->page->set('message', htmlentities(Tools::chop_text($commit['text'], 80, 5)));
222                 $this->page->set('full_message', htmlentities($commit['text']));
223                 $this->page->set('tree_hash', $commit['tree']);
224                 foreach($commit['parent'] as $parent) {
225                         $this->page->append('parents', new ContentProvider('commit', 'parent', array('hash' => $parent, 'head' => $commit['id'])));
226                 }
227                 if(count($commit['parent']) == 0)
228                         $this->page->set('parents', '');
229                 
230                 $refs = new ContentProvider('commit', 'commit_refs');
231                 $found = false;
232                 foreach($project['refs'] as $ref => $rhash) {
233                         if(strtolower($rhash) == strtolower($commit['id'])) {
234                                 $refexp = explode('/', $ref, 3);
235                                 $reftype = $refexp[1];
236                                 if($reftype == 'heads')
237                                         $reftype = 'head';
238                                 else if($reftype == 'remotes')
239                                         $reftype = 'remote';
240                                 else if($reftype == 'tags')
241                                         $reftype = 'tag';
242                                 $refs->append('refs', new ContentProvider('commit', 'commit_ref_'.$reftype, array("name" => $refexp[2], "ref_link" => $ref)));
243                                 $found = true;
244                         }
245                 }
246                 $this->page->set('refs', ($found ? $refs : ""));
247                 
248                 $difftree = new difftree();
249                 $this->page->set('difftree', $difftree->generate_difftree($project, $commit, false));
250                 
251                 return $this->page;
252         }
253         
254         
255         
256 }
257
258 ?>