Cache support for snapshots
Cache support for snapshots

file:b/cache/.gitignore (new)
--- /dev/null
+++ b/cache/.gitignore
@@ -1,1 +1,2 @@
+*
 

--- a/config/gitphp.conf.php.example
+++ b/config/gitphp.conf.php.example
@@ -169,6 +169,35 @@
 $gitphp_conf['filesearch'] = TRUE;
 
 /*
+ * debug
+ * Turns on extra warning messages and benchmarking.
+ * Not recommended for production systems
+ */
+$gitphp_conf['debug'] = FALSE;
+
+/*
+ * cache
+ * Turns on smarty caching
+ * Be very careful with this!  Due to varying cache lifetimes,
+ * you could end up seeing a mix of pages from before and after
+ * a commit!
+ * If in doubt, leave this off
+ */
+$gitphp_conf['cache'] = FALSE;
+
+/*
+ * cachelifetime
+ * Sets how long a page will be cached, in seconds
+ * This will vary greatly depending on how active your projects are.
+ * If you set it too high, commits that happen close to one another
+ * will appear to be mixed together as the user navigates between
+ * pages, depending on when each page was previously accessed and
+ * its current cache lifetime.
+ * If in doubt, set it to something extremely low, like 3 seconds.
+ */
+$gitphp_conf['cachelifetime'] = 3;
+
+/*
  * git_projects
  * Two-dimensional array list of projects
  * First array index is the name of the category the projects

file:a/gitphp.css -> file:b/gitphp.css
--- a/gitphp.css
+++ b/gitphp.css
@@ -105,4 +105,17 @@
 div.error {
 	color: #ff0000;
 }
+.indent {
+	margin-left: 8px;
+}
+table.code td {
+	padding: 0px 0px;
+	font-family:monospace; font-size:12px; white-space:pre;
+}
+table.code td.num {
+	text-align: right;
+}
+table.code td.codeline {
+	padding-left: 5px;
+}
 

--- a/include/display.git_blob.php
+++ b/include/display.git_blob.php
@@ -18,66 +18,77 @@
 function git_blob($projectroot, $project, $hash, $file, $hashbase)
 {
 	global $gitphp_conf,$tpl;
-	if (!isset($hashbase))
-		$hashbase = git_read_head($projectroot . $project);
-	if (!isset($hash) && isset($file))
-		$hash = git_get_hash_by_path($projectroot . $project, $hashbase,$file,"blob");
-	$catout = git_cat_file($projectroot . $project, $hash);
-	$tpl->clear_all_assign();
-	$tpl->assign("hash",$hash);
-	$tpl->assign("project",$project);
-	$tpl->assign("hashbase",$hashbase);
-	if ($co = git_read_commit($projectroot . $project, $hashbase)) {
-		$tpl->assign("fullnav",TRUE);
-		$refs = read_info_ref($projectroot . $project);
-		$tpl->assign("tree",$co['tree']);
-		$tpl->assign("title",$co['title']);
-		if (isset($file))
-			$tpl->assign("file",$file);
-		if (isset($refs[$hashbase]))
-			$tpl->assign("hashbaseref",$refs[$hashbase]);
-	}
-	$paths = git_path_trees($projectroot . $project, $hashbase, $file);
-	$tpl->assign("paths",$paths);
 
-	if ($gitphp_conf['filemimetype']) {
-		$mime = file_mime($catout,$file);
-		if ($mime)
-			$mimetype = strtok($mime, "/");
-	}
+	$cachekey = sha1($project) . "|" . $hashbase . "|" . $hash . "|" . sha1($file);
 
-	if ($mimetype == "image") {
-		$tpl->assign("mime", $mime);
-		$tpl->assign("data", base64_encode($catout));
-	} else {
-		$usedgeshi = $gitphp_conf['geshi'];
-		if ($usedgeshi) {
-			$usedgeshi = FALSE;
-			include_once($gitphp_conf['geshiroot'] . "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->set_source($catout);
-						$geshi->set_language($lang);
-						$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS);
-						$tpl->assign("geshiout",$geshi->parse_code());
-						$usedgeshi = TRUE;
+	if (!$tpl->is_cached('blob.tpl',$cachekey)) {
+		$head = git_read_head($projectroot . $project);
+		if (!isset($hashbase))
+			$hashbase = $head;
+		if (!isset($hash) && isset($file))
+			$hash = git_get_hash_by_path($projectroot . $project, $hashbase,$file,"blob");
+		$catout = git_cat_file($projectroot . $project, $hash);
+		$tpl->assign("hash",$hash);
+		$tpl->assign("hashbase",$hashbase);
+		$tpl->assign("head", $head);
+		if ($co = git_read_commit($projectroot . $project, $hashbase)) {
+			$tpl->assign("fullnav",TRUE);
+			$refs = read_info_ref($projectroot . $project);
+			$tpl->assign("tree",$co['tree']);
+			$tpl->assign("title",$co['title']);
+			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($projectroot . $project, $hashbase, $file);
+		$tpl->assign("paths",$paths);
+
+		if ($gitphp_conf['filemimetype']) {
+			$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_conf['geshi'];
+			if ($usedgeshi) {
+				$usedgeshi = FALSE;
+				include_once($gitphp_conf['geshiroot'] . "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->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());
+							$usedgeshi = TRUE;
+						}
 					}
 				}
 			}
-		}
 
-		if (!$usedgeshi) {
-			$lines = explode("\n",$catout);
-			$tpl->assign("lines",$lines);
+			if (!$usedgeshi) {
+				$lines = explode("\n",$catout);
+				$tpl->assign("lines",$lines);
+			}
 		}
 	}
 
-	$tpl->display("blob.tpl");
+	$tpl->display('blob.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_blobdiff.php
+++ b/include/display.git_blobdiff.php
@@ -16,31 +16,34 @@
 function git_blobdiff($projectroot,$project,$hash,$hashbase,$hashparent,$file)
 {
 	global $tpl;
-	$ret = prep_tmpdir();
-	if ($ret !== TRUE) {
-		echo $ret;
-		return;
+
+	$cachekey = sha1($project) . "|" . $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);
+		if ($co = git_read_commit($projectroot . $project, $hashbase)) {
+			$tpl->assign("fullnav",TRUE);
+			$tpl->assign("tree",$co['tree']);
+			$tpl->assign("title",$co['title']);
+			$refs = read_info_ref($projectroot . $project);
+			if (isset($refs[$hashbase]))
+				$tpl->assign("hashbaseref",$refs[$hashbase]);
+		}
+		$paths = git_path_trees($projectroot . $project, $hashbase, $file);
+		$tpl->assign("paths",$paths);
+		$diffout = explode("\n",git_diff($projectroot . $project, $hashparent,($file?$file:$hashparent),$hash,($file?$file:$hash)));
+		$tpl->assign("diff",$diffout);
 	}
-	$tpl->clear_all_assign();
-	$tpl->assign("hash",$hash);
-	$tpl->assign("hashparent",$hashparent);
-	$tpl->assign("hashbase",$hashbase);
-	$tpl->assign("project",$project);
-	if (isset($file))
-		$tpl->assign("file",$file);
-	if ($co = git_read_commit($projectroot . $project, $hashbase)) {
-		$tpl->assign("fullnav",TRUE);
-		$tpl->assign("tree",$co['tree']);
-		$tpl->assign("title",$co['title']);
-		$refs = read_info_ref($projectroot . $project);
-		if (isset($refs[$hashbase]))
-			$tpl->assign("hashbaseref",$refs[$hashbase]);
-	}
-	$paths = git_path_trees($projectroot . $project, $hashbase, $file);
-	$tpl->assign("paths",$paths);
-	$diffout = explode("\n",git_diff($projectroot . $project, $hashparent,($file?$file:$hashparent),$hash,($file?$file:$hash)));
-	$tpl->assign("diff",$diffout);
-	$tpl->display("blobdiff.tpl");
+	$tpl->display('blobdiff.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_blobdiff_plain.php
+++ b/include/display.git_blobdiff_plain.php
@@ -12,13 +12,21 @@
 
 function git_blobdiff_plain($projectroot,$project,$hash,$hashbase,$hashparent,$file)
 {
-	$ret = prep_tmpdir();
-	if ($ret !== TRUE) {
-		echo $ret;
-		return;
+	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($projectroot . $project, $hashparent,($file?"a/".$file:$hashparent),$hash,($file?"b/".$file:$hash)));
 	}
-	header("Content-type: text/plain; charset=UTF-8");
-	echo git_diff($projectroot . $project, $hashparent,($file?"a/".$file:$hashparent),$hash,($file?"b/".$file:$hash));
+	$tpl->display('blobdiffplain.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_commit.php
+++ b/include/display.git_commit.php
@@ -16,80 +16,83 @@
 function git_commit($projectroot,$project,$hash)
 {
 	global $tpl;
-	$co = git_read_commit($projectroot . $project, $hash);
-	$ad = date_str($co['author_epoch'],$co['author_tz']);
-	$cd = date_str($co['committer_epoch'],$co['committer_tz']);
-	if (isset($co['parent'])) {
-		$root = "";
-		$parent = $co['parent'];
-	} else {
-		$root = "--root";
-		$parent = "";
+
+	$cachekey = sha1($project) . "|" . $hash;
+
+	if (!$tpl->is_cached('commit.tpl', $cachekey)) {
+		$co = git_read_commit($projectroot . $project, $hash);
+		$ad = date_str($co['author_epoch'],$co['author_tz']);
+		$cd = date_str($co['committer_epoch'],$co['committer_tz']);
+		if (isset($co['parent'])) {
+			$root = "";
+			$parent = $co['parent'];
+		} else {
+			$root = "--root";
+			$parent = "";
+		}
+		$diffout = git_diff_tree($projectroot . $project, $root . " " . $parent . " " . $hash, TRUE);
+		$difftree = explode("\n",$diffout);
+		$tpl->assign("hash",$hash);
+		$tpl->assign("tree",$co['tree']);
+		if (isset($co['parent']))
+			$tpl->assign("parent",$co['parent']);
+		$tpl->assign("title",$co['title']);
+		$refs = read_info_ref($projectroot . $project);
+		if (isset($refs[$co['id']]))
+			$tpl->assign("commitref",$refs[$co['id']]);
+		$tpl->assign("author",$co['author']);
+		$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("committer",$co['committer']);
+		$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("id",$co['id']);
+		$tpl->assign("parents",$co['parents']);
+		$tpl->assign("comment",$co['comment']);
+		$tpl->assign("difftreesize",count($difftree)+1);
+		$difftreelines = array();
+		foreach ($difftree as $i => $line) {
+			if (ereg("^:([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);
 	}
-	$diffout = git_diff_tree($projectroot . $project, $root . " " . $parent . " " . $hash, TRUE);
-	$difftree = explode("\n",$diffout);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("tree",$co['tree']);
-	if (isset($co['parent']))
-		$tpl->assign("parent",$co['parent']);
-	$tpl->assign("title",$co['title']);
-	$refs = read_info_ref($projectroot . $project);
-	if (isset($refs[$co['id']]))
-		$tpl->assign("commitref",$refs[$co['id']]);
-	$tpl->assign("author",$co['author']);
-	$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("committer",$co['committer']);
-	$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("id",$co['id']);
-	$tpl->assign("parents",$co['parents']);
-	$tpl->assign("comment",$co['comment']);
-	$tpl->assign("difftreesize",count($difftree)+1);
-	$difftreelines = array();
-	foreach ($difftree as $i => $line) {
-		if (ereg("^:([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");
+	$tpl->display('commit.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_commitdiff.php
+++ b/include/display.git_commitdiff.php
@@ -17,49 +17,52 @@
 function git_commitdiff($projectroot,$project,$hash,$hash_parent)
 {
 	global $tpl;
-	$ret = prep_tmpdir();
-	if ($ret !== TRUE) {
-		echo $ret;
-		return;
+
+	$cachekey = sha1($project) . "|" . $hash . "|" . $hash_parent;
+
+	if (!$tpl->is_cached('commitdiff.tpl', $cachekey)) {
+		$ret = prep_tmpdir();
+		if ($ret !== TRUE) {
+			echo $ret;
+			return;
+		}
+		$co = git_read_commit($projectroot . $project, $hash);
+		if (!isset($hash_parent))
+			$hash_parent = $co['parent'];
+		$diffout = git_diff_tree($projectroot . $project, $hash_parent . " " . $hash);
+		$difftree = explode("\n",$diffout);
+		$refs = read_info_ref($projectroot . $project);
+		$tpl->assign("hash",$hash);
+		$tpl->assign("tree",$co['tree']);
+		$tpl->assign("hashparent",$hash_parent);
+		$tpl->assign("title",$co['title']);
+		if (isset($refs[$co['id']]))
+			$tpl->assign("commitref",$refs[$co['id']]);
+		$tpl->assign("comment",$co['comment']);
+		$difftreelines = array();
+		foreach ($difftree as $i => $line) {
+			if (ereg("^:([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($projectroot . $project, null,"/dev/null",$regs[4],"b/" . $regs[6]));
+				else if ($regs[5] == "D")
+					$difftreeline['diffout'] = explode("\n",git_diff($projectroot . $project, $regs[3],"a/" . $regs[6],null,"/dev/null"));
+				else if (($regs[5] == "M") && ($regs[3] != $regs[4]))
+					$difftreeline['diffout'] = explode("\n",git_diff($projectroot . $project, $regs[3],"a/" . $regs[6],$regs[4],"b/" . $regs[6]));
+				$difftreelines[] = $difftreeline;
+			}
+		}
+		$tpl->assign("difftreelines",$difftreelines);
 	}
-	$co = git_read_commit($projectroot . $project, $hash);
-	if (!isset($hash_parent))
-		$hash_parent = $co['parent'];
-	$diffout = git_diff_tree($projectroot . $project, $hash_parent . " " . $hash);
-	$difftree = explode("\n",$diffout);
-	$refs = read_info_ref($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("tree",$co['tree']);
-	$tpl->assign("hashparent",$hash_parent);
-	$tpl->assign("title",$co['title']);
-	if (isset($refs[$co['id']]))
-		$tpl->assign("commitref",$refs[$co['id']]);
-	$tpl->assign("comment",$co['comment']);
-	$difftreelines = array();
-	foreach ($difftree as $i => $line) {
-		if (ereg("^:([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($projectroot . $project, null,"/dev/null",$regs[4],"b/" . $regs[6]));
-			else if ($regs[5] == "D")
-				$difftreeline['diffout'] = explode("\n",git_diff($projectroot . $project, $regs[3],"a/" . $regs[6],null,"/dev/null"));
-			else if (($regs[5] == "M") && ($regs[3] != $regs[4]))
-				$difftreeline['diffout'] = explode("\n",git_diff($projectroot . $project, $regs[3],"a/" . $regs[6],$regs[4],"b/" . $regs[6]));
-			$difftreelines[] = $difftreeline;
-		}
-	}
-	$tpl->assign("difftreelines",$difftreelines);
-	$tpl->display("commitdiff.tpl");
+	$tpl->display('commitdiff.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_commitdiff_plain.php
+++ b/include/display.git_commitdiff_plain.php
@@ -19,50 +19,55 @@
 function git_commitdiff_plain($projectroot,$project,$hash,$hash_parent)
 {
 	global $tpl;
-	$ret = prep_tmpdir();
-	if ($ret !== TRUE) {
-		echo $ret;
-		return;
-	}
-	$co = git_read_commit($projectroot . $project, $hash);
-	if (!isset($hash_parent))
-		$hash_parent = $co['parent'];
-	$diffout = git_diff_tree($projectroot . $project, $hash_parent . " " . $hash);
-	$difftree = explode("\n",$diffout);
-	$refs = read_info_ref($projectroot . $project,"tags");
-	$listout = git_rev_list($projectroot . $project, "HEAD");
-	$tok = strtok($listout,"\n");
-	while ($tok !== false) {
-		if (isset($refs[$tok]))
-			$tagname = $refs[$tok];
-		if ($tok == $hash)
-			break;
-		$tok = strtok("\n");
-	}
+
+	$cachekey = sha1($project) . "|" . $hash . "|" . $hash_parent;
+
 	header("Content-type: text/plain; charset=UTF-8");
 	header("Content-disposition: inline; filename=\"git-" . $hash . ".patch\"");
-	$ad = date_str($co['author_epoch'],$co['author_tz']);
-	$tpl->clear_all_assign();
-	$tpl->assign("from",$co['author']);
-	$tpl->assign("date",$ad['rfc2822']);
-	$tpl->assign("subject",$co['title']);
-	if (isset($tagname))
-		$tpl->assign("tagname",$tagname);
-	$tpl->assign("url",script_url() . "?p=" . $project . "&a=commitdiff&h=" . $hash);
-	$tpl->assign("comment",$co['comment']);
-	$diffs = array();
-	foreach ($difftree as $i => $line) {
-		if (ereg("^:([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($projectroot . $project, null, "/dev/null", $regs[4], "b/" . $regs[6]);
-			else if ($regs[5] == "D")
-				$diffs[] = git_diff($projectroot . $project, $regs[3], "a/" . $regs[6], null, "/dev/null");
-			else if ($regs[5] == "M")
-				$diffs[] = git_diff($projectroot . $project, $regs[3], "a/" . $regs[6], $regs[4], "b/" . $regs[6]);
+
+	if (!$tpl->is_cached('diff_plaintext.tpl', $cachekey)) {
+		$ret = prep_tmpdir();
+		if ($ret !== TRUE) {
+			echo $ret;
+			return;
 		}
+		$co = git_read_commit($projectroot . $project, $hash);
+		if (!isset($hash_parent))
+			$hash_parent = $co['parent'];
+		$diffout = git_diff_tree($projectroot . $project, $hash_parent . " " . $hash);
+		$difftree = explode("\n",$diffout);
+		$refs = read_info_ref($projectroot . $project,"tags");
+		$listout = git_rev_list($projectroot . $project, "HEAD");
+		$tok = strtok($listout,"\n");
+		while ($tok !== false) {
+			if (isset($refs[$tok]))
+				$tagname = $refs[$tok];
+			if ($tok == $hash)
+				break;
+			$tok = strtok("\n");
+		}
+		$ad = date_str($co['author_epoch'],$co['author_tz']);
+		$tpl->assign("from",$co['author']);
+		$tpl->assign("date",$ad['rfc2822']);
+		$tpl->assign("subject",$co['title']);
+		if (isset($tagname))
+			$tpl->assign("tagname",$tagname);
+		$tpl->assign("url",script_url() . "?p=" . $project . "&a=commitdiff&h=" . $hash);
+		$tpl->assign("comment",$co['comment']);
+		$diffs = array();
+		foreach ($difftree as $i => $line) {
+			if (ereg("^:([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($projectroot . $project, null, "/dev/null", $regs[4], "b/" . $regs[6]);
+				else if ($regs[5] == "D")
+					$diffs[] = git_diff($projectroot . $project, $regs[3], "a/" . $regs[6], null, "/dev/null");
+				else if ($regs[5] == "M")
+					$diffs[] = git_diff($projectroot . $project, $regs[3], "a/" . $regs[6], $regs[4], "b/" . $regs[6]);
+			}
+		}
+		$tpl->assign("diffs",$diffs);
 	}
-	$tpl->assign("diffs",$diffs);
-	$tpl->display("diff_plaintext.tpl");
+	$tpl->display('diff_plaintext.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_heads.php
+++ b/include/display.git_heads.php
@@ -13,13 +13,16 @@
 function git_heads($projectroot,$project)
 {
 	global $tpl;
-	$head = git_read_head($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("head",$head);
-	$headlist = git_read_refs($projectroot, $project, "refs/heads");
-	$tpl->assign("headlist",$headlist);
-	$tpl->display("heads.tpl");
+
+	$cachekey = sha1($project);
+
+	if (!$tpl->is_cached('heads.tpl', $cachekey)) {
+		$head = git_read_head($projectroot . $project);
+		$tpl->assign("head",$head);
+		$headlist = git_read_refs($projectroot, $project, "refs/heads");
+		$tpl->assign("headlist",$headlist);
+	}
+	$tpl->display('heads.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_history.php
+++ b/include/display.git_history.php
@@ -17,48 +17,51 @@
 function git_history($projectroot,$project,$hash,$file)
 {
 	global $tpl;
-	if (!isset($hash))
-		$hash = git_read_head($projectroot . $project);
-	$co = git_read_commit($projectroot . $project, $hash);
-	$refs = read_info_ref($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	if (isset($refs[$hash]))
-		$tpl->assign("hashbaseref",$refs[$hash]);
-	$tpl->assign("tree",$co['tree']);
-	$tpl->assign("title",$co['title']);
-	$paths = git_path_trees($projectroot . $project, $hash, $file);
-	$tpl->assign("paths",$paths);
-	$cmdout = git_history_list($projectroot . $project, $hash, $file);
-	$lines = explode("\n", $cmdout);
-	$historylines = array();
-	foreach ($lines as $i => $line) {
-		if (ereg("^([0-9a-fA-F]{40})",$line,$regs))
-			$commit = $regs[1];
-		else if (ereg(":([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$",$line,$regs) && isset($commit)) {
-				$historyline = array();
-				$co = git_read_commit($projectroot . $project, $commit);
-				$historyline["agestringage"] = $co['age_string_age'];
-				$historyline["agestringdate"] = $co['age_string_date'];
-				$historyline["authorname"] = $co['author_name'];
-				$historyline["commit"] = $commit;
-				$historyline["file"] = $file;
-				$historyline["title"] = $co['title_short'];
-				if (isset($refs[$commit]))
-					$historyline["commitref"] = $refs[$commit];
-				$blob = git_get_hash_by_path($projectroot . $project, $hash,$file);
-				$blob_parent = git_get_hash_by_path($projectroot . $project, $commit,$file);
-				if ($blob && $blob_parent && ($blob != $blob_parent)) {
-					$historyline["blob"] = $blob;
-					$historyline["blobparent"] = $blob_parent;
-				}
-				$historylines[] = $historyline;
-				unset($commit);
+
+	$cachekey = sha1($project) . "|" . $hash . "|" . sha1($file);
+
+	if (!$tpl->is_cached('history.tpl', $cachekey)) {
+		if (!isset($hash))
+			$hash = git_read_head($projectroot . $project);
+		$co = git_read_commit($projectroot . $project, $hash);
+		$refs = read_info_ref($projectroot . $project);
+		$tpl->assign("hash",$hash);
+		if (isset($refs[$hash]))
+			$tpl->assign("hashbaseref",$refs[$hash]);
+		$tpl->assign("tree",$co['tree']);
+		$tpl->assign("title",$co['title']);
+		$paths = git_path_trees($projectroot . $project, $hash, $file);
+		$tpl->assign("paths",$paths);
+		$cmdout = git_history_list($projectroot . $project, $hash, $file);
+		$lines = explode("\n", $cmdout);
+		$historylines = array();
+		foreach ($lines as $i => $line) {
+			if (ereg("^([0-9a-fA-F]{40})",$line,$regs))
+				$commit = $regs[1];
+			else if (ereg(":([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$",$line,$regs) && isset($commit)) {
+					$historyline = array();
+					$co = git_read_commit($projectroot . $project, $commit);
+					$historyline["agestringage"] = $co['age_string_age'];
+					$historyline["agestringdate"] = $co['age_string_date'];
+					$historyline["authorname"] = $co['author_name'];
+					$historyline["commit"] = $commit;
+					$historyline["file"] = $file;
+					$historyline["title"] = $co['title_short'];
+					if (isset($refs[$commit]))
+						$historyline["commitref"] = $refs[$commit];
+					$blob = git_get_hash_by_path($projectroot . $project, $hash,$file);
+					$blob_parent = git_get_hash_by_path($projectroot . $project, $commit,$file);
+					if ($blob && $blob_parent && ($blob != $blob_parent)) {
+						$historyline["blob"] = $blob;
+						$historyline["blobparent"] = $blob_parent;
+					}
+					$historylines[] = $historyline;
+					unset($commit);
+			}
 		}
+		$tpl->assign("historylines",$historylines);
 	}
-	$tpl->assign("historylines",$historylines);
-	$tpl->display("history.tpl");
+	$tpl->display('history.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_log.php
+++ b/include/display.git_log.php
@@ -16,53 +16,56 @@
 function git_log($projectroot,$project,$hash,$page)
 {
 	global $tpl;
-	$head = git_read_head($projectroot . $project);
-	if (!isset($hash))
-		$hash = $head;
-	if (!isset($page))
-		$page = 0;
-	$refs = read_info_ref($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("head",$head);
 
-	if ($page)
-		$tpl->assign("page",$page);
+	$cachekey = sha1($project) . "|" . $hash . "|" . $page;
 
-	$revlist = git_read_revlist($projectroot . $project, $hash, 101, ($page * 100));
+	if (!$tpl->is_cached('log.tpl', $cachekey)) {
+		$head = git_read_head($projectroot . $project);
+		if (!isset($hash))
+			$hash = $head;
+		if (!isset($page))
+			$page = 0;
+		$refs = read_info_ref($projectroot . $project);
+		$tpl->assign("hash",$hash);
+		$tpl->assign("head",$head);
 
-	$revlistcount = count($revlist);
-	$tpl->assign("revlistcount",$revlistcount);
+		if ($page)
+			$tpl->assign("page",$page);
 
-	if (!$revlist) {
-		$tpl->assign("norevlist",TRUE);
-		$co = git_read_commit($hash);
-		$tpl->assign("lastchange",$co['age_string']);
+		$revlist = git_read_revlist($projectroot . $project, $hash, 101, ($page * 100));
+
+		$revlistcount = count($revlist);
+		$tpl->assign("revlistcount",$revlistcount);
+
+		if (!$revlist) {
+			$tpl->assign("norevlist",TRUE);
+			$co = git_read_commit($hash);
+			$tpl->assign("lastchange",$co['age_string']);
+		}
+
+		$commitlines = array();
+		$commitcount = min(100,$revlistcount);
+		for ($i = 0; $i < $commitcount; $i++) {
+			$commit = $revlist[$i];
+			if (isset($commit) && strlen($commit) > 1) {
+				$commitline = array();
+				$co = git_read_commit($projectroot . $project, $commit);
+				$ad = date_str($co['author_epoch']);
+				$commitline["project"] = $project;
+				$commitline["commit"] = $commit;
+				if (isset($refs[$commit]))
+					$commitline["commitref"] = $refs[$commit];
+				$commitline["agestring"] = $co['age_string'];
+				$commitline["title"] = $co['title'];
+				$commitline["authorname"] = $co['author_name'];
+				$commitline["rfc2822"] = $ad['rfc2822'];
+				$commitline["comment"] = $co['comment'];
+				$commitlines[] = $commitline;
+			}
+		}
+		$tpl->assign("commitlines",$commitlines);
 	}
-
-	$commitlines = array();
-	$commitcount = min(100,$revlistcount);
-	for ($i = 0; $i < $commitcount; $i++) {
-		$commit = $revlist[$i];
-		if (isset($commit) && strlen($commit) > 1) {
-			$commitline = array();
-			$co = git_read_commit($projectroot . $project, $commit);
-			$ad = date_str($co['author_epoch']);
-			$commitline["project"] = $project;
-			$commitline["commit"] = $commit;
-			if (isset($refs[$commit]))
-				$commitline["commitref"] = $refs[$commit];
-			$commitline["agestring"] = $co['age_string'];
-			$commitline["title"] = $co['title'];
-			$commitline["authorname"] = $co['author_name'];
-			$commitline["rfc2822"] = $ad['rfc2822'];
-			$commitline["comment"] = $co['comment'];
-			$commitlines[] = $commitline;
-		}
-	}
-	$tpl->assign("commitlines",$commitlines);
-	$tpl->display("log.tpl");
+	$tpl->display('log.tpl', $cachekey);
 }
 
 ?>

--- /dev/null
+++ b/include/display.git_message.php
@@ -1,1 +1,27 @@
+<?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
+++ b/include/display.git_opml.php
@@ -13,23 +13,27 @@
 function git_opml($projectroot,$projectlist)
 {
 	global $tpl,$gitphp_conf;
-	$projlist = git_read_projects($projectroot,$projectlist);
-	header("Content-type: text/xml; charset=UTF-8");
-	$tpl->clear_all_assign();
-	$tpl->assign("title",$gitphp_conf['title']);
-	$tpl->assign("self",script_url());
-	$opmllist = array();
-	foreach ($projlist as $cat => $plist) {
-		if (is_array($plist)) {
-			foreach ($plist as $i => $proj) {
-				$opmllist[] = $proj;
+
+	$cachekey = sha1(serialize($projectlist));
+
+	if (!$tpl->is_cached('opml.tpl', $cachekey)) {
+		header("Content-type: text/xml; charset=UTF-8");
+		$projlist = git_read_projects($projectroot,$projectlist);
+		$tpl->assign("title",$gitphp_conf['title']);
+		$tpl->assign("self",script_url());
+		$opmllist = array();
+		foreach ($projlist as $cat => $plist) {
+			if (is_array($plist)) {
+				foreach ($plist as $i => $proj) {
+					$opmllist[] = $proj;
+				}
+			} else {
+				$opmllist[] = $plist;
 			}
-		} else {
-			$opmllist[] = $plist;
 		}
+		$tpl->assign("opmllist",$opmllist);
 	}
-	$tpl->assign("opmllist",$opmllist);
-	$tpl->display("opml.tpl");
+	$tpl->display('opml.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_project_index.php
+++ b/include/display.git_project_index.php
@@ -11,16 +11,20 @@
 
 function git_project_index($projectroot, $projectlist)
 {
-	$projlist = git_read_projects($projectroot, $projectlist);
+	global $tpl, $git_projects;
+
 	header("Content-type: text/plain; charset=utf-8");
 	header("Content-Disposition: inline; filename=\"index.aux\"");
-	foreach ($projlist as $cat => $plist) {
-		if (is_array($plist)) {
-			foreach ($plist as $i => $proj)
-				echo $proj . "\n";
-		} else
-			echo $plist . "\n";
+
+	$cachekey = sha1(serialize($projectlist));
+
+	if (!$tpl->is_cached('projectindex.tpl', $cachekey)) {
+		if (isset($git_projects))
+			$tpl->assign("categorized", TRUE);
+		$projlist = git_read_projects($projectroot, $projectlist);
+		$tpl->assign("projlist", $projlist);
 	}
+	$tpl->display('projectindex.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_project_list.php
+++ b/include/display.git_project_list.php
@@ -11,74 +11,65 @@
  require_once('util.descrcmp.php');
  require_once('util.ownercmp.php');
  require_once('util.agecmp.php');
- require_once('display.git_project_listentry.php');
  require_once('gitutil.git_read_projects.php');
 
 function git_project_list($projectroot,$projectlist,$order = "project")
 {
 	global $tpl,$git_projects;
-	$projects = git_read_projects($projectroot,$projectlist);
-	if (is_array($projects)) {
-		if (count($projects) > 0) {
-			$tpl->clear_all_assign();
-			if ($order)
-				$tpl->assign("order",$order);
-			$tpl->display("projlist_header.tpl");
-			if (!isset($git_projects)) {
-				switch ($order) {
-					case "project":
-						usort($projects,"projectcmp");
-						break;
-					case "descr":
-						usort($projects,"descrcmp");
-						break;
-					case "owner":
-						usort($projects,"ownercmp");
-						break;
-					case "age":
-						usort($projects,"agecmp");
-						break;
-				}
-			}
-			$alternate = false;
-			foreach ($projects as $cat => $plist) {
-				if (is_array($plist)) {
-					if ($cat != "none") {
-						$tpl->clear_all_assign();
-						$tpl->assign("category",$cat);
-						$tpl->display("projlist_category.tpl");
+
+	$cachekey = sha1(serialize($projectlist)) . "|" . sha1($order);
+
+	if (!$tpl->is_cached('projectlist.tpl', $cachekey)) {
+		$projects = git_read_projects($projectroot,$projectlist, TRUE);
+		if (is_array($projects)) {
+			if (count($projects) > 0) {
+				if ($order)
+					$tpl->assign("order",$order);
+				if (!isset($git_projects)) {
+					switch ($order) {
+						case "project":
+							usort($projects,"projectcmp");
+							break;
+						case "descr":
+							usort($projects,"descrcmp");
+							break;
+						case "owner":
+							usort($projects,"ownercmp");
+							break;
+						case "age":
+							usort($projects,"agecmp");
+							break;
 					}
-					if (isset($git_projects)) {
+					$tpl->assign("projects",$projects);
+				} else {
+					foreach ($projects as $cat => $plist) {
 						switch ($order) {
 							case "project":
-								usort($plist,"projectcmp");
+								usort($projects[$cat],"projectcmp");
 								break;
 							case "descr":
-								usort($plist,"descrcmp");
+								usort($projects[$cat],"descrcmp");
 								break;
 							case "owner":
-								usort($plist,"ownercmp");
+								usort($projects[$cat],"ownercmp");
 								break;
 							case "age":
-								usort($plist,"agecmp");
+								usort($projects[$cat],"agecmp");
 								break;
 						}
 					}
-					foreach ($plist as $i => $proj) {
-						git_project_listentry($projectroot,$proj,($alternate?"dark":"light"),($cat=="none"?FALSE:TRUE));
-						$alternate = !$alternate;
-					}
-				} else {
-					git_project_listentry($projectroot,$plist,($alternate?"dark":"light"),FALSE);
-					$alternate = !$alternate;
+					$tpl->assign("categorizedprojects",$projects);
 				}
+			} else {
+				$tpl->assign("message","No projects found");
+				$tpl->assign("error",TRUE);
 			}
-			$tpl->clear_all_assign();
-			$tpl->display("projlist_footer.tpl");
-		} else
-			echo "No projects found";
-	} else
-		echo $projects;
+		} else {
+			$tpl->assign("message",$projects);
+			$tpl->assign("error",TRUE);
+		}
+	}
+	$tpl->display('projectlist.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_project_listentry.php
+++ /dev/null
@@ -1,36 +1,1 @@
-<?php
-/*
- *  display.git_project_listentry.php
- *  gitphp: A PHP git repository browser
- *  Component: Display - single project list item
- *
- *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
- */
 
