Added a simple 'change' search.
[viewgit.git] / index.php
blob2584e2b0087df77ed7de925192518f4618469211
1 <?php
2 /** @file
3 * The main "controller" file of ViewGit.
5 * All requests come to this file. You can think of it as the controller in the
6 * Model-View-Controller pattern. It reads config, processes user input,
7 * fetches required data using git commandline, and finally passes the data to
8 * templates to be shown to the user.
9 */
10 error_reporting(E_ALL);
12 require_once('inc/config.php');
13 require_once('inc/functions.php');
15 if (isset($conf['projects_glob'])) {
16 foreach ($conf['projects_glob'] as $glob) {
17 foreach (glob($glob) as $path) {
18 // Get the last part of the path before .git
19 $name = preg_replace(array('#/?\.git$#', '#^.*/#'), array('', ''), $path);
21 // Workaround against name collisions; proj, proj1, proj2, ...
22 $i = '';
23 while (in_array($name . $i, array_keys($conf['projects']))) {
24 @$i++;
26 $name = $name . $i;
27 $conf['projects'][$name] = array('repo' => $path);
32 $action = 'index';
33 $template = 'index';
34 $page['title'] = 'ViewGit';
36 if (isset($_REQUEST['a'])) {
37 $action = strtolower($_REQUEST['a']);
39 $page['action'] = $action;
42 * index - list of projects
44 if ($action === 'index') {
45 $template = 'index';
46 $page['title'] = 'List of projects - ViewGit';
48 foreach (array_keys($conf['projects']) as $p) {
49 $page['projects'][] = get_project_info($p);
54 * archive - send a tree as an archive to client
55 * @param p project
56 * @param h tree hash
57 * @param t type, "targz" or "zip"
58 * @param n OPTIONAL name suggestion
60 elseif ($action === 'archive') {
61 $project = validate_project($_REQUEST['p']);
62 $tree = validate_hash($_REQUEST['h']);
63 $type = $_REQUEST['t'];
65 $basename = "$project-tree-". substr($tree, 0, 7);
66 if (isset($_REQUEST['n'])) {
67 $basename = "$project-$_REQUEST[n]-". substr($tree, 0, 6);
70 if ($type === 'targz') {
71 header("Content-Type: application/x-tar-gz");
72 header("Content-Transfer-Encoding: binary");
73 header("Content-Disposition: attachment; filename=\"$basename.tar.gz\";");
74 run_git_passthru($project, "archive --format=tar $tree |gzip");
76 elseif ($type === 'zip') {
77 header("Content-Type: application/x-zip");
78 header("Content-Transfer-Encoding: binary");
79 header("Content-Disposition: attachment; filename=\"$basename.zip\";");
80 run_git_passthru($project, "archive --format=zip $tree");
82 else {
83 die('Invalid archive type requested');
86 die();
90 * blob - send a blob to browser with filename suggestion
91 * @param p project
92 * @param h blob hash
93 * @param n filename
95 elseif ($action === 'blob') {
96 $project = validate_project($_REQUEST['p']);
97 $hash = validate_hash($_REQUEST['h']);
98 $name = $_REQUEST['n'];
100 header('Content-type: application/octet-stream');
101 header("Content-Disposition: attachment; filename=$name"); // FIXME needs quotation
103 run_git_passthru($project, "cat-file blob $hash");
104 die();
108 * co - git checkout. These requests come from mod_rewrite, see the .htaccess file.
109 * @param p project
110 * @param r path
112 elseif ($action === 'co') {
113 if (!$conf['allow_checkout']) { die('Checkout not allowed'); }
115 // For debugging
116 debug("Project: $_REQUEST[p] Request: $_REQUEST[r]");
118 // eg. info/refs, HEAD
119 $p = validate_project($_REQUEST['p']); // project
120 $r = $_REQUEST['r']; // path
122 $gitdir = $conf['projects'][$p]['repo'];
123 $filename = $gitdir .'/'. $r;
125 // make sure the request is legit (no reading of other files besides those under git projects)
126 if ($r === 'HEAD' || $r === 'info/refs' || preg_match('!^objects/info/(packs|http-alternates|alternates)$!', $r) > 0 || preg_match('!^objects/[0-9a-f]{2}/[0-9a-f]{38}$!', $r) > 0) {
127 if (file_exists($filename)) {
128 debug('OK, sending');
129 readfile($filename);
130 } else {
131 debug('Not found');
132 header('HTTP/1.0 404 Not Found');
134 } else {
135 debug("Denied");
138 die();
142 * commit - view commit information
143 * @param p project
144 * @param h commit hash
146 elseif ($action === 'commit') {
147 $template = 'commit';
148 $page['project'] = validate_project($_REQUEST['p']);
149 $page['title'] = "$page[project] - Commit - ViewGit";
150 $page['commit_id'] = validate_hash($_REQUEST['h']);
152 $info = git_get_commit_info($page['project'], $page['commit_id']);
154 $page['author_name'] = $info['author_name'];
155 $page['author_mail'] = $info['author_mail'];
156 $page['author_datetime'] = strftime($conf['datetime_full'], $info['author_utcstamp']);
157 $page['author_datetime_local'] = strftime($conf['datetime_full'], $info['author_stamp']) .' '. $info['author_timezone'];
158 $page['committer_name'] = $info['committer_name'];
159 $page['committer_mail'] = $info['committer_mail'];
160 $page['committer_datetime'] = strftime($conf['datetime_full'], $info['committer_utcstamp']);
161 $page['committer_datetime_local'] = strftime($conf['datetime_full'], $info['committer_stamp']) .' '. $info['committer_timezone'];
162 $page['tree_id'] = $info['tree'];
163 $page['parents'] = $info['parents'];
164 $page['message'] = $info['message'];
165 $page['message_firstline'] = $info['message_firstline'];
166 $page['message_full'] = $info['message_full'];
171 * commitdiff - view diff of a commit
172 * @param p project
173 * @param h commit hash
175 elseif ($action === 'commitdiff') {
176 $template = 'commitdiff';
177 $page['project'] = validate_project($_REQUEST['p']);
178 $page['title'] = "$page[project] - Commitdiff - ViewGit";
179 $hash = validate_hash($_REQUEST['h']);
180 $page['commit_id'] = $hash;
182 $info = git_get_commit_info($page['project'], $hash);
184 $page['tree_id'] = $info['tree'];
186 $page['message'] = $info['message'];
187 $page['message_firstline'] = $info['message_firstline'];
188 $page['message_full'] = $info['message_full'];
189 $page['author_name'] = $info['author_name'];
190 $page['author_mail'] = $info['author_mail'];
191 $page['author_datetime'] = strftime($conf['datetime'], $info['author_utcstamp']);
193 $text = git_diff($page['project'], "$hash^", $hash);
194 list($page['files'], $page['diffdata']) = format_diff($text);
195 //$page['diffdata'] = format_diff($text);
198 elseif ($action === 'patch') {
199 $project = validate_project($_REQUEST['p']);
200 $hash = validate_hash($_REQUEST['h']);
201 $filename = "$project-". substr($hash, 0, 7) .".patch";
203 //header("Content-Type: text/x-diff");
204 header("Content-Type: application/octet-stream");
205 header("Content-Transfer-Encoding: binary");
206 // TODO git-style filename
207 header("Content-Disposition: attachment; filename=\"$filename\";");
209 run_git_passthru($project, "format-patch --stdout $hash^..$hash");
210 die();
214 * rss-log - RSS feed of project changes
215 * @param p project
217 elseif ($action === 'rss-log') {
218 $page['project'] = validate_project($_REQUEST['p']);
220 $ext_url = 'http://'. $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']) .'/';
222 $page['rss_title'] = "Log for $page[project]";
223 $page['rss_link'] = $ext_url . makelink(array('a' => 'summary', 'p' => $page['project']));
224 $page['rss_description'] = "Git log for project $page[project], generated by ViewGit.";
225 $page['rss_pubDate'] = rss_pubdate(time());
226 $page['rss_ttl'] = $conf['rss_ttl'];
228 $page['rss_items'] = array();
230 $diffstat = strstr($conf['rss_item_description'], '{DIFFSTAT}');
232 $revs = git_get_rev_list($page['project'], $conf['rss_max_items']);
233 foreach ($revs as $rev) {
234 $info = git_get_commit_info($page['project'], $rev);
235 $link = $ext_url . makelink(array('a' => 'commit', 'p' => $page['project'], 'h' => $rev));
236 if ($diffstat) {
237 $info['diffstat'] = git_diffstat($page['project'], $rev);
240 $page['rss_items'][] = array(
241 'title' => rss_item_format($conf['rss_item_title'], $info),
242 'guid' => $link,
243 'link' => $link,
244 'description' => rss_item_format($conf['rss_item_description'], $info),
245 'pubdate' => rss_pubdate($info['author_stamp']),
249 require('templates/rss.php');
250 die();
254 * search - search project history
255 * @param p project
256 * @param st search type: commit,grep,author,committer,pickaxe
257 * @param s string to search for
259 elseif ($action === 'search') {
260 $template = 'shortlog';
262 $page['project'] = validate_project($_REQUEST['p']);
264 $info = git_get_commit_info($page['project']);
265 $page['commit_id'] = $info['h'];
266 $page['tree_id'] = $info['tree'];
268 $type = $_REQUEST['st'];
269 $string = $_REQUEST['s'];
271 $page['search_t'] = $type;
272 $page['search_s'] = $string;
274 $commits = git_search_commits($page['project'], $type, $string);
275 $shortlog = array();
276 foreach ($commits as $c) {
277 $info = git_get_commit_info($page['project'], $c);
278 $shortlog[] = array(
279 'author' => $info['author_name'],
280 'date' => strftime($conf['datetime'], $info['author_utcstamp']),
281 'message' => $info['message'],
282 'commit_id' => $info['h'],
283 'tree' => $info['tree'],
284 'refs' => array(),
287 $page['shortlog'] = $shortlog;
291 * shortlog - project shortlog entries
292 * @param p project
293 * @param h OPTIONAL commit id to start showing log from
295 elseif ($action === 'shortlog') {
296 $template = 'shortlog';
297 $page['project'] = validate_project($_REQUEST['p']);
298 $page['title'] = "$page[project] - Shortlog - ViewGit";
299 if (isset($_REQUEST['h'])) {
300 $page['ref'] = validate_hash($_REQUEST['h']);
301 } else {
302 $page['ref'] = 'HEAD';
305 $info = git_get_commit_info($page['project'], $page['ref']);
306 $page['commit_id'] = $info['h'];
307 $page['tree_id'] = $info['tree'];
309 $page['shortlog'] = handle_shortlog($page['project'], $page['ref']);
311 elseif ($action === 'summary') {
312 $template = 'summary';
313 $page['project'] = validate_project($_REQUEST['p']);
314 $page['title'] = "$page[project] - Summary - ViewGit";
316 $info = git_get_commit_info($page['project']);
317 $page['commit_id'] = $info['h'];
318 $page['tree_id'] = $info['tree'];
320 $page['shortlog'] = handle_shortlog($page['project']);
322 $page['tags'] = handle_tags($page['project'], $conf['summary_tags']);
324 $heads = git_get_heads($page['project']);
325 $page['heads'] = array();
326 foreach ($heads as $h) {
327 $info = git_get_commit_info($page['project'], $h['h']);
328 $page['heads'][] = array(
329 'date' => strftime($conf['datetime'], $info['author_utcstamp']),
330 'h' => $h['h'],
331 'fullname' => $h['fullname'],
332 'name' => $h['name'],
336 elseif ($action === 'tags') {
337 $template = 'tags';
338 $page['project'] = validate_project($_REQUEST['p']);
339 $page['title'] = "$page[project] - Tags - ViewGit";
341 $info = git_get_commit_info($page['project']);
342 $page['commit_id'] = $info['h'];
343 $page['tree_id'] = $info['tree'];
345 $page['tags'] = handle_tags($page['project']);
348 * Shows a tree, with list of directories/files, links to them and download
349 * links to archives.
351 * @param p project
352 * @param h tree hash
353 * @param hb OPTIONAL base commit (trees can be part of multiple commits, this
354 * one denotes which commit the user navigated from)
355 * @param f OPTIONAL path the user has followed to view this tree
357 elseif ($action === 'tree') {
358 $template = 'tree';
359 $page['project'] = validate_project($_REQUEST['p']);
360 if (isset($_REQUEST['h'])) {
361 $page['tree_id'] = validate_hash($_REQUEST['h']);
364 else {
365 // TODO walk the tree
366 $page['tree_id'] = 'HEAD';
369 $page['title'] = "$page[project] - Tree - ViewGit";
371 // 'hb' optionally contains the commit_id this tree is related to
372 if (isset($_REQUEST['hb'])) {
373 $page['commit_id'] = validate_hash($_REQUEST['hb']);
375 else {
376 // for the header
377 $info = git_get_commit_info($page['project']);
378 $page['commit_id'] = $info['h'];
381 $page['path'] = '';
382 if (isset($_REQUEST['f'])) {
383 $page['path'] = $_REQUEST['f']; // TODO validate?
386 // get path info for the header
387 $page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
388 if (!isset($page['tree_id'])) {
389 // Take the last hash from the tree
390 if (count($page['pathinfo']) > 0) {
391 $page['tree_id'] = $page['pathinfo'][count($page['pathinfo']) - 1]['hash'];
392 } else {
393 $page['tree_id'] = 'HEAD';
397 $page['entries'] = git_ls_tree($page['project'], $page['tree_id']);
400 * View a blob as inline, embedded on the page.
401 * @param p project
402 * @param h blob hash
403 * @param hb OPTIONAL base commit
405 elseif ($action === 'viewblob') {
406 $template = 'blob';
407 $page['project'] = validate_project($_REQUEST['p']);
408 $page['hash'] = validate_hash($_REQUEST['h']);
409 $page['title'] = "$page[project] - Blob - ViewGit";
410 if (isset($_REQUEST['hb'])) {
411 $page['commit_id'] = validate_hash($_REQUEST['hb']);
413 else {
414 $page['commit_id'] = 'HEAD';
417 $page['path'] = '';
418 if (isset($_REQUEST['f'])) {
419 $page['path'] = $_REQUEST['f']; // TODO validate?
422 // For the header's pagenav
423 $info = git_get_commit_info($page['project'], $page['commit_id']);
424 $page['commit_id'] = $info['h'];
425 $page['tree_id'] = $info['tree'];
427 $page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
429 $page['data'] = join("\n", run_git($page['project'], "cat-file blob $page[hash]"));
431 // GeSHi support
432 if ($conf['geshi'] && strpos($page['path'], '.')) {
433 error_reporting(E_ALL ^ E_NOTICE);
434 require_once($conf['geshi_path']);
435 $ext = array_pop(explode('.', $page['path']));
436 $lang = Geshi::get_language_name_from_extension($ext);
437 if (strlen($lang) > 0) {
438 $geshi =& new Geshi($page['data'], $lang);
439 if (is_int($conf['geshi_line_numbers'])) {
440 if ($conf['geshi_line_numbers'] == 0) {
441 $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
443 else {
444 $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, $conf['geshi_line_numbers']);
447 $page['html_data'] = $geshi->parse_code();
449 error_reporting(E_ALL);
452 else {
453 die('Invalid action');
456 require 'templates/header.php';
457 require "templates/$template.php";
458 require 'templates/footer.php';