Move all display functions into controller classes to share code and move the messy supercontroller code out of the index
Move all display functions into controller classes to share code and move the messy supercontroller code out of the index

--- a/include/Config.class.php
+++ b/include/Config.class.php
@@ -78,7 +78,7 @@
 		global $gitphp_version, $gitphp_appstring;
 
 		if (!is_file($configFile)) {
-			throw new Exception('Invalid config file ' . $configFile);
+			throw new Exception('Could not load config file ' . $configFile);
 		}
 
 		if (!include($configFile)) {

--- /dev/null
+++ b/include/MessageException.class.php
@@ -1,1 +1,37 @@
+<?php
+/**
+ * GitPHP Message exception
+ *
+ * Custom exception for signalling display of a message to user
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ */
 
+/**
+ * Message Exception
+ *
+ * @package GitPHP
+ */
+class GitPHP_MessageException extends Exception
+{
+
+	public $Error;
+	
+	/**
+	 * Constructor
+	 *
+	 * @access public
+	 * @param string $message message string
+	 * @param boolean $error true if this is an error rather than informational
+	 * @param integer $code exception code
+	 * @param Exception $previous previous exception
+	 * @return Exception message exception object
+	 */
+	public function __construct($message, $error = false, $code = 0) {
+		$this->Error = $error;
+		parent::__construct($message, $code);
+	}
+}
+

--- a/include/cache.cache_expire.php
+++ /dev/null
@@ -1,34 +1,1 @@
-<?php
-/*
- *  cache.cache_expire.pehe
- *  gitphp: A PHP git repository browser
- *  Component: Cache - cache expire
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- */
 
-function cache_expire($expireall = false)
-{
-	global $tpl, $gitphp_current_project;
-
-	if ($expireall) {
-		$tpl->clear_all_cache();
-		return;
-	}
-
-	if (!$gitphp_current_project)
-		return;
-
-	$headlist = $gitphp_current_project->GetHeads();
-
-	if (count($headlist) > 0) {
-		$age = $headlist[0]->GetCommit()->GetAge();
-
-		$tpl->clear_cache(null, sha1($gitphp_current_project->GetProject()), null, $age);
-
-		$tpl->clear_cache('projectlist.tpl', sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig())), null, $age);
-	}
-}
-
-?>
-