- require_once('gitutil.git_project_descr.php');
- require_once('gitutil.git_project_owner.php');
- require_once('gitutil.git_read_head.php');
- require_once('gitutil.git_read_commit.php');
-
-function git_project_listentry($projectroot,$project,$class,$indent)
-{
-	global $tpl;
-	$tpl->clear_all_assign();
-	$tpl->assign("class",$class);
-	$tpl->assign("project",$project);
-	$tpl->assign("descr",git_project_descr($projectroot,$project,TRUE));
-	$tpl->assign("owner",git_project_owner($projectroot,$project));
-	if ($indent)
-		$tpl->assign("idt",TRUE);
-	$head = git_read_head($projectroot . $project);
-	$commit = git_read_commit($projectroot . $project,$head);
-	if ($commit['age'] < 60*60*24*2)
-		$tpl->assign("age_colored",TRUE);
-	if ($commit['age'] < 60*60*2)
-		$tpl->assign("age_bold",TRUE);
-	$tpl->assign("age_string",$commit['age_string']);
-	$tpl->display("projlist_item.tpl");
-}
-
-?>
-

--- a/include/display.git_rss.php
+++ b/include/display.git_rss.php
@@ -18,46 +18,46 @@
 function git_rss($projectroot,$project)
 {
 	global $tpl;
-	$head = git_read_head($projectroot . $project);
-	$revlist = git_read_revlist($projectroot . $project, $head, GITPHP_RSS_ITEMS);
 	header("Content-type: text/xml; charset=UTF-8");
-	$tpl->clear_all_assign();
-	$tpl->assign("self",script_url());
-	$tpl->assign("project",$project);
-	$tpl->display("rss_header.tpl");
 
-	for ($i = 0; $i <= count($revlist); $i++) {
-		$commit = $revlist[$i];
-		$co = git_read_commit($projectroot . $project, $commit);
-		if (($i >= 20) && ((time() - $co['committer_epoch']) > 48*60*60))
-			break;
-		$cd = date_str($co['committer_epoch']);
-		$difftree = array();
-		$diffout = git_diff_tree($projectroot . $project, $co['parent'] . " " . $co['id']);
-		$tok = strtok($diffout,"\n");
-		while ($tok !== false) {
-			if (ereg("^:([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");
+	$cachekey = sha1($project);
+
+	if (!$tpl->is_cached('rss.tpl', $cachekey)) {
+		$head = git_read_head($projectroot . $project);
+		$revlist = git_read_revlist($projectroot . $project, $head, GITPHP_RSS_ITEMS);
+		$tpl->assign("self",script_url());
+
+		$commitlines = array();
+		for ($i = 0; $i <= count($revlist); $i++) {
+			$commit = $revlist[$i];
+			$co = git_read_commit($projectroot . $project, $commit);
+			if (($i >= 20) && ((time() - $co['committer_epoch']) > 48*60*60))
+				break;
+			$cd = date_str($co['committer_epoch']);
+			$difftree = array();
+			$diffout = git_diff_tree($projectroot . $project, $co['parent'] . " " . $co['id']);
+			$tok = strtok($diffout,"\n");
+			while ($tok !== false) {
+				if (ereg("^:([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 = array();
+			$commitline["cdmday"] = $cd['mday'];
+			$commitline["cdmonth"] = $cd['month'];
+			$commitline["cdhour"] = $cd['hour'];
+			$commitline["cdminute"] = $cd['minute'];
+			$commitline["title"] = $co['title'];
+			$commitline["author"] = $co['author'];
+			$commitline["cdrfc2822"] = $cd['rfc2822'];
+			$commitline["commit"] = $commit;
+			$commitline["comment"] = $co['comment'];
+			$commitline["difftree"] = $difftree;
+			$commitlines[] = $commitline;
 		}
-		$tpl->clear_all_assign();
-		$tpl->assign("cdmday",$cd['mday']);
-		$tpl->assign("cdmonth",$cd['month']);
-		$tpl->assign("cdhour",$cd['hour']);
-		$tpl->assign("cdminute",$cd['minute']);
-		$tpl->assign("title",htmlentities($co['title']));
-		$tpl->assign("author",htmlentities($co['author']));
-		$tpl->assign("cdrfc2822",$cd['rfc2822']);
-		$tpl->assign("self",script_url());
-		$tpl->assign("project",$project);
-		$tpl->assign("commit",$commit);
-		$tpl->assign("comment",$co['comment']);
-		$tpl->assign("difftree",$difftree);
-		$tpl->display("rss_item.tpl");
+		$tpl->assign("commitlines",$commitlines);
 	}
-
-	$tpl->clear_all_assign();
-	$tpl->display("rss_footer.tpl");
+	$tpl->display('rss.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_search.php
+++ b/include/display.git_search.php
@@ -11,105 +11,78 @@
 require_once('util.highlight.php');
 require_once('gitutil.git_read_commit.php');
 require_once('gitutil.git_rev_list.php');
+require_once('display.git_message.php');
 
 function git_search($projectroot, $project, $hash, $search, $searchtype, $page = 0)
 {
 	global $tpl,$gitphp_conf;
 
-	if (!$gitphp_conf['search']) {
-		$tpl->clear_all_assign();
-		$tpl->assign("message","Search has been disabled");
-		$tpl->display("message.tpl");
-		return;
+	$cachekey = sha1($project) . "|" . $hash . "|" . sha1($searchtype) . "|" . sha1($search) . "|" . $page;
+
+	if (!$tpl->is_cached('search.tpl', $cachekey)) {
+
+		if (!$gitphp_conf['search']) {
+			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 = git_read_head($projectroot . $project);
+			$hash = "HEAD";
+		}
+
+		$co = git_read_commit($projectroot . $project, $hash);
+
+		$revlist = explode("\n",trim(git_rev_list($projectroot . $project, $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['tree']);
+
+		$tpl->assign("search",$search);
+		$tpl->assign("searchtype",$searchtype);
+		$tpl->assign("page",$page);
+		$revlistcount = count($revlist);
+		$tpl->assign("revlistcount",$revlistcount);
+
+		$tpl->assign("title",$co['title']);
+
+		$commitlines = array();
+		$commitcount = min(100,$revlistcount);
+		for ($i = 0; $i < $commitcount; $i++) {
+			$commit = $revlist[$i];
+			if (strlen(trim($commit)) > 0) {
+				$commitline = array();
+				$co2 = git_read_commit($projectroot . $project, $commit);
+				$commitline["commit"] = $commit;
+				$commitline["agestringage"] = $co2['age_string_age'];
+				$commitline["agestringdate"] = $co2['age_string_date'];
+				$commitline["authorname"] = $co2['author_name'];
+				$commitline["title_short"] = $co2['title_short'];
+				if (strlen($co2['title_short']) < strlen($co2['title']))
+					$commitline["title"] = $co2['title'];
+				$commitline["committree"] = $co2['tree'];
+				$matches = array();
+				foreach ($co2['comment'] as $comline) {
+					$hl = highlight($comline, $search, "searchmatch", GITPHP_TRIM_LENGTH);
+					if ($hl && (strlen($hl) > 0))
+						$matches[] = $hl;
+				}
+				$commitline["matches"] = $matches;
+				$commitlines[] = $commitline;
+			}
+		}
+		
+		$tpl->assign("commitlines",$commitlines);
 	}
-
-	if (!isset($search) || (strlen($search) < 2)) {
-		$tpl->clear_all_assign();
-		$tpl->assign("error",TRUE);
-		$tpl->assign("message","You must enter search text of at least 2 characters");
-		$tpl->display("message.tpl");
-		return;
-	}
-	if (!isset($hash)) {
-		//$hash = git_read_head($projectroot . $project);
-		$hash = "HEAD";
-	}
-
-	$co = git_read_commit($projectroot . $project, $hash);
-
-	$revlist = explode("\n",trim(git_rev_list($projectroot . $project, $hash, 101, ($page * 100), FALSE, FALSE, $searchtype, $search)));
-	if (count($revlist) < 1 || (strlen($revlist[0]) < 1)) {
-		$tpl->clear_all_assign();
-		$tpl->assign("message","No matches for '" . $search . "'.");
-		$tpl->display("message.tpl");
-		return;
-	}
-
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("treehash",$co['tree']);
-	$tpl->display("search_nav.tpl");
-
-	$tpl->assign("search",$search);
-	$tpl->assign("searchtype",$searchtype);
-	if ($page > 0) {
-		$tpl->assign("firstlink",TRUE);
-		$tpl->assign("prevlink",TRUE);
-		if ($page > 1)
-			$tpl->assign("prevpage",$page-1);
-	}
-	if (count($revlist) > 100) {
-		$tpl->assign("nextlink",TRUE);
-		$tpl->assign("nextpage",$page+1);
-	}
-	$tpl->display("search_pagenav.tpl");
-
-	$tpl->assign("title",$co['title']);
-	$tpl->display("search_header.tpl");
-
-	$alternate = FALSE;
-	$commitcount = min(100,count($revlist));
-	for ($i = 0; $i < $commitcount; $i++) {
-		$tpl->clear_all_assign();
-		$commit = $revlist[$i];
-		if (strlen(trim($commit)) > 0) {
-			$co2 = git_read_commit($projectroot . $project, $commit);
-			if ($alternate)
-				$tpl->assign("class","dark");
-			else
-				$tpl->assign("class","light");
-			$alternate = !$alternate;
-			$tpl->assign("project",$project);
-			$tpl->assign("commit",$commit);
-			$tpl->assign("agestringage",$co2['age_string_age']);
-			$tpl->assign("agestringdate",$co2['age_string_date']);
-			$tpl->assign("authorname",$co2['author_name']);
-			$tpl->assign("title_short",$co2['title_short']);
-			if (strlen($co2['title_short']) < strlen($co2['title']))
-				$tpl->assign("title",$co2['title']);
-			$tpl->assign("committree",$co2['tree']);
-			$matches = array();
-			foreach ($co2['comment'] as $comline) {
-				$hl = highlight($comline, $search, "searchmatch", GITPHP_TRIM_LENGTH);
-				if ($hl && (strlen($hl) > 0))
-					$matches[] = $hl;
-			}
-			$tpl->assign("matches",$matches);
-			$tpl->display("search_item.tpl");
-		}
-	}
-
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("search",$search);
-	$tpl->assign("searchtype",$searchtype);
-	if (count($revlist) > 100) {
-		$tpl->assign("nextlink",TRUE);
-		$tpl->assign("nextpage",$page+1);
-	}
-	$tpl->display("search_footer.tpl");
+	$tpl->display('search.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_search_files.php
+++ b/include/display.git_search_files.php
@@ -11,119 +11,90 @@
 require_once('util.highlight.php');
 require_once('gitutil.git_filesearch.php');
 require_once('gitutil.git_read_commit.php');
+require_once('display.git_message.php');
 
 function git_search_files($projectroot, $project, $hash, $search, $page = 0)
 {
 	global $tpl,$gitphp_conf;
 
-	if (!($gitphp_conf['search'] && $gitphp_conf['filesearch'])) {
-		$tpl->clear_all_assign();
-		$tpl->assign("message","File search has been disabled");
-		$tpl->display("message.tpl");
-		return;
+	$cachekey = sha1($project) . "|" . $hash . "|" . "filesearch" . "|" . sha1($search) . "|" . $page;
+
+	if (!$tpl->is_cached('searchfiles.tpl', $cachekey)) {
+
+		if (!($gitphp_conf['search'] && $gitphp_conf['filesearch'])) {
+			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 = git_read_head($projectroot . $project);
+			$hash = "HEAD";
+		}
+
+		$co = git_read_commit($projectroot . $project, $hash);
+
+		$filesearch = git_filesearch($projectroot . $project, $hash, $search, false, ($page * 100), 101);
+
+		if (count($filesearch) < 1) {
+			git_message("No matches for '" . $search . "'.", FALSE, TRUE);
+			return;
+		}
+
+		$tpl->assign("hash",$hash);
+		$tpl->assign("treehash",$co['tree']);
+
+		$tpl->assign("search",$search);
+		$tpl->assign("searchtype","file");
+		$tpl->assign("page",$page);
+		$filesearchcount = count($filesearch);
+		$tpl->assign("filesearchcount",$filesearchcount);
+
+		$tpl->assign("title",$co['title']);
+
+		$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);
 	}
-
-	if (!isset($search) || (strlen($search) < 2)) {
-		$tpl->clear_all_assign();
-		$tpl->assign("error",TRUE);
-		$tpl->assign("message","You must enter search text of at least 2 characters");
-		$tpl->display("message.tpl");
-		return;
-	}
-	if (!isset($hash)) {
-		//$hash = git_read_head($projectroot . $project);
-		$hash = "HEAD";
-	}
-
-	$co = git_read_commit($projectroot . $project, $hash);
-
-	$filesearch = git_filesearch($projectroot . $project, $hash, $search, false, ($page * 100), 101);
-
-	if (count($filesearch) < 1) {
-		$tpl->clear_all_assign();
-		$tpl->assign("message","No matches for '" . $search . "'.");
-		$tpl->display("message.tpl");
-		return;
-	}
-
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("treehash",$co['tree']);
-	$tpl->display("search_nav.tpl");
-
-	$tpl->assign("search",$search);
-	$tpl->assign("searchtype","file");
-	if ($page > 0) {
-		$tpl->assign("firstlink",TRUE);
-		$tpl->assign("prevlink",TRUE);
-		if ($page > 1)
-			$tpl->assign("prevpage",$page-1);
-	}
-	if (count($filesearch) > 100) {
-		$tpl->assign("nextlink",TRUE);
-		$tpl->assign("nextpage",$page+1);
-	}
-	$tpl->display("search_pagenav.tpl");
-
-	$tpl->assign("title",$co['title']);
-	$tpl->display("search_header.tpl");
-
-	$alternate = FALSE;
-	$i = 0;
-	foreach ($filesearch as $file => $data) {
-		$tpl->clear_all_assign();
-		if ($alternate)
-			$tpl->assign("class","dark");
-		else
-			$tpl->assign("class","light");
-		$alternate = !$alternate;
-		$tpl->assign("project",$project);
-		$tpl->assign("hashbase",$hash);
-		$tpl->assign("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)
-			$tpl->assign("filename",$hlt);
-		else
-			$tpl->assign("filename",$file);
-		$tpl->assign("hash",$data['hash']);
-		if ($data['type'] == "tree")
-			$tpl->assign("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)
-				$tpl->assign("matches",$matches);
-		}
-		$tpl->display("search_fileitem.tpl");
-		$i++;
-		if ($i >= 100)
-			break;
-	}
-
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->assign("search",$search);
-	$tpl->assign("searchtype","file");
-	if (count($filesearch) > 100) {
-		$tpl->assign("nextlink",TRUE);
-		$tpl->assign("nextpage",$page+1);
-	}
-	$tpl->display("search_footer.tpl");
+	$tpl->display('searchfiles.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_shortlog.php
+++ b/include/display.git_shortlog.php
@@ -16,66 +16,50 @@
 function git_shortlog($projectroot,$project,$hash,$page)
 {
 	global $tpl;
-	$head = git_read_head($projectroot . $project);
-	if (!isset($hash))
-		$hash = $head;
-	if (!isset($page))
-		$page = 0;
-	$refs = read_info_ref($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	$tpl->display("shortlog_nav.tpl");
 
-	$revlist = git_read_revlist($projectroot . $project, $hash, 101, ($page * 100));
+	$cachekey = sha1($project) . "|" . $hash . "|" . $page;
 
-	if (($hash != $head) || $page)
-		$tpl->assign("headlink",TRUE);
-	if ($page > 0) {
-		$tpl->assign("prevlink",TRUE);
-		$tpl->assign("prevpage",$page-1);
+	if (!$tpl->is_cached('shortlog.tpl', $cachekey)) {
+		$head = git_read_head($projectroot . $project);
+		if (!isset($hash))
+			$hash = $head;
+		if (!isset($page))
+			$page = 0;
+		$refs = read_info_ref($projectroot . $project);
+		$tpl->assign("hash",$hash);
+		$tpl->assign("head",$head);
+
+		if ($page)
+			$tpl->assign("page",$page);
+
+		$revlist = git_read_revlist($projectroot . $project, $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 = git_read_commit($projectroot . $project, $commit);
+				$ad = date_str($co['author_epoch']);
+				$commitline["commit"] = $commit;
+				$commitline["agestringage"] = $co['age_string_age'];
+				$commitline["agestringdate"] = $co['age_string_date'];
+				$commitline["authorname"] = $co['author_name'];
+				$commitline["title_short"] = $co['title_short'];
+				if (strlen($co['title_short']) < strlen($co['title']))
+					$commitline["title"] = $co['title'];
+				$commitlines[] = $commitline;
+			}
+		}
+		$tpl->assign("commitlines",$commitlines);
 	}
-	if (count($revlist) > 100) {
-		$tpl->assign("nextlink",TRUE);
-		$tpl->assign("nextpage",$page+1);
-	}
-	$tpl->display("shortlog_pagenav.tpl");
-
-	$alternate = FALSE;
-	$commitcount = min(100,count($revlist));
-	for ($i = 0; $i < $commitcount; $i++) {
-		$tpl->clear_all_assign();
-		$commit = $revlist[$i];
-		if (strlen(trim($commit)) > 0) {
-			if (isset($refs[$commit]))
-				$tpl->assign("commitref",$refs[$commit]);
-			$co = git_read_commit($projectroot . $project, $commit);
-			$ad = date_str($co['author_epoch']);
-			if ($alternate)
-				$tpl->assign("class","dark");
-			else
-				$tpl->assign("class","light");
-			$alternate = !$alternate;
-			$tpl->assign("project",$project);
-			$tpl->assign("commit",$commit);
-			$tpl->assign("agestringage",$co['age_string_age']);
-			$tpl->assign("agestringdate",$co['age_string_date']);
-			$tpl->assign("authorname",$co['author_name']);
-			$tpl->assign("title_short",$co['title_short']);
-			if (strlen($co['title_short']) < strlen($co['title']))
-				$tpl->assign("title",$co['title']);
-			$tpl->display("shortlog_item.tpl");
-		}
-	}
-
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hash",$hash);
-	if (count($revlist) > 100) {
-		$tpl->assign("nextlink",TRUE);
-		$tpl->assign("nextpage",$page+1);
-	}
-	$tpl->display("shortlog_footer.tpl");
+	$tpl->display('shortlog.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_snapshot.php
+++ b/include/display.git_snapshot.php
@@ -12,33 +12,46 @@
 
 function git_snapshot($projectroot,$project,$hash)
 {
-	global $gitphp_conf;
+	global $gitphp_conf, $tpl;
+
 	if (!isset($hash))
 		$hash = "HEAD";
+
+	$cachekey = sha1($project) . "|" . $hash;
+
+	$bzcompress = false;
+	$gzencode = false;
+
 	$rname = str_replace(array("/",".git"),array("-",""),$project);
-	$arc = git_archive($projectroot . $project, $hash, $rname,
-		(($gitphp_conf['compressformat'] == GITPHP_COMPRESS_ZIP) ? "zip" : "tar"));
-
 	if ($gitphp_conf['compressformat'] == GITPHP_COMPRESS_ZIP) {
 		header("Content-Type: application/x-zip");
 		header("Content-Disposition: attachment; filename=" . $rname . ".zip");
-		echo $arc;
-		return;
 	} else if (($gitphp_conf['compressformat'] == GITPHP_COMPRESS_BZ2) && function_exists("bzcompress")) {
+		$bzcompress = true;
 		header("Content-Type: application/x-bzip2");
 		header("Content-Disposition: attachment; filename=" . $rname . ".tar.bz2");
-		echo bzcompress($arc,(isset($gitphp_conf['compresslevel'])?$gitphp_conf['compresslevel']:4));
-		return;
 	} else if (($gitphp_conf['compressformat'] == GITPHP_COMPRESS_GZ) && function_exists("gzencode")) {
+		$gzencode = true;
 		header("Content-Type: application/x-gzip");
 		header("Content-Disposition: attachment; filename=" . $rname . ".tar.gz");
-		echo gzencode($arc,(isset($gitphp_conf['compresslevel'])?$gitphp_conf['compresslevel']:-1));
-		return;
+	} else {
+		header("Content-Type: application/x-tar");
+		header("Content-Disposition: attachment; filename=" . $rname . ".tar");
 	}
 
-	header("Content-Type: application/x-tar");
-	header("Content-Disposition: attachment; filename=" . $rname . ".tar");
-	echo $arc;
+	if (!$tpl->is_cached('snapshot.tpl', $cachekey)) {
+
+		$arc = git_archive($projectroot . $project, $hash, $rname,
+			(($gitphp_conf['compressformat'] == GITPHP_COMPRESS_ZIP) ? "zip" : "tar"));
+
+		if (($gitphp_conf['compressformat'] == GITPHP_COMPRESS_BZ2) && $bzcompress) {
+			$arc = bzcompress($arc,(isset($gitphp_conf['compresslevel'])?$gitphp_conf['compresslevel']:4));
+		} else if (($gitphp_conf['compressformat'] == GITPHP_COMPRESS_GZ) && $gzencode) {
+			$arc = gzencode($arc,(isset($gitphp_conf['compresslevel'])?$gitphp_conf['compresslevel']:-1));
+		}
+		$tpl->assign("archive",$arc);
+	}
+	$tpl->display('snapshot.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_summary.php
+++ b/include/display.git_summary.php
@@ -19,115 +19,58 @@
 function git_summary($projectroot,$project)
 {
 	global $tpl;
-	$descr = git_project_descr($projectroot,$project);
-	$head = git_read_head($projectroot . $project);
-	$commit = git_read_commit($projectroot . $project, $head);
-	$commitdate = date_str($commit['committer_epoch'],$commit['committer_tz']);
-	$owner = git_project_owner($projectroot,$project);
-	$refs = read_info_ref($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("head",$head);
-	$tpl->display("project_nav.tpl");
-	$tpl->clear_all_assign();
-	$tpl->assign("description",$descr);
-	$tpl->assign("owner",$owner);
-	$tpl->assign("lastchange",$commitdate['rfc2822']);
-	$tpl->display("project_brief.tpl");
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->display("project_revlist_header.tpl");
-	$revlist = git_read_revlist($projectroot . $project, $head, 17);
-	$alternate = FALSE;
-	foreach ($revlist as $i => $rev) {
-		$tpl->clear_all_assign();
-		$revco = git_read_commit($projectroot . $project, $rev);
-		$authordate = date_str($revco['author_epoch']);
-		if ($alternate)
-			$tpl->assign("class","dark");
-		else
-			$tpl->assign("class","light");
-		$alternate = !$alternate;
-		$tpl->assign("project",$project);
-		if ($i <= 16) {
-			$tpl->assign("commit",$rev);
+
+	$cachekey = sha1($project);
+
+	if (!$tpl->is_cached('project.tpl', $cachekey)) {
+		$descr = git_project_descr($projectroot,$project);
+		$head = git_read_head($projectroot . $project);
+		$commit = git_read_commit($projectroot . $project, $head);
+		$commitdate = date_str($commit['committer_epoch'],$commit['committer_tz']);
+		$owner = git_project_owner($projectroot,$project);
+		$refs = read_info_ref($projectroot . $project);
+		$tpl->assign("head",$head);
+		$tpl->assign("description",$descr);
+		$tpl->assign("owner",$owner);
+		$tpl->assign("lastchange",$commitdate['rfc2822']);
+		$revlist = git_read_revlist($projectroot . $project, $head, 17);
+		foreach ($revlist as $i => $rev) {
+			$revdata = array();
+			$revco = git_read_commit($projectroot . $project, $rev);
+			$authordate = date_str($revco['author_epoch']);
+			$revdata["commit"] = $rev;
 			if (isset($refs[$rev]))
-				$tpl->assign("commitref",$refs[$rev]);
-			$tpl->assign("commitage",$revco['age_string']);
-			$tpl->assign("commitauthor",$revco['author_name']);
+				$revdata["commitref"] = $refs[$rev];
+			$revdata["commitage"] = $revco['age_string'];
+			$revdata["commitauthor"] = $revco['author_name'];
 			if (strlen($revco['title_short']) < strlen($revco['title'])) {
-				$tpl->assign("title",$revco['title']);
-				$tpl->assign("title_short",$revco['title_short']);
+				$revdata["title"] = $revco['title'];
+				$revdata["title_short"] = $revco['title_short'];
 			} else
-				$tpl->assign("title_short",$revco['title']);
-		} else {
-			$tpl->assign("truncate",TRUE);
+				$revdata["title_short"] = $revco['title'];
+			$revlist[$i] = $revdata;
 		}
-		$tpl->display("project_revlist_item.tpl");
-	}
-	$tpl->clear_all_assign();
-	$tpl->display("project_revlist_footer.tpl");
+		$tpl->assign("revlist",$revlist);
 
-	$taglist = git_read_refs($projectroot,$project,"refs/tags");
-	if (isset($taglist) && (count($taglist) > 0)) {
-		$tpl->clear_all_assign();
-		$tpl->assign("project",$project);
-		$tpl->display("project_taglist_header.tpl");
-		$alternate = FALSE;
-		foreach ($taglist as $i => $tag) {
-			$tpl->clear_all_assign();
-			$tpl->assign("project",$project);
-			if ($alternate)
-				$tpl->assign("class","dark");
-			else
-				$tpl->assign("class","light");
-			$alternate = !$alternate;
-			if ($i < 16) {
-				$tpl->assign("tagage",$tag['age']);
-				$tpl->assign("tagname",$tag['name']);
-				$tpl->assign("tagid",$tag['id']);
-				$tpl->assign("tagtype",$tag['type']);
-				$tpl->assign("refid",$tag['refid']);
-				$tpl->assign("reftype",$tag['reftype']);
+		$taglist = git_read_refs($projectroot,$project,"refs/tags");
+		if (isset($taglist) && (count($taglist) > 0)) {
+			foreach ($taglist as $i => $tag) {
 				if (isset($tag['comment'])) {
 					$com = trim($tag['comment'][0]);
 					if (strlen($com) > GITPHP_TRIM_LENGTH)
 						$com = substr($trimmed,0,GITPHP_TRIM_LENGTH) . "...";
-					$tpl->assign("comment",$com);
+					$taglist[$i]['comment'] = $com;
 				}
-			} else
-				$tpl->assign("truncate",TRUE);
-			$tpl->display("project_taglist_item.tpl");
-		}
-		$tpl->clear_all_assign();
-		$tpl->display("project_taglist_footer.tpl");
-	}
-
-	$headlist = git_read_refs($projectroot,$project,"refs/heads");
-	if (isset($headlist) && (count($headlist) > 0)) {
-		$tpl->clear_all_assign();
-		$tpl->assign("project",$project);
-		$tpl->display("project_headlist_header.tpl");
-		$alternate = FALSE;
-		foreach ($headlist as $i => $head) {
-			$tpl->clear_all_assign();
-			$tpl->assign("project",$project);
-			if ($alternate)
-				$tpl->assign("class","dark");
-			else
-				$tpl->assign("class","light");
-			$alternate = !$alternate;
-			if ($i < 16) {
-				$tpl->assign("headage",$head['age']);
-				$tpl->assign("headname",$head['name']);
-			} else
-				$tpl->assign("truncate",TRUE);
-			$tpl->display("project_headlist_item.tpl");
+			}
+			$tpl->assign("taglist",$taglist);
 		}
 
-		$tpl->clear_all_assign();
-		$tpl->display("project_headlist_footer.tpl");
+		$headlist = git_read_refs($projectroot,$project,"refs/heads");
+		if (isset($headlist) && (count($headlist) > 0)) {
+			$tpl->assign("headlist",$headlist);
+		}
 	}
+	$tpl->display('project.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_tag.php
+++ b/include/display.git_tag.php
@@ -15,30 +15,23 @@
 {
 	global $tpl;
 
-	$head = git_read_head($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("head",$head);
-	$tpl->display("tag_nav.tpl");
+	$cachekey = sha1($project) . "|" . $hash;
 
-	$tag = git_read_tag($projectroot . $project, $hash);
+	if (!$tpl->is_cached('tag.tpl', $cachekey)) {
 
-	$tpl->clear_all_assign();
-	$tpl->assign("project", $project);
-	$tpl->assign("hash", $hash);
-	$tpl->assign("title",$tag['name']);
-	$tpl->assign("type",$tag['type']);
-	$tpl->assign("object",$tag['object']);
-	if (isset($tag['author'])) {
-		$tpl->assign("author",$tag['author']);
-		$ad = date_str($tag['epoch'],$tag['tz']);
-		$tpl->assign("adrfc2822",$ad['rfc2822']);
-		$tpl->assign("adhourlocal",$ad['hour_local']);
-		$tpl->assign("adminutelocal",$ad['minute_local']);
-		$tpl->assign("adtzlocal",$ad['tz_local']);
+		$head = git_read_head($projectroot . $project);
+		$tpl->assign("head",$head);
+		$tpl->assign("hash", $hash);
+
+		$tag = git_read_tag($projectroot . $project, $hash);
+
+		$tpl->assign("tag",$tag);
+		if (isset($tag['author'])) {
+			$ad = date_str($tag['epoch'],$tag['tz']);
+			$tpl->assign("datedata",$ad);
+		}
 	}
-	$tpl->assign("comment",$tag['comment']);
-	$tpl->display("tag_data.tpl");
+	$tpl->display('tag.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_tags.php
+++ b/include/display.git_tags.php
@@ -13,36 +13,18 @@
 function git_tags($projectroot,$project)
 {
 	global $tpl;
-	$head = git_read_head($projectroot . $project);
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("head",$head);
-	$tpl->display("tags_nav.tpl");
-	$tpl->display("tags_header.tpl");
-	$taglist = git_read_refs($projectroot, $project, "refs/tags");
-	if (isset($taglist) && (count($taglist) > 0)) {
-		$alternate = FALSE;
-		foreach ($taglist as $i => $entry) {
-			$tpl->clear_all_assign();
-			if ($alternate)
-				$tpl->assign("class","dark");
-			else
-				$tpl->assign("class","light");
-			$alternate = !$alternate;
-			$tpl->assign("project",$project);
-			$tpl->assign("age",$entry['age']);
-			$tpl->assign("name",$entry['name']);
-			$tpl->assign("reftype",$entry['reftype']);
-			$tpl->assign("refid",$entry['refid']);
-			$tpl->assign("id",$entry['id']);
-			$tpl->assign("type",$entry['type']);
-			if (isset($entry['comment']) && isset($entry['comment'][0]))
-				$tpl->assign("comment",$entry['comment'][0]);
-			$tpl->display("tags_item.tpl");
+
+	$cachekey = sha1($project);
+
+	if (!$tpl->is_cached('tags.tpl', $cachekey)) {
+		$head = git_read_head($projectroot . $project);
+		$tpl->assign("head",$head);
+		$taglist = git_read_refs($projectroot, $project, "refs/tags");
+		if (isset($taglist) && (count($taglist) > 0)) {
+			$tpl->assign("taglist",$taglist);
 		}
 	}
-	$tpl->clear_all_assign();
-	$tpl->display("tags_footer.tpl");
+	$tpl->display('tags.tpl', $cachekey);
 }
 
 ?>

--- a/include/display.git_tree.php
+++ b/include/display.git_tree.php
@@ -18,61 +18,51 @@
 function git_tree($projectroot,$project,$hash,$file,$hashbase)
 {
 	global $tpl;
-	if (!isset($hash)) {
-		$hash = git_read_head($projectroot . $project);
+
+	$cachekey = sha1($project) . "|" . $hashbase . "|" . $hash . "|" . sha1($file);
+
+	if (!$tpl->is_cached('tree.tpl', $cachekey)) {
+		if (!isset($hash)) {
+			$hash = git_read_head($projectroot . $project);
+			if (isset($file))
+				$hash = git_get_hash_by_path($projectroot . $project, ($hashbase?$hashbase:$hash),$file,"tree");
+				if (!isset($hashbase))
+					$hashbase = $hash;
+		}
+		$lsout = git_ls_tree($projectroot . $project, $hash, TRUE);
+		$refs = read_info_ref($projectroot . $project);
+		$tpl->assign("hash",$hash);
+		if (isset($hashbase))
+			$tpl->assign("hashbase",$hashbase);
+		if (isset($hashbase) && ($co = git_read_commit($projectroot . $project, $hashbase))) {
+			$basekey = $hashbase;
+			$tpl->assign("fullnav",TRUE);
+			$tpl->assign("title",$co['title']);
+			if (isset($refs[$hashbase]))
+				$tpl->assign("hashbaseref",$refs[$hashbase]);
+		}
+		$paths = git_path_trees($projectroot . $project, $hashbase, $file);
+		$tpl->assign("paths",$paths);
+
 		if (isset($file))
-			$hash = git_get_hash_by_path($projectroot . $project, ($hashbase?$hashbase:$hash),$file,"tree");
-			if (!isset($hashbase))
-				$hashbase = $hash;
+			$tpl->assign("base",$file . "/");
+
+		$treelines = array();
+		$tok = strtok($lsout,"\0");
+		while ($tok !== false) {
+			if (ereg("^([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);
 	}
-	$lsout = git_ls_tree($projectroot . $project, $hash, TRUE);
-	$refs = read_info_ref($projectroot . $project);
-	$tpl->clear_all_assign();
-	if (isset($hashbase) && ($co = git_read_commit($projectroot . $project, $hashbase))) {
-		$basekey = $hashbase;
-		$tpl->assign("hashbase",$hashbase);
-		$tpl->assign("project",$project);
-		$tpl->assign("title",$co['title']);
-		if (isset($refs[$hashbase]))
-			$tpl->assign("hashbaseref",$refs[$hashbase]);
-		$tpl->display("tree_nav.tpl");
-	} else {
-		$tpl->assign("hash",$hash);
-		$tpl->display("tree_emptynav.tpl");
-	}
-	$tpl->clear_all_assign();
-	$tpl->assign("project",$project);
-	$tpl->assign("hashbase",$hashbase);
-	$paths = git_path_trees($projectroot . $project, $hashbase, $file);
-	$tpl->assign("paths",$paths);
-	$tpl->display("tree_filelist_header.tpl");
-
-	$tok = strtok($lsout,"\0");
-	$alternate = FALSE;
-	while ($tok !== false) {
-		if (ereg("^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$",$tok,$regs)) {
-			$tpl->clear_all_assign();
-			if ($alternate)
-				$tpl->assign("class","dark");
-			else
-				$tpl->assign("class","light");
-			$alternate = !$alternate;
-			$tpl->assign("filemode",mode_str($regs[1]));
-			$tpl->assign("type",$regs[2]);
-			$tpl->assign("hash",$regs[3]);
-			$tpl->assign("name",$regs[4]);
-			$tpl->assign("project",$project);
-			if (isset($file))
-				$tpl->assign("base",$file . "/");
-			if (isset($basekey))
-				$tpl->assign("hashbase",$basekey);
-			$tpl->display("tree_filelist_item.tpl");
-		}
-		$tok = strtok("\0");
-	}
-
-	$tpl->clear_all_assign();
-	$tpl->display("tree_filelist_footer.tpl");
+	$tpl->display('tree.tpl', $cachekey);
 }
 
 ?>

--- /dev/null
+++ b/include/gitutil.git_project_info.php
@@ -1,1 +1,29 @@
+<?php
+/*
+ *  gitutil.git_project_info.php
+ *  gitphp: A PHP git repository browser
+ *  Component: Git utility - single project info
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ */
 
+ require_once('gitutil.git_project_descr.php');
+ require_once('gitutil.git_project_owner.php');
+ require_once('gitutil.git_read_head.php');
+ require_once('gitutil.git_read_commit.php');
+
+function git_project_info($projectroot,$project)
+{
+	$projinfo = array();
+	$projinfo["project"] = $project;
+	$projinfo["descr"] = git_project_descr($projectroot,$project,TRUE);
+	$projinfo["owner"] = git_project_owner($projectroot,$project);
+	$head = git_read_head($projectroot . $project);
+	$commit = git_read_commit($projectroot . $project,$head);
+	$projinfo["age"] = $commit['age'];
+	$projinfo["age_string"] = $commit['age_string'];
+	return $projinfo;
+}
+
+?>
+

--- a/include/gitutil.git_read_projects.php
+++ b/include/gitutil.git_read_projects.php
@@ -8,8 +8,9 @@
  */
 
  require_once('gitutil.git_recurse_projects.php');
+ require_once('gitutil.git_project_info.php');
 
-function git_read_projects($projectroot,$projectlist)
+function git_read_projects($projectroot,$projectlist,$projdata = FALSE)
 {
 	$projects = array();
 	if (isset($projectroot)) {
@@ -19,8 +20,12 @@
 					if (is_array($plist)) {
 						$projs = array();
 						foreach ($plist as $pname => $ppath) {
-							if (is_dir($projectroot . $ppath) && is_file($projectroot . $ppath . "/HEAD"))
-								$projs[] = $ppath;
+							if (is_dir($projectroot . $ppath) && is_file($projectroot . $ppath . "/HEAD")) {
+								if ($projdata)
+									$projs[] = git_project_info($projectroot, $ppath);
+								else
+									$projs[] = $ppath;
+							}
 						}
 						if (count($projs) > 0) {
 							sort($projs);
@@ -33,7 +38,11 @@
 				$len = count($projects);
 				$cut = strlen($projectroot);
 				for ($i = 0; $i < $len; $i++) {
-					$projects[$i] = substr($projects[$i],$cut + 1);
+					$p = substr($projects[$i],$cut + 1);
+					if ($projdata)
+						$projects[$i] = git_project_info($projectroot, $p);
+					else
+						$projects[$i] = $p;
 				}
 			}
 		} else

--- a/include/util.agecmp.php
+++ b/include/util.agecmp.php
@@ -7,17 +7,11 @@
  *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
  */
 
- require_once('gitutil.git_read_commit.php');
- require_once('gitutil.git_read_head.php');
-
 function agecmp($a,$b)
 {
-	global $gitphp_conf;
-	$ca = git_read_commit($gitphp_conf['projectroot'] . $a, git_read_head($gitphp_conf['projectroot'] . $a));
-	$cb = git_read_commit($gitphp_conf['projectroot'] . $b, git_read_head($gitphp_conf['projectroot'] . $b));
-	if ($ca['age'] == $cb['age'])
+	if ($a["age"] == $b["age"])
 		return 0;
-	return ($ca['age'] < $cb['age'] ? -1 : 1);
+	return ($a["age"] < $b["age"] ? -1 : 1);
 }
 
 ?>

--- a/include/util.descrcmp.php
+++ b/include/util.descrcmp.php
@@ -7,12 +7,9 @@
  *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
  */
 
- require_once('gitutil.git_project_descr.php');
-
 function descrcmp($a,$b)
 {
-	global $gitphp_conf;
-	return strcmp(git_project_descr($gitphp_conf['projectroot'],$a),git_project_descr($gitphp_conf['projectroot'],$b));
+	return strcmp($a["descr"],$b["descr"]);
 }
 
 ?>

--- a/include/util.ownercmp.php
+++ b/include/util.ownercmp.php
@@ -7,12 +7,9 @@
  *  Copyright (C) 2008 Christopher Han <xiphux@gmail.com>
  */
 
- require_once('gitutil.git_project_owner.php');
-
 function ownercmp($a,$b)
 {
-	global $gitphp_conf;
-	return strcmp(git_project_owner($gitphp_conf['projectroot'],$a),git_project_owner($gitphp_conf['projectroot'],$b));
+	return strcmp($a["owner"],$b["owner"]);
 }
 
 ?>

--- a/include/util.projectcmp.php
+++ b/include/util.projectcmp.php
@@ -9,7 +9,7 @@
 
 function projectcmp($a,$b)
 {
-	return strcmp($a,$b);
+	return strcmp($a["project"],$b["project"]);
 }
 
 ?>

file:a/index.php -> file:b/index.php
--- a/index.php
+++ b/index.php
@@ -6,7 +6,6 @@
  *
  *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
  */
- ob_start();
 
  /*
   * Version
@@ -22,40 +21,91 @@
   * Configuration
   */
  require_once('config/gitphp.conf.php');
+
+ $extraoutput = FALSE;
 
  /*
   * Instantiate Smarty
   */
  require_once($gitphp_conf['smarty_prefix'] . "Smarty.class.php");
  $tpl =& new Smarty;
- if (isset($_GET['a']) &&
-     ($_GET['a'] != "commitdiff_plain") &&
-     ($_GET['a'] != "blob_plain") &&
-     ($_GET['a'] != "blobdiff_plain") &&
-     ($_GET['a'] != "opml")) {
+ if ((!isset($_GET['a'])) || (
+     	($_GET['a'] != "commitdiff_plain") &&
+     	($_GET['a'] != "blob_plain") &&
+     	($_GET['a'] != "blobdiff_plain") &&
+     	($_GET['a'] != "rss") &&
+     	($_GET['a'] != "opml")
+     	($_GET['a'] != "snapshot"))) {
 	$tpl->load_filter('output','trimwhitespace');
+	$extraoutput = TRUE;
 }
 
-
- $rss_link = FALSE;
- $suppress_headers = FALSE;
-
- ob_start();
+ /*
+  * Debug
+  */
+ if ($gitphp_conf['debug']) {
+ 	if ($extraoutput) {
+		define('GITPHP_START_TIME', microtime(true));
+		error_reporting(E_ALL|E_STRICT);
+	}
+ }
+
+/*
+ * Caching
+ */
+ if ($gitphp_conf['cache']) {
+ 	$tpl->caching = 2;
+	$tpl->cache_lifetime = $gitphp_conf['cachelifetime'];
+ }
+
+/*
+ * Setup global assigns used everywhere (such as header/footer)
+ */
+ $tpl->assign("stylesheet",$gitphp_conf['stylesheet']);
+ $tpl->assign("version",$gitphp_version);
+ $tpl->assign("pagetitle",$gitphp_conf['title']);
+ if (isset($_GET['p'])) {
+	$tpl->assign("validproject",TRUE);
+	$tpl->assign("project",$_GET['p']);
+	require_once('include/gitutil.git_project_descr.php');
+	$tpl->assign("projectdescription",git_project_descr($gitphp_conf['projectroot'],$_GET['p']));
+	if (isset($_GET['a'])) {
+		$tpl->assign("action",$_GET['a']);
+		$tpl->assign("validaction", TRUE);
+	}
+ }
+ 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_conf['search'])
+	$tpl->assign("enablesearch",TRUE);
+if ($gitphp_conf['filesearch'])
+	$tpl->assign("filesearch",TRUE);
+
+
  if (isset($_GET['a']) && $_GET['a'] == "opml") {
-	$suppress_headers = TRUE;
 	require_once('include/display.git_opml.php');
 	git_opml($gitphp_conf['projectroot'],$git_projects);
  } else if (isset($_GET['a']) && $_GET['a'] == "project_index") {
-	$suppress_headers = TRUE;
 	require_once('include/display.git_project_index.php');
 	git_project_index($gitphp_conf['projectroot'],$git_projects);
  } else if (isset($_GET['p'])) {
- 	if (!is_dir($gitphp_conf['projectroot'] . $_GET['p']))
-		echo "No such directory";
-	else if (!is_file($gitphp_conf['projectroot'] . $_GET['p'] . "/HEAD"))
-		echo "No such project";
-	else {
-		$rss_link = TRUE;
+ 	if (!is_dir($gitphp_conf['projectroot'] . $_GET['p'])) {
+		$tpl->assign("validproject",FALSE);
+		require_once('include/display.git_message.php');
+		git_message("No such directory",TRUE);
+	} else if (!is_file($gitphp_conf['projectroot'] . $_GET['p'] . "/HEAD")) {
+		$tpl->assign("validproject",FALSE);
+		require_once('include/display.git_message.php');
+		git_message("No such project",TRUE);
+	} else {
 		if (!isset($_GET['a'])) {
 			require_once('include/display.git_summary.php');
 			git_summary($gitphp_conf['projectroot'],$_GET['p']);
@@ -86,7 +136,6 @@
 					git_commitdiff($gitphp_conf['projectroot'],$_GET['p'],$_GET['h'], (isset($_GET['hp']) ? $_GET['hp'] : NULL));
 					break;
 				case "commitdiff_plain":
-					$suppress_headers = TRUE;
 					require_once('include/display.git_commitdiff_plain.php');
 					git_commitdiff_plain($gitphp_conf['projectroot'],$_GET['p'],$_GET['h'],(isset($_GET['hp']) ? $_GET['hp'] : NULL));
 					break;
@@ -99,7 +148,6 @@
 					git_tags($gitphp_conf['projectroot'],$_GET['p']);
 					break;
 				case "rss":
-					$suppress_headers = TRUE;
 					require_once('include/display.git_rss.php');
 					git_rss($gitphp_conf['projectroot'],$_GET['p']);
 					break;
@@ -108,7 +156,6 @@
 					git_blob($gitphp_conf['projectroot'],$_GET['p'], (isset($_GET['h']) ? $_GET['h'] : NULL), (isset($_GET['f']) ? $_GET['f'] : NULL), (isset($_GET['hb']) ? $_GET['hb'] : NULL));
 					break;
 				case "blob_plain":
-					$suppress_headers = TRUE;
 					require_once('include/display.git_blob_plain.php');
 					git_blob_plain($gitphp_conf['projectroot'],$_GET['p'],$_GET['h'],(isset($_GET['f']) ? $_GET['f'] : NULL));
 					break;
@@ -117,12 +164,10 @@
 					git_blobdiff($gitphp_conf['projectroot'],$_GET['p'],$_GET['h'],$_GET['hb'],$_GET['hp'],(isset($_GET['f']) ? $_GET['f'] : NULL));
 					break;
 				case "blobdiff_plain":
-					$suppress_headers = TRUE;
 					require_once('include/display.git_blobdiff_plain.php');
 					git_blobdiff_plain($gitphp_conf['projectroot'],$_GET['p'],$_GET['h'],$_GET['hb'],$_GET['hp'], (isset($_GET['f']) ? $_GET['f'] : NULL));
 					break;
 				case "snapshot":
-					$suppress_headers = TRUE;
 					require_once('include/display.git_snapshot.php');
 					git_snapshot($gitphp_conf['projectroot'],$_GET['p'], (isset($_GET['h']) ? $_GET['h'] : NULL));
 					break;
@@ -144,63 +189,20 @@
 					git_tag($gitphp_conf['projectroot'],$_GET['p'],$_GET['h']);
 					break;
 				default:
-					echo "Unknown action";
+					$tpl->assign("validaction", FALSE);
+					require_once('include/display.git_message.php');
+					git_message("Unknown action", TRUE);
 					break;
 			}
 		}
 	}
  } else {
- 	$tpl->display("hometext.tpl");
 	require_once('include/display.git_project_list.php');
  	git_project_list($gitphp_conf['projectroot'],$git_projects,(isset($_GET['o']) ? $_GET['o'] : "project"));
  }
- $main = ob_get_contents();
- ob_end_clean();
-
- if (!$suppress_headers) {
-	 $tpl->clear_all_assign();
-	 $tpl->assign("stylesheet",$gitphp_conf['stylesheet']);
-	 $tpl->assign("version",$gitphp_version);
-	 $title = $gitphp_conf['title'];
-	 if ($rss_link) {
-		$tpl->assign("rss_link",TRUE);
-		$tpl->assign("project",$_GET['p']);
-		$title .= " :: " . $_GET['p'];
-		if (isset($_GET['a'])) {
-			$tpl->assign("action",$_GET['a']);
-			$title .= "/" . $_GET['a'];
-		}
-	 }
-	 $tpl->assign("title",$title);
-	 if (isset($_GET['st']))
-	 	$tpl->assign("searchtype",$_GET['st']);
-	else
-		$tpl->assign("searchtype","commit");
-	if (isset($_GET['s']))
-		$tpl->assign("search",$_GET['s']);
-	if (isset($_GET['hb']))
-		$tpl->assign("hash",$_GET['hb']);
-	else if (isset($_GET['h']))
-		$tpl->assign("hash",$_GET['h']);
-	if ($gitphp_conf['search'])
-		$tpl->assign("enablesearch",TRUE);
-	if ($gitphp_conf['filesearch'])
-		$tpl->assign("filesearch",TRUE);
-	 $tpl->display("header.tpl");
- }
-
- echo $main;
-
- if (!$suppress_headers) {
-	 if ($rss_link) {
-		$tpl->assign("project",$_GET['p']);
-		require_once('include/gitutil.git_project_descr.php');
-		$tpl->assign("descr",git_project_descr($gitphp_conf['projectroot'],$_GET['p']));
-	 }
-	 $tpl->display("footer.tpl");
- }
-
- ob_end_flush();
+
+ if ($gitphp_conf['debug'] && $extraoutput)
+ 	echo "Execution time: " . round(microtime(true) - GITPHP_START_TIME, 8) . " sec";
 
 ?>
 

--- a/templates/blob.tpl
+++ b/templates/blob.tpl
@@ -5,12 +5,21 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
  {* If we managed to look up commit info, we have enough info to display the full header - othewise just use a simple header *}
  <div class="page_nav">
    {if $fullnav}
      <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hashbase}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hashbase}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$tree}&hb={$hashbase}">tree</a><br />
      {if $file}
-       <a href="{$SCRIPT_NAME}?p={$project}&a=blob_plain&h={$hash}&f={$file}">plain</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=blob&hb=HEAD&f={$file}">head</a><br />
+       <a href="{$SCRIPT_NAME}?p={$project}&a=blob_plain&h={$hash}&f={$file}">plain</a> | 
+       {if ($hashbase != "HEAD") && ($hashbase != $head)}
+         <a href="{$SCRIPT_NAME}?p={$project}&a=blob&hb=HEAD&f={$file}">HEAD</a>
+       {else}
+         HEAD
+       {/if}
+       <br />
      {else}
        <a href="{$SCRIPT_NAME}?p={$project}&a=blob_plain&h={$hash}">plain</a><br />
      {/if}
@@ -53,11 +62,15 @@
      {$geshiout}
    {else}
      {* Just plain display *}
-     <div class="pre">
+     <table class="code">
      {foreach from=$lines item=line name=lines}
-       {if $smarty.foreach.lines.iteration < 10} {/if}{if $smarty.foreach.lines.iteration < 100} {/if}{if $smarty.foreach.lines.iteration < 1000} {/if}<a id="l{$smarty.foreach.lines.iteration}" href="#l{$smarty.foreach.lines.iteration}" class="linenr">{$smarty.foreach.lines.iteration}</a> {$line|escape:'htmlall'}
+       <tr>
+         <td class="num"><a id="l{$smarty.foreach.lines.iteration}" href="#l{$smarty.foreach.lines.iteration}" class="linenr">{$smarty.foreach.lines.iteration}</a></td>
+	 <td class="codeline">{$line|escape:'htmlall'}</td>
      {/foreach}
-     </div>
+     </table>
    {/if}
  </div>
 
+ {include file='footer.tpl'}
+

--- a/templates/blobdiff.tpl
+++ b/templates/blobdiff.tpl
@@ -5,6 +5,9 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
  {* If we managed to look up commit info, we have enough info to display the full header - othewise just use a simple header *}
  <div class="page_nav">
    {if $fullnav}
@@ -48,3 +51,6 @@
    {include file='filediff.tpl'}
  </div>
 
+ {include file='footer.tpl'}
+
+

--- /dev/null
+++ b/templates/blobdiffplain.tpl
@@ -1,1 +1,9 @@
+{*
+ *  blobdiffplain.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Blobdiff plain template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
+{$blobdiff}
 

--- a/templates/commit.tpl
+++ b/templates/commit.tpl
@@ -5,6 +5,9 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
  <div class="page_nav">
    {* Nav *}
    <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | commit | {if $parent}<a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | {/if}<a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$tree}&hb={$hash}">tree</a>
@@ -103,3 +106,6 @@
    {/section}
  </table>
 
+ {include file='footer.tpl'}
+
+

--- a/templates/commitdiff.tpl
+++ b/templates/commitdiff.tpl
@@ -5,6 +5,10 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
+ {* Nav *}
  <div class="page_nav">
    <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | commitdiff | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$tree}&hb={$hash}">tree</a><br /><a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff_plain&h={$hash}&hp={$hashparent}">plain</a>
  </div>
@@ -19,6 +23,7 @@
      {$line}<br />
    {/foreach}
    <br />
+   {* Diff each file changed *}
    {section name=difftree loop=$difftreelines}
      {if $difftreelines[difftree].status == "A"}
        <div class="diff_info">
@@ -39,3 +44,6 @@
    {/section}
  </div>
 
+ {include file='footer.tpl'}
+
+

file:a/templates/diff_line.tpl (deleted)
--- a/templates/diff_line.tpl
+++ /dev/null
@@ -1,9 +1,1 @@
-{*
- *  diff_line.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Diff line template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="pre{if $class} {$class}{/if}">{$line}</div>
 

--- a/templates/footer.tpl
+++ b/templates/footer.tpl
@@ -5,17 +5,19 @@
  *
  *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
  *}
-<div class="page_footer">
-{if $project}
-  {if $descr}
-    <div class="page_footer_text">{$descr}</div>
-  {/if}
-  <a href="{$SCRIPT_NAME}?p={$project}&a=rss" class="rss_logo">RSS</a>
-{else}
-  <a href="{$SCRIPT_NAME}?a=opml" class="rss_logo">OPML</a>
-  <a href="{$SCRIPT_NAME}?a=project_index" class="rss_logo">TXT</a>
-{/if}
-</div>
-</body>
+    <div class="page_footer">
+      {if $project}
+        {if $projectdescription}
+          <div class="page_footer_text">{$projectdescription}</div>
+        {/if}
+	{if $validproject}
+          <a href="{$SCRIPT_NAME}?p={$project}&a=rss" class="rss_logo">RSS</a>
+	{/if}
+      {else}
+        <a href="{$SCRIPT_NAME}?a=opml" class="rss_logo">OPML</a>
+        <a href="{$SCRIPT_NAME}?a=project_index" class="rss_logo">TXT</a>
+      {/if}
+    </div>
+  </body>
 </html>
 

--- a/templates/header.tpl
+++ b/templates/header.tpl
@@ -5,48 +5,47 @@
  *
  *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
  *}
-{* $contentType = strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml') === false ? 'text/html' : 'application/xhtml+xml';
-header("Content-Type: $contentType; charset=utf-8"); *}
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<!-- gitphp web interface {$version}, (C) 2006 Christopher Han <xiphux@gmail.com> -->
-<head>
-<title>{$title}</title>
-{if $rss_link}
-<link rel="alternate" title="{$project} log" href="{$SCRIPT_NAME}?p={$project}&a=rss" type="application/rss+xml" />
-{/if}
-<link rel="stylesheet" href="{$stylesheet}" type="text/css" />
-{$smarty.capture.header}
-</head>
-<body>
-<div class="page_header">
-<a href="http://www.kernel.org/pub/software/scm/git/docs/" title="git documentation">
-<img src="git-logo.png" width="72" height="27" alt="git" class="logo" />
-</a>
-<a href="index.php">projects</a> / 
-{if $project}
-  <a href="{$SCRIPT_NAME}?p={$project}&a=summary">{$project}</a>
-  {if $action}
-    / {$action}
-  {/if}
-  {if $enablesearch}
-<form method="get" action="index.php" enctype="application/x-www-form-urlencoded">
-<div class="search">
-<input type="hidden" name="p" value="{$project}" />
-<input type="hidden" name="a" value="search" />
-<input type="hidden" name="h" value="{if $hash}{$hash}{else}HEAD{/if}" />
-<select name="st">
-<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 $searchtype == 'file'}selected="selected"{/if} value="file">file</option>
-{/if}
-</select> search: <input type="text" name="s" {if $search}value="{$search}"{/if} />
-</div>
-</form>
-  {/if}
-{/if}
-</div>
+  <!-- gitphp web interface {$version}, (C) 2006 Christopher Han <xiphux@gmail.com> -->
+  <head>
+    <title>{$pagetitle}{if $project && $validproject} :: {$project}{if $action && $validaction}/{$action}{/if}{/if}</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    {if $validproject}
+      <link rel="alternate" title="{$project} log" href="{$SCRIPT_NAME}?p={$project}&a=rss" type="application/rss+xml" />
+    {/if}
+    <link rel="stylesheet" href="{$stylesheet}" type="text/css" />
+    {$smarty.capture.header}
+  </head>
+  <body>
+    <div class="page_header">
+      <a href="http://www.kernel.org/pub/software/scm/git/docs/" title="git documentation">
+        <img src="git-logo.png" width="72" height="27" alt="git" class="logo" />
+      </a>
+      <a href="index.php">projects</a> / 
+      {if $project && $validproject}
+        <a href="{$SCRIPT_NAME}?p={$project}&a=summary">{$project}</a>
+        {if $action && $validaction}
+           / {$action}
+        {/if}
+        {if $enablesearch}
+          <form method="get" action="index.php" enctype="application/x-www-form-urlencoded">
+            <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}" />
+              <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>
+                {if $filesearch}
+                  <option {if $currentsearchtype == 'file'}selected="selected"{/if} value="file">file</option>
+                {/if}
+              </select> search: <input type="text" name="s" {if $currentsearch}value="{$currentsearch}"{/if} />
+            </div>
+          </form>
+        {/if}
+      {/if}
+    </div>
 

--- a/templates/heads.tpl
+++ b/templates/heads.tpl
@@ -5,6 +5,9 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
  {* Nav *}
  <div class="page_nav">
    <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$head}">tree</a>
@@ -24,3 +27,6 @@
    {/foreach}
  </table>
 
+ {include file='footer.tpl'}
+
+

--- a/templates/history.tpl
+++ b/templates/history.tpl
@@ -5,6 +5,9 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
  {* Page header *}
  <div class="page_nav">
    <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$tree}&hb={$hash}">tree</a>
@@ -31,6 +34,7 @@
    </b>
  </div>
  <table cellspacing="0">
+   {* Display each history line *}
    {section name=history loop=$historylines}
      <tr class="{cycle values="light,dark"}">
        <td title="{$historylines[history].agestringage}"><i>{$historylines[history].agestringdate}</i></td>
@@ -42,3 +46,6 @@
    {/section}
  </table>
 
+ {include file='footer.tpl'}
+
+

--- a/templates/log.tpl
+++ b/templates/log.tpl
@@ -5,6 +5,10 @@
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+ {include file='header.tpl'}
+
+ {* Nav *}
  <div class="page_nav">
    <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}">shortlog</a> | log | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}&hb={$hash}">tree</a>
    <br />
@@ -36,6 +40,7 @@
      <br /><br />
    </div>
  {/if}
+ {* Display each commit *}
  {section name=log loop=$commitlines}
    <div>
      <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commitlines[log].commit}" class="title"><span class="age">{$commitlines[log].agestring}</span>{$commitlines[log].title}
@@ -61,3 +66,6 @@
    </div>
  {/section}
 
+ {include file='footer.tpl'}
+
+

--- a/templates/message.tpl
+++ b/templates/message.tpl
@@ -1,9 +1,18 @@
 {*
- *  error.tpl
+ *  message.tpl
  *  gitphp: A PHP git repository browser
- *  Component: Error message template
+ *  Component: Warning/error message template
  *
  *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
  *}
+
+{if $standalone}
+  {include file='header.tpl'}
+{/if}
+
 <div class="message {if $error}error{/if}">{$message}</div>
 
+{if $standalone}
+  {include file='footer.tpl'}
+{/if}
+

--- /dev/null
+++ b/templates/project.tpl
@@ -1,1 +1,100 @@
+{*
+ *  project.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Project summary template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+ {include file='header.tpl'}
+
+ {* Nav *}
+ <div class="page_nav">
+   summary | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree">tree</a>
+   <br /><br />
+ </div>
+ <div class="title">&nbsp;</div>
+ {* Project brief *}
+ <table cellspacing="0">
+   <tr><td>description</td><td>{$description}</td></tr>
+   <tr><td>owner</td><td>{$owner}</td></tr>
+   <tr><td>last change</td><td>{$lastchange}</td></tr>
+ </table>
+ <div>
+   <a class="title" href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a>
+ </div>
+ <table cellspacing="0">
+   {* Recent revisions *}
+   {section name=rev max=17 loop=$revlist}
+     {if $smarty.section.rev.index == 16}
+       <tr class="light">
+         <td><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">...</a></td>
+       </tr>
+     {else}
+         <tr class="{cycle name=revs values="light,dark"}">
+         <td><i>{$revlist[rev].commitage}</i></td>
+         <td><i>{$revlist[rev].commitauthor}</i></td>
+         <td>
+           <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$revlist[rev].commit}" class="list" {if $revlist[rev].title}title="{$revlist[rev].title}"{/if}><b>{$revlist[rev].title_short}
+             {if $revlist[rev].commitref}
+               <span class="tag">{$revlist[rev].commitref}</span>
+             {/if}
+           </b>
+         </td>
+         <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$revlist[rev].commit}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$revlist[rev].commit}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$revlist[rev].commit}&hb={$revlist[rev].commit}">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$revlist[rev].commit}">snapshot</a></td>
+       </tr>
+     {/if}
+   {/section}
+ </table>
+ {if $taglist}
+   {* Tags *}
+   <div>
+     <a href="{$SCRIPT_NAME}?p={$project}&a=tags" class="title">tags</a>
+   </div>
+   <table cellspacing="0">
+     {section name=tag max=17 loop=$taglist}
+       <tr class="{cycle name=tags values="light,dark"}">
+         {if $smarty.section.tag.index == 16}
+           <td><a href="{$SCRIPT_NAME}?p={$project}&a=tags">...</a></td>
+         {else}
+           <td><i>{$taglist[tag].age}</i></td>
+           <td><a href="{$SCRIPT_NAME}?p={$project}&a={$taglist[tag].reftype}&h={$taglist[tag].refid}" class="list"><b>{$taglist[tag].name}</b></a></td>
+           <td>
+             {if $taglist[tag].comment}
+               <a class="list" href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$taglist[tag].id}">{$taglist[tag].comment}</a>
+             {/if}
+           </td>
+           <td class="link">
+             {if $taglist[tag].type == "tag"}
+   	       <a href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$taglist[tag].id}">tag</a> | 
+             {/if}
+             <a href="{$SCRIPT_NAME}?p={$project}&a={$taglist[tag].reftype}&h={$taglist[tag].refid}">{$taglist[tag].reftype}</a>{if $taglist[tag].reftype == "commit"} | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/tags/{$taglist[tag].name}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h=refs/tags/{$taglist[tag].name}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$taglist[tag].refid}">snapshot</a>{/if}
+           </td>
+         {/if}
+       </tr>
+     {/section}
+   </table>
+ {/if}
+ {if $headlist}
+   {* Heads *}
+   <div>
+     <a href="{$SCRIPT_NAME}?p={$project}&a=heads" class="title">heads</a>
+   </div>
+   <table cellspacing="0">
+     {section name=head max=17 loop=$headlist}
+       <tr class="{cycle name=heads values="light,dark"}">
+         {if $smarty.section.head.index == 16}
+           <td><a href="{$SCRIPT_NAME}?p={$project}&a=heads">...</a></td>
+         {else}
+           <td><i>{$headlist[head].age}</i></td>
+           <td><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/heads/{$headlist[head].name}" class="list"><b>{$headlist[head].name}</b></td>
+           <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/heads/{$headlist[head].name}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h=refs/heads/{$headlist[head].name}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h=refs/heads/{$headlist[head].name}&hb={$headlist[head].name}">tree</a></td>
+         {/if}
+       </tr>
+     {/section}
+   </table>
+ {/if}
+
+ {include file='footer.tpl'}
+
+

--- a/templates/project_brief.tpl
+++ /dev/null
@@ -1,14 +1,1 @@
-{*
- *  project_brief.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project brief info template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="title">&nbsp;</div>
- <table cellspacing="0">
- <tr><td>description</td><td>{$description}</td></tr>
- <tr><td>owner</td><td>{$owner}</td></tr>
- <tr><td>last change</td><td>{$lastchange}</td></tr>
- </table>
 

--- a/templates/project_headlist_footer.tpl
+++ /dev/null
@@ -1,9 +1,1 @@
-{*
- *  project_headlist_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project head list footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- </table>
 

--- a/templates/project_headlist_header.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  project_headlist_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project head list header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div>
- <a href="{$SCRIPT_NAME}?p={$project}&a=heads" class="title">heads</a>
- </div>
- <table cellspacing="0">
 

--- a/templates/project_headlist_item.tpl
+++ /dev/null
@@ -1,17 +1,1 @@
-{*
- *  project_headlist_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project head list item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <tr class="{$class}">
- {if $truncate}
-   <td><a href="{$SCRIPT_NAME}?p={$project}&a=heads">...</a></td>
- {else}
-   <td><i>{$headage}</i></td>
-   <td><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/heads/{$headname}" class="list"><b>{$headname}</b></td>
-   <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/heads/{$headname}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h=refs/heads/{$headname}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h=refs/heads/{$headname}&hb={$headname}">tree</a></td>
- {/if}
- </tr>
 

--- a/templates/project_nav.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  project_nav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project navbar template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_nav">
- summary | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree">tree</a>
- <br /><br />
- </div>
 

--- a/templates/project_revlist_footer.tpl
+++ /dev/null
@@ -1,9 +1,1 @@
-{*
- *  project_revlist_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project revision list footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- </table>
 

--- a/templates/project_revlist_header.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  project_revlist_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project revision list header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div>
- <a class="title" href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a>
- </div>
- <table cellspacing="0">
 

--- a/templates/project_revlist_item.tpl
+++ /dev/null
@@ -1,25 +1,1 @@
-{*
- *  project_revlist_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project revision list item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- {if $truncate}
- <tr class="light">
-  <td><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">...</td>
- {else}
- <tr class="{$class}">
-  <td><i>{$commitage}</i></td>
-  <td><i>{$commitauthor}</i></td>
-  <td>
-  <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commit}" class="list" {if $title}title="{$title}"{/if}><b>{$title_short}
-  {if $commitref}
-  <span class="tag">{$commitref}</span>
-  {/if}
-  </b>
-  </td>
-  <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commit}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$commit}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$commit}&hb={$commit}">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$commit}">snapshot</a></td>
-  </tr>
- {/if}
 

--- a/templates/project_taglist_footer.tpl
+++ /dev/null
@@ -1,9 +1,1 @@
-{*
- *  project_taglist_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project tag list footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- </table>
 

--- a/templates/project_taglist_header.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  project_taglist_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project tag list header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div>
- <a href="{$SCRIPT_NAME}?p={$project}&a=tags" class="title">tags</a>
- </div>
- <table cellspacing="0">
 

--- a/templates/project_taglist_item.tpl
+++ /dev/null
@@ -1,27 +1,1 @@
-{*
- *  project_taglist_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project tag list item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <tr class="{$class}">
- {if $truncate}
-   <td><a href="{$SCRIPT_NAME}?p={$project}&a=tags">...</a></td>
- {else}
-   <td><i>{$tagage}</i></td>
-   <td><a href="{$SCRIPT_NAME}?p={$project}&a={$reftype}&h={$refid}" class="list"><b>{$tagname}</b></a></td>
-   <td>
-   {if $comment}
-     <a class="list" href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$tagid}">{$comment}</a>
-   {/if}
-   </td>
-   <td class="link">
-   {if $tagtype == "tag"}
-   	<a href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$tagid}">tag</a> | 
-   {/if}
-   <a href="{$SCRIPT_NAME}?p={$project}&a={$reftype}&h={$refid}">{$reftype}</a>{if $reftype == "commit"} | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/tags/{$tagname}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h=refs/tags/{$tagname}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$refid}">snapshot</a>{/if}
-   </td>
- {/if}
- </tr>
 

--- /dev/null
+++ b/templates/projectindex.tpl
@@ -1,1 +1,19 @@
+{*
+ *  projectindex.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Project index template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
+{if $categorized}
+{foreach from=$projlist item=plist}
+{foreach from=$plist item=proj}
+{$proj}
+{/foreach}
+{/foreach}
+{else}
+{foreach from=$projlist item=proj}
+{$proj}
+{/foreach}
+{/if}
 

--- /dev/null
+++ b/templates/projectlist.tpl
@@ -1,1 +1,107 @@
+{*
+ *  projectlist.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Project list template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+{include file='header.tpl'}
+
+{include file='hometext.tpl'}
+
+{if $message}
+  {* Something is wrong; display an error message instead of trying to list *}
+  {include file='message.tpl'}
+{else}
+  <table cellspacing="0">
+    {* Header *}
+    <tr>
+      {if $order == "project"}
+        <th>Project</th>
+      {else}
+        <th><a class="header" href="{$SCRIPT_NAME}?o=project">Project</a></th>
+      {/if}
+      {if $order == "descr"}
+        <th>Description</th>
+      {else}
+        <th><a class="header" href="{$SCRIPT_NAME}?o=descr">Description</a></th>
+      {/if}
+      {if $order == "owner"}
+        <th>Owner</th>
+      {else}
+        <th><a class="header" href="{$SCRIPT_NAME}?o=owner">Owner</a></th>
+      {/if}
+      {if $order == "age"}
+        <th>Last Change</th>
+      {else}
+        <th><a class="header" href="{$SCRIPT_NAME}?o=age">Last Change</a></th>
+      {/if}
+      <th>Actions</th>
+    </tr>
+
+    {if $categorizedprojects}
+      {* Show categorized; categorized project lists nested associatively in the project
+         list by category key *}
+      {foreach from=$categorizedprojects key=categ item=plist}
+        {if $categ != "none"}
+          <tr>
+            <th>{$categ}</th>
+            <th></th>
+            <th></th>
+            <th></th>
+            <th></th>
+          </tr>
+	{/if}
+        {section name=proj loop=$plist}
+          <tr class="{cycle values="light,dark"}">
+            <td>
+              <a href="{$SCRIPT_NAME}?p={$plist[proj].project}&a=summary" class="list {if $categ != "none"}indent{/if}">{$plist[proj].project}</a>
+            </td>
+            <td>{$plist[proj].descr}</td>
+            <td><i>{$plist[proj].owner}</i></td>
+            <td>
+              {if $plist[proj].age < 7200}   {* 60*60*2, or 2 hours *}
+                <span class="agehighlight"><b><i>{$plist[proj].age_string}</i></b></span>
+	      {elseif $plist[proj].age < 172800}   {* 60*60*24*2, or 2 days *}
+                <span class="agehighlight"><i>{$plist[proj].age_string}</i></span>
+              {else}
+                <i>{$plist[proj].age_string}</i>
+              {/if}
+            </td>
+            <td class="link"><a href="{$SCRIPT_NAME}?p={$plist[proj].project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$plist[proj].project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$plist[proj].project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$plist[proj].project}&a=tree">tree</a> | <a href="{$SCRIPT_NAME}?p={$plist[proj].project}&a=snapshot&h=HEAD">snapshot</a></td>
+          </tr>
+        {/section}
+      {/foreach}
+
+    {else}
+      
+      {* Show flat uncategorized project array *}
+      {section name=proj loop=$projects}
+        <tr class="{cycle values="light,dark"}">
+          <td>
+            <a href="{$SCRIPT_NAME}?p={$projects[proj].project}&a=summary" class="list">{$projects[proj].project}</a>
+          </td>
+          <td>{$projects[proj].descr}</td>
+          <td><i>{$projects[proj].owner}</i></td>
+          <td>
+            {if $projects[proj].age < 7200}   {* 60*60*2, or 2 hours *}
+              <span class="agehighlight"><b><i>{$projects[proj].age_string}</i></b></span>
+	    {elseif $projects[proj].age < 172800}   {* 60*60*24*2, or 2 days *}
+              <span class="agehighlight"><i>{$projects[proj].age_string}</i></span>
+	    {else}
+              <i>{$projects[proj].age_string}</i>
+            {/if}
+          </td>
+          <td class="link"><a href="{$SCRIPT_NAME}?p={$projects[proj].project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$projects[proj].project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$projects[proj].project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$projects[proj].project}&a=tree">tree</a> | <a href="{$SCRIPT_NAME}?p={$projects[proj].project}&a=snapshot&h=HEAD">snapshot</a></td>
+        </tr>
+      {/section}
+
+    {/if}
+
+  </table>
+{/if}
+
+{include file='footer.tpl'}
+
+

--- a/templates/projlist_category.tpl
+++ /dev/null
@@ -1,15 +1,1 @@
-{*
- *  projlist_category.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project list category template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-<tr>
-<th>{$category}</th>
-<th></th>
-<th></th>
-<th></th>
-<th></th>
-</tr>
 

--- a/templates/projlist_footer.tpl
+++ /dev/null
@@ -1,9 +1,1 @@
-{*
- *  projlist_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project list footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-</table>
 

--- a/templates/projlist_header.tpl
+++ /dev/null
@@ -1,32 +1,1 @@
-{*
- *  projlist_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project list header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-<table cellspacing="0">
-<tr>
-{if $order == "project"}
-<th>Project</th>
-{else}
-<th><a class="header" href="{$SCRIPT_NAME}?o=project">Project</a></th>
-{/if}
-{if $order == "descr"}
-<th>Description</th>
-{else}
-<th><a class="header" href="{$SCRIPT_NAME}?o=descr">Description</a></th>
-{/if}
-{if $order == "owner"}
-<th>Owner</th>
-{else}
-<th><a class="header" href="{$SCRIPT_NAME}?o=owner">Owner</a></th>
-{/if}
-{if $order == "age"}
-<th>Last Change</th>
-{else}
-<th><a class="header" href="{$SCRIPT_NAME}?o=age">Last Change</a></th>
-{/if}
-<th>Actions</th>
-</tr>
 

--- a/templates/projlist_item.tpl
+++ /dev/null
@@ -1,33 +1,1 @@
-{*
- *  projlist_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Project list item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-<tr class="{$class}">
-<td>
-{if $idt}<span class="respectwhitespace">  {/if}<a href="{$SCRIPT_NAME}?p={$project}&a=summary" class="list">{$project}</a>{if $idt}</span>{/if}
-</td>
-<td>{$descr}</td>
-<td><i>{$owner}</i></td>
-<td>
-{if $age_colored}
-<span class="agehighlight">
-{/if}
-{if $age_bold}
-<b>
-{/if}
-<i>
-{$age_string}
-</i>
-{if $age_bold}
-</b>
-{/if}
-{if $age_colored}
-</span>
-{/if}
-</td>
-<td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h=HEAD">snapshot</a></td>
-</tr>
 

file:b/templates/rss.tpl (new)
--- /dev/null
+++ b/templates/rss.tpl
@@ -1,1 +1,39 @@
+{*
+ *  rss.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: RSS template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
+<?xml version="1.0" encoding="utf-8"?>
+<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
+  <channel>
+    <title>{$project}</title>
+    <link>{$self}?p={$project}&amp;a=summary</link>
+    <description>{$project} log</description>
+    <language>en</language>
 
+    {section name=rssitem loop=$commitlines}
+      <item>
+        <title>{$commitlines[rssitem].cdmday} {$commitlines[rssitem].cdmonth} {$commitlines[rssitem].cdhour}:{$commitlines[rssitem].cdminute} - {$commitlines[rssitem].title|escape:'htmlall'}</title>
+        <author>{$commitlines[rssitem].author|escape:'htmlall'}</author>
+        <pubDate>{$commitlines[rssitem].cdrfc2822}</pubDate>
+        <guid isPermaLink="true">{$self}?p={$project}&amp;a=commit&amp;h={$commitlines[rssitem].commit}</guid>
+        <link>{$self}?p={$project}&amp;a=commit&amp;h={$commitlines[rssitem].commit}</link>
+        <description>{$commitlines[rssitem].title|escape:'htmlall'}</description>
+        <content:encoded>
+          <![CDATA[
+          {foreach from=$commitlines[rssitem].comment item=line}
+            {$line}<br />
+          {/foreach}
+          {foreach from=$commitlines[rssitem].difftree item=line}
+            {$line}<br />
+          {/foreach}
+          ]]>
+        </content:encoded>
+      </item>
+    {/section}
+
+  </channel>
+</rss>
+

--- a/templates/rss_footer.tpl
+++ /dev/null
@@ -1,10 +1,1 @@
-{*
- *  rss_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: RSS footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-</channel>
-</rss>
 

--- a/templates/rss_header.tpl
+++ /dev/null
@@ -1,15 +1,1 @@
-{*
- *  rss_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: RSS header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-<?xml version="1.0" encoding="utf-8"?>
-<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
-<channel>
-<title>{$project}</title>
-<link>{$self}?p={$project}&amp;a=summary</link>
-<description>{$project} log</description>
-<language>en</language>
 

file:a/templates/rss_item.tpl (deleted)
--- a/templates/rss_item.tpl
+++ /dev/null
@@ -1,26 +1,1 @@
-{*
- *  rss_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: RSS item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
-<item>
-<title>{$cdmday} {$cdmonth} {$cdhour}:{$cdminute} - {$title}</title>
-<author>{$author}</author>
-<pubDate>{$cdrfc2822}</pubDate>
-<guid isPermaLink="true">{$self}?p={$project}&amp;a=commit&amp;h={$commit}</guid>
-<link>{$self}?p={$project}&amp;a=commit&amp;h={$commit}</link>
-<description>{$title}</description>
-<content:encoded>
-<![CDATA[
-{foreach from=$comment item=line}
-{$line}<br />
-{/foreach}
-{foreach from=$difftree item=line}
-{$line}<br />
-{/foreach}
-]]>
-</content:encoded>
-</item>
 

--- /dev/null
+++ b/templates/search.tpl
@@ -1,1 +1,62 @@
+{*
+ *  search.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Search view template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+{include file='header.tpl'}
+
+{* Nav *}
+<div class="page_nav">
+  <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$treehash}&hb={$hash}">tree</a>
+  <br />
+  {if $page > 0}
+    <a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}">first</a>
+  {else}
+    first
+  {/if}
+    &sdot; 
+  {if $page > 0}
+    <a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}{if $page > 1}&pg={$page-1}{/if}" accesskey="p" title="Alt-p">prev</a>
+  {else}
+    prev
+  {/if}
+    &sdot; 
+  {if $revlistcount > 100}
+    <a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}&pg={$page+1}" accesskey="n" title="Alt-n">next</a>
+  {else}
+    next
+  {/if}
+  <br />
+</div>
+<div>
+  <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}" class="title">{$title}</a>
+</div>
+<table cellspacing="0">
+  {* Print each match *}
+  {section name=match loop=$commitlines}
+    <tr class="{cycle values="light,dark"}">
+      <td title="{$commitlines[match].agestringage}"><i>{$commitlines[match].agestringdate}</i></td>
+      <td><i>{$commitlines[match].authorname}</i></td>
+      <td><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commitlines[match].commit}" class="list" {if $title}title="{$commitlines[match].title}"{/if}><b>{$commitlines[match].title_short}</b>
+        {foreach from=$commitlines[match].matches item=line name=match}
+          {if $smarty.foreach.match.first}<br />{/if}{$line}<br />
+        {/foreach}
+      </td>
+      <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commitlines[match].commit}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$commitlines[match].commit}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$commitlines[match].committree}&hb={$commitlines[match].commit}">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$commitlines[match].commit}">snapshot</a>
+      </td>
+    </tr>
+  {/section}
+
+  {if $revlistcount > 100}
+    <tr>
+      <td><a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}&pg={$page+1}" title="Alt-n">next</a></td>
+    </tr>
+  {/if}
+</table>
+
+{include file='footer.tpl'}
+
+

--- a/templates/search_fileitem.tpl
+++ /dev/null
@@ -1,21 +1,1 @@
-{*
- *  search_fileitem.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Search file item template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
-<tr class="{$class}">
-<td>
-{if $tree}
-<a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}&hb={$hashbase}&f={$file}" class="list"><b>{$filename}</b></a>
-{else}
-<a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$hash}&hb={$hashbase}&f={$file}" class="list"><b>{$filename}</b></a>
-{foreach from=$matches item=line name=match}
-{if $smarty.foreach.match.first}<br />{/if}<span class="respectwhitespace">{$line}</span><br />
-{/foreach}
-{/if}
-</td>
-<td class="link">{if $tree}<a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}&hb={$hashbase}&f={$file}">tree</a>{else}<a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$hash}&hb={$hashbase}&f={$file}">blob</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=history&h={$hashbase}&f={$file}">history</a>{/if}</td>
-</tr>
 

--- a/templates/search_footer.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  search_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Search view footer template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
- {if $nextlink}
- <tr><td><a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}&pg={$nextpage}" title="Alt-n">next</a></td></tr>
- {/if}
- </table>
 

--- a/templates/search_header.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  search_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Search header template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
-<div>
-<a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}" class="title">{$title}</a>
-</div>
-<table cellspacing="0">
 

--- a/templates/search_item.tpl
+++ /dev/null
@@ -1,19 +1,1 @@
-{*
- *  search_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Search view item template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
-<tr class="{$class}">
-<td title="{$agestringage}"><i>{$agestringdate}</i></td>
-<td><i>{$authorname}</i></td>
-<td><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commit}" class="list" {if $title}title="{$title}"{/if}><b>{$title_short}</b>
-{foreach from=$matches item=line name=match}
-{if $smarty.foreach.match.first}<br />{/if}{$line}<br />
-{/foreach}
-</td>
-<td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commit}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$commit}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$committree}&hb={$commit}">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$commit}">snapshot</a>
-</td>
-</tr>
 

--- a/templates/search_nav.tpl
+++ /dev/null
@@ -1,11 +1,1 @@
-{*
- *  search_nav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Search view nav template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
-<div class="page_nav">
-<a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$treehash}&hb={$hash}">tree</a>
-<br />
 

--- a/templates/search_pagenav.tpl
+++ /dev/null
@@ -1,11 +1,1 @@
-{*
- *  search_pagenav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Log view page nav template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- {if $firstlink}<a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}">{/if}first{if $firstlink}</a>{/if} &sdot; {if $prevlink}<a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}{if $prevpage}&pg={$prevpage}{/if}" accesskey="p" title="Alt-p">{/if}prev{if $prevlink}</a>{/if} &sdot; {if $nextlink}<a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}&pg={$nextpage}" accesskey="n" title="Alt-n">{/if}next{if $nextlink}</a>{/if}
- <br />
- </div>
 

--- /dev/null
+++ b/templates/searchfiles.tpl
@@ -1,1 +1,70 @@
+{*
+ *  searchfiles.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Search files template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+{include file='header.tpl'}
+
+{* Nav *}
+<div class="page_nav">
+  <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$treehash}&hb={$hash}">tree</a>
+  <br />
+  {if $page > 0}
+    <a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}">first</a>
+  {else}
+    first
+  {/if}
+    &sdot; 
+  {if $page > 0}
+    <a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}{if $page > 1}&pg={$page-1}{/if}" accesskey="p" title="Alt-p">prev</a>
+  {else}
+    prev
+  {/if}
+    &sdot; 
+  {if $filesearchcount > 100}
+    <a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}&pg={$page+1}" accesskey="n" title="Alt-n">next</a>
+  {else}
+    next
+  {/if}
+  <br />
+</div>
+<div>
+  <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}" class="title">{$title}</a>
+</div>
+<table cellspacing="0">
+  {* Print each match *}
+  {section name=match loop=$filesearchlines}
+    <tr class="{cycle values="light,dark"}">
+      <td>
+        {if $filesearchlines[match].tree}
+          <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$filesearchlines[match].hash}&hb={$hash}&f={$filesearchlines[match].file}" class="list"><b>{$filesearchlines[match].filename}</b></a>
+        {else}
+          <a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$filesearchlines[match].hash}&hb={$hash}&f={$filesearchlines[match].file}" class="list"><b>{$filesearchlines[match].filename}</b></a>
+          {foreach from=$filesearchlines[match].matches item=line name=match}
+            {if $smarty.foreach.match.first}<br />{/if}<span class="respectwhitespace">{$line}</span><br />
+          {/foreach}
+        {/if}
+      </td>
+      <td class="link">
+        {if $filesearchlines[match].tree}
+          <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$filesearchlines[match].hash}&hb={$hash}&f={$filesearchlines[match].file}">tree</a>
+        {else}
+          <a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$filesearchlines[match].hash}&hb={$hash}&f={$filesearchlines[match].file}">blob</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=history&h={$hash}&f={$filesearchlines[match].file}">history</a>
+        {/if}
+      </td>
+    </tr>
+  {/section}
+
+  {if $filesearchcount > 100}
+    <tr>
+      <td><a href="{$SCRIPT_NAME}?p={$project}&a=search&h={$hash}&s={$search}&st={$searchtype}&pg={$page+1}" title="Alt-n">next</a></td>
+    </tr>
+  {/if}
+</table>
+
+{include file='footer.tpl'}
+
+

--- /dev/null
+++ b/templates/shortlog.tpl
@@ -1,1 +1,64 @@
+{*
+ *  shortlog.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Shortlog view template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+ {include file='header.tpl'}
+
+ {* Nav *}
+ <div class="page_nav">
+   <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | shortlog | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}&hb={$hash}">tree</a>
+   <br />
+   {if ($hash != $head) || $page}
+     <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">HEAD</a>
+   {else}
+     HEAD
+   {/if}
+     &sdot; 
+   {if $page > 0}
+     <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}&pg={$page-1}" accesskey="p" title="Alt-p">prev</a>
+   {else}
+     prev
+   {/if}
+     &sdot; 
+   {if $revlistcount > 100}
+     <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}&pg={$page+1}" accesskey="n" title="Alt-n">next</a>
+   {else}
+     next
+   {/if}
+   <br />
+ </div>
+ <div>
+   <a href="{$SCRIPT_NAME}?p={$project}&a=summary" class="title">&nbsp;</a>
+ </div>
+ <table cellspacing="0">
+   {* Display each log entry *}
+   {section name=log loop=$commitlines}
+     <tr class="{cycle values="light,dark"}">
+       <td title="{$commitlines[log].agestringage}"><i>{$commitlines[log].agestringdate}</i></td>
+       <td><i>{$commitlines[log].authorname}</i></td>
+       <td>
+         <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commitlines[log].commit}" class="list" {if $commitlines[log].title}title="{$commitlines[log].title}"{/if}><b>{$commitlines[log].title_short}
+         {if $commitlines[log].commitref}
+           <span class="tag">{$commitlines[log].commitref}</span>
+         {/if}
+         </b>
+       </td>
+       <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commitlines[log].commit}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$commitlines[log].commit}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$commitlines[log].commit}&hb={$commitlines[log].commit}">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$commitlines[log].commit}">snapshot</a>
+       </td>
+     </tr>
+   {/section}
+
+   {if $revlistcount > 100}
+     <tr>
+       <td><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}&pg={$page+1}" title="Alt-n">next</a></td>
+     </tr>
+   {/if}
+ </table>
+
+ {include file='footer.tpl'}
+
+

--- a/templates/shortlog_footer.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  shortlog_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Shortlog view footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- {if $nextlink}
- <tr><td><a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}&pg={$nextpage}" title="Alt-n">next</a></td></tr>
- {/if}
- </table>
 

--- a/templates/shortlog_item.tpl
+++ /dev/null
@@ -1,21 +1,1 @@
-{*
- *  shortlog_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Shortlog view item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <tr class="{$class}">
- <td title="{$agestringage}"><i>{$agestringdate}</i></td>
- <td><i>{$authorname}</i></td>
- <td>
- <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commit}" class="list" {if $title}title="{$title}"{/if}><b>{$title_short}
- {if $commitref}
- <span class="tag">{$commitref}</span>
- {/if}
- </b>
- </td>
- <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$commit}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$commit}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$commit}&hb={$commit}">tree</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$commit}">snapshot</a>
- </td>
- </tr>
 

--- a/templates/shortlog_nav.tpl
+++ /dev/null
@@ -1,11 +1,1 @@
-{*
- *  shortlog_nav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Shortlog view nav template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_nav">
- <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | shortlog | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hash}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hash}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}&hb={$hash}">tree</a>
- <br />
 

--- a/templates/shortlog_pagenav.tpl
+++ /dev/null
@@ -1,15 +1,1 @@
-{*
- *  shortlog_pagenav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Shortlog view page nav template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- {if $headlink}<a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">{/if}HEAD{if $headlink}</a>{/if} &sdot; {if $prevlink}<a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}&pg={$prevpage}" accesskey="p" title="Alt-p">{/if}prev{if $prevlink}</a>{/if} &sdot; {if $nextlink}<a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hash}&pg={$nextpage}" accesskey="n" title="Alt-n">{/if}next{if $nextlink}</a>{/if}
- <br />
- </div>
- <div>
- <a href="{$SCRIPT_NAME}?p={$project}&a=summary" class="title">&nbsp;</a>
- </div>
- <table cellspacing="0">
 