--- /dev/null
+++ b/include/controller/Controller.class.php
@@ -1,1 +1,134 @@
+<?php
+/**
+ * GitPHP Controller
+ *
+ * Controller factory
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_CONTROLLERDIR . 'ControllerBase.class.php');
+
+/**
+ * Controller
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller
+{
+
+	/**
+	 * GetController
+	 *
+	 * Gets a controller for an action
+	 *
+	 * @access public
+	 * @static
+	 * @param string $action action
+	 * @return mixed controller object
+	 */
+	public static function GetController($action)
+	{
+		$controller = null;
+
+		switch ($action) {
+			case 'search':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Search.class.php');
+				$controller = new GitPHP_Controller_Search();
+				break;
+			case 'commitdiff':
+			case 'commitdiff_plain':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Commitdiff.class.php');
+				$controller = new GitPHP_Controller_Commitdiff();
+				if ($action === 'commitdiff_plain')
+					$controller->SetParam('plain', true);
+				break;
+			case 'blobdiff':
+			case 'blobdiff_plain':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Blobdiff.class.php');
+				$controller = new GitPHP_Controller_Blobdiff();
+				if ($action === 'blobdiff_plain')
+					$controller->SetParam('plain', true);
+				break;
+			case 'history':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_History.class.php');
+				$controller = new GitPHP_Controller_History();
+				break;
+			case 'shortlog':
+			case 'log':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Log.class.php');
+				$controller = new GitPHP_Controller_Log();
+				if ($action === 'shortlog')
+					$controller->SetParam('short', true);
+				break;
+			case 'snapshot':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Snapshot.class.php');
+				$controller = new GitPHP_Controller_Snapshot();
+				break;
+			case 'tree':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Tree.class.php');
+				$controller = new GitPHP_Controller_Tree();
+				break;
+			case 'tag':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Tag.class.php');
+				$controller = new GitPHP_Controller_Tag();
+				break;
+			case 'tags':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Tags.class.php');
+				$controller = new GitPHP_Controller_Tags();
+				break;
+			case 'heads':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Heads.class.php');
+				$controller = new GitPHP_Controller_Heads();
+				break;
+			case 'blame':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Blame.class.php');
+				$controller = new GitPHP_Controller_Blame();
+				break;
+			case 'blob':
+			case 'blob_plain':	
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Blob.class.php');
+				$controller = new GitPHP_Controller_Blob();
+				if ($action === 'blob_plain')
+					$controller->SetParam('plain', true);
+				break;
+			case 'rss':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Rss.class.php');
+				$controller = new GitPHP_Controller_Rss();
+				break;
+			case 'commit':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Commit.class.php');
+				$controller = new GitPHP_Controller_Commit();
+				break;
+			case 'summary':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_Project.class.php');
+				$controller = new GitPHP_Controller_Project();
+				break;
+			case 'project_index':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_ProjectList.class.php');
+				$controller = new GitPHP_Controller_ProjectList();
+				$controller->SetParam('txt', true);
+			case 'opml':
+				require_once(GITPHP_CONTROLLERDIR . 'Controller_ProjectList.class.php');
+				$controller = new GitPHP_Controller_ProjectList();
+				$controller->SetParam('opml', true);
+				break;
+			default:
+				if (isset($_GET['p'])) {
+					require_once(GITPHP_CONTROLLERDIR . 'Controller_Project.class.php');
+					$controller = new GitPHP_Controller_Project();
+				} else {
+					require_once(GITPHP_CONTROLLERDIR . 'Controller_ProjectList.class.php');
+					$controller = new GitPHP_Controller_ProjectList();
+				}
+		}
+
+		return $controller;
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/ControllerBase.class.php
@@ -1,1 +1,300 @@
-
+<?php
+/**
+ * GitPHP ControllerBase
+ *
+ * Base class that all controllers extend
+ *
+ * @author Christopher Han <xiphux@gmail.com
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
+
+require_once(GITPHP_INCLUDEDIR . 'util.script_url.php');
+
+/**
+ * ControllerBase class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ * @abstract
+ */
+abstract class GitPHP_ControllerBase
+{
+
+	/**
+	 * tpl
+	 *
+	 * Smarty instance
+	 *
+	 * @access protected
+	 */
+	protected $tpl;
+
+	/**
+	 * project
+	 *
+	 * Current project
+	 *
+	 * @access protected
+	 */
+	protected $project;
+
+	/**
+	 * params
+	 *
+	 * Parameters
+	 *
+	 * @access protected
+	 */
+	protected $params = array();
+
+	/**
+	 * headers
+	 *
+	 * Headers
+	 *
+	 * @access protected
+	 */
+	protected $headers = array();
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return mixed controller object
+	 * @throws Exception on invalid project
+	 */
+	public function __construct()
+	{
+		require_once(GitPHP_Config::GetInstance()->GetValue('smarty_prefix', 'lib/smarty/libs/') . 'Smarty.class.php');
+		$this->tpl = new Smarty;
+
+		require_once(GITPHP_INCLUDEDIR . 'util.age_string.php');
+		$this->tpl->register_modifier('agestring', 'age_string');
+
+		if (GitPHP_Config::GetInstance()->GetValue('cache', false)) {
+			$this->tpl->caching = 2;
+			if (GitPHP_Config::GetInstance()->HasKey('cachelifetime')) {
+				$this->tpl->cache_lifetime = GitPHP_Config::GetInstance()->GetValue('cachelifetime');
+			}
+		}
+
+		if (isset($_GET['p'])) {
+			$this->project = GitPHP_ProjectList::GetInstance()->GetProject(str_replace(chr(0), '', $_GET['p']));
+			if (!$this->project) {
+				throw new GitPHP_MessageException('Invalid project ' . $_GET['p'], true);
+			}
+		}
+
+		$this->ReadQuery();
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @abstract
+	 * @return string template filename
+	 */
+	protected abstract function GetTemplate();
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @abstract
+	 * @return string cache key
+	 */
+	protected abstract function GetCacheKey();
+
+	/**
+	 * GetCacheKeyPrefix
+	 *
+	 * Get the prefix for all cache keys
+	 *
+	 * @access private
+	 * @return string cache key prefix
+	 */
+	private function GetCacheKeyPrefix()
+	{
+		$cacheKeyPrefix = sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig));
+		if ($this->project) {
+			$cacheKeyPrefix .= '|' . sha1($this->project->GetProject());
+		}
+		
+		return $cacheKeyPrefix;
+	}
+
+	/** 
+	 * GetFullCacheKey
+	 *
+	 * Get the full cache key
+	 *
+	 * @access protected
+	 * @return string full cache key
+	 */
+	protected function GetFullCacheKey()
+	{
+		$cacheKey = $this->GetCacheKeyPrefix();
+
+		$subCacheKey = $this->GetCacheKey();
+
+		if (!empty($subCacheKey))
+			$cacheKey .= '|' . $subCacheKey;
+
+		return $cacheKey;
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @abstract
+	 * @access prtected
+	 */
+	protected abstract function ReadQuery();
+
+	/**
+	 * SetParam
+	 *
+	 * Set a parameter
+	 *
+	 * @access protected
+	 * @param string $key key to set
+	 * @param mixed $value value to set
+	 */
+	public function SetParam($key, $value)
+	{
+		if (empty($key))
+			return;
+
+		if (empty($value))
+			unset($this->params[$key]);
+
+		$this->params[$key] = $value;
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 * @abstract
+	 */
+	protected abstract function LoadData();
+
+	/**
+	 * LoadCommonData
+	 *
+	 * Loads common data used by all templates
+	 *
+	 * @access private
+	 */
+	private function LoadCommonData()
+	{
+		global $gitphp_version, $gitphp_appstring;
+
+		$this->tpl->assign('version', $gitphp_version);
+		$this->tpl->assign('stylesheet', GitPHP_Config::GetInstance()->GetValue('stylesheet', 'gitphp.css'));
+		$this->tpl->assign('pagetitle', GitPHP_Config::GetInstance()->GetValue('title', $gitphp_appstring));
+		$this->tpl->assign("self",script_url());
+		if ($this->project) {
+			$this->tpl->assign('validproject', true);
+			$this->tpl->assign('project', $this->project->GetProject());
+			$this->tpl->assign('projectdescription', $this->project->GetDescription());
+		}
+		if (GitPHP_Config::GetInstance()->GetValue('search', true))
+			$this->tpl->assign('enablesearch', true);
+		if (GitPHP_Config::GetInstance()->GetValue('filesearch', true))
+			$this->tpl->assign('filesearch', true);
+	}
+
+	/**
+	 * RenderHeaders
+	 *
+	 * Renders any special headers
+	 *
+	 * @access public
+	 */
+	public function RenderHeaders()
+	{
+		$this->LoadHeaders();
+
+		if (count($this->headers) > 0) {
+			foreach ($this->headers as $hdr) {
+				header($hdr);
+			}
+		}
+	}
+
+	/**
+	 * Render
+	 *
+	 * Renders the output
+	 *
+	 * @access public
+	 */
+	public function Render()
+	{
+		if ((GitPHP_Config::GetInstance()->GetValue('cache', false) == true) && (GitPHP_Config::GetInstance()->GetValue('cacheexpire', true) === true))
+			$this->CacheExpire();
+
+		if (!$this->tpl->is_cached($this->GetTemplate(), $this->GetFullCacheKey())) {
+			$this->tpl->clear_all_assign();
+			$this->LoadCommonData();
+			$this->LoadData();
+		}
+
+		$this->tpl->display($this->GetTemplate(), $this->GetFullCacheKey());
+	}
+
+	/**
+	 * CacheExpire
+	 *
+	 * Expires the cache
+	 *
+	 * @access public
+	 * @param boolean $expireAll expire the whole cache
+	 */
+	public function CacheExpire($expireAll = false)
+	{
+		if ($expireAll) {
+			$this->tpl->clear_all_cache();
+			return;
+		}
+
+		if (!$this->project)
+			return;
+
+		$headList = $this->project->GetHeads();
+
+		if (count($headlist) > 0) {
+			$age = $headlist[0]->GetCommit()->GetAge();
+
+			$this->tpl->clear_cache(null, $this->GetCacheKeyPrefix(), null, $age);
+			$this->tpl->clear_cache('projectlist.tpl', sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig())), null, $age);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Blame.class.php
@@ -1,1 +1,128 @@
+<?php
+/**
+ * GitPHP Controller Blame
+ *
+ * Controller for displaying blame
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_parse_blame.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_path_trees.php');
+
+/**
+ * Blame controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Blame extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for blame', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'blame.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hashbase']) ? $this->params['hashbase'] : '') . '|' . (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['hb']))
+			$this->params['hashbase'] = $_GET['hb'];
+		else
+			$this->params['hashbase'] = 'HEAD';
+		if (isset($_GET['f']))
+			$this->params['file'] = $_GET['f'];
+		if (isset($_GET['h'])) {
+			$this->params['hash'] = $_GET['h'];
+		}
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		if ((!isset($this->params['hash'])) && (isset($this->params['file']))) {
+			$this->params['hash'] = git_get_hash_by_path($this->params['hashbase'], $this->params['file'], 'blob');
+		}
+		$head = $this->project->GetHeadCommit()->GetHash();
+		$this->tpl->assign("hash", $this->params['hash']);
+		$this->tpl->assign("hashbase", $this->params['hashbase']);
+		$this->tpl->assign("head", $head);
+		$co = $this->project->GetCommit($this->params['hashbase']);
+		if ($co) {
+			$this->tpl->assign("fullnav",TRUE);
+			$refs = read_info_ref();
+			$this->tpl->assign("tree",$co->GetTree()->GetHash());
+			$this->tpl->assign("title",$co->GetTitle());
+			if (isset($this->params['file']))
+				$this->tpl->assign("file",$this->params['file']);
+			if ($this->params['hashbase'] == "HEAD") {
+				if (isset($refs[$head]))
+					$this->tpl->assign("hashbaseref",$refs[$head]);
+			} else {
+				if (isset($refs[$this->params['hashbase']]))
+					$this->tpl->assign("hashbaseref",$refs[$this->params['hashbase']]);
+			}
+		}
+		$paths = git_path_trees($this->params['hashbase'], $this->params['file']);
+		$this->tpl->assign("paths",$paths);
+
+		$blamedata = git_parse_blame($this->params['file'], $this->params['hashbase']);
+		$this->tpl->assign("blamedata",$blamedata);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Blob.class.php
@@ -1,1 +1,216 @@
-
+<?php
+/**
+ * GitPHP Controller Blob
+ *
+ * Controller for displaying a blob
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
+
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_get_hash_by_path.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_cat_file.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_path_trees.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+require_once(GITPHP_INCLUDEDIR . 'util.file_mime.php');
+
+/**
+ * Blob controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Blob extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for blob', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		if ($this->params['plain'])
+			return 'blobplain.tpl';
+		return 'blob.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hashbase']) ? $this->params['hashbase'] : '') . '|' . (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['hb']))
+			$this->params['hashbase'] = $_GET['hb'];
+		else
+			$this->params['hashbase'] = 'HEAD';
+		if (isset($_GET['f']))
+			$this->params['file'] = $_GET['f'];
+		if (isset($_GET['h'])) {
+			$this->params['hash'] = $_GET['h'];
+		}
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		if ($this->params['plain']) {
+			// XXX: Nasty hack to cache headers
+			if (!$this->tpl->is_cached('blobheaders.tpl', $this->GetFullCacheKey())) {
+				if (isset($this->params['file']))
+					$saveas = $this->params['file'];
+				else
+					$saveas = $hash . ".txt";
+
+				$buffer = git_cat_file($hash);
+
+				if (GitPHP_Config::GetInstance()->GetValue('filemimetype', true))
+					$mime = file_mime($buffer, $this->params['file']);
+
+				$headers = array();
+
+				if ($mime)
+					$headers[] = "Content-type: " . $mime;
+				else
+					$headers[] = "Content-type: text/plain; charset=UTF-8";
+
+				$headers[] = "Content-disposition: inline; filename=\"" . $saveas . "\"";
+
+				$this->tpl->assign("blobheaders", serialize($headers));
+			}
+			$out = $this->tpl->fetch('blobheaders.tpl', $this->GetFullCacheKey());
+
+			$this->headers = unserialize($out);
+		}
+
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		if ((!isset($this->params['hash'])) && (isset($this->params['file']))) {
+			$this->params['hash'] = git_get_hash_by_path($this->params['hashbase'], $this->params['file'], 'blob');
+		}
+
+		if ($this->params['plain']) {
+			$this->tpl->assign("blob", git_cat_file($this->params['hash']));
+			return;
+		}
+
+		$head = $this->project->GetHeadCommit()->GetHash();
+		$catout = git_cat_file($this->params['hash']);
+		$this->tpl->assign("hash",$this->params['hash']);
+		$this->tpl->assign("hashbase",$this->params['hashbase']);
+		$this->tpl->assign("head", $head);
+		$co = $this->project->GetCommit($this->params['hashbase']);
+		if ($co) {
+			$this->tpl->assign("fullnav",TRUE);
+			$refs = read_info_ref();
+			$this->tpl->assign("tree",$co->GetTree()->GetHash());
+			$this->tpl->assign("title",$co->GetTitle());
+			if (isset($this->params['file']))
+				$this->tpl->assign("file",$this->params['file']);
+			if ($this->params['hashbase'] == "HEAD") {
+				if (isset($refs[$head]))
+					$this->tpl->assign("hashbaseref",$refs[$head]);
+			} else {
+				if (isset($refs[$this->params['hashbase']]))
+					$this->tpl->assign("hashbaseref",$refs[$this->params['hashbase']]);
+			}
+		}
+		$paths = git_path_trees($this->params['hashbase'], $this->params['file']);
+		$this->tpl->assign("paths",$paths);
+
+		if (GitPHP_Config::GetInstance()->GetValue('filemimetype', true)) {
+			$mime = file_mime($catout,$this->params['file']);
+			if ($mime)
+				$mimetype = strtok($mime, "/");
+		}
+
+		if ($mimetype == "image") {
+			$this->tpl->assign("mime", $mime);
+			$this->tpl->assign("data", base64_encode($catout));
+		} else {
+			$usedgeshi = GitPHP_Config::GetInstance()->GetValue('geshi', true);
+			if ($usedgeshi) {
+				$usedgeshi = FALSE;
+				include_once(GitPHP_Config::GetInstance()->GetValue('geshiroot', 'lib/geshi/') . "geshi.php");
+				if (class_exists("GeSHi")) {
+					$geshi = new GeSHi("",'php');
+					if ($geshi) {
+						$lang = "";
+						if (isset($this->params['file']))
+							$lang = $geshi->get_language_name_from_extension(substr(strrchr($this->params['file'],'.'),1));
+						if (isset($lang) && (strlen($lang) > 0)) {
+							$geshi->enable_classes();
+							$geshi->set_source($catout);
+							$geshi->set_language($lang);
+							$geshi->set_header_type(GESHI_HEADER_DIV);
+							$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS);
+							$this->tpl->assign("geshiout",$geshi->parse_code());
+							$this->tpl->assign("extracss",$geshi->get_stylesheet());
+							$usedgeshi = TRUE;
+						}
+					}
+				}
+			}
+
+			if (!$usedgeshi) {
+				$lines = explode("\n",$catout);
+				$this->tpl->assign("lines",$lines);
+			}
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Blobdiff.class.php
@@ -1,1 +1,146 @@
+<?php
+/**
+ * GitPHP Controller Blobdiff
+ *
+ * Controller for displaying a blobdiff
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.prep_tmpdir.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_path_trees.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_diff.php');
+
+/**
+ * Blobdiff controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Blobdiff extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for blob diff', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			return 'blobdiffplain.tpl';
+		}
+		return 'blobdiff.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hashbase']) ? $this->params['hashbase'] : '') . '|' . (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['hashparent']) ? $this->params['hashparent'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['f']))
+			$this->params['file'] = $_GET['f'];
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+		if (isset($_GET['hb']))
+			$this->params['hashbase'] = $_GET['hb'];
+		if (isset($_GET['hp']))
+			$this->params['hashparent'] = $_GET['hp'];
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			$this->headers[] = 'Content-type: text/plain; charset=UTF-8';
+		}
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$ret = prep_tmpdir();
+		if ($ret !== TRUE) {
+			echo $ret;
+			return;
+		}
+
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			$this->tpl->assign("blobdiff",git_diff($this->params['hashparent'],($this->params['file']?"a/".$this->params['file']:$this->params['hashparent']),$this->params['hash'],($this->params['file']?"b/".$this->params['file']:$this->params['hash'])));
+			return;
+		}
+
+		$this->tpl->assign("hash",$this->params['hash']);
+		$this->tpl->assign("hashparent",$this->params['hashparent']);
+		$this->tpl->assign("hashbase",$this->params['hashbase']);
+		if (isset($this->params['file']))
+			$this->tpl->assign("file",$this->params['file']);
+		$co = $this->project->GetCommit($this->params['hashbase']);
+		if ($co) {
+			$this->tpl->assign("fullnav",TRUE);
+			$this->tpl->assign("tree",$co->GetTree()->GetHash());
+			$this->tpl->assign("title",$co->GetTitle());
+			$refs = read_info_ref();
+			if (isset($refs[$this->params['hashbase']]))
+				$this->tpl->assign("hashbaseref",$refs[$this->params['hashbase']]);
+		}
+		$paths = git_path_trees($this->params['hashbase'], $this->params['file']);
+		$this->tpl->assign("paths",$paths);
+		$diffout = explode("\n",git_diff($this->params['hashparent'],($this->params['file']?$this->params['file']:$this->params['hashparent']),$this->params['hash'],($this->params['file']?$this->params['file']:$this->params['hash'])));
+		$this->tpl->assign("diff",$diffout);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Commit.class.php
@@ -1,1 +1,160 @@
+<?php
+/**
+ * GitPHP Controller Commit
+ *
+ * Controller for displaying a commit
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.file_type.php');
+require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_diff_tree.php');
+
+/**
+ * Commit controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Commit extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for commit', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'commit.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return $this->params['hash'];
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+		else
+			$this->params['hash'] = 'HEAD';
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$commit = $this->project->GetCommit($this->params['hash']);
+		$ad = date_str($commit->GetAuthorEpoch(), $commit->GetAuthorTimezone());
+		$cd = date_str($commit->GetCommitterEpoch(), $commit->GetCommitterTimezone());
+		$parentObj = $commit->GetParent();
+		if ($parentObj) {
+			$root = "";
+			$parent = $parentObj->GetHash();
+		} else {
+			$root = "--root";
+			$parent = "";
+		}
+		$diffout = git_diff_tree($root . " " . $parent . " " . $hash, TRUE);
+		$difftree = explode("\n",$diffout);
+		$treeObj = $commit->GetTree();
+		if ($treeObj)
+			$this->tpl->assign("tree", $treeObj->GetHash());
+		if ($parentObj)
+			$this->tpl->assign("parent", $parentObj->GetHash());
+		$this->tpl->assign("commit", $commit);
+		$this->tpl->assign("adrfc2822",$ad['rfc2822']);
+		$this->tpl->assign("adhourlocal",$ad['hour_local']);
+		$this->tpl->assign("adminutelocal",$ad['minute_local']);
+		$this->tpl->assign("adtzlocal",$ad['tz_local']);
+		$this->tpl->assign("cdrfc2822",$cd['rfc2822']);
+		$this->tpl->assign("cdhourlocal",$cd['hour_local']);
+		$this->tpl->assign("cdminutelocal",$cd['minute_local']);
+		$this->tpl->assign("cdtzlocal",$cd['tz_local']);
+		$this->tpl->assign("difftreesize",count($difftree)+1);
+		$difftreelines = array();
+		foreach ($difftree as $i => $line) {
+			if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/",$line,$regs)) {
+				$difftreeline = array();
+				$difftreeline["from_mode"] = $regs[1];
+				$difftreeline["to_mode"] = $regs[2];
+				$difftreeline["from_mode_cut"] = substr($regs[1],-4);
+				$difftreeline["to_mode_cut"] = substr($regs[2],-4);
+				$difftreeline["from_id"] = $regs[3];
+				$difftreeline["to_id"] = $regs[4];
+				$difftreeline["status"] = $regs[5];
+				$difftreeline["similarity"] = ltrim($regs[6],"0");
+				$difftreeline["file"] = $regs[7];
+				$difftreeline["from_file"] = strtok($regs[7],"\t");
+				$difftreeline["from_filetype"] = file_type($regs[1]);
+				$difftreeline["to_file"] = strtok("\t");
+				$difftreeline["to_filetype"] = file_type($regs[2]);
+				if ((octdec($regs[2]) & 0x8000) == 0x8000)
+					$difftreeline["isreg"] = TRUE;
+				$modestr = "";
+				if ((octdec($regs[1]) & 0x17000) != (octdec($regs[2]) & 0x17000))
+					$modestr .= " from " . file_type($regs[1]) . " to " . file_type($regs[2]);
+				if ((octdec($regs[1]) & 0777) != (octdec($regs[2]) & 0777)) {
+					if ((octdec($regs[1]) & 0x8000) && (octdec($regs[2]) & 0x8000))
+						$modestr .= " mode: " . (octdec($regs[1]) & 0777) . "->" . (octdec($regs[2]) & 0777);
+					else if (octdec($regs[2]) & 0x8000)
+						$modestr .= " mode: " . (octdec($regs[2]) & 0777);
+				}
+				$difftreeline["modechange"] = $modestr;
+				$simmodechg = "";
+				if ($regs[1] != $regs[2])
+					$simmodechg .= ", mode: " . (octdec($regs[2]) & 0777);
+				$difftreeline["simmodechg"] = $simmodechg;
+				$difftreelines[] = $difftreeline;
+			}
+		}
+		$this->tpl->assign("difftreelines",$difftreelines);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Commitdiff.class.php
@@ -1,1 +1,194 @@
+<?php
+/**
+ * GitPHP Controller Commitdiff
+ *
+ * Controller for displaying a commitdiff
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
+require_once(GITPHP_INCLUDEDIR . 'util.file_type.php');
+require_once(GITPHP_INCLUDEDIR . 'util.prep_tmpdir.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_diff_tree.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_read_revlist.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_diff.php');
+
+/**
+ * Commitdiff controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Commitdiff extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for commit diff', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			return 'diff_plaintext.tpl';
+		}
+		return 'commitdiff.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['hashparent']) ? $this->params['hashparent'] : '');
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+		if (isset($_GET['hp']))
+			$this->params['hashparent'] = $_GET['hp'];
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			$this->headers[] = 'Content-type: text/plain; charset=UTF-8';
+			$this->headers[] = 'Content-disposition: inline; filename="git-' . $this->params['hash'] . '.patch"';
+		}
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$ret = prep_tmpdir();
+		if ($ret !== TRUE) {
+			echo $ret;
+			return;
+		}
+		$co = $this->project->GetCommit($this->params['hash']);
+		if (!isset($this->params['hashparent'])) {
+			$parent = $co->GetParent();
+			if ($parent)
+				$this->params['hashparent'] = $parent->GetHash();
+		}
+		$diffout = git_diff_tree($this->params['hashparent'] . " " . $this->params['hash']);
+		$difftree = explode("\n",$diffout);
+
+		if (isset($this->params['plain']) && ($this->params['plain'] === true)) {
+			$refs = read_info_ref('tags');
+			$listout = git_read_revlist('HEAD');
+			foreach ($listout as $i => $rev) {
+				if (isset($refs[$rev]))
+					$tagname = $refs[$rev];
+				if ($rev == $this->params['hash'])
+					break;
+			}
+			$ad = date_str($co->GetAuthorEpoch(), $co->GetAuthorTimezone());
+			$this->tpl->assign("from", $co->GetAuthor());
+			$this->tpl->assign("date",$ad['rfc2822']);
+			$this->tpl->assign("subject", $co->GetTitle());
+			if (isset($tagname))
+				$this->tpl->assign("tagname",$tagname);
+			$this->tpl->assign("url",script_url() . "?p=" . $this->project->GetProject() . "&a=commitdiff&h=" . $this->params['hash']);
+			$this->tpl->assign("comment", $co->GetComment());
+			$diffs = array();
+			foreach ($difftree as $i => $line) {
+				if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/",$line,$regs)) {
+					if ($regs[5] == "A")
+						$diffs[] = git_diff(null, "/dev/null", $regs[4], "b/" . $regs[6]);
+					else if ($regs[5] == "D")
+						$diffs[] = git_diff($regs[3], "a/" . $regs[6], null, "/dev/null");
+					else if ($regs[5] == "M")
+						$diffs[] = git_diff($regs[3], "a/" . $regs[6], $regs[4], "b/" . $regs[6]);
+				}
+			}
+			$this->tpl->assign("diffs",$diffs);
+		} else {
+			$refs = read_info_ref();
+			$this->tpl->assign("hash",$this->params['hash']);
+			$tree = $co->GetTree();
+			if ($tree)
+				$this->tpl->assign("tree", $tree->GetHash());
+			$this->tpl->assign("hashparent",$this->params['hashparent']);
+			$this->tpl->assign("title", $co->GetTitle());
+			if (isset($refs[$co->GetHash()]))
+				$this->tpl->assign("commitref",$refs[$co->GetHash()]);
+			$this->tpl->assign("comment",$co->GetComment());
+			$difftreelines = array();
+			foreach ($difftree as $i => $line) {
+				if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/",$line,$regs)) {
+					$difftreeline = array();
+					$difftreeline["from_mode"] = $regs[1];
+					$difftreeline["to_mode"] = $regs[2];
+					$difftreeline["from_id"] = $regs[3];
+					$difftreeline["to_id"] = $regs[4];
+					$difftreeline["status"] = $regs[5];
+					$difftreeline["file"] = $regs[6];
+					$difftreeline["from_type"] = file_type($regs[1]);
+					$difftreeline["to_type"] = file_type($regs[2]);
+					if ($regs[5] == "A")
+						$difftreeline['diffout'] = explode("\n",git_diff(null,"/dev/null",$regs[4],"b/" . $regs[6]));
+					else if ($regs[5] == "D")
+						$difftreeline['diffout'] = explode("\n",git_diff($regs[3],"a/" . $regs[6],null,"/dev/null"));
+					else if (($regs[5] == "M") && ($regs[3] != $regs[4]))
+						$difftreeline['diffout'] = explode("\n",git_diff($regs[3],"a/" . $regs[6],$regs[4],"b/" . $regs[6]));
+					$difftreelines[] = $difftreeline;
+				}
+			}
+			$this->tpl->assign("difftreelines",$difftreelines);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Heads.class.php
@@ -1,1 +1,94 @@
+<?php
+/**
+ * GitPHP Controller Heads
+ *
+ * Controller for displaying heads
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+/**
+ * Heads controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Heads extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for heads', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'heads.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return '';
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$head = $this->project->GetHeadCommit()->GetHash();
+		$this->tpl->assign("head",$head);
+
+		$headlist = $this->project->GetHeads();
+		if (isset($headlist) && (count($headlist) > 0)) {
+			$this->tpl->assign("headlist",$headlist);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_History.class.php
@@ -1,1 +1,145 @@
+<?php
+/**
+ * GitPHP Controller History
+ *
+ * Controller for displaying file history
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_get_hash_by_path.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_history_list.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_path_trees.php');
+
+/**
+ * History controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_History extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for file history', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'history.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['f']))
+			$this->params['file'] = $_GET['f'];
+		if (isset($_GET['h'])) {
+			$this->params['hash'] = $_GET['h'];
+		}
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		if (!isset($this->params['hash']))
+			$this->params['hash'] = $this->project->GetHeadCommit()->GetHash();
+
+		$co = $this->project->GetCommit($this->params['hash']);
+		$refs = read_info_ref();
+		$this->tpl->assign("hash",$this->params['hash']);
+		if (isset($refs[$this->params['hash']]))
+			$this->tpl->assign("hashbaseref",$refs[$this->params['hash']]);
+		$this->tpl->assign("tree", $co->GetTree()->GetHash());
+		$this->tpl->assign("title", $co->GetTitle());
+		$paths = git_path_trees($this->params['hash'], $this->params['file']);
+		$this->tpl->assign("paths",$paths);
+		date_default_timezone_set('UTC');
+		$cmdout = git_history_list($this->params['hash'], $this->params['file']);
+		$lines = explode("\n", $cmdout);
+		$historylines = array();
+		foreach ($lines as $i => $line) {
+			if (preg_match("/^([0-9a-fA-F]{40})/",$line,$regs))
+				$commit = $regs[1];
+			else if (preg_match("/:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/",$line,$regs) && isset($commit)) {
+					$historyline = array();
+					$co2 = $this->project->GetCommit($commit);
+					$age = $co2->GetAge();
+					if ($age > 60*60*24*7*2) {
+						$historyline['agestringdate'] = date('Y-m-d', $co2->GetCommitterEpoch());
+						$historyline['agestringage'] = age_string($age);
+					} else {
+						$historyline['agestringdate'] = age_string($age);
+						$historyline['agestringage'] = date('Y-m-d', $co2->GetCommitterEpoch());
+					}
+					$historyline["authorname"] = $co2->GetAuthorName();
+					$historyline["commit"] = $commit;
+					$historyline["file"] = $this->params['file'];
+					$historyline["title"] = $co2->GetTitle(GITPHP_TRIM_LENGTH);
+					if (isset($refs[$commit]))
+						$historyline["commitref"] = $refs[$commit];
+					$blob = git_get_hash_by_path($this->params['hash'],$this->params['file']);
+					$blob_parent = git_get_hash_by_path($commit,$this->params['file']);
+					if ($blob && $blob_parent && ($blob != $blob_parent)) {
+						$historyline["blob"] = $blob;
+						$historyline["blobparent"] = $blob_parent;
+					}
+					$historylines[] = $historyline;
+					unset($co2);
+					unset($commit);
+			}
+		}
+		$this->tpl->assign("historylines",$historylines);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Log.class.php
@@ -1,1 +1,201 @@
-
+<?php
+/**
+ * GitPHP Controller Log
+ *
+ * Controller for displaying a log
+ *
+ * @author Christopher Han
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
+
+require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
+require_once(GITPHP_INCLUDEDIR . 'util.age_string.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_read_revlist.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+
+/**
+ * Log controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Log extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for log', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		if (isset($this->params['short']) && ($this->params['short'] === true)) {
+			return 'shortlog.tpl';
+		}
+		return 'log.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return $this->params['hash'] . '|' . $this->params['page'];
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+		else
+			$this->params['hash'] = 'HEAD';
+		if (isset($_GET['pg']))
+			$this->params['page'] = $_GET['pg'];
+		else
+			$this->params['page'] = 0;
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		if (isset($this->params['short']) && ($this->params['short'] === true)) {
+			$this->LoadDataShort();
+			return;
+		}
+		$head = $this->project->GetHeadCommit()->GetHash();
+		$refs = read_info_ref();
+		$this->tpl->assign("hash",$this->params['hash']);
+		$this->tpl->assign("head",$head);
+
+		if ($this->params['page'])
+			$this->tpl->assign("page",$this->params['page']);
+
+		$revlist = git_read_revlist($this->params['hash'], 101, ($this->params['page'] * 100));
+
+		$revlistcount = count($revlist);
+		$this->tpl->assign("revlistcount",$revlistcount);
+
+		if (!$revlist) {
+			$this->tpl->assign("norevlist",TRUE);
+			$co = $this->project->GetCommit($this->params['hash']);
+			$this->tpl->assign("lastchange", age_string($co->GetAge()));
+		}
+
+		$commitlines = array();
+		$commitcount = min(100,$revlistcount);
+		for ($i = 0; $i < $commitcount; ++$i) {
+			$commit = $revlist[$i];
+			if (isset($commit) && strlen($commit) > 1) {
+				$commitline = array();
+				$co = $this->project->GetCommit($commit);
+				$ad = date_str($co->GetAuthorEpoch());
+				$commitline["project"] = $this->project->GetProject();
+				$commitline["commit"] = $commit;
+				if (isset($refs[$commit]))
+					$commitline["commitref"] = $refs[$commit];
+				$commitline["agestring"] = age_string($co->GetAge());
+				$commitline["title"] = $co->GetTitle();
+				$commitline["authorname"] = $co->GetAuthorName();
+				$commitline["rfc2822"] = $ad['rfc2822'];
+				$commitline["comment"] = $co->GetComment();
+				$commitlines[] = $commitline;
+				unset($co);
+			}
+		}
+		$this->tpl->assign("commitlines",$commitlines);
+	}
+
+	/**
+	 * LoadDataShort
+	 *
+	 * Load data for shortlog
+	 * TODO: temporary until templates get cleaned up more
+	 */
+	private function LoadDataShort()
+	{
+		$head = $this->project->GetHeadCommit();;
+		$refs = read_info_ref();
+		$this->tpl->assign("hash",$this->params['hash']);
+		$this->tpl->assign("head",$head->GetHash());
+
+		if ($page)
+			$this->tpl->assign("page",$page);
+
+		$revlist = git_read_revlist($this->params['hash'], 101, ($page * 100));
+
+		$revlistcount = count($revlist);
+		$this->tpl->assign("revlistcount",$revlistcount);
+
+		$commitlines = array();
+		$commitcount = min(100,count($revlist));
+		for ($i = 0; $i < $commitcount; ++$i) {
+			$commit = $revlist[$i];
+			if (strlen(trim($commit)) > 0) {
+				$commitline = array();
+				if (isset($refs[$commit]))
+					$commitline["commitref"] = $refs[$commit];
+				$co = $this->project->GetCommit($commit);
+				$ad = date_str($co->GetAuthorEpoch());
+				$commitline["commit"] = $commit;
+				$age = $co->GetAge();
+				if ($age > 60*60*24*7*2) {
+					$commitline["agestringdate"] = date('Y-m-d', $co->GetCommitterEpoch());
+					$commitline["agestringage"] = age_string($age);
+				} else {
+					$commitline["agestringdate"] = age_string($age);
+					$commitline["agestringage"] = date('Y-m-d', $co->GetCommitterEpoch());
+				}
+				$commitline["authorname"] = $co->GetAuthorName();
+				$titleshort = $co->GetTitle(GITPHP_TRIM_LENGTH);
+				$title = $co->GetTitle();
+				$commitline["title_short"] = $titleshort;
+				if (strlen($titleshort) < strlen($title))
+					$commitline["title"] = $title;
+				$commitlines[] = $commitline;
+				unset($co);
+			}
+		}
+		$this->tpl->assign("commitlines",$commitlines);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Message.class.php
@@ -1,1 +1,90 @@
+<?php
+/**
+ * GitPHP Controller Message
+ *
+ * Controller for displaying a message page
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+/**
+ * Message controller class
+ * 
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Message extends GitPHP_ControllerBase
+{
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		try {
+			parent::__construct();
+		} catch (Exception $e) {
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'message.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return sha1($this->params['message']) . '|' . ($this->params['error'] ? '1' : '0');;
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$this->tpl->assign('message', $this->params['message']);
+		if (isset($this->params['error']) && ($this->params['error'])) {
+			$this->tpl->assign('error', true);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Project.class.php
@@ -1,1 +1,117 @@
+<?php
+/**
+ * GitPHP Controller Project
+ *
+ * Controller for displaying a project summary
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
+require_once(GITPHP_INCLUDEDIR . 'util.age_string.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_read_revlist.php');
+
+/**
+ * Project controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Project extends GitPHP_ControllerBase
+{
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for project summary', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'project.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return '';
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$descr = $this->project->GetDescription();
+		$headCommit = $this->project->GetHeadCommit();
+		$commitdate = date_str($headCommit->GetCommitterEpoch(), $headCommit->GetCommitterTimezone());
+		$owner = $this->project->GetOwner();
+		$this->tpl->assign("head", $headCommit->GetHash());
+		$this->tpl->assign("description",$descr);
+		$this->tpl->assign("owner",$owner);
+		$this->tpl->assign("lastchange",$commitdate['rfc2822']);
+		if (GitPHP_Config::GetInstance()->HasKey('cloneurl'))
+			$this->tpl->assign('cloneurl', GitPHP_Config::GetInstance()->GetValue('cloneurl') . $this->project->GetProject());
+		if (GitPHP_Config::GetInstance()->HasKey('pushurl'))
+			$this->tpl->assign('pushurl', GitPHP_Config::GetInstance()->GetValue('pushurl') . $this->project->GetProject());
+		$revlist = git_read_revlist($headCommit->GetHash(), 17);
+		foreach ($revlist as $i => $rev) {
+			$revlist[$i] = $this->project->GetCommit($rev);
+		}
+		$this->tpl->assign("revlist",$revlist);
+
+		$taglist = $this->project->GetTags();
+		if (isset($taglist) && (count($taglist) > 0)) {
+			$this->tpl->assign("taglist",$taglist);
+		}
+
+		$headlist = $this->project->GetHeads();
+		if (isset($headlist) && (count($headlist) > 0)) {
+			$this->tpl->assign("headlist",$headlist);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_ProjectList.class.php
@@ -1,1 +1,122 @@
+<?php
+/**
+ * GitPHP Controller ProjectList
+ *
+ * Controller for listing projects
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+/**
+ * ProjectList controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_ProjectList extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
+			return 'opml.tpl';
+		} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
+			return 'projectindex.tpl';
+		}
+		return 'projectlist.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
+			return '';
+		} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
+			return '';
+		}
+		return $this->params['order'];
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['o']))
+			$this->params['order'] = $_GET['o'];
+		else
+			$this->params['order'] = 'project';
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		if (isset($this->params['opml']) && ($this->params['opml'] === true)) {
+			$this->headers[] = "Content-type: text/xml; charset=UTF-8";
+		} else if (isset($this->params['txt']) && ($this->params['txt'] === true)) {
+			$this->headers[] = "Content-type: text/plain; charset=utf-8";
+			$this->headers[] = "Content-Disposition: inline; filename=\"index.aux\"";
+		}
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$this->tpl->assign('order', $this->params['order']);
+		
+		$projectList = GitPHP_ProjectList::GetInstance();
+		$projectList->Sort($this->params['order']);
+		
+		if ($projectList->Count() > 0)
+			$this->tpl->assign('projectlist', $projectList);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Rss.class.php
@@ -1,1 +1,141 @@
+<?php
+/**
+ * GitPHP Controller RSS
+ *
+ * Controller for displaying a project's RSS
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_read_revlist.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_diff_tree.php');
+
+/**
+ * RSS controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Rss extends GitPHP_ControllerBase
+{
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for RSS', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'rss.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return '';
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		$this->headers[] = "Content-type: text/xml; charset=UTF-8";
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$head = $this->project->GetHeadCommit();;
+		$revlist = git_read_revlist($head->GetHash(), GITPHP_RSS_ITEMS);
+
+		$commitlines = array();
+		$revlistcount = count($revlist);
+		for ($i = 0; $i < $revlistcount; ++$i) {
+			$commit = $revlist[$i];
+			$co = $this->project->GetCommit($commit);
+			if (($i >= 20) && ((time() - $co->GetCommitterEpoch()) > 48*60*60))
+				break;
+			$cd = date_str($co->GetCommitterEpoch());
+			$commitline = array();
+			$commitline["cdmday"] = $cd['mday'];
+			$commitline["cdmonth"] = $cd['month'];
+			$commitline["cdhour"] = $cd['hour'];
+			$commitline["cdminute"] = $cd['minute'];
+			$commitline["title"] = $co->GetTitle();
+			$commitline["author"] = $co->GetAuthor();
+			$commitline["cdrfc2822"] = $cd['rfc2822'];
+			$commitline["commit"] = $commit;
+			$commitline["comment"] = $co->GetComment();
+
+			$parent = $co->GetParent();
+			if ($parent) {
+				$difftree = array();
+				$diffout = git_diff_tree($parent->GetHash() . " " . $co->GetHash());
+				$tok = strtok($diffout,"\n");
+				while ($tok !== false) {
+					if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/",$tok,$regs))
+						$difftree[] = $regs[7];
+					$tok = strtok("\n");
+				}
+				$commitline["difftree"] = $difftree;
+			}
+
+			$commitlines[] = $commitline;
+			unset($co);
+		}
+		$this->tpl->assign("commitlines",$commitlines);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Search.class.php
@@ -1,1 +1,256 @@
-
+<?php
+/**
+ * GitPHP Controller Search
+ *
+ * Controller for running a search
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
+
+require_once(GITPHP_INCLUDEDIR . 'util.age_string.php');
+require_once(GITPHP_INCLUDEDIR . 'util.highlight.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_read_revlist.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_filesearch.php');
+
+/**
+ * Search controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Search extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		if (!GitPHP_Config::GetInstance()->GetValue('search', true)) {
+			throw new GitPHP_MessageException('Search has been disabled', true);
+		}
+
+		parent::__construct();
+
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for search', true);
+		}
+
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		if (isset($this->params['searchtype']) && ($this->params['searchtype'] == 'file')) {
+			return 'searchfiles.tpl';
+		}
+		return 'search.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['searchtype']) ? sha1($this->params['searchtype']) : '') . '|' . (isset($this->params['search']) ? sha1($this->params['search']) : '') . '|' . (isset($this->params['page']) ? $this->params['page'] : 0);
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['st']))
+			$this->params['searchtype'] = $_GET['st'];
+
+		if (isset($this->params['searchtype']) && ($this->params['searchtype'] == 'file')) {
+			if (!GitPHP_Config::GetInstance()->GetValue('filesearch', true)) {
+				throw new GitPHP_MessageException('File search has been disabled', true);
+			}
+
+		}
+
+		if (isset($_GET['s']))
+			$this->params['search'] = $_GET['s'];
+
+		if ((!isset($this->params['search'])) || (strlen($this->params['search']) < 2)) {
+			throw new GitPHP_MessageException('You must enter search text of at least 2 characters', true);
+		}
+
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+		else
+			$this->params['hash'] = 'HEAD';
+		if (isset($_GET['pg']))
+			$this->params['page'] = $_GET['pg'];
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		if (isset($this->params['searchtype']) && ($this->params['searchtype'] == 'file')) {
+			$this->LoadFilesearchData();
+			return;
+		}
+
+		$co = $this->project->GetCommit($this->params['hash']);
+
+		$revlist = git_read_revlist($this->params['hash'], 101, ($this->params['page'] * 100), FALSE, FALSE, $this->params['searchtype'], $this->params['search']);
+		if (count($revlist) < 1 || (strlen($revlist[0]) < 1)) {
+			throw new GitPHP_MessageException('No matches for "' . $this->params['search'] . '"', false);
+		}
+
+		$this->tpl->assign("hash",$this->params['hash']);
+		$this->tpl->assign("treehash", $co->GetTree()->GetHash());
+
+		$this->tpl->assign("search",$this->params['search']);
+		$this->tpl->assign("searchtype",$this->params['searchtype']);
+		$this->tpl->assign("page",$this->params['page']);
+		$revlistcount = count($revlist);
+		$this->tpl->assign("revlistcount",$revlistcount);
+
+		$this->tpl->assign("title", $co->GetTitle());
+
+		date_default_timezone_set('UTC');
+		$commitlines = array();
+		$commitcount = min(100,$revlistcount);
+		for ($i = 0; $i < $commitcount; ++$i) {
+			$commit = $revlist[$i];
+			if (strlen(trim($commit)) > 0) {
+				$commitline = array();
+				$co2 = $this->project->GetCommit($commit);
+				$commitline["commit"] = $commit;
+				$age = $co2->GetAge();
+				if ($age > 60*60*24*7*2) {
+					$commitline['agestringdate'] = date('Y-m-d', $co2->GetCommitterEpoch());
+					$commitline['agestringage'] = age_string($age);
+				} else {
+					$commitline['agestringdate'] = age_string($age);
+					$commitline['agestringage'] = date('Y-m-d', $co2->GetCommitterEpoch());
+				}
+				$commitline["authorname"] = $co2->GetAuthorName();
+				$title = $co2->GetTitle();
+				$titleshort = $co2->GetTitle(GITPHP_TRIM_LENGTH);
+				$commitline["title_short"] = $titleshort;
+				if (strlen($titleshort) < strlen($title))
+					$commitline["title"] = $title;
+				$commitline["committree"] = $co2->GetTree()->GetHash();
+				$matches = array();
+				$commentlines = $co2->GetComment();
+				foreach ($commentlines as $comline) {
+					$hl = highlight($comline, $this->params['search'], "searchmatch", GITPHP_TRIM_LENGTH);
+					if ($hl && (strlen($hl) > 0))
+						$matches[] = $hl;
+				}
+				$commitline["matches"] = $matches;
+				$commitlines[] = $commitline;
+				unset($co2);
+			}
+		}
+		
+		$this->tpl->assign("commitlines",$commitlines);
+	}
+	
+	/**
+	 * LoadFilesearchData
+	 *
+	 * TODO temporary until templates are cleaned up
+	 */
+	private function LoadFilesearchData()
+	{
+		$filesearch = git_filesearch($this->params['hash'], $this->params['search'], false, ($this->params['page'] * 100), 101);
+
+		if (count($filesearch) < 1) {
+			throw new GitPHP_MessageException('No matches for "' . $this->params['search'] . '"');
+		}
+
+		$this->tpl->assign("hash",$this->params['hash']);
+
+		$co = $this->project->GetCommit($this->params['hash']);
+
+		if ($co) {
+			$tree = $co->GetTree();
+			if ($tree)
+				$this->tpl->assign("treehash", $tree->GetHash());
+			$this->tpl->assign("title", $co->GetTitle());
+		}
+
+		$this->tpl->assign("search",$this->params['search']);
+		$this->tpl->assign("searchtype","file");
+		$this->tpl->assign("page",$this->params['page']);
+		$filesearchcount = count($filesearch);
+		$this->tpl->assign("filesearchcount",$filesearchcount);
+
+
+		$filesearchlines = array();
+		$i = 0;
+		foreach ($filesearch as $file => $data) {
+			$filesearchline = array();
+			$filesearchline["file"] = $file;
+			if (strpos($file,"/") !== false) {
+				$f = basename($file);
+				$d = dirname($file);
+				if ($d == "/")
+					$d = "";
+				$hlt = highlight($f, $this->params['search'], "searchmatch");
+				if ($hlt)
+					$hlt = $d . "/" . $hlt;
+			} else
+				$hlt = highlight($file, $this->params['search'], "searchmatch");
+			if ($hlt)
+				$filesearchline["filename"] = $hlt;
+			else
+				$filesearchline["filename"] = $file;
+			$filesearchline["hash"] = $data['hash'];
+			if ($data['type'] == "tree")
+				$filesearchline["tree"] = TRUE;
+			if (isset($data['lines'])) {
+				$matches = array();
+				foreach ($data['lines'] as $line) {
+					$hlt = highlight($line,$this->params['search'],"searchmatch",floor(GITPHP_TRIM_LENGTH*1.5),true);
+					if ($hlt)
+						$matches[] = $hlt;
+				}
+				if (count($matches) > 0)
+					$filesearchline["matches"] = $matches;
+			}
+			$filesearchlines[] = $filesearchline;
+			++$i;
+			if ($i >= 100)
+				break;
+		}
+		$this->tpl->assign("filesearchlines",$filesearchlines);
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Snapshot.class.php
@@ -1,1 +1,125 @@
+<?php
+/**
+ * GitPHP Controller Snapshot
+ *
+ * Controller for getting a snapshot
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+/**
+ * Snapshot controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Snapshot extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for snapshot', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'snapshot.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return isset($this->params['hash']) ? $this->params['hash'] : '';
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+	}
+
+	/**
+	 * LoadHeaders
+	 *
+	 * Loads headers for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadHeaders()
+	{
+		$this->params['compressformat'] = GitPHP_Config::GetInstance()->GetValue('compressformat', GITPHP_COMPRESS_ZIP);
+		$rname = $this->project->GetSlug();
+
+		if ($this->params['compressformat'] == GITPHP_COMPRESS_ZIP) {
+			$this->headers[] = 'Content-Type: application/x-zip';
+			$this->headers[] = 'Content-Disposition: attachment; filename=' . $rname . '.zip';
+		} else if (($this->params['compressformat'] == GITPHP_COMPRESS_BZ2) && function_exists('bzcompress')) {
+			$this->headers[] = 'Content-Type: application/x-bzip2';
+			$this->headers[] = 'Content-Disposition: attachment; filename=' . $rname . '.tar.bz2';
+		} else if (($this->params['compressformat'] == GITPHP_COMPRESS_GZ) && function_exists('gzencode')) {
+			$this->headers[] = 'Content-Type: application/x-gzip';
+			$this->headers[] = 'Content-Disposition: attachment; filename=' . $rname . '.tar.gz';
+		} else {
+			$this->headers[] = 'Content-Type: application/x-tar';
+			$this->headers[] = 'Content-Disposition: attachment; filename=' . $rname . '.tar';
+		}
+
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$commit = null;
+
+		if (!isset($hash))
+			$commit = $this->project->GetHeadCommit();
+		else
+			$commit = $this->project->GetCommit($hash);
+
+		$this->tpl->assign("archive", $commit->GetArchive($this->params['compressformat']));
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Tag.class.php
@@ -1,1 +1,104 @@
+<?php
+/**
+ * GitPHP Controller Tag
+ *
+ * Controller for displaying a tag
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
+
+/**
+ * Tag controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Tag extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for tag', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'tag.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return isset($this->params['hash']) ? sha1($this->params['hash']) : '';
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['h'])) {
+			$this->params['hash'] = $_GET['h'];
+		}
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$head = $this->project->GetHeadCommit()->GetHash();
+		$this->tpl->assign("head",$head);
+		$this->tpl->assign("hash", $this->params['hash']);
+
+		$tag = new GitPHP_Tag($this->project, $this->params['hash']);
+
+		$this->tpl->assign("tag", $tag);
+		$tagger = $tag->GetTagger();
+		if (!empty($tagger)) {
+			$ad = date_str($tag->GetTaggerEpoch(), $tag->GetTaggerTimezone());
+			$this->tpl->assign("datedata",$ad);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Tags.class.php
@@ -1,1 +1,94 @@
+<?php
+/**
+ * GitPHP Controller Tags
+ *
+ * Controller for displaying tags
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+/**
+ * Tags controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Tags extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for tags', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'tags.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return '';
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		$head = $this->project->GetHeadCommit()->GetHash();
+		$this->tpl->assign("head",$head);
+
+		$taglist = $this->project->GetTags();
+		if (isset($taglist) && (count($taglist) > 0)) {
+			$this->tpl->assign("taglist",$taglist);
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/controller/Controller_Tree.class.php
@@ -1,1 +1,141 @@
+<?php
+/**
+ * GitPHP Controller Tree
+ *
+ * Controller for displaying a tree
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Controller
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'util.mode_str.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_get_hash_by_path.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_ls_tree.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.read_info_ref.php');
+require_once(GITPHP_INCLUDEDIR . 'gitutil.git_path_trees.php');
+
+/**
+ * Tree controller class
+ *
+ * @package GitPHP
+ * @subpackage Controller
+ */
+class GitPHP_Controller_Tree extends GitPHP_ControllerBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @return controller
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+		if (!$this->project) {
+			throw new GitPHP_MessageException('Project is required for tree', true);
+		}
+	}
+
+	/**
+	 * GetTemplate
+	 *
+	 * Gets the template for this controller
+	 *
+	 * @access protected
+	 * @return string template filename
+	 */
+	protected function GetTemplate()
+	{
+		return 'tree.tpl';
+	}
+
+	/**
+	 * GetCacheKey
+	 *
+	 * Gets the cache key for this controller
+	 *
+	 * @access protected
+	 * @return string cache key
+	 */
+	protected function GetCacheKey()
+	{
+		return (isset($this->params['hashbase']) ? $this->params['hashbase'] : '') . '|' . (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
+	}
+
+	/**
+	 * ReadQuery
+	 *
+	 * Read query into parameters
+	 *
+	 * @access protected
+	 */
+	protected function ReadQuery()
+	{
+		if (isset($_GET['f']))
+			$this->params['file'] = $_GET['f'];
+		if (isset($_GET['h']))
+			$this->params['hash'] = $_GET['h'];
+		if (isset($_GET['hb']))
+			$this->params['hashbase'] = $_GET['hb'];
+	}
+
+	/**
+	 * LoadData
+	 *
+	 * Loads data for this template
+	 *
+	 * @access protected
+	 */
+	protected function LoadData()
+	{
+		if (!isset($this->params['hash'])) {
+			$this->params['hash'] = $this->project->GetHeadCommit()->GetHash();
+			if (isset($this->params['file']))
+				$this->params['hash'] = git_get_hash_by_path((isset($this->params['hashbase']) ? $this->params['hashbase'] : $this->params['hash']), $this->params['file'], 'tree');
+		}
+		if (!isset($this->params['hashbase'])) {
+			$this->params['hashbase'] = $this->params['hash'];
+		}
+		$lsout = git_ls_tree($this->params['hash'], TRUE);
+		$refs = read_info_ref();
+		$this->tpl->assign("hash",$this->params['hash']);
+		if (isset($this->params['hashbase']))
+			$this->tpl->assign("hashbase",$this->params['hashbase']);
+		if (isset($this->params['hashbase'])) {
+			$co = $this->project->GetCommit($this->params['hashbase']);
+			if ($co) {
+				$this->tpl->assign("fullnav",TRUE);
+				$this->tpl->assign("title",$co->GetTitle());
+				if (isset($refs[$this->params['hashbase']]))
+					$this->tpl->assign("hashbaseref",$refs[$this->params['hashbase']]);
+			}
+		}
+		$paths = git_path_trees($this->params['hashbase'], $this->params['file']);
+		$this->tpl->assign("paths",$paths);
+
+		if (isset($this->params['file']))
+			$this->tpl->assign("base",$this->params['file'] . "/");
+
+		$treelines = array();
+		$tok = strtok($lsout,"\0");
+		while ($tok !== false) {
+			if (preg_match("/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/",$tok,$regs)) {
+				$treeline = array();
+				$treeline["filemode"] = mode_str($regs[1]);
+				$treeline["type"] = $regs[2];
+				$treeline["hash"] = $regs[3];
+				$treeline["name"] = $regs[4];
+				$treelines[] = $treeline;
+			}
+			$tok = strtok("\0");
+		}
+		$this->tpl->assign("treelines",$treelines);
+	}
+
+}
+

--- a/include/display.git_blame.php
+++ /dev/null
@@ -1,60 +1,1 @@
-<?php
-/*
- * display.git_blame.php
- * gitphp: A PHP git repository browser
- * Component: Display - blame
- *
- * Copyright (C) 2010 Christopher Han <xiphux@gmail.com>
- */
 
-require_once('gitutil.git_parse_blame.php');
-require_once('gitutil.read_info_ref.php');
-require_once('gitutil.git_path_trees.php');
-
-function git_blame($hash, $file, $hashbase)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . sha1($file) . "|" . $hashbase;
-
-	if (!$tpl->is_cached('blame.tpl', $cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit()->GetHash();
-		if (!isset($hashbase))
-			$hashbase = $head;
-		if (!isset($hash) && isset($file))
-			$hash = git_get_hash_by_path($hashbase,$file,"blob");
-		$tpl->assign("hash",$hash);
-		$tpl->assign("hashbase",$hashbase);
-		$tpl->assign("head", $head);
-		$co = $gitphp_current_project->GetCommit($hashbase);
-		if ($co) {
-			$tpl->assign("fullnav",TRUE);
-			$refs = read_info_ref();
-			$tpl->assign("tree",$co->GetTree()->GetHash());
-			$tpl->assign("title",$co->GetTitle());
-			if (isset($file))
-				$tpl->assign("file",$file);
-			if ($hashbase == "HEAD") {
-				if (isset($refs[$head]))
-					$tpl->assign("hashbaseref",$refs[$head]);
-			} else {
-				if (isset($refs[$hashbase]))
-					$tpl->assign("hashbaseref",$refs[$hashbase]);
-			}
-		}
-		$paths = git_path_trees($hashbase, $file);
-		$tpl->assign("paths",$paths);
-
-		$blamedata = git_parse_blame($file, $hashbase);
-		$tpl->assign("blamedata",$blamedata);
-
-	}
-
-	$tpl->display('blame.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_blob.php
+++ /dev/null
@@ -1,99 +1,1 @@
-<?php
-/*
- *  display.git_blob.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - blob
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('gitutil.git_get_hash_by_path.php');
- require_once('gitutil.git_cat_file.php');
- require_once('gitutil.git_path_trees.php');
- require_once('gitutil.read_info_ref.php');
- require_once('util.file_mime.php');
-
-function git_blob($hash, $file, $hashbase)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hashbase . "|" . $hash . "|" . sha1($file);
-
-	if (!$tpl->is_cached('blob.tpl',$cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit()->GetHash();
-		if (!isset($hashbase))
-			$hashbase = $head;
-		if (!isset($hash) && isset($file))
-			$hash = git_get_hash_by_path($hashbase,$file,"blob");
-		$catout = git_cat_file($hash);
-		$tpl->assign("hash",$hash);
-		$tpl->assign("hashbase",$hashbase);
-		$tpl->assign("head", $head);
-		$co = $gitphp_current_project->GetCommit($hashbase);
-		if ($co) {
-			$tpl->assign("fullnav",TRUE);
-			$refs = read_info_ref();
-			$tpl->assign("tree",$co->GetTree()->GetHash());
-			$tpl->assign("title",$co->GetTitle());
-			if (isset($file))
-				$tpl->assign("file",$file);
-			if ($hashbase == "HEAD") {
-				if (isset($refs[$head]))
-					$tpl->assign("hashbaseref",$refs[$head]);
-			} else {
-				if (isset($refs[$hashbase]))
-					$tpl->assign("hashbaseref",$refs[$hashbase]);
-			}
-		}
-		$paths = git_path_trees($hashbase, $file);
-		$tpl->assign("paths",$paths);
-
-		if (GitPHP_Config::GetInstance()->GetValue('filemimetype', true)) {
-			$mime = file_mime($catout,$file);
-			if ($mime)
-				$mimetype = strtok($mime, "/");
-		}
-
-		if ($mimetype == "image") {
-			$tpl->assign("mime", $mime);
-			$tpl->assign("data", base64_encode($catout));
-		} else {
-			$usedgeshi = GitPHP_Config::GetInstance()->GetValue('geshi', true);
-			if ($usedgeshi) {
-				$usedgeshi = FALSE;
-				include_once(GitPHP_Config::GetInstance()->GetValue('geshiroot', 'lib/geshi/') . "geshi.php");
-				if (class_exists("GeSHi")) {
-					$geshi = new GeSHi("",'php');
-					if ($geshi) {
-						$lang = "";
-						if (isset($file))
-							$lang = $geshi->get_language_name_from_extension(substr(strrchr($file,'.'),1));
-						if (isset($lang) && (strlen($lang) > 0)) {
-							$geshi->enable_classes();
-							$geshi->set_source($catout);
-							$geshi->set_language($lang);
-							$geshi->set_header_type(GESHI_HEADER_DIV);
-							$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS);
-							$tpl->assign("geshiout",$geshi->parse_code());
-							$tpl->assign("extracss",$geshi->get_stylesheet());
-							$usedgeshi = TRUE;
-						}
-					}
-				}
-			}
-
-			if (!$usedgeshi) {
-				$lines = explode("\n",$catout);
-				$tpl->assign("lines",$lines);
-			}
-		}
-	}
-
-	$tpl->display('blob.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_blob_plain.php
+++ /dev/null
@@ -1,61 +1,1 @@
-<?php
-/*
- *  display.git_blob_plain.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - blob (plaintext)
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('gitutil.git_cat_file.php');
- require_once('util.file_mime.php');
-
-function git_blob_plain($projectroot,$project,$hash,$file)
-{
-	global $tpl;
-
-	$cachekey = sha1($project) . "|" . $hash . "|" . sha1($file);
-
-	$buffer = null;
-
-	// XXX: Nasty hack to cache headers
-	if (!$tpl->is_cached('blobheaders.tpl', $cachekey)) {
-		if ($file)
-			$saveas = $file;
-		else
-			$saveas = $hash . ".txt";
-
-		$buffer = git_cat_file($hash);
-
-		if (GitPHP_Config::GetInstance()->GetValue('filemimetype', true))
-			$mime = file_mime($buffer, $file);
-
-		$headers = array();
-
-		if ($mime)
-			$headers[] = "Content-type: " . $mime;
-		else
-			$headers[] = "Content-type: text/plain; charset=UTF-8";
-
-		$headers[] = "Content-disposition: inline; filename=\"" . $saveas . "\"";
-
-		$tpl->assign("blobheaders", serialize($headers));
-	}
-	$out = $tpl->fetch('blobheaders.tpl', $cachekey);
-
-	$returnedheaders = unserialize($out);
-
-	foreach ($returnedheaders as $i => $header)
-		header($header);
-
-
-	if (!$tpl->is_cached('blobplain.tpl', $cachekey)) {
-		if (!$buffer)
-			$buffer = git_cat_file($hash);
-		$tpl->assign("blob", $buffer);
-	}
-	$tpl->display('blobplain.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_blobdiff.php
+++ /dev/null
@@ -1,53 +1,1 @@
-<?php
-/*
- *  display.git_blobdiff.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - blob diff
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.prep_tmpdir.php');
- require_once('gitutil.read_info_ref.php');
- require_once('gitutil.git_path_trees.php');
- require_once('gitutil.git_diff.php');
-
-function git_blobdiff($hash,$hashbase,$hashparent,$file)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hashbase . "|" . $hash . "|" . $hashparent . "|" . sha1($file);
-
-	if (!$tpl->is_cached('blobdiff.tpl', $cachekey)) {
-		$ret = prep_tmpdir();
-		if ($ret !== TRUE) {
-			echo $ret;
-			return;
-		}
-		$tpl->assign("hash",$hash);
-		$tpl->assign("hashparent",$hashparent);
-		$tpl->assign("hashbase",$hashbase);
-		if (isset($file))
-			$tpl->assign("file",$file);
-		$co = $gitphp_current_project->GetCommit($hashbase);
-		if ($co) {
-			$tpl->assign("fullnav",TRUE);
-			$tpl->assign("tree",$co->GetTree()->GetHash());
-			$tpl->assign("title",$co->GetTitle());
-			$refs = read_info_ref();
-			if (isset($refs[$hashbase]))
-				$tpl->assign("hashbaseref",$refs[$hashbase]);
-		}
-		$paths = git_path_trees($hashbase, $file);
-		$tpl->assign("paths",$paths);
-		$diffout = explode("\n",git_diff($hashparent,($file?$file:$hashparent),$hash,($file?$file:$hash)));
-		$tpl->assign("diff",$diffout);
-	}
-	$tpl->display('blobdiff.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_blobdiff_plain.php
+++ /dev/null
@@ -1,33 +1,1 @@
-<?php
-/*
- *  display.git_blobdiff_plain.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - blob diff (plaintext)
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.prep_tmpdir.php');
- require_once('gitutil.git_diff.php');
-
-function git_blobdiff_plain($projectroot,$project,$hash,$hashbase,$hashparent,$file)
-{
-	global $tpl;
-
-	header("Content-type: text/plain; charset=UTF-8");
-
-	$cachekey = sha1($project) . "|" . $hashbase . "|" . $hash . "|" . $hashparent . "|" . sha1($file);
-
-	if (!$tpl->is_cached('blobdiffplain.tpl', $cachekey)) {
-		$ret = prep_tmpdir();
-		if ($ret !== TRUE) {
-			echo $ret;
-			return;
-		}
-		$tpl->assign("blobdiff",git_diff($hashparent,($file?"a/".$file:$hashparent),$hash,($file?"b/".$file:$hash)));
-	}
-	$tpl->display('blobdiffplain.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_commit.php
+++ /dev/null
@@ -1,94 +1,1 @@
-<?php
-/*
- *  display.git_commit.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - commit
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.file_type.php');
- require_once('util.date_str.php');
- require_once('gitutil.git_diff_tree.php');
-
-function git_commit($hash)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash;
-
-	if (!$tpl->is_cached('commit.tpl', $cachekey)) {
-		$commit = $gitphp_current_project->GetCommit($hash);
-		$ad = date_str($commit->GetAuthorEpoch(), $commit->GetAuthorTimezone());
-		$cd = date_str($commit->GetCommitterEpoch(), $commit->GetCommitterTimezone());
-		$parentObj = $commit->GetParent();
-		if ($parentObj) {
-			$root = "";
-			$parent = $parentObj->GetHash();
-		} else {
-			$root = "--root";
-			$parent = "";
-		}
-		$diffout = git_diff_tree($root . " " . $parent . " " . $hash, TRUE);
-		$difftree = explode("\n",$diffout);
-		$treeObj = $commit->GetTree();
-		if ($treeObj)
-			$tpl->assign("tree", $treeObj->GetHash());
-		if ($parentObj)
-			$tpl->assign("parent", $parentObj->GetHash());
-		$tpl->assign("commit", $commit);
-		$tpl->assign("adrfc2822",$ad['rfc2822']);
-		$tpl->assign("adhourlocal",$ad['hour_local']);
-		$tpl->assign("adminutelocal",$ad['minute_local']);
-		$tpl->assign("adtzlocal",$ad['tz_local']);
-		$tpl->assign("cdrfc2822",$cd['rfc2822']);
-		$tpl->assign("cdhourlocal",$cd['hour_local']);
-		$tpl->assign("cdminutelocal",$cd['minute_local']);
-		$tpl->assign("cdtzlocal",$cd['tz_local']);
-		$tpl->assign("difftreesize",count($difftree)+1);
-		$difftreelines = array();
-		foreach ($difftree as $i => $line) {
-			if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/",$line,$regs)) {
-				$difftreeline = array();
-				$difftreeline["from_mode"] = $regs[1];
-				$difftreeline["to_mode"] = $regs[2];
-				$difftreeline["from_mode_cut"] = substr($regs[1],-4);
-				$difftreeline["to_mode_cut"] = substr($regs[2],-4);
-				$difftreeline["from_id"] = $regs[3];
-				$difftreeline["to_id"] = $regs[4];
-				$difftreeline["status"] = $regs[5];
-				$difftreeline["similarity"] = ltrim($regs[6],"0");
-				$difftreeline["file"] = $regs[7];
-				$difftreeline["from_file"] = strtok($regs[7],"\t");
-				$difftreeline["from_filetype"] = file_type($regs[1]);
-				$difftreeline["to_file"] = strtok("\t");
-				$difftreeline["to_filetype"] = file_type($regs[2]);
-				if ((octdec($regs[2]) & 0x8000) == 0x8000)
-					$difftreeline["isreg"] = TRUE;
-				$modestr = "";
-				if ((octdec($regs[1]) & 0x17000) != (octdec($regs[2]) & 0x17000))
-					$modestr .= " from " . file_type($regs[1]) . " to " . file_type($regs[2]);
-				if ((octdec($regs[1]) & 0777) != (octdec($regs[2]) & 0777)) {
-					if ((octdec($regs[1]) & 0x8000) && (octdec($regs[2]) & 0x8000))
-						$modestr .= " mode: " . (octdec($regs[1]) & 0777) . "->" . (octdec($regs[2]) & 0777);
-					else if (octdec($regs[2]) & 0x8000)
-						$modestr .= " mode: " . (octdec($regs[2]) & 0777);
-				}
-				$difftreeline["modechange"] = $modestr;
-				$simmodechg = "";
-				if ($regs[1] != $regs[2])
-					$simmodechg .= ", mode: " . (octdec($regs[2]) & 0777);
-				$difftreeline["simmodechg"] = $simmodechg;
-				$difftreelines[] = $difftreeline;
-			}
-		}
-		$tpl->assign("difftreelines",$difftreelines);
-	}
-	$tpl->display('commit.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_commitdiff.php
+++ /dev/null
@@ -1,76 +1,1 @@
-<?php
-/*
- *  display.git_commitdiff.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - commit diff
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.file_type.php');
- require_once('util.prep_tmpdir.php');
- require_once('gitutil.git_diff_tree.php');
- require_once('gitutil.read_info_ref.php');
- require_once('gitutil.git_diff.php');
-
-function git_commitdiff($hash,$hash_parent)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . $hash_parent;
-
-	if (!$tpl->is_cached('commitdiff.tpl', $cachekey)) {
-		$ret = prep_tmpdir();
-		if ($ret !== TRUE) {
-			echo $ret;
-			return;
-		}
-		$co = $gitphp_current_project->GetCommit($hash);
-		if (!isset($hash_parent)) {
-			$parent = $co->GetParent();
-			if ($parent)
-				$hash_parent = $parent->GetHash();
-		}
-		$diffout = git_diff_tree($hash_parent . " " . $hash);
-		$difftree = explode("\n",$diffout);
-		$refs = read_info_ref();
-		$tpl->assign("hash",$hash);
-		$tree = $co->GetTree();
-		if ($tree)
-			$tpl->assign("tree", $tree->GetHash());
-		$tpl->assign("hashparent",$hash_parent);
-		$tpl->assign("title", $co->GetTitle());
-		if (isset($refs[$co->GetHash()]))
-			$tpl->assign("commitref",$refs[$co->GetHash()]);
-		$tpl->assign("comment",$co->GetComment());
-		$difftreelines = array();
-		foreach ($difftree as $i => $line) {
-			if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/",$line,$regs)) {
-				$difftreeline = array();
-				$difftreeline["from_mode"] = $regs[1];
-				$difftreeline["to_mode"] = $regs[2];
-				$difftreeline["from_id"] = $regs[3];
-				$difftreeline["to_id"] = $regs[4];
-				$difftreeline["status"] = $regs[5];
-				$difftreeline["file"] = $regs[6];
-				$difftreeline["from_type"] = file_type($regs[1]);
-				$difftreeline["to_type"] = file_type($regs[2]);
-				if ($regs[5] == "A")
-					$difftreeline['diffout'] = explode("\n",git_diff(null,"/dev/null",$regs[4],"b/" . $regs[6]));
-				else if ($regs[5] == "D")
-					$difftreeline['diffout'] = explode("\n",git_diff($regs[3],"a/" . $regs[6],null,"/dev/null"));
-				else if (($regs[5] == "M") && ($regs[3] != $regs[4]))
-					$difftreeline['diffout'] = explode("\n",git_diff($regs[3],"a/" . $regs[6],$regs[4],"b/" . $regs[6]));
-				$difftreelines[] = $difftreeline;
-			}
-		}
-		$tpl->assign("difftreelines",$difftreelines);
-	}
-	$tpl->display('commitdiff.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_commitdiff_plain.php
+++ /dev/null
@@ -1,77 +1,1 @@
-<?php
-/*
- *  display.git_commitdiff_plain.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - commit diff (plaintext)
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.prep_tmpdir.php');
- require_once('util.date_str.php');
- require_once('util.script_url.php');
- require_once('gitutil.git_diff_tree.php');
- require_once('gitutil.git_read_revlist.php');
- require_once('gitutil.read_info_ref.php');
- require_once('gitutil.git_diff.php');
-
-function git_commitdiff_plain($hash,$hash_parent)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . $hash_parent;
-
-	header("Content-type: text/plain; charset=UTF-8");
-	header("Content-disposition: inline; filename=\"git-" . $hash . ".patch\"");
-
-	if (!$tpl->is_cached('diff_plaintext.tpl', $cachekey)) {
-		$ret = prep_tmpdir();
-		if ($ret !== TRUE) {
-			echo $ret;
-			return;
-		}
-		$co = $gitphp_current_project->GetCommit($hash);
-		if (!isset($hash_parent)) {
-			$parent = $co->GetParent();
-			if ($parent)
-				$hash_parent = $parent->GetHash();
-		}
-		$diffout = git_diff_tree($hash_parent . " " . $hash);
-		$difftree = explode("\n",$diffout);
-		$refs = read_info_ref('tags');
-		$listout = git_read_revlist('HEAD');
-		foreach ($listout as $i => $rev) {
-			if (isset($refs[$rev]))
-				$tagname = $refs[$rev];
-			if ($rev == $hash)
-				break;
-		}
-		$ad = date_str($co->GetAuthorEpoch(), $co->GetAuthorTimezone());
-		$tpl->assign("from", $co->GetAuthor());
-		$tpl->assign("date",$ad['rfc2822']);
-		$tpl->assign("subject", $co->GetTitle());
-		if (isset($tagname))
-			$tpl->assign("tagname",$tagname);
-		$tpl->assign("url",script_url() . "?p=" . $gitphp_current_project->GetProject() . "&a=commitdiff&h=" . $hash);
-		$tpl->assign("comment", $co->GetComment());
-		$diffs = array();
-		foreach ($difftree as $i => $line) {
-			if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/",$line,$regs)) {
-				if ($regs[5] == "A")
-					$diffs[] = git_diff(null, "/dev/null", $regs[4], "b/" . $regs[6]);
-				else if ($regs[5] == "D")
-					$diffs[] = git_diff($regs[3], "a/" . $regs[6], null, "/dev/null");
-				else if ($regs[5] == "M")
-					$diffs[] = git_diff($regs[3], "a/" . $regs[6], $regs[4], "b/" . $regs[6]);
-			}
-		}
-		$tpl->assign("diffs",$diffs);
-	}
-	$tpl->display('diff_plaintext.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_heads.php
+++ /dev/null
@@ -1,32 +1,1 @@
-<?php
-/*
- *  display.git_heads.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - heads
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
-function git_heads()
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject());
-
-	if (!$tpl->is_cached('heads.tpl', $cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit()->GetHash();
-		$tpl->assign("head",$head);
-
-		$headlist = $gitphp_current_project->GetHeads();
-		if (isset($headlist) && (count($headlist) > 0)) {
-			$tpl->assign("headlist",$headlist);
-		}
-	}
-	$tpl->display('heads.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_history.php
+++ /dev/null
@@ -1,79 +1,1 @@
-<?php
-/*
- *  display.git_history.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - history
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once(GITPHP_INCLUDEDIR . 'defs.constants.php');
- require_once('gitutil.git_get_hash_by_path.php');
- require_once('gitutil.read_info_ref.php');
- require_once('gitutil.git_history_list.php');
- require_once('gitutil.git_path_trees.php');
-
-function git_history($hash,$file)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . sha1($file);
-
-	if (!$tpl->is_cached('history.tpl', $cachekey)) {
-		if (!isset($hash))
-			$hash = $gitphp_current_project->GetHeadCommit()->GetHash();
-
-		$co = $gitphp_current_project->GetCommit($hash);
-		$refs = read_info_ref();
-		$tpl->assign("hash",$hash);
-		if (isset($refs[$hash]))
-			$tpl->assign("hashbaseref",$refs[$hash]);
-		$tpl->assign("tree", $co->GetTree()->GetHash());
-		$tpl->assign("title", $co->GetTitle());
-		$paths = git_path_trees($hash, $file);
-		$tpl->assign("paths",$paths);
-		date_default_timezone_set('UTC');
-		$cmdout = git_history_list($hash, $file);
-		$lines = explode("\n", $cmdout);
-		$historylines = array();
-		foreach ($lines as $i => $line) {
-			if (preg_match("/^([0-9a-fA-F]{40})/",$line,$regs))
-				$commit = $regs[1];
-			else if (preg_match("/:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/",$line,$regs) && isset($commit)) {
-					$historyline = array();
-					$co2 = $gitphp_current_project->GetCommit($commit);
-					$age = $co2->GetAge();
-					if ($age > 60*60*24*7*2) {
-						$historyline['agestringdate'] = date('Y-m-d', $co2->GetCommitterEpoch());
-						$historyline['agestringage'] = age_string($age);
-					} else {
-						$historyline['agestringdate'] = age_string($age);
-						$historyline['agestringage'] = date('Y-m-d', $co2->GetCommitterEpoch());
-					}
-					$historyline["authorname"] = $co2->GetAuthorName();
-					$historyline["commit"] = $commit;
-					$historyline["file"] = $file;
-					$historyline["title"] = $co2->GetTitle(GITPHP_TRIM_LENGTH);
-					if (isset($refs[$commit]))
-						$historyline["commitref"] = $refs[$commit];
-					$blob = git_get_hash_by_path($hash,$file);
-					$blob_parent = git_get_hash_by_path($commit,$file);
-					if ($blob && $blob_parent && ($blob != $blob_parent)) {
-						$historyline["blob"] = $blob;
-						$historyline["blobparent"] = $blob_parent;
-					}
-					$historylines[] = $historyline;
-					unset($co2);
-					unset($commit);
-			}
-		}
-		$tpl->assign("historylines",$historylines);
-	}
-	$tpl->display('history.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_log.php
+++ /dev/null
@@ -1,75 +1,1 @@
-<?php
-/*
- *  display.git_log.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - log
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.date_str.php');
- require_once('util.age_string.php');
- require_once('gitutil.git_read_revlist.php');
- require_once('gitutil.read_info_ref.php');
-
-function git_log($hash,$page)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . (isset($page) ? $page : 0);
-
-	if (!$tpl->is_cached('log.tpl', $cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit()->GetHash();
-		if (!isset($hash))
-			$hash = $head;
-		if (!isset($page))
-			$page = 0;
-		$refs = read_info_ref();
-		$tpl->assign("hash",$hash);
-		$tpl->assign("head",$head);
-
-		if ($page)
-			$tpl->assign("page",$page);
-
-		$revlist = git_read_revlist($hash, 101, ($page * 100));
-
-		$revlistcount = count($revlist);
-		$tpl->assign("revlistcount",$revlistcount);
-
-		if (!$revlist) {
-			$tpl->assign("norevlist",TRUE);
-			$co = $gitphp_current_project->GetCommit($hash);
-			$tpl->assign("lastchange", age_string($co->GetAge()));
-		}
-
-		$commitlines = array();
-		$commitcount = min(100,$revlistcount);
-		for ($i = 0; $i < $commitcount; ++$i) {
-			$commit = $revlist[$i];
-			if (isset($commit) && strlen($commit) > 1) {
-				$commitline = array();
-				$co = $gitphp_current_project->GetCommit($commit);
-				$ad = date_str($co->GetAuthorEpoch());
-				$commitline["project"] = $gitphp_current_project->GetProject();
-				$commitline["commit"] = $commit;
-				if (isset($refs[$commit]))
-					$commitline["commitref"] = $refs[$commit];
-				$commitline["agestring"] = age_string($co->GetAge());
-				$commitline["title"] = $co->GetTitle();
-				$commitline["authorname"] = $co->GetAuthorName();
-				$commitline["rfc2822"] = $ad['rfc2822'];
-				$commitline["comment"] = $co->GetComment();
-				$commitlines[] = $commitline;
-				unset($co);
-			}
-		}
-		$tpl->assign("commitlines",$commitlines);
-	}
-	$tpl->display('log.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_message.php
+++ /dev/null
@@ -1,27 +1,1 @@
-<?php
-/*
- *  display.git_message.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - message
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- */
 
-function git_message($message, $error = FALSE, $standalone = TRUE)
-{
-	global $tpl;
-
-	$cachekey = sha1($message) . "|" . ($error ? "1" : "0") . "|" . ($standalone ? "1" : "0");
-
-	if (!$tpl->is_cached('message.tpl', $cachekey)) {
-		$tpl->assign("message",$message);
-		if ($error)
-			$tpl->assign("error", TRUE);
-		if ($standalone)
-			$tpl->assign("standalone", TRUE);
-	}
-	$tpl->display('message.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_opml.php
+++ /dev/null
@@ -1,28 +1,1 @@
-<?php
-/*
- *  display.git_opml.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - OPML feed
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.script_url.php');
-
-function git_opml()
-{
-	global $tpl,$gitphp_appstring;
-
-	$cachekey = sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig()));
-
-	if (!$tpl->is_cached('opml.tpl', $cachekey)) {
-		header("Content-type: text/xml; charset=UTF-8");
-		$tpl->assign("title", GitPHP_Config::GetInstance()->GetValue('title', $gitphp_appstring));
-		$tpl->assign("self",script_url());
-		$tpl->assign("opmllist", GitPHP_ProjectList::GetInstance());
-	}
-	$tpl->display('opml.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_project_index.php
+++ /dev/null
@@ -1,26 +1,1 @@
-<?php
-/*
- *  display.git_project_index.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - project index
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
-function git_project_index()
-{
-	global $tpl;
-
-	header("Content-type: text/plain; charset=utf-8");
-	header("Content-Disposition: inline; filename=\"index.aux\"");
-
-	$cachekey = sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig()));
-
-	if (!$tpl->is_cached('projectindex.tpl', $cachekey)) {
-		$tpl->assign("projlist", GitPHP_ProjectList::GetInstance());
-	}
-	$tpl->display('projectindex.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_project_list.php
+++ /dev/null
@@ -1,29 +1,1 @@
-<?php
-/*
- *  display.git_project_list.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - project list
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
-function git_project_list($order = "project")
-{
-	global $tpl;
-
-	$projectlist = GitPHP_ProjectList::GetInstance();
-
-	$cachekey = sha1(serialize($projectlist->GetConfig())) . "|" . sha1($order);
-
-	if (!$tpl->is_cached('projectlist.tpl', $cachekey)) {
-		if ($order)
-			$tpl->assign("order",$order);
-		$projectlist->Sort($order);
-		if ($projectlist->Count() > 0)
-			$tpl->assign("projectlist", $projectlist);
-	}
-	$tpl->display('projectlist.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_rss.php
+++ /dev/null
@@ -1,73 +1,1 @@
-<?php
-/*
- *  display.git_rss.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - RSS feed
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('defs.constants.php');
- require_once('util.date_str.php');
- require_once('util.script_url.php');
- require_once('gitutil.git_read_revlist.php');
- require_once('gitutil.git_diff_tree.php');
-
-function git_rss()
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	header("Content-type: text/xml; charset=UTF-8");
-
-	$cachekey = sha1($gitphp_current_project->GetProject());
-
-	if (!$tpl->is_cached('rss.tpl', $cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit();;
-		$revlist = git_read_revlist($head->GetHash(), GITPHP_RSS_ITEMS);
-		$tpl->assign("self",script_url());
-
-		$commitlines = array();
-		$revlistcount = count($revlist);
-		for ($i = 0; $i < $revlistcount; ++$i) {
-			$commit = $revlist[$i];
-			$co = $gitphp_current_project->GetCommit($commit);
-			if (($i >= 20) && ((time() - $co->GetCommitterEpoch()) > 48*60*60))
-				break;
-			$cd = date_str($co->GetCommitterEpoch());
-			$commitline = array();
-			$commitline["cdmday"] = $cd['mday'];
-			$commitline["cdmonth"] = $cd['month'];
-			$commitline["cdhour"] = $cd['hour'];
-			$commitline["cdminute"] = $cd['minute'];
-			$commitline["title"] = $co->GetTitle();
-			$commitline["author"] = $co->GetAuthor();
-			$commitline["cdrfc2822"] = $cd['rfc2822'];
-			$commitline["commit"] = $commit;
-			$commitline["comment"] = $co->GetComment();
-
-			$parent = $co->GetParent();
-			if ($parent) {
-				$difftree = array();
-				$diffout = git_diff_tree($parent->GetHash() . " " . $co->GetHash());
-				$tok = strtok($diffout,"\n");
-				while ($tok !== false) {
-					if (preg_match("/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/",$tok,$regs))
-						$difftree[] = $regs[7];
-					$tok = strtok("\n");
-				}
-				$commitline["difftree"] = $difftree;
-			}
-
-			$commitlines[] = $commitline;
-			unset($co);
-		}
-		$tpl->assign("commitlines",$commitlines);
-	}
-	$tpl->display('rss.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_search.php
+++ /dev/null
@@ -1,102 +1,1 @@
-<?php
-/*
- *  display.git_search.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - search
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- */
 
-require_once('defs.constants.php');
-require_once('util.age_string.php');
-require_once('util.highlight.php');
-require_once('gitutil.git_read_revlist.php');
-require_once('display.git_message.php');
-
-function git_search($hash, $search, $searchtype, $page = 0)
-{
-	global $tpl, $gitphp_current_project;
-	
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . sha1($searchtype) . "|" . sha1($search) . "|" . (isset($page) ? $page : 0);
-
-	if (!$tpl->is_cached('search.tpl', $cachekey)) {
-
-		if (!GitPHP_Config::GetInstance()->GetValue('search', true)) {
-			git_message("Search has been disabled", TRUE, TRUE);
-			return;
-		}
-
-		if (!isset($search) || (strlen($search) < 2)) {
-			git_message("You must enter search text of at least 2 characters", TRUE, TRUE);
-			return;
-		}
-		if (!isset($hash)) {
-			$hash = 'HEAD';
-		}
-
-		$co = $gitphp_current_project->GetCommit($hash);
-
-		$revlist = git_read_revlist($hash, 101, ($page * 100), FALSE, FALSE, $searchtype, $search);
-		if (count($revlist) < 1 || (strlen($revlist[0]) < 1)) {
-			git_message("No matches for '" . $search . "'.", FALSE, TRUE);
-			return;
-		}
-
-		$tpl->assign("hash",$hash);
-		$tpl->assign("treehash", $co->GetTree()->GetHash());
-
-		$tpl->assign("search",$search);
-		$tpl->assign("searchtype",$searchtype);
-		$tpl->assign("page",$page);
-		$revlistcount = count($revlist);
-		$tpl->assign("revlistcount",$revlistcount);
-
-		$tpl->assign("title", $co->GetTitle());
-
-		date_default_timezone_set('UTC');
-		$commitlines = array();
-		$commitcount = min(100,$revlistcount);
-		for ($i = 0; $i < $commitcount; ++$i) {
-			$commit = $revlist[$i];
-			if (strlen(trim($commit)) > 0) {
-				$commitline = array();
-				$co2 = $gitphp_current_project->GetCommit($commit);
-				$commitline["commit"] = $commit;
-				$age = $co2->GetAge();
-				if ($age > 60*60*24*7*2) {
-					$commitline['agestringdate'] = date('Y-m-d', $co2->GetCommitterEpoch());
-					$commitline['agestringage'] = age_string($age);
-				} else {
-					$commitline['agestringdate'] = age_string($age);
-					$commitline['agestringage'] = date('Y-m-d', $co2->GetCommitterEpoch());
-				}
-				$commitline["authorname"] = $co2->GetAuthorName();
-				$title = $co2->GetTitle();
-				$titleshort = $co2->GetTitle(GITPHP_TRIM_LENGTH);
-				$commitline["title_short"] = $titleshort;
-				if (strlen($titleshort) < strlen($title))
-					$commitline["title"] = $title;
-				$commitline["committree"] = $co2->GetTree()->GetHash();
-				$matches = array();
-				$commentlines = $co2->GetComment();
-				foreach ($commentlines as $comline) {
-					$hl = highlight($comline, $search, "searchmatch", GITPHP_TRIM_LENGTH);
-					if ($hl && (strlen($hl) > 0))
-						$matches[] = $hl;
-				}
-				$commitline["matches"] = $matches;
-				$commitlines[] = $commitline;
-				unset($co2);
-			}
-		}
-		
-		$tpl->assign("commitlines",$commitlines);
-	}
-	$tpl->display('search.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_search_files.php
+++ /dev/null
@@ -1,107 +1,1 @@
-<?php
-/*
- *  display.git_search_files.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - search in files
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- */
 
-require_once('defs.constants.php');
-require_once('util.highlight.php');
-require_once('gitutil.git_filesearch.php');
-require_once('display.git_message.php');
-
-function git_search_files($hash, $search, $page = 0)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . "filesearch" . "|" . sha1($search) . "|" . (isset($page) ? $page : 0);
-
-	if (!$tpl->is_cached('searchfiles.tpl', $cachekey)) {
-
-		if (!(GitPHP_Config::GetInstance()->GetValue('search', true) && GitPHP_Config::GetInstance()->GetValue('filesearch', true))) {
-			git_message("File search has been disabled", TRUE, TRUE);
-			return;
-		}
-
-		if (!isset($search) || (strlen($search) < 2)) {
-			git_message("You must enter search text of at least 2 characters", TRUE, TRUE);
-			return;
-		}
-		if (!isset($hash)) {
-			$hash = "HEAD";
-		}
-
-		$filesearch = git_filesearch($hash, $search, false, ($page * 100), 101);
-
-		if (count($filesearch) < 1) {
-			git_message("No matches for '" . $search . "'.", FALSE, TRUE);
-			return;
-		}
-
-		$tpl->assign("hash",$hash);
-
-		$co = $gitphp_current_project->GetCommit($hash);
-
-		if ($co) {
-			$tree = $co->GetTree();
-			if ($tree)
-				$tpl->assign("treehash", $tree->GetHash());
-			$tpl->assign("title", $co->GetTitle());
-		}
-
-		$tpl->assign("search",$search);
-		$tpl->assign("searchtype","file");
-		$tpl->assign("page",$page);
-		$filesearchcount = count($filesearch);
-		$tpl->assign("filesearchcount",$filesearchcount);
-
-
-		$filesearchlines = array();
-		$i = 0;
-		foreach ($filesearch as $file => $data) {
-			$filesearchline = array();
-			$filesearchline["file"] = $file;
-			if (strpos($file,"/") !== false) {
-				$f = basename($file);
-				$d = dirname($file);
-				if ($d == "/")
-					$d = "";
-				$hlt = highlight($f, $search, "searchmatch");
-				if ($hlt)
-					$hlt = $d . "/" . $hlt;
-			} else
-				$hlt = highlight($file, $search, "searchmatch");
-			if ($hlt)
-				$filesearchline["filename"] = $hlt;
-			else
-				$filesearchline["filename"] = $file;
-			$filesearchline["hash"] = $data['hash'];
-			if ($data['type'] == "tree")
-				$filesearchline["tree"] = TRUE;
-			if (isset($data['lines'])) {
-				$matches = array();
-				foreach ($data['lines'] as $line) {
-					$hlt = highlight($line,$search,"searchmatch",floor(GITPHP_TRIM_LENGTH*1.5),true);
-					if ($hlt)
-						$matches[] = $hlt;
-				}
-				if (count($matches) > 0)
-					$filesearchline["matches"] = $matches;
-			}
-			$filesearchlines[] = $filesearchline;
-			++$i;
-			if ($i >= 100)
-				break;
-		}
-		$tpl->assign("filesearchlines",$filesearchlines);
-	}
-	$tpl->display('searchfiles.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_shortlog.php
+++ /dev/null
@@ -1,78 +1,1 @@
-<?php
-/*
- *  display.git_shortlog.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - short log
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('defs.constants.php');
- require_once('util.date_str.php');
- require_once('util.age_string.php');
- require_once('gitutil.git_read_revlist.php');
- require_once('gitutil.read_info_ref.php');
-
-function git_shortlog($hash,$page)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hash . "|" . (isset($page) ? $page : 0);
-
-	if (!$tpl->is_cached('shortlog.tpl', $cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit();;
-		if (!isset($hash))
-			$hash = $head->GetHash();
-		if (!isset($page))
-			$page = 0;
-		$refs = read_info_ref();
-		$tpl->assign("hash",$hash);
-		$tpl->assign("head",$head->GetHash());
-
-		if ($page)
-			$tpl->assign("page",$page);
-
-		$revlist = git_read_revlist($hash, 101, ($page * 100));
-
-		$revlistcount = count($revlist);
-		$tpl->assign("revlistcount",$revlistcount);
-
-		$commitlines = array();
-		$commitcount = min(100,count($revlist));
-		for ($i = 0; $i < $commitcount; ++$i) {
-			$commit = $revlist[$i];
-			if (strlen(trim($commit)) > 0) {
-				$commitline = array();
-				if (isset($refs[$commit]))
-					$commitline["commitref"] = $refs[$commit];
-				$co = $gitphp_current_project->GetCommit($commit);
-				$ad = date_str($co->GetAuthorEpoch());
-				$commitline["commit"] = $commit;
-				$age = $co->GetAge();
-				if ($age > 60*60*24*7*2) {
-					$commitline["agestringdate"] = date('Y-m-d', $co->GetCommitterEpoch());
-					$commitline["agestringage"] = age_string($age);
-				} else {
-					$commitline["agestringdate"] = age_string($age);
-					$commitline["agestringage"] = date('Y-m-d', $co->GetCommitterEpoch());
-				}
-				$commitline["authorname"] = $co->GetAuthorName();
-				$titleshort = $co->GetTitle(GITPHP_TRIM_LENGTH);
-				$title = $co->GetTitle();
-				$commitline["title_short"] = $titleshort;
-				if (strlen($titleshort) < strlen($title))
-					$commitline["title"] = $title;
-				$commitlines[] = $commitline;
-				unset($co);
-			}
-		}
-		$tpl->assign("commitlines",$commitlines);
-	}
-	$tpl->display('shortlog.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_snapshot.php
+++ /dev/null
@@ -1,47 +1,1 @@
-<?php
-/*
- *  display.git_snapshot.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - snapshot
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
-function git_snapshot($projectroot,$project,$hash)
-{
-	global $tpl, $gitphp_current_project;
-
-	$commit = null;
-
-	if (!isset($hash))
-		$commit = $gitphp_current_project->GetHeadCommit();
-	else
-		$commit = $gitphp_current_project->GetCommit($hash);
-
-	$cachekey = sha1($project) . "|" . $hash;
-
-	$compressformat = GitPHP_Config::GetInstance()->GetValue('compressformat', GITPHP_COMPRESS_ZIP);
-
-	$rname = $gitphp_current_project->GetSlug();;
-	if ($compressformat == GITPHP_COMPRESS_ZIP) {
-		header("Content-Type: application/x-zip");
-		header("Content-Disposition: attachment; filename=" . $rname . ".zip");
-	} else if (($compressformat == GITPHP_COMPRESS_BZ2) && function_exists("bzcompress")) {
-		header("Content-Type: application/x-bzip2");
-		header("Content-Disposition: attachment; filename=" . $rname . ".tar.bz2");
-	} else if (($compressformat == GITPHP_COMPRESS_GZ) && function_exists("gzencode")) {
-		header("Content-Type: application/x-gzip");
-		header("Content-Disposition: attachment; filename=" . $rname . ".tar.gz");
-	} else {
-		header("Content-Type: application/x-tar");
-		header("Content-Disposition: attachment; filename=" . $rname . ".tar");
-	}
-
-	if (!$tpl->is_cached('snapshot.tpl', $cachekey)) {
-		$tpl->assign("archive", $commit->GetArchive($compressformat));
-	}
-	$tpl->display('snapshot.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_summary.php
+++ /dev/null
@@ -1,60 +1,1 @@
-<?php
-/*
- *  display.git_summary.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - summary page
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once(GITPHP_INCLUDEDIR . 'defs.constants.php');
- require_once(GITPHP_INCLUDEDIR . 'util.date_str.php');
- require_once(GITPHP_INCLUDEDIR . 'util.age_string.php');
- require_once(GITPHP_INCLUDEDIR . 'gitutil.git_read_revlist.php');
- require_once(GITPHP_INCLUDEDIR . 'git/Project.class.php');
-
-function git_summary()
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject());
-
-	if (!$tpl->is_cached('project.tpl', $cachekey)) {
-		$projectroot = GitPHP_Config::GetInstance()->GetValue('projectroot');
-
-		$descr = $gitphp_current_project->GetDescription();
-		$headCommit = $gitphp_current_project->GetHeadCommit();
-		$commitdate = date_str($headCommit->GetCommitterEpoch(), $headCommit->GetCommitterTimezone());
-		$owner = $gitphp_current_project->GetOwner();
-		$tpl->assign("head", $headCommit->GetHash());
-		$tpl->assign("description",$descr);
-		$tpl->assign("owner",$owner);
-		$tpl->assign("lastchange",$commitdate['rfc2822']);
-		if (GitPHP_Config::GetInstance()->HasKey('cloneurl'))
-			$tpl->assign('cloneurl', GitPHP_Config::GetInstance()->GetValue('cloneurl') . $gitphp_current_project->GetProject());
-		if (GitPHP_Config::GetInstance()->HasKey('pushurl'))
-			$tpl->assign('pushurl', GitPHP_Config::GetInstance()->GetValue('pushurl') . $gitphp_current_project->GetProject());
-		$revlist = git_read_revlist($headCommit->GetHash(), 17);
-		foreach ($revlist as $i => $rev) {
-			$revlist[$i] = $gitphp_current_project->GetCommit($rev);
-		}
-		$tpl->assign("revlist",$revlist);
-
-		$taglist = $gitphp_current_project->GetTags();
-		if (isset($taglist) && (count($taglist) > 0)) {
-			$tpl->assign("taglist",$taglist);
-		}
-
-		$headlist = $gitphp_current_project->GetHeads();
-		if (isset($headlist) && (count($headlist) > 0)) {
-			$tpl->assign("headlist",$headlist);
-		}
-	}
-	$tpl->display('project.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_tag.php
+++ /dev/null
@@ -1,41 +1,1 @@
-<?php
-/*
- *  display.git_tag.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - tag
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.date_str.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Tag.class.php');
-
-function git_tag($hash)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . sha1($hash);
-
-	if (!$tpl->is_cached('tag.tpl', $cachekey)) {
-
-		$head = $gitphp_current_project->GetHeadCommit()->GetHash();
-		$tpl->assign("head",$head);
-		$tpl->assign("hash", $hash);
-
-		$tag = new GitPHP_Tag($gitphp_current_project, $hash);
-
-		$tpl->assign("tag", $tag);
-		$tagger = $tag->GetTagger();
-		if (!empty($tagger)) {
-			$ad = date_str($tag->GetTaggerEpoch(), $tag->GetTaggerTimezone());
-			$tpl->assign("datedata",$ad);
-		}
-	}
-	$tpl->display('tag.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_tags.php
+++ /dev/null
@@ -1,32 +1,1 @@
-<?php
-/*
- *  display.git_tags.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - tags
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
-function git_tags()
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject());
-
-	if (!$tpl->is_cached('tags.tpl', $cachekey)) {
-		$head = $gitphp_current_project->GetHeadCommit()->GetHash();
-		$tpl->assign("head",$head);
-
-		$taglist = $gitphp_current_project->GetTags();
-		if (isset($taglist) && (count($taglist) > 0)) {
-			$tpl->assign("taglist",$taglist);
-		}
-	}
-	$tpl->display('tags.tpl', $cachekey);
-}
-
-?>
-

--- a/include/display.git_tree.php
+++ /dev/null
@@ -1,73 +1,1 @@
-<?php
-/*
- *  display.git_tree.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - tree
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('util.mode_str.php');
- require_once('gitutil.git_get_hash_by_path.php');
- require_once('gitutil.git_ls_tree.php');
- require_once('gitutil.read_info_ref.php');
- require_once('gitutil.git_path_trees.php');
-
-function git_tree($hash,$file,$hashbase)
-{
-	global $tpl, $gitphp_current_project;
-
-	if (!$gitphp_current_project)
-		return;
-
-	$cachekey = sha1($gitphp_current_project->GetProject()) . "|" . $hashbase . "|" . $hash . "|" . sha1($file);
-
-	if (!$tpl->is_cached('tree.tpl', $cachekey)) {
-		
-		if (!isset($hash)) {
-			$hash = $gitphp_current_project->GetHeadCommit()->GetHash();
-			if (isset($file))
-				$hash = git_get_hash_by_path(($hashbase?$hashbase:$hash),$file,"tree");
-		}
-		if (!isset($hashbase))
-			$hashbase = $hash;
-		$lsout = git_ls_tree($hash, TRUE);
-		$refs = read_info_ref();
-		$tpl->assign("hash",$hash);
-		if (isset($hashbase))
-			$tpl->assign("hashbase",$hashbase);
-		if (isset($hashbase)) {
-			$co = $gitphp_current_project->GetCommit($hashbase);
-			if ($co) {
-				$tpl->assign("fullnav",TRUE);
-				$tpl->assign("title",$co->GetTitle());
-				if (isset($refs[$hashbase]))
-					$tpl->assign("hashbaseref",$refs[$hashbase]);
-			}
-		}
-		$paths = git_path_trees($hashbase, $file);
-		$tpl->assign("paths",$paths);
-
-		if (isset($file))
-			$tpl->assign("base",$file . "/");
-
-		$treelines = array();
-		$tok = strtok($lsout,"\0");
-		while ($tok !== false) {
-			if (preg_match("/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/",$tok,$regs)) {
-				$treeline = array();
-				$treeline["filemode"] = mode_str($regs[1]);
-				$treeline["type"] = $regs[2];
-				$treeline["hash"] = $regs[3];
-				$treeline["name"] = $regs[4];
-				$treelines[] = $treeline;
-			}
-			$tok = strtok("\0");
-		}
-		$tpl->assign("treelines",$treelines);
-	}
-	$tpl->display('tree.tpl', $cachekey);
-}
-
-?>
-

--- a/include/git/Commit.class.php
+++ b/include/git/Commit.class.php
@@ -11,8 +11,8 @@
  */
 
 require_once(GITPHP_INCLUDEDIR . 'defs.commands.php');
-require_once(GITPHP_INCLUDEDIR . 'git/GitObject.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Tree.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitObject.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Tree.class.php');
 
 /**
  * Commit class

--- a/include/git/Head.class.php
+++ b/include/git/Head.class.php
@@ -10,7 +10,7 @@
  * @subpackage Git
  */
 
-require_once(GITPHP_INCLUDEDIR . 'git/Ref.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Ref.class.php');
 
 /**
  * Head class

--- a/include/git/Project.class.php
+++ b/include/git/Project.class.php
@@ -11,10 +11,10 @@
  */
 
 require_once(GITPHP_INCLUDEDIR . 'defs.commands.php');
-require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Commit.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Head.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Tag.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Commit.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Head.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Tag.class.php');
 
 /**
  * Project class

--- a/include/git/ProjectList.class.php
+++ b/include/git/ProjectList.class.php
@@ -10,10 +10,9 @@
  * @subpackage Git
  */
 
-require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/ProjectListDirectory.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/ProjectListFile.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/ProjectListArray.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'ProjectListDirectory.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'ProjectListFile.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'ProjectListArray.class.php');
 
 /**
  * ProjectList class
@@ -46,10 +45,6 @@
 	 */
 	public static function GetInstance()
 	{
-		if (!self::$instance) {
-			throw new Exception('ProjectList has not been instantiated.');
-		}
-
 		return self::$instance;
 	}
 
@@ -70,17 +65,11 @@
 
 		if (!empty($file) && is_file($file) && include($file)) {
 			if (is_string($git_projects)) {
-				try {
 					self::$instance = new GitPHP_ProjectListFile($git_projects);
 					return;
-				} catch (Exception $e) {
-				}
 			} else if (is_array($git_projects)) {
-				try {
 					self::$instance = new GitPHP_ProjectListArray($git_projects);
 					return;
-				} catch (Exception $e) {
-				}
 			}
 		}
 

--- a/include/git/ProjectListArray.class.php
+++ b/include/git/ProjectListArray.class.php
@@ -11,8 +11,8 @@
  */
 
 require_once(GITPHP_INCLUDEDIR . 'defs.constants.php');
-require_once(GITPHP_INCLUDEDIR . 'git/ProjectListBase.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Project.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
 
 define('GITPHP_NO_CATEGORY', 'none');
 

--- a/include/git/ProjectListBase.class.php
+++ b/include/git/ProjectListBase.class.php
@@ -10,7 +10,7 @@
  * @subpackage Git
  */
 
-require_once(GITPHP_INCLUDEDIR . 'git/Project.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
 
 define('GITPHP_SORT_PROJECT', 'project');
 define('GITPHP_SORT_DESCRIPTION', 'descr');

--- a/include/git/ProjectListDirectory.class.php
+++ b/include/git/ProjectListDirectory.class.php
@@ -11,8 +11,8 @@
  */
 
 require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/ProjectListBase.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Project.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
 
 /**
  * ProjectListDirectory class

--- a/include/git/ProjectListFile.class.php
+++ b/include/git/ProjectListFile.class.php
@@ -11,8 +11,8 @@
  */
 
 require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/ProjectListBase.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Project.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
 
 /**
  * ProjectListFile class
@@ -35,7 +35,7 @@
 	public function __construct($projectFile)
 	{
 		if (!(is_string($projectFile) && is_file($projectFile))) {
-			throw new Exception($projectFile . ' is not a file.');
+			throw new Exception($projectFile . ' is not a file');
 		}
 
 		$this->projectConfig = $projectFile;
@@ -54,7 +54,7 @@
 	protected function PopulateProjects()
 	{
 		if (!($fp = fopen($this->projectConfig, 'r'))) {
-			throw new Exception('Failed to open project list file ' . $this->projectConfig . '.');
+			throw new Exception('Failed to open project list file ' . $this->projectConfig);
 		}
 
 		$projectRoot = GitPHP_Config::GetInstance()->GetValue('projectroot');

--- a/include/git/Ref.class.php
+++ b/include/git/Ref.class.php
@@ -10,7 +10,7 @@
  * @subpackage Git
  */
 
-require_once(GITPHP_INCLUDEDIR . 'git/GitObject.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitObject.class.php');
 
 /**
  * Git Ref class

--- a/include/git/Tag.class.php
+++ b/include/git/Tag.class.php
@@ -10,8 +10,8 @@
  * @subpackage Git
  */
 
-require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
-require_once(GITPHP_INCLUDEDIR . 'git/Ref.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Ref.class.php');
 
 /**
  * Tag class

--- a/include/git/Tree.class.php
+++ b/include/git/Tree.class.php
@@ -10,7 +10,7 @@
  * @subpackge Git
  */
 
-require_once(GITPHP_INCLUDEDIR . 'git/GitObject.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitObject.class.php');
 
 /**
  * Tree class

--- a/include/gitutil.git_cat_file.php
+++ b/include/gitutil.git_cat_file.php
@@ -8,7 +8,7 @@
  */
 
  require_once('defs.commands.php');
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_cat_file($hash,$pipeto = NULL, $type = "blob")
 {

--- a/include/gitutil.git_diff_tree.php
+++ b/include/gitutil.git_diff_tree.php
@@ -8,7 +8,7 @@
  */
 
  require_once('defs.commands.php');
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_diff_tree($hashes,$renames = FALSE)
 {

--- a/include/gitutil.git_grep.php
+++ b/include/gitutil.git_grep.php
@@ -8,7 +8,7 @@
  */
 
 require_once('defs.commands.php');
-require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_grep($hash, $search, $case = false, $binary = false, $fullname = true)
 {

--- a/include/gitutil.git_history_list.php
+++ b/include/gitutil.git_history_list.php
@@ -8,7 +8,7 @@
  */
 
  require_once('defs.commands.php');
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_history_list($hash,$file)
 {

--- a/include/gitutil.git_ls_tree.php
+++ b/include/gitutil.git_ls_tree.php
@@ -8,7 +8,7 @@
  */
 
  require_once('defs.commands.php');
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_ls_tree($hash,$nullterm = FALSE, $recurse = FALSE)
 {

--- a/include/gitutil.git_read_blame.php
+++ b/include/gitutil.git_read_blame.php
@@ -8,7 +8,7 @@
  */
 
 require_once('defs.commands.php');
-require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_read_blame($file, $rev = null)
 {

--- a/include/gitutil.git_rev_list.php
+++ b/include/gitutil.git_rev_list.php
@@ -8,7 +8,7 @@
  */
 
  require_once('defs.commands.php');
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function git_rev_list($head,$count = NULL,$skip = NULL,$header = FALSE,$parents = FALSE,$greptype = NULL, $search = NULL)
 {

--- a/include/gitutil.git_version.php
+++ b/include/gitutil.git_version.php
@@ -7,7 +7,7 @@
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  */
 
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
  function git_version()
  {

--- a/include/gitutil.read_info_ref.php
+++ b/include/gitutil.read_info_ref.php
@@ -8,7 +8,7 @@
  */
 
  require_once('defs.commands.php');
- require_once(GITPHP_INCLUDEDIR . 'git/GitExe.class.php');
+ require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
 
 function read_info_ref($type = "")
 {

file:a/index.php -> file:b/index.php
--- a/index.php
+++ b/index.php
@@ -1,10 +1,12 @@
 <?php
-/*
- *  index.php
- *  gitphp: A PHP git repository browser
- *  Component: Index script
+/**
+ * GitPHP
  *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
+ * Index
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
  */
 
 /**
@@ -13,241 +15,71 @@
 define('GITPHP_BASEDIR', dirname(__FILE__) . '/');
 define('GITPHP_CONFIGDIR', GITPHP_BASEDIR . 'config/');
 define('GITPHP_INCLUDEDIR', GITPHP_BASEDIR . 'include/');
-
- /*
-  * Version
-  */
- include_once(GITPHP_INCLUDEDIR . 'version.php');
-
- /*
-  * Constants
-  */
- require_once(GITPHP_INCLUDEDIR . 'defs.constants.php');
-
- /*
-  * Configuration
-  */
- require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
- try {
- 	GitPHP_Config::GetInstance()->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php.example');
- } catch (Exception $e) {
- }
- GitPHP_Config::GetInstance()->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php');
-
- /**
-  * Project list
-  */
- require_once(GITPHP_INCLUDEDIR . 'git/ProjectList.class.php');
- GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'gitphp.conf.php');
-
- /**
-  * Check for projectroot
-  */
-if (!GitPHP_Config::GetInstance()->GetValue('projectroot', null)) {
-	throw new Exception ('A projectroot must be set in the config.');
-}
-
- $project = null;
- $gitphp_current_project = null;
-
- if (isset($_GET['p'])) {
- 	$gitphp_current_project = GitPHP_ProjectList::GetInstance()->GetProject(str_replace(chr(0), '', $_GET['p']));
-	$project = $gitphp_current_project->GetProject();
- }
-
- $extraoutput = FALSE;
-
- /*
-  * Instantiate Smarty
-  */
- require_once(GitPHP_Config::GetInstance()->GetValue('smarty_prefix', 'lib/smarty/libs/') . "Smarty.class.php");
- $tpl = new Smarty;
- if (!isset($_GET['a']) ||
-	!in_array($_GET['a'], array('commitdiff_plain', 'blob_plain',
-		'blobdiff_plain', 'rss', 'opml', 'snapshot'))) {
-	$tpl->load_filter('output','trimwhitespace');
-	$extraoutput = TRUE;
-}
-
-require_once(GITPHP_INCLUDEDIR . 'util.age_string.php');
-$tpl->register_modifier('agestring', 'age_string');
-
- /*
-  * Debug
-  */
- if (GitPHP_Config::GetInstance()->GetValue('debug', false)) {
- 	if ($extraoutput) {
-		define('GITPHP_START_TIME', microtime(true));
-		error_reporting(E_ALL|E_STRICT);
-	}
- }
+define('GITPHP_GITOBJECTDIR', GITPHP_INCLUDEDIR . 'git/');
+define('GITPHP_CONTROLLERDIR', GITPHP_INCLUDEDIR . 'controller/');
 
 /*
- * Caching
+ * Version
  */
- if (GitPHP_Config::GetInstance()->GetValue('cache', false)) {
- 	$tpl->caching = 2;
-	if (GitPHP_Config::GetInstance()->HasKey('cachelifetime'))
-		$tpl->cache_lifetime = GitPHP_Config::GetInstance()->GetValue('cachelifetime');
-	if (GitPHP_Config::GetInstance()->GetValue('cacheexpire', true) === true) {
-		require_once(GITPHP_INCLUDEDIR . 'cache.cache_expire.php');
-		cache_expire();
-	}
- }
+include_once(GITPHP_INCLUDEDIR . 'version.php');
 
 /*
- * Setup global assigns used everywhere (such as header/footer)
+ * Constants
  */
- $tpl->assign("stylesheet", GitPHP_Config::GetInstance()->GetValue('stylesheet', 'gitphp.css'));
- $tpl->assign("version",$gitphp_version);
- $tpl->assign("pagetitle", GitPHP_Config::GetInstance()->GetValue('title', $gitphp_appstring));
- if ($project) {
-	$tpl->assign("validproject",TRUE);
-	$tpl->assign("project",$project);
-	$tpl->assign("projectdescription", $gitphp_current_project->GetDescription());
-	if (isset($_GET['a'])) {
-		$tpl->assign("action",$_GET['a']);
-		$tpl->assign("validaction", TRUE);
+require_once(GITPHP_INCLUDEDIR . 'defs.constants.php');
+
+require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
+
+require_once(GITPHP_GITOBJECTDIR . 'ProjectList.class.php');
+
+require_once(GITPHP_INCLUDEDIR . 'MessageException.class.php');
+require_once(GITPHP_CONTROLLERDIR . 'Controller.class.php');
+
+try {
+
+	/*
+	 * Configuration
+	 */
+	try {
+		GitPHP_Config::GetInstance()->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php.example');
+	} catch (Exception $e) {
 	}
- }
- if (isset($_GET['st']))
- 	$tpl->assign("currentsearchtype",$_GET['st']);
- else
-	$tpl->assign("currentsearchtype","commit");
-if (isset($_GET['s']))
-	$tpl->assign("currentsearch",$_GET['s']);
-if (isset($_GET['hb']))
-	$tpl->assign("currentsearchhash",$_GET['hb']);
-else if (isset($_GET['h']))
-	$tpl->assign("currentsearchhash",$_GET['h']);
-if (GitPHP_Config::GetInstance()->GetValue('search', true))
-	$tpl->assign("enablesearch",TRUE);
-if (GitPHP_Config::GetInstance()->GetValue('filesearch', true))
-	$tpl->assign("filesearch",TRUE);
+	GitPHP_Config::GetInstance()->LoadConfig(GITPHP_CONFIGDIR . 'gitphp.conf.php');
 
+	/*
+	 * Project list
+	 */
+	GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'gitphp.conf.php');
 
- if (isset($_GET['a']) && $_GET['a'] == "expire") {
- 	require_once(GITPHP_INCLUDEDIR . 'cache.cache_expire.php');
-	require_once(GITPHP_INCLUDEDIR . 'display.git_message.php');
-	cache_expire(true);
-	git_message("Cache expired");
- } else if (isset($_GET['a']) && $_GET['a'] == "opml") {
-	require_once(GITPHP_INCLUDEDIR . 'display.git_opml.php');
-	git_opml(GitPHP_Config::GetInstance()->GetValue('projectroot'), GitPHP_ProjectList::GetInstance()->GetConfig());
- } else if (isset($_GET['a']) && $_GET['a'] == "project_index") {
-	require_once(GITPHP_INCLUDEDIR . 'display.git_project_index.php');
-	git_project_index();
- } else if ($project) {
- 	if (!is_dir(GitPHP_Config::GetInstance()->GetValue('projectroot') . $project)) {
-		$tpl->assign("validproject",FALSE);
-		require_once(GITPHP_INCLUDEDIR . 'display.git_message.php');
-		git_message("No such directory",TRUE);
-	} else if (!is_file(GitPHP_Config::GetInstance()->GetValue('projectroot') . $project . "/HEAD")) {
-		$tpl->assign("validproject",FALSE);
-		require_once(GITPHP_INCLUDEDIR . 'display.git_message.php');
-		git_message("No such project",TRUE);
+	if (!GitPHP_Config::GetInstance()->GetValue('projectroot', null)) {
+		throw new GitPHP_MessageException('A projectroot must be set in the config', true);
+	}
+
+	$gitphp_current_project = null;
+
+	if (isset($_GET['p'])) {
+		$gitphp_current_project = GitPHP_ProjectList::GetInstance()->GetProject(str_replace(chr(0), '', $_GET['p']));
+	}
+
+	$controller = GitPHP_Controller::GetController((isset($_GET['a']) ? $_GET['a'] : null));
+	if ($controller) {
+		$controller->RenderHeaders();
+		$controller->Render();
+	}
+
+} catch (Exception $e) {
+
+	require_once(GITPHP_CONTROLLERDIR . 'Controller_Message.class.php');
+	$controller = new GitPHP_Controller_Message();
+	$controller->SetParam('message', $e->getMessage());
+	if ($e instanceof GitPHP_MessageException) {
+		$controller->SetParam('error', $e->Error);
 	} else {
-		if (!isset($_GET['a'])) {
-			require_once(GITPHP_INCLUDEDIR . 'display.git_summary.php');
-			git_summary();
-		} else {
-			switch ($_GET['a']) {
-				case "summary":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_summary.php');
-					git_summary();
-					break;
-				case "tree":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_tree.php');
-					git_tree((isset($_GET['h']) ? $_GET['h'] : NULL), (isset($_GET['f']) ? $_GET['f'] : NULL), (isset($_GET['hb']) ? $_GET['hb'] : NULL));
-					break;
-				case "shortlog":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_shortlog.php');
-					git_shortlog((isset($_GET['h']) ? $_GET['h'] : NULL), (isset($_GET['pg']) ? $_GET['pg'] : NULL));
-					break;
-				case "log":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_log.php');
-					git_log((isset($_GET['h']) ? $_GET['h'] : NULL), (isset($_GET['pg']) ? $_GET['pg'] : NULL));
-					break;
-				case "commit":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_commit.php');
-					git_commit($_GET['h']);
-					break;
-				case "commitdiff":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_commitdiff.php');
-					git_commitdiff($_GET['h'], (isset($_GET['hp']) ? $_GET['hp'] : NULL));
-					break;
-				case "commitdiff_plain":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_commitdiff_plain.php');
-					git_commitdiff_plain($_GET['h'],(isset($_GET['hp']) ? $_GET['hp'] : NULL));
-					break;
-				case "heads":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_heads.php');
-					git_heads();
-					break;
-				case "tags":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_tags.php');
-					git_tags();
-					break;
-				case "rss":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_rss.php');
-					git_rss();
-					break;
-				case "blob":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_blob.php');
-					git_blob((isset($_GET['h']) ? $_GET['h'] : NULL), (isset($_GET['f']) ? $_GET['f'] : NULL), (isset($_GET['hb']) ? $_GET['hb'] : NULL));
-					break;
-				case "blob_plain":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_blob_plain.php');
-					git_blob_plain(GitPHP_Config::GetInstance()->GetValue('projectroot'),$project,$_GET['h'],(isset($_GET['f']) ? $_GET['f'] : NULL));
-					break;
-				case "blobdiff":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_blobdiff.php');
-					git_blobdiff($_GET['h'],$_GET['hb'],$_GET['hp'],(isset($_GET['f']) ? $_GET['f'] : NULL));
-					break;
-				case "blobdiff_plain":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_blobdiff_plain.php');
-					git_blobdiff_plain(GitPHP_Config::GetInstance()->GetValue('projectroot'),$project,$_GET['h'],$_GET['hb'],$_GET['hp'], (isset($_GET['f']) ? $_GET['f'] : NULL));
-					break;
-				case "blame":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_blame.php');
-					git_blame((isset($_GET['h']) ? $_GET['h'] : NULL), (isset($_GET['f']) ? $_GET['f'] : NULL), (isset($_GET['hb']) ? $_GET['hb'] : NULL));
-					break;
-				case "snapshot":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_snapshot.php');
-					git_snapshot(GitPHP_Config::GetInstance()->GetValue('projectroot'),$project, (isset($_GET['h']) ? $_GET['h'] : NULL));
-					break;
-				case "history":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_history.php');
-					git_history((isset($_GET['h']) ? $_GET['h'] : NULL),$_GET['f']);
-					break;
-				case "search":
-					if (isset($_GET['st']) && ($_GET['st'] == 'file')) {
-						require_once(GITPHP_INCLUDEDIR . 'display.git_search_files.php');
-						git_search_files((isset($_GET['h']) ? $_GET['h'] : NULL),(isset($_GET['s']) ? $_GET['s'] : NULL),(isset($_GET['pg']) ? $_GET['pg'] : 0));
-					} else {
-						require_once(GITPHP_INCLUDEDIR . 'display.git_search.php');
-						git_search((isset($_GET['h']) ? $_GET['h'] : NULL),(isset($_GET['s']) ? $_GET['s'] : NULL),(isset($_GET['st']) ? $_GET['st'] : "commit"),(isset($_GET['pg']) ? $_GET['pg'] : 0));
-					}
-					break;
-				case "tag":
-					require_once(GITPHP_INCLUDEDIR . 'display.git_tag.php');
-					git_tag($_GET['h']);
-					break;
-				default:
-					$tpl->assign("validaction", FALSE);
-					require_once(GITPHP_INCLUDEDIR . 'display.git_message.php');
-					git_message("Unknown action", TRUE);
-					break;
-			}
-		}
+		$controller->SetParam('error', true);
 	}
- } else {
-	require_once(GITPHP_INCLUDEDIR . 'display.git_project_list.php');
-	git_project_list((isset($_GET['o']) ? $_GET['o'] : "project"));
- }
+	$controller->Render();
 
- if (GitPHP_Config::GetInstance()->GetValue('debug', false) && $extraoutput)
- 	echo "Execution time: " . round(microtime(true) - GITPHP_START_TIME, 8) . " sec";
+}
 
 ?>
 

--- a/templates/header.tpl
+++ b/templates/header.tpl
@@ -39,15 +39,15 @@
             <div class="search">
               <input type="hidden" name="p" value="{$project}" />
               <input type="hidden" name="a" value="search" />
-              <input type ="hidden" name="h" value="{if $currentsearchhash}{$currentsearchhash}{else}HEAD{/if}" />
+              <input type ="hidden" name="h" value="{if $hashbase}{$hashbase}{elseif $hash}{$hash}{else}HEAD{/if}" />
               <select name="st">
-                <option {if $currentsearchtype == 'commit'}selected="selected"{/if} value="commit">commit</option>
-                <option {if $currentsearchtype == 'author'}selected="selected"{/if} value="author">author</option>
-                <option {if $currentsearchtype == 'committer'}selected="selected"{/if} value="committer">committer</option>
+                <option {if $searchtype == 'commit'}selected="selected"{/if} value="commit">commit</option>
+                <option {if $searchtype == 'author'}selected="selected"{/if} value="author">author</option>
+                <option {if $searchtype == 'committer'}selected="selected"{/if} value="committer">committer</option>
                 {if $filesearch}
-                  <option {if $currentsearchtype == 'file'}selected="selected"{/if} value="file">file</option>
+                  <option {if $searchtype == 'file'}selected="selected"{/if} value="file">file</option>
                 {/if}
-              </select> search: <input type="text" name="s" {if $currentsearch}value="{$currentsearch}"{/if} />
+              </select> search: <input type="text" name="s" {if $search}value="{$search}"{/if} />
             </div>
           </form>
         {/if}

--- a/templates/message.tpl
+++ b/templates/message.tpl
@@ -6,13 +6,9 @@
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
 
-{if $standalone}
-  {include file='header.tpl'}
-{/if}
+{include file='header.tpl'}
 
 <div class="message {if $error}error{/if}">{$message}</div>
 
-{if $standalone}
-  {include file='footer.tpl'}
-{/if}
+{include file='footer.tpl'}
 

--- a/templates/opml.tpl
+++ b/templates/opml.tpl
@@ -8,12 +8,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <opml version="1.0">
   <head>
-    <title>{$title} OPML Export</title>
+    <title>{$pagetitle} OPML Export</title>
   </head>
   <body>
     <outline text="git RSS feeds">
 
-      {foreach from=$opmllist item=proj}
+      {foreach from=$projectlist item=proj}
       <outline type="rss" text="{$proj->GetProject()}" title="{$proj->GetProject()}" xmlUrl="{$self}?p={$proj->GetProject()}&amp;a=rss" htmlUrl="{$self}?p={$proj->GetProject()}&amp;a=summary" />
 
       {/foreach}

--- a/templates/projectindex.tpl
+++ b/templates/projectindex.tpl
@@ -5,7 +5,7 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
-{foreach from=$projlist item=proj}
+{foreach from=$projectlist item=proj}
 {$proj->GetProject()}
 {/foreach}
 

--- a/templates/projectlist.tpl
+++ b/templates/projectlist.tpl
@@ -11,7 +11,7 @@
 {include file='hometext.tpl'}
 
 <table cellspacing="0">
-  {foreach name=projects from=$projectlist item=project}
+  {foreach name=projects from=$projectlist item=proj}
     {if $smarty.foreach.projects.first}
       {* Header *}
       <tr>
@@ -39,8 +39,8 @@
       </tr>
     {/if}
 
-    {if $currentcategory != $project->GetCategory()}
-      {assign var=currentcategory value=$project->GetCategory()}
+    {if $currentcategory != $proj->GetCategory()}
+      {assign var=currentcategory value=$proj->GetCategory()}
       {if $currentcategory != ''}
         <tr class="light">
           <th>{$currentcategory}</th>
@@ -54,12 +54,12 @@
 
     <tr class="{cycle values="light,dark"}">
       <td>
-        <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=summary" class="list {if $currentcategory != ''}indent{/if}">{$project->GetProject()}</a>
+        <a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=summary" class="list {if $currentcategory != ''}indent{/if}">{$proj->GetProject()}</a>
       </td>
-      <td><a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=summary" class="list">{$project->GetDescription()}</a></td>
-      <td><em>{$project->GetOwner()}</em></td>
+      <td><a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=summary" class="list">{$proj->GetDescription()}</a></td>
+      <td><em>{$proj->GetOwner()}</em></td>
       <td>
-        {assign var=projecthead value=$project->GetHeadCommit()}
+        {assign var=projecthead value=$proj->GetHeadCommit()}
         {if $projecthead->GetAge() < 7200}   {* 60*60*2, or 2 hours *}
           <span class="agehighlight"><strong><em>{$projecthead->GetAge()|agestring}</em></strong></span>
         {elseif $projecthead->GetAge() < 172800}   {* 60*60*24*2, or 2 days *}
@@ -69,11 +69,11 @@
         {/if}
       </td>
       <td class="link">
-        <a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=summary">summary</a> | 
-	<a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=shortlog">shortlog</a> | 
-	<a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=log">log</a> | 
-	<a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=tree">tree</a> | 
-	<a href="{$SCRIPT_NAME}?p={$project->GetProject()}&a=snapshot&h=HEAD">snapshot</a>
+        <a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=summary">summary</a> | 
+	<a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=shortlog">shortlog</a> | 
+	<a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=log">log</a> | 
+	<a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=tree">tree</a> | 
+	<a href="{$SCRIPT_NAME}?p={$proj->GetProject()}&a=snapshot&h=HEAD">snapshot</a>
       </td>
     </tr>
   {foreachelse}

comments