--- /dev/null
+++ b/templates/snapshot.tpl
@@ -1,1 +1,9 @@
+{*
+ *  snapshots.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Snapshot template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
+{$archive}
 

file:b/templates/tag.tpl (new)
--- /dev/null
+++ b/templates/tag.tpl
@@ -1,1 +1,48 @@
+{*
+ *  tag.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Tag view template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+ {include file='header.tpl'}
+
+ {* Nav *}
+ <div class="page_nav">
+   <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$head}">tree</a>
+   <br /><br />
+ </div>
+ {* Tag data *}
+ <div>
+   <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}" class="title">{$tag.name}</a>
+ </div>
+ <div class="title_text">
+   <table cellspacing="0">
+     <tr>
+       <td>object</td>
+       <td class="monospace"><a href="{$SCRIPT_NAME}?p={$project}&a={$tag.type}&h={$tag.object}" class="list">{$tag.object}</a></td>
+       <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a={$tag.type}&h={$tag.object}">{$tag.type}</a></td>
+     </tr>
+     {if $tag.author}
+       <tr>
+         <td>author</td>
+	 <td>{$tag.author}</td>
+       </tr>
+       <tr>
+         <td></td>
+	 <td> {$datedata.rfc2822} ({if $datedata.hour_local < 6}<span class="latenight">{/if}{$datedata.hour_local}:{$datedata.minute_local}{if $datedata.hour_local < 6}</span>{/if} {$datedata.tz_local})
+         </td>
+       </tr>
+     {/if}
+   </table>
+ </div>
+ <div class="page_body">
+   {foreach from=$tag.comment item=line}
+     {$line}<br />
+   {/foreach}
+ </div>
+
+ {include file='footer.tpl'}
+
+

file:a/templates/tag_data.tpl (deleted)
--- a/templates/tag_data.tpl
+++ /dev/null
@@ -1,26 +1,1 @@
-{*
- *  tag_data.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tag view data template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
- <div>
-   <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hash}" class="title">{$title}</a>
- </div>
- <div class="title_text">
- <table cellspacing="0">
- <tr><td>object</td><td class="monospace"><a href="{$SCRIPT_NAME}?p={$project}&a={$type}&h={$object}" class="list">{$object}</a></td><td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a={$type}&h={$object}">{$type}</a></td></tr>
- {if $author}
- <tr><td>author</td><td>{$author}</td></tr>
- <tr><td></td><td> {$adrfc2822} ({if $adhourlocal < 6}<span class="latenight">{/if}{$adhourlocal}:{$adminutelocal}{if $adhourlocal < 6}</span>{/if} {$adtzlocal})
- </td></tr>
- {/if}
- </table>
- </div>
- <div class="page_body">
- {foreach from=$comment item=line}
- {$line}<br />
- {/foreach}
- </div>
 

file:a/templates/tag_nav.tpl (deleted)
--- a/templates/tag_nav.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  tag_nav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tag navbar template
- *
- *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_nav">
- <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$head}">tree</a>
- <br /><br />
- </div>
 

--- /dev/null
+++ b/templates/tags.tpl
@@ -1,1 +1,45 @@
+{*
+ *  tags.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Tag view template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+ {include file='header.tpl'}
+
+ {* Nav *}
+ <div class="page_nav">
+   <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$head}">tree</a>
+   <br /><br />
+ </div>
+ <div>
+   <a href="{$SCRIPT_NAME}?p={$project}&a=summary" class="title">&nbsp;</a>
+ </div>
+ {* Display tags *}
+ <table cellspacing="0">
+   {section name=tag loop=$taglist}
+     <tr class="{cycle values="light,dark"}">
+       <td><i>{$taglist[tag].age}</i></td>
+       <td><a href="{$SCRIPT_NAME}?p={$project}&a={$taglist[tag].reftype}&h={$taglist[tag].refid}" class="list"><b>{$taglist[tag].name}</b></a></td>
+       <td>
+         {if count($taglist[tag].comment) > 0}
+           <a href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$taglist[tag].id}" class="list">{$taglist[tag].comment[0]}</a>
+         {/if}
+       </td>
+       <td class="link">
+         {if $taglist[tag].type == "tag"}
+	   <a href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$taglist[tag].id}">tag</a> | 
+	 {/if}
+	 <a href="{$SCRIPT_NAME}?p={$project}&a={$taglist[tag].reftype}&h={$taglist[tag].refid}">{$taglist[tag].reftype}</a>
+	 {if $taglist[tag].reftype == "commit"}
+	   | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/tags/{$taglist[tag].name}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h=refs/tags/{$taglist[tag].name}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$taglist[tag].refid}">snapshot</a>
+	 {/if}
+       </td>
+     </tr>
+   {/section}
+ </table>
+
+ {include file='footer.tpl'}
+
+

--- a/templates/tags_footer.tpl
+++ /dev/null
@@ -1,9 +1,1 @@
-{*
- *  tags_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tag view footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- </table>
 

--- a/templates/tags_header.tpl
+++ /dev/null
@@ -1,10 +1,1 @@
-{*
- *  tags_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tag view header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div><a href="{$SCRIPT_NAME}?p={$project}&a=summary" class="title">&nbsp;</a></div>
- <table cellspacing="0">
 

file:a/templates/tags_item.tpl (deleted)
--- a/templates/tags_item.tpl
+++ /dev/null
@@ -1,20 +1,1 @@
-{*
- *  tags_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tag view item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <tr class="{$class}">
- <td><i>{$age}</i></td>
- <td><a href="{$SCRIPT_NAME}?p={$project}&a={$reftype}&h={$refid}" class="list"><b>{$name}</b></a></td>
- <td>
- {if $comment}
- <a href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$id}" class="list">{$comment}</a>
- {/if}
- </td>
- <td class="link">
- {if $type == "tag"}<a href="{$SCRIPT_NAME}?p={$project}&a=tag&h={$id}">tag</a> | {/if}<a href="{$SCRIPT_NAME}?p={$project}&a={$reftype}&h={$refid}">{$reftype}</a>{if $reftype == "commit"} | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h=refs/tags/{$name}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h=refs/tags/{$name}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=snapshot&h={$refid}">snapshot</a>{/if}
- </td>
- </tr>
 

file:a/templates/tags_nav.tpl (deleted)
--- a/templates/tags_nav.tpl
+++ /dev/null
@@ -1,12 +1,1 @@
-{*
- *  tags_nav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tag view nav template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_nav">
- <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$head}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$head}">commitdiff</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$head}">tree</a>
- <br /><br />
- </div>
 

--- /dev/null
+++ b/templates/tree.tpl
@@ -1,1 +1,64 @@
+{*
+ *  tree.tpl
+ *  gitphp: A PHP git repository browser
+ *  Component: Tree view template
+ *
+ *  Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
+ *}
 
+ {include file='header.tpl'}
+
+ {* Nav *}
+ {if $fullnav}
+   <div class="page_nav">
+     <a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hashbase}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hashbase}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hashbase}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hashbase}">commitdiff</a> | tree<br /><br />
+   </div>
+   <div>
+     <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hashbase}" class="title">{$title}
+       {if $hashbaseref}
+         <span class="tag">{$hashbaseref}</span>
+       {/if}
+     </a>
+   </div>
+ {else}
+   <div class="page_nav"><br /><br /></div>
+   <div class="title">{$hash}</div>
+ {/if}
+ {* Path *}
+ <div class="page_path">
+   <b>
+     <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$hashbase}&h={$hashbase}">[{$project}]</a> / 
+     {foreach from=$paths item=path}
+       <a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$hashbase}&h={$path.tree}&f={$path.full}">{$path.short}</a> / 
+     {/foreach}
+   </b>
+ </div>
+ <div class="page_body">
+   {* List files *}
+   <table cellspacing="0">
+     {section name=tree loop=$treelines}
+       <tr class="{cycle values="light,dark"}">
+         <td class="monospace">{$treelines[tree].filemode}</td>
+         {if $treelines[tree].type == "blob"}
+           <td class="list">
+             <a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$treelines[tree].hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$treelines[tree].name}" class="list">{$treelines[tree].name}</a>
+	   </td>
+           <td class="link">
+	     <a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$treelines[tree].hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$treelines[tree].name}">blob</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=history&h={$hashbase}&f={if $base}{$base}{/if}{$treelines[tree].name}">history</a>
+	   </td>
+         {elseif $treelines[tree].type == "tree"}
+           <td class="list">
+             <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$treelines[tree].hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$treelines[tree].name}">{$treelines[tree].name}</a>
+	   </td>
+           <td class="link">
+	     <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$treelines[tree].hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$treelines[tree].name}">tree</a>
+	   </td>
+         {/if}
+       </tr>
+     {/section}
+   </table>
+ </div>
+
+ {include file='footer.tpl'}
+
+

--- a/templates/tree_emptynav.tpl
+++ /dev/null
@@ -1,10 +1,1 @@
-{*
- *  tree_emptynav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tree view emptynav template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_nav"><br /><br /></div>
- <div class="title">{$hash}</div>
 

--- a/templates/tree_filelist_footer.tpl
+++ /dev/null
@@ -1,10 +1,1 @@
-{*
- *  tree_filelist_footer.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tree view filelist footer template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- </table>
- </div>
 

--- a/templates/tree_filelist_header.tpl
+++ /dev/null
@@ -1,11 +1,1 @@
-{*
- *  tree_filelist_header.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tree view filelist header template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_path"><b><a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$hashbase}&h={$hashbase}">[{$project}]</a> / {foreach from=$paths item=path}<a href="{$SCRIPT_NAME}?p={$project}&a=tree&hb={$hashbase}&h={$path.tree}&f={$path.full}">{$path.short}</a> / {/foreach}</b></div>
- <div class="page_body">
- <table cellspacing="0">
 

--- a/templates/tree_filelist_item.tpl
+++ /dev/null
@@ -1,20 +1,1 @@
-{*
- *  tree_filelist_item.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tree view filelist item template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <tr class="{$class}">
- <td class="monospace">{$filemode}</td>
- <td class="list">
- {if $type == "blob"}
- <a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$name}" class="list">{$name}</a></td>
- <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=blob&h={$hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$name}">blob</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=history&h={$hashbase}&f={if $base}{$base}{/if}{$name}">history</a>
- {elseif $type == "tree"}
-   <a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$name}">{$name}</a></td>
-   <td class="link"><a href="{$SCRIPT_NAME}?p={$project}&a=tree&h={$hash}{if $hashbase}&hb={$hashbase}{/if}&f={if $base}{$base}{/if}{$name}">tree</a>
- {/if}
- </td>
- </tr>
 

file:a/templates/tree_nav.tpl (deleted)
--- a/templates/tree_nav.tpl
+++ /dev/null
@@ -1,15 +1,1 @@
-{*
- *  tree_nav.tpl
- *  gitphp: A PHP git repository browser
- *  Component: Tree view nav template
- *
- *  Copyright (C) 2006 Christopher Han <xiphux@gmail.com>
- *}
- <div class="page_nav"><a href="{$SCRIPT_NAME}?p={$project}&a=summary">summary</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=shortlog&h={$hashbase}">shortlog</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=log&h={$hashbase}">log</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hashbase}">commit</a> | <a href="{$SCRIPT_NAME}?p={$project}&a=commitdiff&h={$hashbase}">commitdiff</a> | tree<br /><br />
- </div>
- <div><a href="{$SCRIPT_NAME}?p={$project}&a=commit&h={$hashbase}" class="title">{$title}
- {if $hashbaseref}
- <span class="tag">{$hashbaseref}</span>
- {/if}
- </a></div>
 

comments