Factor out command creation from execution
Factor out command creation from execution

--- a/config/gitphp.conf.defaults.php
+++ b/config/gitphp.conf.defaults.php
@@ -394,11 +394,18 @@
 
 /*
  * debug
- * Turns on extra warning messages and benchmarking.
+ * Turns on extra warning messages
  * Not recommended for production systems, as it will give
- * way more benchmarking info than you care about, and
+ * way more info about what's happening than you care about, and
  * will screw up non-html output (rss, opml, snapshots, etc)
  */
 $gitphp_conf['debug'] = false;
 
-
+/*
+ * benchmark
+ * Turns on extra timestamp and memory benchmarking info
+ * when debug mode is turned on.  Generates lots of output.
+ */
+$gitphp_conf['benchmark'] = false;
+
+

--- a/config/projects.conf.php.example
+++ b/config/projects.conf.php.example
@@ -87,6 +87,12 @@
  *	     setting in the config for this project.  This can also be
  *	     an empty string to override the global bug url to say that
  *	     only this project has no bug url.
+ *
+ * 'compat': whether this project runs in compatibility mode.  (true/false)
+ *	     This overrides the compat setting in the config for this project.
+ *	     Compatibility mode relies more on the git executable for loading
+ *	     data, at the expense of performance.  Use if you are having
+ *	     trouble loading data for this project.
  */
 //$git_projects_settings['php/gitphp.git'] = array(
 //	'category' => 'PHP',
@@ -95,10 +101,12 @@
 //	'cloneurl' => 'http://git.xiphux.com/php/gitphp.git',
 //	'pushurl' => '',
 //	'bugpattern' => '/#([0-9]+)/',
-//	'bugurl' => 'http://mantis.xiphux.com/view.php?id=${1}'
+//	'bugurl' => 'http://mantis.xiphux.com/view.php?id=${1}',
+//	'compat' => false
 //);
 //$git_projects_settings['gentoo.git'] = array(
-//	'description' => 'Gentoo portage overlay'
+//	'description' => 'Gentoo portage overlay',
+//	'compat' => true
 //);
 //$git_projects_settings['core/fbx.git'] = array(
 //	'description' => 'FBX music player',

--- a/include/Log.class.php
+++ b/include/Log.class.php
@@ -36,6 +36,15 @@
 	protected $enabled = false;
 
 	/**
+	 * benchmark
+	 *
+	 * Stores whether benchmarking is enabled
+	 *
+	 * @access protected
+	 */
+	protected $benchmark = false;
+
+	/**
 	 * startTime
 	 *
 	 * Stores the starting instant
@@ -94,6 +103,7 @@
 		$this->startMem = memory_get_usage();
 
 		$this->enabled = GitPHP_Config::GetInstance()->GetValue('debug', false);
+		$this->benchmark = GitPHP_Config::GetInstance()->GetValue('benchmark', false);
 	}
 
 	/**
@@ -136,8 +146,12 @@
 			return;
 
 		$entry = array();
-		$entry['time'] = microtime(true);
-		$entry['mem'] = memory_get_usage();
+		
+		if ($this->benchmark) {
+			$entry['time'] = microtime(true);
+			$entry['mem'] = memory_get_usage();
+		}
+
 		$entry['msg'] = $message;
 		$this->entries[] = $entry;
 	}
@@ -166,6 +180,32 @@
 	public function SetEnabled($enable)
 	{
 		$this->enabled = $enable;
+	}
+
+	/**
+	 * GetBenchmark
+	 *
+	 * Gets whether benchmarking is enabled
+	 *
+	 * @access public
+	 * @return boolean true if benchmarking is enabled
+	 */
+	public function GetBenchmark()
+	{
+		return $this->benchmark;
+	}
+
+	/**
+	 * SetBenchmark
+	 *
+	 * Sets whether benchmarking is enabled
+	 *
+	 * @access public
+	 * @param boolean $bench true if benchmarking is enabled
+	 */
+	public function SetBenchmark($bench)
+	{
+		$this->benchmark = $bench;
 	}
 
 	/**
@@ -181,21 +221,31 @@
 		$data = array();
 	
 		if ($this->enabled) {
-			$endTime = microtime(true);
-			$endMem = memory_get_usage();
-
-			$lastTime = $this->startTime;
-			$lastMem = $this->startMem;
-
-			$data[] = '[' . $this->startTime . '] [' . $this->startMem . ' bytes] Start';
+
+			if ($this->benchmark) {
+				$endTime = microtime(true);
+				$endMem = memory_get_usage();
+
+				$lastTime = $this->startTime;
+				$lastMem = $this->startMem;
+
+				$data[] = 'DEBUG: [' . $this->startTime . '] [' . $this->startMem . ' bytes] Start';
+
+			}
 
 			foreach ($this->entries as $entry) {
-				$data[] = '[' . $entry['time'] . '] [' . ($entry['time'] - $this->startTime) . ' sec since start] [' . ($entry['time'] - $lastTime) . ' sec since last] [' . $entry['mem'] . ' bytes] [' . ($entry['mem'] - $this->startMem) . ' bytes since start] [' . ($entry['mem'] - $lastMem) . ' bytes since last] ' . $entry['msg'];
-				$lastTime = $entry['time'];
-				$lastMem = $entry['mem'];
+				if ($this->benchmark) {
+					$data[] = 'DEBUG: [' . $entry['time'] . '] [' . ($entry['time'] - $this->startTime) . ' sec since start] [' . ($entry['time'] - $lastTime) . ' sec since last] [' . $entry['mem'] . ' bytes] [' . ($entry['mem'] - $this->startMem) . ' bytes since start] [' . ($entry['mem'] - $lastMem) . ' bytes since last] ' . $entry['msg'];
+					$lastTime = $entry['time'];
+					$lastMem = $entry['mem'];
+				} else {
+					$data[] = 'DEBUG: ' . $entry['msg'];
+				}
 			}
 
-			$data[] = '[' . $endTime . '] [' . ($endTime - $this->startTime) . ' sec since start] [' . ($endTime - $lastTime) . ' sec since last] [' . $endMem . ' bytes] [' . ($endMem - $this->startMem) . ' bytes since start] [' . ($endMem - $lastMem) . ' bytes since last] End';
+			if ($this->benchmark) {
+				$data[] = '[' . $endTime . '] [' . ($endTime - $this->startTime) . ' sec since start] [' . ($endTime - $lastTime) . ' sec since last] [' . $endMem . ' bytes] [' . ($endMem - $this->startMem) . ' bytes since start] [' . ($endMem - $lastMem) . ' bytes since last] End';
+			}
 		}
 
 		return $data;

--- a/include/cache/Cache.class.php
+++ b/include/cache/Cache.class.php
@@ -26,30 +26,34 @@
 	const Template = 'data.tpl';
 
 	/**
-	 * instance
-	 *
-	 * Stores the singleton instance
+	 * objectCacheInstance
+	 *
+	 * Stores the singleton instance of the object cache
 	 *
 	 * @access protected
 	 * @static
 	 */
-	protected static $instance;
-
-	/**
-	 * GetInstance
-	 *
-	 * Return the singleton instance
+	protected static $objectCacheInstance;
+
+	/**
+	 * GetObjectCacheInstance
+	 *
+	 * Return the singleton instance of the object cache
 	 *
 	 * @access public
 	 * @static
 	 * @return mixed instance of cache class
 	 */
-	public static function GetInstance()
-	{
-		if (!self::$instance) {
-			self::$instance = new GitPHP_Cache();
+	public static function GetObjectCacheInstance()
+	{
+		if (!self::$objectCacheInstance) {
+			self::$objectCacheInstance = new GitPHP_Cache();
+			if (GitPHP_Config::GetInstance()->GetValue('objectcache', false)) {
+				self::$objectCacheInstance->SetEnabled(true);
+				self::$objectCacheInstance->SetLifetime(GitPHP_Config::GetInstance()->GetValue('objectcachelifetime', 86400));
+			}
 		}
-		return self::$instance;
+		return self::$objectCacheInstance;
 	}
 
 	/**
@@ -80,8 +84,6 @@
 	 */
 	public function __construct()
 	{
-		if (GitPHP_Config::GetInstance()->GetValue('objectcache', false))
-			$this->SetEnabled(true);
 	}
 
 	/**
@@ -119,6 +121,38 @@
 	}
 
 	/**
+	 * GetLifetime
+	 *
+	 * Gets the cache lifetime
+	 *
+	 * @access public
+	 * @return int cache lifetime in seconds
+	 */
+	public function GetLifetime()
+	{
+		if (!$this->enabled)
+			return false;
+
+		return $this->tpl->cache_lifetime;
+	}
+
+	/**
+	 * SetLifetime
+	 *
+	 * Sets the cache lifetime
+	 *
+	 * @access public
+	 * @param int $lifetime cache lifetime in seconds
+	 */
+	public function SetLifetime($lifetime)
+	{
+		if (!$this->enabled)
+			return;
+
+		$this->tpl->cache_lifetime = $lifetime;
+	}
+
+	/**
 	 * Get
 	 *
 	 * Get an item from the cache
@@ -151,14 +185,21 @@
 	 * @access public
 	 * @param string $key cache key
 	 * @param mixed $value value
-	 */
-	public function Set($key = null, $value = null)
+	 * @param int $lifetime override the lifetime for this data
+	 */
+	public function Set($key = null, $value = null, $lifetime = null)
 	{
 		if (empty($key) || empty($value))
 			return;
 
 		if (!$this->enabled)
 			return;
+
+		$oldLifetime = null;
+		if ($lifetime !== null) {
+			$oldLifetime = $this->tpl->cache_lifetime;
+			$this->tpl->cache_lifetime = $lifetime;
+		}
 
 		$this->Delete($key);
 		$this->tpl->clear_all_assign();
@@ -167,6 +208,10 @@
 		// Force it into smarty's cache
 		$tmp = $this->tpl->fetch(GitPHP_Cache::Template, $key);
 		unset($tmp);
+
+		if ($lifetime !== null) {
+			$this->tpl->cache_lifetime = $oldLifetime;
+		}
 	}
 
 	/**
@@ -235,11 +280,11 @@
 		if ($this->tpl)
 			return;
 
+		require_once(GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('smarty_prefix', 'lib/smarty/libs/')) . 'Smarty.class.php');
 		$this->tpl = new Smarty;
+		$this->tpl->plugins_dir[] = GITPHP_INCLUDEDIR . 'smartyplugins';
 
 		$this->tpl->caching = 2;
-
-		$this->tpl->cache_lifetime = GitPHP_Config::GetInstance()->GetValue('objectcachelifetime', 86400);
 
 		$servers = GitPHP_Config::GetInstance()->GetValue('memcache', null);
 		if (isset($servers) && is_array($servers) && (count($servers) > 0)) {

--- a/include/git/Blob.class.php
+++ b/include/git/Blob.class.php
@@ -132,7 +132,7 @@
 	{
 		$this->dataRead = true;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetProject()->GetCompat()) {
 			$exe = new GitPHP_GitExe($this->GetProject());
 
 			$args = array();
@@ -144,7 +144,7 @@
 			$this->data = $this->GetProject()->GetObject($this->hash);
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**

--- a/include/git/Commit.class.php
+++ b/include/git/Commit.class.php
@@ -541,7 +541,7 @@
 
 		$lines = null;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetProject()->GetCompat()) {
 
 			/* get data from git_rev_list */
 			$exe = new GitPHP_GitExe($this->GetProject());
@@ -617,7 +617,7 @@
 			}
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**
@@ -708,7 +708,7 @@
 			}
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**
@@ -763,13 +763,13 @@
 	{
 		$this->hashPathsRead = true;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetProject()->GetCompat()) {
 			$this->ReadHashPathsGit();
 		} else {
 			$this->ReadHashPathsRaw($this->GetTree());
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**

--- a/include/git/GitExe.class.php
+++ b/include/git/GitExe.class.php
@@ -97,21 +97,37 @@
 	 */
 	public function Execute($command, $args)
 	{
+		$fullCommand = $this->CreateCommand($command, $args);
+
+		GitPHP_Log::GetInstance()->Log('Begin executing "' . $fullCommand . '"');
+
+		$ret = shell_exec($fullCommand);
+
+		GitPHP_Log::GetInstance()->Log('Finish executing "' . $fullCommand . '"' .
+			"\nwith result: " . $ret);
+
+		return $ret;
+	}
+
+	/**
+	 * BuildCommand
+	 *
+	 * Creates a command
+	 *
+	 * @access protected
+	 *
+	 * @param string $command the command to execute
+	 * @param array $args arguments
+	 * @return string result of command
+	 */
+	protected function CreateCommand($command, $args)
+	{
 		$gitDir = '';
 		if ($this->project) {
 			$gitDir = '--git-dir=' . $this->project->GetPath();
 		}
 		
-		$fullCommand = $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args);
-
-		GitPHP_Log::GetInstance()->Log('Begin executing "' . $fullCommand . '"');
-
-		$ret = shell_exec($fullCommand);
-
-		GitPHP_Log::GetInstance()->Log('Finish executing "' . $fullCommand . '"' .
-			"\nwith result: " . $ret);
-
-		return $ret;
+		return $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args);
 	}
 
 	/**

--- a/include/git/Project.class.php
+++ b/include/git/Project.class.php
@@ -25,6 +25,8 @@
 class GitPHP_Project
 {
 
+/* internal variables {{{1*/
+
 	/**
 	 * projectRoot
 	 *
@@ -43,6 +45,8 @@
 	 */
 	protected $project;
 
+/* owner internal variables {{{2*/
+
 	/**
 	 * owner
 	 *
@@ -60,6 +64,10 @@
 	 * @access protected
 	 */
 	protected $ownerRead = false;
+
+/*}}}2*/
+
+/* description internal variables {{{2*/
 
 	/**
 	 * description
@@ -80,6 +88,8 @@
 	 */
 	protected $readDescription = false;
 
+/*}}}2*/
+
 	/**
 	 * category
 	 *
@@ -89,6 +99,8 @@
 	 */
 	protected $category = '';
 
+/* epoch internal variables {{{2*/
+
 	/**
 	 * epoch
 	 *
@@ -107,6 +119,10 @@
 	 */
 	protected $epochRead = false;
 
+/*}}}2*/
+
+/* HEAD internal variables {{{2*/
+
 	/**
 	 * head
 	 *
@@ -125,6 +141,10 @@
 	 */
 	protected $readHeadRef = false;
 
+/*}}}*/
+
+/* ref internal variables {{{2*/
+
 	/**
 	 * tags
 	 *
@@ -152,6 +172,10 @@
 	 */
 	protected $readRefs = false;
 
+/*}}}2*/
+
+/* url internal variables {{{2*/
+
 	/**
 	 * cloneUrl
 	 *
@@ -170,6 +194,10 @@
 	 */
 	protected $pushUrl = null;
 
+/*}}}2*/
+
+/* bugtracker internal variables {{{2*/
+
 	/**
 	 * bugUrl
 	 *
@@ -187,6 +215,8 @@
 	 * @access protected
 	 */
 	protected $bugPattern = null;
+
+/*}}}2*/
 
 	/**
 	 * commitCache
@@ -198,6 +228,8 @@
 	 */
 	protected $commitCache = array();
 
+/* packfile internal variables {{{2*/
+
 	/**
 	 * packs
 	 *
@@ -215,6 +247,22 @@
 	 * @access protected
 	 */
 	protected $packsRead = false;
+
+/*}}}2*/
+
+	/**
+	 * compat
+	 *
+	 * Stores whether this project is running
+	 * in compatibility mode
+	 *
+	 * @access protected
+	 */
+	protected $compat = null;
+
+/*}}}1*/
+
+/* class methods {{{1*/
 
 	/**
 	 * __construct
@@ -232,6 +280,25 @@
 		$this->SetProject($project);
 	}
 
+/*}}}1*/
+
+/* accessors {{{1*/
+
+/* project accessors {{{2*/
+
+	/**
+	 * GetProject
+	 *
+	 * Gets the project
+	 *
+	 * @access public
+	 * @return string the project
+	 */
+	public function GetProject()
+	{
+		return $this->project;
+	}
+
 	/**
 	 * SetProject
 	 *
@@ -268,6 +335,41 @@
 
 	}
 
+/*}}}2*/
+
+	/**
+	 * GetSlug
+	 *
+	 * Gets the project as a filename/url friendly slug
+	 *
+	 * @access public
+	 * @return string the slug
+	 */
+	public function GetSlug()
+	{
+		$project = $this->project;
+
+		if (substr($project, -4) == '.git')
+			$project = substr($project, 0, -4);
+		
+		return GitPHP_Util::MakeSlug($project);
+	}
+
+	/**
+	 * GetPath
+	 *
+	 * Gets the full project path
+	 *
+	 * @access public
+	 * @return string project path
+	 */
+	public function GetPath()
+	{
+		return $this->projectRoot . $this->project;
+	}
+
+/* owner accessors {{{2 */
+
 	/**
 	 * GetOwner
 	 *
@@ -294,7 +396,7 @@
 	 */
 	protected function ReadOwner()
 	{
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetCompat()) {
 			$this->ReadOwnerGit();
 		} else {
 			$this->ReadOwnerRaw();
@@ -385,18 +487,9 @@
 		$this->owner = $owner;
 	}
 
-	/**
-	 * GetProject
-	 *
-	 * Gets the project
-	 *
-	 * @access public
-	 * @return string the project
-	 */
-	public function GetProject()
-	{
-		return $this->project;
-	}
+/*}}}2*/
+
+/* projectroot accessors {{{2*/
 
 	/**
 	 * GetProjectRoot
@@ -411,36 +504,9 @@
 		return $this->projectRoot;
 	}
 
-	/**
-	 * GetSlug
-	 *
-	 * Gets the project as a filename/url friendly slug
-	 *
-	 * @access public
-	 * @return string the slug
-	 */
-	public function GetSlug()
-	{
-		$project = $this->project;
-
-		if (substr($project, -4) == '.git')
-			$project = substr($project, 0, -4);
-		
-		return GitPHP_Util::MakeSlug($project);
-	}
-
-	/**
-	 * GetPath
-	 *
-	 * Gets the full project path
-	 *
-	 * @access public
-	 * @return string project path
-	 */
-	public function GetPath()
-	{
-		return $this->projectRoot . $this->project;
-	}
+/*}}}2*/
+
+/* description accessors {{{2*/
 
 	/**
 	 * GetDescription
@@ -454,7 +520,10 @@
 	public function GetDescription($trim = 0)
 	{
 		if (!$this->readDescription) {
-			$this->description = file_get_contents($this->GetPath() . '/description');
+			if (file_exists($this->GetPath() . '/description')) {
+				$this->description = file_get_contents($this->GetPath() . '/description');
+			}
+			$this->readDescription = true;
 		}
 		
 		if (($trim > 0) && (strlen($this->description) > $trim)) {
@@ -478,6 +547,8 @@
 		$this->readDescription = true;
 	}
 
+/*}}}2*/
+
 	/**
 	 * GetDaemonEnabled
 	 *
@@ -490,6 +561,8 @@
 	{
 		return file_exists($this->GetPath() . '/git-daemon-export-ok');
 	}
+
+/* category accessors {{{2*/
 
 	/**
 	 * GetCategory
@@ -517,6 +590,10 @@
 		$this->category = $category;
 	}
 
+/*}}}2*/
+
+/* clone url accessors {{{2*/
+
 	/**
 	 * GetCloneUrl
 	 *
@@ -550,6 +627,10 @@
 		$this->cloneUrl = $cUrl;
 	}
 
+/*}}}2*/
+
+/* push url accessors {{{2*/
+
 	/**
 	 * GetPushUrl
 	 *
@@ -583,6 +664,10 @@
 		$this->pushUrl = $pUrl;
 	}
 
+/*}}}2*/
+
+/* bugtracker accessors {{{2*/
+
 	/**
 	 * GetBugUrl
 	 *
@@ -641,6 +726,10 @@
 		$this->bugPattern = $bPat;
 	}
 
+/*}}}2*/
+
+/* HEAD accessors {{{2*/
+
 	/**
 	 * GetHeadCommit
 	 *
@@ -669,7 +758,7 @@
 	{
 		$this->readHeadRef = true;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetCompat()) {
 			$this->ReadHeadCommitGit();
 		} else {
 			$this->ReadHeadCommitRaw();
@@ -715,6 +804,157 @@
 			}
 		}
 	}
+
+/*}}}2*/
+
+/* epoch accessors {{{2*/
+
+	/**
+	 * GetEpoch
+	 *
+	 * Gets this project's epoch
+	 * (time of last change)
+	 *
+	 * @access public
+	 * @return integer timestamp
+	 */
+	public function GetEpoch()
+	{
+		if (!$this->epochRead)
+			$this->ReadEpoch();
+
+		return $this->epoch;
+	}
+
+	/**
+	 * GetAge
+	 *
+	 * Gets this project's age
+	 * (time since most recent change)
+	 *
+	 * @access public
+	 * @return integer age
+	 */
+	public function GetAge()
+	{
+		if (!$this->epochRead)
+			$this->ReadEpoch();
+
+		return time() - $this->epoch;
+	}
+
+	/**
+	 * ReadEpoch
+	 *
+	 * Reads this project's epoch
+	 * (timestamp of most recent change)
+	 *
+	 * @access private
+	 */
+	private function ReadEpoch()
+	{
+		$this->epochRead = true;
+
+		if ($this->GetCompat()) {
+			$this->ReadEpochGit();
+		} else {
+			$this->ReadEpochRaw();
+		}
+	}
+
+	/**
+	 * ReadEpochGit
+	 *
+	 * Reads this project's epoch using git executable
+	 *
+	 * @access private
+	 */
+	private function ReadEpochGit()
+	{
+		$exe = new GitPHP_GitExe($this);
+
+		$args = array();
+		$args[] = '--format="%(committer)"';
+		$args[] = '--sort=-committerdate';
+		$args[] = '--count=1';
+		$args[] = 'refs/heads';
+
+		$epochstr = trim($exe->Execute(GIT_FOR_EACH_REF, $args));
+
+		if (preg_match('/ (\d+) [-+][01]\d\d\d$/', $epochstr, $regs)) {
+			$this->epoch = $regs[1];
+		}
+
+		unset($exe);
+	}
+
+	/**
+	 * ReadEpochRaw
+	 *
+	 * Reads this project's epoch using raw objects
+	 *
+	 * @access private
+	 */
+	private function ReadEpochRaw()
+	{
+		if (!$this->readRefs)
+			$this->ReadRefList();
+
+		$epoch = 0;
+		foreach ($this->heads as $head) {
+			$commit = $head->GetCommit();
+			if ($commit) {
+				if ($commit->GetCommitterEpoch() > $epoch) {
+					$epoch = $commit->GetCommitterEpoch();
+				}
+			}
+		}
+		if ($epoch > 0) {
+			$this->epoch = $epoch;
+		}
+	}
+
+/*}}}2*/
+
+/* compatibility accessors {{{2*/
+
+	/**
+	 * GetCompat
+	 *
+	 * Gets whether this project is running in compatibility mode
+	 *
+	 * @access public
+	 * @return boolean true if compatibilty mode
+	 */
+	public function GetCompat()
+	{
+		if ($this->compat !== null) {
+			return $this->compat;
+		}
+
+		return GitPHP_Config::GetInstance()->GetValue('compat', false);
+	}
+
+	/**
+	 * SetCompat
+	 *
+	 * Sets whether this project is running in compatibility mode
+	 *
+	 * @access public
+	 * @param boolean true if compatibility mode
+	 */
+	public function SetCompat($compat)
+	{
+		$this->compat = $compat;
+	}
+
+/*}}}2*/
+
+/*}}}1*/
+
+/* data loading methods {{{1*/
+
+/* commit loading methods {{{2*/
 
 	/**
 	 * GetCommit
@@ -751,7 +991,7 @@
 
 			if (!isset($this->commitCache[$hash])) {
 				$cacheKey = 'project|' . $this->project . '|commit|' . $hash;
-				$cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
+				$cached = GitPHP_Cache::GetObjectCacheInstance()->Get($cacheKey);
 				if ($cached)
 					$this->commitCache[$hash] = $cached;
 				else
@@ -774,87 +1014,9 @@
 		return null;
 	}
 
-	/**
-	 * CompareProject
-	 *
-	 * Compares two projects by project name
-	 *
-	 * @access public
-	 * @static
-	 * @param mixed $a first project
-	 * @param mixed $b second project
-	 * @return integer comparison result
-	 */
-	public static function CompareProject($a, $b)
-	{
-		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
-		if ($catCmp !== 0)
-			return $catCmp;
-
-		return strcmp($a->GetProject(), $b->GetProject());
-	}
-
-	/**
-	 * CompareDescription
-	 *
-	 * Compares two projects by description
-	 *
-	 * @access public
-	 * @static
-	 * @param mixed $a first project
-	 * @param mixed $b second project
-	 * @return integer comparison result
-	 */
-	public static function CompareDescription($a, $b)
-	{
-		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
-		if ($catCmp !== 0)
-			return $catCmp;
-
-		return strcmp($a->GetDescription(), $b->GetDescription());
-	}
-
-	/**
-	 * CompareOwner
-	 *
-	 * Compares two projects by owner
-	 *
-	 * @access public
-	 * @static
-	 * @param mixed $a first project
-	 * @param mixed $b second project
-	 * @return integer comparison result
-	 */
-	public static function CompareOwner($a, $b)
-	{
-		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
-		if ($catCmp !== 0)
-			return $catCmp;
-
-		return strcmp($a->GetOwner(), $b->GetOwner());
-	}
-
-	/**
-	 * CompareAge
-	 *
-	 * Compares two projects by age
-	 *
-	 * @access public
-	 * @static
-	 * @param mixed $a first project
-	 * @param mixed $b second project
-	 * @return integer comparison result
-	 */
-	public static function CompareAge($a, $b)
-	{
-		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
-		if ($catCmp !== 0)
-			return $catCmp;
-
-		if ($a->GetAge() === $b->GetAge())
-			return 0;
-		return ($a->GetAge() < $b->GetAge() ? -1 : 1);
-	}
+/*}}}2*/
+
+/* ref loading methods {{{2*/
 
 	/**
 	 * GetRefs
@@ -890,7 +1052,7 @@
 	{
 		$this->readRefs = true;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetCompat()) {
 			$this->ReadRefListGit();
 		} else {
 			$this->ReadRefListRaw();
@@ -1050,6 +1212,10 @@
 		return $files;
 	}
 
+/*}}}2*/
+
+/* tag loading methods {{{2*/
+
 	/**
 	 * GetTags
 	 *
@@ -1064,7 +1230,7 @@
 		if (!$this->readRefs)
 			$this->ReadRefList();
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetCompat()) {
 			return $this->GetTagsGit($count);
 		} else {
 			return $this->GetTagsRaw($count);
@@ -1169,7 +1335,7 @@
 			return;
 
 		$cacheKey = 'project|' . $this->project . '|tag|' . $tag;
-		$cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
+		$cached = GitPHP_Cache::GetObjectCacheInstance()->Get($cacheKey);
 		if ($cached) {
 			return $cached;
 		} else {
@@ -1177,6 +1343,10 @@
 		}
 	}
 
+/*}}}2*/
+
+/* head loading methods {{{2*/
+
 	/**
 	 * GetHeads
 	 *
@@ -1191,7 +1361,7 @@
 		if (!$this->readRefs)
 			$this->ReadRefList();
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetCompat()) {
 			return $this->GetHeadsGit($count);
 		} else {
 			return $this->GetHeadsRaw($count);
@@ -1280,6 +1450,10 @@
 		return $this->heads[$key];
 	}
 
+/*}}}2*/
+
+/* log methods {{{2*/
+
 	/**
 	 * GetLogHash
 	 *
@@ -1309,7 +1483,7 @@
 	 */
 	public function GetLog($hash, $count = 50, $skip = 0)
 	{
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false) || ($skip > GitPHP_Config::GetInstance()->GetValue('largeskip', 200)) ) {
+		if ($this->GetCompat() || ($skip > GitPHP_Config::GetInstance()->GetValue('largeskip', 200)) ) {
 			return $this->GetLogGit($hash, $count, $skip);
 		} else {
 			return $this->GetLogRaw($hash, $count, $skip);
@@ -1393,6 +1567,10 @@
 		return $log;
 	}
 
+/*}}}2*/
+
+/* blob loading methods {{{2*/
+
 	/**
 	 * GetBlob
 	 *
@@ -1407,13 +1585,17 @@
 			return null;
 
 		$cacheKey = 'project|' . $this->project . '|blob|' . $hash;
-		$cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
+		$cached = GitPHP_Cache::GetObjectCacheInstance()->Get($cacheKey);
 		if ($cached)
 			return $cached;
 
 		return new GitPHP_Blob($this, $hash);
 	}
 
+/*}}}2*/
+
+/* tree loading methods {{{2*/
+
 	/**
 	 * GetTree
 	 *
@@ -1428,271 +1610,16 @@
 			return null;
 
 		$cacheKey = 'project|' . $this->project . '|tree|' . $hash;
-		$cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
+		$cached = GitPHP_Cache::GetObjectCacheInstance()->Get($cacheKey);
 		if ($cached)
 			return $cached;
 
 		return new GitPHP_Tree($this, $hash);
 	}
 
-	/**
-	 * SearchCommit
-	 *
-	 * Gets a list of commits with commit messages matching the given pattern
-	 *
-	 * @access public
-	 * @param string $pattern search pattern
-	 * @param string $hash hash to start searching from
-	 * @param integer $count number of results to get
-	 * @param integer $skip number of results to skip
-	 * @return array array of matching commits
-	 */
-	public function SearchCommit($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
-	{
-		if (empty($pattern))
-			return;
-
-		$args = array();
-
-		$exe = new GitPHP_GitExe($this);
-		if ($exe->CanIgnoreRegexpCase())
-			$args[] = '--regexp-ignore-case';
-		unset($exe);
-
-		$args[] = '--grep=\'' . $pattern . '\'';
-
-		$ret = $this->RevList($hash, $count, $skip, $args);
-		$len = count($ret);
-
-		for ($i = 0; $i < $len; ++$i) {
-			$ret[$i] = $this->GetCommit($ret[$i]);
-		}
-		return $ret;
-	}
-
-	/**
-	 * SearchAuthor
-	 *
-	 * Gets a list of commits with authors matching the given pattern
-	 *
-	 * @access public
-	 * @param string $pattern search pattern
-	 * @param string $hash hash to start searching from
-	 * @param integer $count number of results to get
-	 * @param integer $skip number of results to skip
-	 * @return array array of matching commits
-	 */
-	public function SearchAuthor($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
-	{
-		if (empty($pattern))
-			return;
-
-		$args = array();
-
-		$exe = new GitPHP_GitExe($this);
-		if ($exe->CanIgnoreRegexpCase())
-			$args[] = '--regexp-ignore-case';
-		unset($exe);
-
-		$args[] = '--author=\'' . $pattern . '\'';
-
-		$ret = $this->RevList($hash, $count, $skip, $args);
-		$len = count($ret);
-
-		for ($i = 0; $i < $len; ++$i) {
-			$ret[$i] = $this->GetCommit($ret[$i]);
-		}
-		return $ret;
-	}
-
-	/**
-	 * SearchCommitter
-	 *
-	 * Gets a list of commits with committers matching the given pattern
-	 *
-	 * @access public
-	 * @param string $pattern search pattern
-	 * @param string $hash hash to start searching from
-	 * @param integer $count number of results to get
-	 * @param integer $skip number of results to skip
-	 * @return array array of matching commits
-	 */
-	public function SearchCommitter($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
-	{
-		if (empty($pattern))
-			return;
-
-		$args = array();
-
-		$exe = new GitPHP_GitExe($this);
-		if ($exe->CanIgnoreRegexpCase())
-			$args[] = '--regexp-ignore-case';
-		unset($exe);
-
-		$args[] = '--committer=\'' . $pattern . '\'';
-
-		$ret = $this->RevList($hash, $count, $skip, $args);
-		$len = count($ret);
-
-		for ($i = 0; $i < $len; ++$i) {
-			$ret[$i] = $this->GetCommit($ret[$i]);
-		}
-		return $ret;
-	}
-
-	/**
-	 * RevList
-	 *
-	 * Common code for using rev-list command
-	 *
-	 * @access private
-	 * @param string $hash hash to list from
-	 * @param integer $count number of results to get
-	 * @param integer $skip number of results to skip
-	 * @param array $args args to give to rev-list
-	 * @return array array of hashes
-	 */
-	private function RevList($hash, $count = 50, $skip = 0, $args = array())
-	{
-		if ($count < 1)
-			return;
-
-		$exe = new GitPHP_GitExe($this);
-
-		$canSkip = true;
-		
-		if ($skip > 0)
-			$canSkip = $exe->CanSkip();
-
-		if ($canSkip) {
-			$args[] = '--max-count=' . $count;
-			if ($skip > 0) {
-				$args[] = '--skip=' . $skip;
-			}
-		} else {
-			$args[] = '--max-count=' . ($count + $skip);
-		}
-
-		$args[] = $hash;
-
-		$revlist = explode("\n", $exe->Execute(GIT_REV_LIST, $args));
-
-		if (!$revlist[count($revlist)-1]) {
-			/* the last newline creates a null entry */
-			array_splice($revlist, -1, 1);
-		}
-
-		if (($skip > 0) && (!$exe->CanSkip())) {
-			return array_slice($revlist, $skip, $count);
-		}
-
-		return $revlist;
-	}
-
-	/**
-	 * GetEpoch
-	 *
-	 * Gets this project's epoch
-	 * (time of last change)
-	 *
-	 * @access public
-	 * @return integer timestamp
-	 */
-	public function GetEpoch()
-	{
-		if (!$this->epochRead)
-			$this->ReadEpoch();
-
-		return $this->epoch;
-	}
-
-	/**
-	 * GetAge
-	 *
-	 * Gets this project's age
-	 * (time since most recent change)
-	 *
-	 * @access public
-	 * @return integer age
-	 */
-	public function GetAge()
-	{
-		if (!$this->epochRead)
-			$this->ReadEpoch();
-
-		return time() - $this->epoch;
-	}
-
-	/**
-	 * ReadEpoch
-	 *
-	 * Reads this project's epoch
-	 * (timestamp of most recent change)
-	 *
-	 * @access private
-	 */
-	private function ReadEpoch()
-	{
-		$this->epochRead = true;
-
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
-			$this->ReadEpochGit();
-		} else {
-			$this->ReadEpochRaw();
-		}
-	}
-
-	/**
-	 * ReadEpochGit
-	 *
-	 * Reads this project's epoch using git executable
-	 *
-	 * @access private
-	 */
-	private function ReadEpochGit()
-	{
-		$exe = new GitPHP_GitExe($this);
-
-		$args = array();
-		$args[] = '--format="%(committer)"';
-		$args[] = '--sort=-committerdate';
-		$args[] = '--count=1';
-		$args[] = 'refs/heads';
-
-		$epochstr = trim($exe->Execute(GIT_FOR_EACH_REF, $args));
-
-		if (preg_match('/ (\d+) [-+][01]\d\d\d$/', $epochstr, $regs)) {
-			$this->epoch = $regs[1];
-		}
-
-		unset($exe);
-	}
-
-	/**
-	 * ReadEpochRaw
-	 *
-	 * Reads this project's epoch using raw objects
-	 *
-	 * @access private
-	 */
-	private function ReadEpochRaw()
-	{
-		if (!$this->readRefs)
-			$this->ReadRefList();
-
-		$epoch = 0;
-		foreach ($this->heads as $head) {
-			$commit = $head->GetCommit();
-			if ($commit) {
-				if ($commit->GetCommitterEpoch() > $epoch) {
-					$epoch = $commit->GetCommitterEpoch();
-				}
-			}
-		}
-		if ($epoch > 0) {
-			$this->epoch = $epoch;
-		}
-	}
+/*}}}2*/
+
+/* raw object loading methods {{{2*/
 
 	/**
 	 * GetObject
@@ -1766,5 +1693,257 @@
 		$this->packsRead = true;
 	}
 
+/*}}}2*/
+
+/*}}}1*/
+
+/* search methods {{{1*/
+
+	/**
+	 * SearchCommit
+	 *
+	 * Gets a list of commits with commit messages matching the given pattern
+	 *
+	 * @access public
+	 * @param string $pattern search pattern
+	 * @param string $hash hash to start searching from
+	 * @param integer $count number of results to get
+	 * @param integer $skip number of results to skip
+	 * @return array array of matching commits
+	 */
+	public function SearchCommit($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
+	{
+		if (empty($pattern))
+			return;
+
+		$args = array();
+
+		$exe = new GitPHP_GitExe($this);
+		if ($exe->CanIgnoreRegexpCase())
+			$args[] = '--regexp-ignore-case';
+		unset($exe);
+
+		$args[] = '--grep=\'' . $pattern . '\'';
+
+		$ret = $this->RevList($hash, $count, $skip, $args);
+		$len = count($ret);
+
+		for ($i = 0; $i < $len; ++$i) {
+			$ret[$i] = $this->GetCommit($ret[$i]);
+		}
+		return $ret;
+	}
+
+	/**
+	 * SearchAuthor
+	 *
+	 * Gets a list of commits with authors matching the given pattern
+	 *
+	 * @access public
+	 * @param string $pattern search pattern
+	 * @param string $hash hash to start searching from
+	 * @param integer $count number of results to get
+	 * @param integer $skip number of results to skip
+	 * @return array array of matching commits
+	 */
+	public function SearchAuthor($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
+	{
+		if (empty($pattern))
+			return;
+
+		$args = array();
+
+		$exe = new GitPHP_GitExe($this);
+		if ($exe->CanIgnoreRegexpCase())
+			$args[] = '--regexp-ignore-case';
+		unset($exe);
+
+		$args[] = '--author=\'' . $pattern . '\'';
+
+		$ret = $this->RevList($hash, $count, $skip, $args);
+		$len = count($ret);
+
+		for ($i = 0; $i < $len; ++$i) {
+			$ret[$i] = $this->GetCommit($ret[$i]);
+		}
+		return $ret;
+	}
+
+	/**
+	 * SearchCommitter
+	 *
+	 * Gets a list of commits with committers matching the given pattern
+	 *
+	 * @access public
+	 * @param string $pattern search pattern
+	 * @param string $hash hash to start searching from
+	 * @param integer $count number of results to get
+	 * @param integer $skip number of results to skip
+	 * @return array array of matching commits
+	 */
+	public function SearchCommitter($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
+	{
+		if (empty($pattern))
+			return;
+
+		$args = array();
+
+		$exe = new GitPHP_GitExe($this);
+		if ($exe->CanIgnoreRegexpCase())
+			$args[] = '--regexp-ignore-case';
+		unset($exe);
+
+		$args[] = '--committer=\'' . $pattern . '\'';
+
+		$ret = $this->RevList($hash, $count, $skip, $args);
+		$len = count($ret);
+
+		for ($i = 0; $i < $len; ++$i) {
+			$ret[$i] = $this->GetCommit($ret[$i]);
+		}
+		return $ret;
+	}
+
+/*}}}1*/
+
+/* private utilities {{{1*/
+
+	/**
+	 * RevList
+	 *
+	 * Common code for using rev-list command
+	 *
+	 * @access private
+	 * @param string $hash hash to list from
+	 * @param integer $count number of results to get
+	 * @param integer $skip number of results to skip
+	 * @param array $args args to give to rev-list
+	 * @return array array of hashes
+	 */
+	private function RevList($hash, $count = 50, $skip = 0, $args = array())
+	{
+		if ($count < 1)
+			return;
+
+		$exe = new GitPHP_GitExe($this);
+
+		$canSkip = true;
+		
+		if ($skip > 0)
+			$canSkip = $exe->CanSkip();
+
+		if ($canSkip) {
+			$args[] = '--max-count=' . $count;
+			if ($skip > 0) {
+				$args[] = '--skip=' . $skip;
+			}
+		} else {
+			$args[] = '--max-count=' . ($count + $skip);
+		}
+
+		$args[] = $hash;
+
+		$revlist = explode("\n", $exe->Execute(GIT_REV_LIST, $args));
+
+		if (!$revlist[count($revlist)-1]) {
+			/* the last newline creates a null entry */
+			array_splice($revlist, -1, 1);
+		}
+
+		if (($skip > 0) && (!$exe->CanSkip())) {
+			return array_slice($revlist, $skip, $count);
+		}
+
+		return $revlist;
+	}
+
+/*}}}1*/
+
+/* static utilities {{{1*/
+
+	/**
+	 * CompareProject
+	 *
+	 * Compares two projects by project name
+	 *
+	 * @access public
+	 * @static
+	 * @param mixed $a first project
+	 * @param mixed $b second project
+	 * @return integer comparison result
+	 */
+	public static function CompareProject($a, $b)
+	{
+		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
+		if ($catCmp !== 0)
+			return $catCmp;
+
+		return strcmp($a->GetProject(), $b->GetProject());
+	}
+
+	/**
+	 * CompareDescription
+	 *
+	 * Compares two projects by description
+	 *
+	 * @access public
+	 * @static
+	 * @param mixed $a first project
+	 * @param mixed $b second project
+	 * @return integer comparison result
+	 */
+	public static function CompareDescription($a, $b)
+	{
+		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
+		if ($catCmp !== 0)
+			return $catCmp;
+
+		return strcmp($a->GetDescription(), $b->GetDescription());
+	}
+
+	/**
+	 * CompareOwner
+	 *
+	 * Compares two projects by owner
+	 *
+	 * @access public
+	 * @static
+	 * @param mixed $a first project
+	 * @param mixed $b second project
+	 * @return integer comparison result
+	 */
+	public static function CompareOwner($a, $b)
+	{
+		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
+		if ($catCmp !== 0)
+			return $catCmp;
+
+		return strcmp($a->GetOwner(), $b->GetOwner());
+	}
+
+	/**
+	 * CompareAge
+	 *
+	 * Compares two projects by age
+	 *
+	 * @access public
+	 * @static
+	 * @param mixed $a first project
+	 * @param mixed $b second project
+	 * @return integer comparison result
+	 */
+	public static function CompareAge($a, $b)
+	{
+		$catCmp = strcmp($a->GetCategory(), $b->GetCategory());
+		if ($catCmp !== 0)
+			return $catCmp;
+
+		if ($a->GetAge() === $b->GetAge())
+			return 0;
+		return ($a->GetAge() < $b->GetAge() ? -1 : 1);
+	}
+
+/*}}}1*/
+
 }
 

--- a/include/git/ProjectList.class.php
+++ b/include/git/ProjectList.class.php
@@ -66,6 +66,7 @@
 		if (self::$instance)
 			return;
 
+
 		if (!empty($file) && is_file($file) && include($file)) {
 			if (isset($git_projects)) {
 				if (is_string($git_projects)) {
@@ -84,8 +85,10 @@
 			}
 		}
 
-		if (!self::$instance)
+		if (!self::$instance) {
+
 			self::$instance = new GitPHP_ProjectListDirectory(GitPHP_Config::GetInstance()->GetValue('projectroot'));
+		}
 
 		if (isset($git_projects_settings) && !$legacy)
 			self::$instance->ApplySettings($git_projects_settings);

--- a/include/git/ProjectListArray.class.php
+++ b/include/git/ProjectListArray.class.php
@@ -74,6 +74,7 @@
 					}
 				}
 			} catch (Exception $e) {
+				GitPHP_Log::GetInstance()->Log($e->getMessage());
 			}
 		}
 	}

--- a/include/git/ProjectListArrayLegacy.class.php
+++ b/include/git/ProjectListArrayLegacy.class.php
@@ -66,6 +66,7 @@
 							$projObj->SetCategory($cat);
 						$this->projects[$ppath] = $projObj;
 					} catch (Exception $e) {
+						GitPHP_Log::GetInstance()->Log($e->getMessage());
 					}
 				}
 			}

--- a/include/git/ProjectListBase.class.php
+++ b/include/git/ProjectListBase.class.php
@@ -295,6 +295,9 @@
 		if (isset($projData['bugurl']) && is_string($projData['bugurl'])) {
 			$projectObj->SetBugUrl($projData['bugurl']);
 		}
+		if (isset($projData['compat'])) {
+			$projectObj->SetCompat($projData['compat']);
+		}
 	}
 
 	/**

--- a/include/git/ProjectListDirectory.class.php
+++ b/include/git/ProjectListDirectory.class.php
@@ -61,7 +61,25 @@
 	 */
 	protected function PopulateProjects()
 	{
+		$key = 'projectdir|' . $this->projectDir . '|projectlist|directory';
+		$cached = GitPHP_Cache::GetObjectCacheInstance()->Get($key);
+		if ($cached && (count($cached) > 0)) {
+			foreach ($cached as $proj) {
+				$this->AddProject($proj);
+			}
+			GitPHP_Log::GetInstance()->Log('Loaded ' . count($this->projects) . ' projects from cache');
+			return;
+		}
+
 		$this->RecurseDir($this->projectDir);
+
+		if (count($this->projects) > 0) {
+			$projects = array();
+			foreach ($this->projects as $proj) {
+				$projects[] = $proj->GetProject();;
+			}
+			GitPHP_Cache::GetObjectCacheInstance()->Set($key, $projects, GitPHP_Config::GetInstance()->GetValue('cachelifetime', 3600));
+		}
 	}
 
 	/**
@@ -76,29 +94,50 @@
 		if (!is_dir($dir))
 			return;
 
+		GitPHP_Log::GetInstance()->Log(sprintf('Searching directory %1$s', $dir));
+
 		if ($dh = opendir($dir)) {
 			$trimlen = strlen($this->projectDir) + 1;
 			while (($file = readdir($dh)) !== false) {
 				$fullPath = $dir . '/' . $file;
 				if ((strpos($file, '.') !== 0) && is_dir($fullPath)) {
 					if (is_file($fullPath . '/HEAD')) {
+						GitPHP_Log::GetInstance()->Log(sprintf('Found project %1$s', $fullPath));
 						$projectPath = substr($fullPath, $trimlen);
-						try {
-							$proj = new GitPHP_Project($this->projectDir, $projectPath);
-							$proj->SetCategory(trim(substr($dir, strlen($this->projectDir)), '/'));
-							if ((!GitPHP_Config::GetInstance()->GetValue('exportedonly', false)) || $proj->GetDaemonEnabled()) {
-								$this->projects[$projectPath] = $proj;
-							}
-						} catch (Exception $e) {
-						}
+						$this->AddProject($projectPath);
 					} else {
 						$this->RecurseDir($fullPath);
 					}
+				} else {
+					GitPHP_Log::GetInstance()->Log(sprintf('Skipping %1$s', $fullPath));
 				}
 			}
 			closedir($dh);
 		}
 	}
 
+	/**
+	 * AddProject
+	 *
+	 * Add project to collection
+	 *
+	 * @access private
+	 */
+	private function AddProject($projectPath)
+	{
+		try {
+			$proj = new GitPHP_Project($this->projectDir, $projectPath);
+			$category = trim(dirname($projectPath));
+			if (!(empty($category) || (strpos($category, '.') === 0))) {
+				$proj->SetCategory($category);
+			}
+			if ((!GitPHP_Config::GetInstance()->GetValue('exportedonly', false)) || $proj->GetDaemonEnabled()) {
+				$this->projects[$projectPath] = $proj;
+			}
+		} catch (Exception $e) {
+			GitPHP_Log::GetInstance()->Log($e->getMessage());
+		}
+	}
+
 }
 

--- a/include/git/ProjectListFile.class.php
+++ b/include/git/ProjectListFile.class.php
@@ -72,7 +72,10 @@
 						}
 						$this->projects[$regs[1]] = $projObj;
 					} catch (Exception $e) {
+						GitPHP_Log::GetInstance()->Log($e->getMessage());
 					}
+				} else {
+					GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not a git project', $projectRoot . $regs[1]));
 				}
 			}
 		}

--- a/include/git/ProjectListScmManager.class.php
+++ b/include/git/ProjectListScmManager.class.php
@@ -67,10 +67,15 @@
 
 		foreach ($xml->repositories->repository as $repository) {
 
-			if ($repository->type != 'git')
+			if ($repository->type != 'git') {
+				GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not a git project', $repository->name));
 				continue;
-			if ($repository->public != 'true')
+			}
+
+			if ($repository->public != 'true') {
+				GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not public', $repository->name));
 				continue;
+			}
 
 			$projName = trim($repository->name);
 			if (empty($projName))
@@ -89,7 +94,10 @@
 					}
 					$this->projects[$projName] = $projObj;
 				} catch (Exception $e) {
+					GitPHP_Log::GetInstance()->Log($e->getMessage());
 				}
+			} else {
+				GitPHP_Log::GetInstance()->Log(sprintf('%1$s is not a git project', $projName));
 			}
 		}
 	}

--- a/include/git/Tag.class.php
+++ b/include/git/Tag.class.php
@@ -330,13 +330,13 @@
 	{
 		$this->dataRead = true;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetProject()->GetCompat()) {
 			$this->ReadDataGit();
 		} else {
 			$this->ReadDataRaw();
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**
@@ -359,7 +359,7 @@
 			$this->object = $this->GetProject()->GetCommit($this->GetHash());
 			$this->commit = $this->object;
 			$this->type = 'commit';
-			GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+			GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 			return;
 		}
 
@@ -450,7 +450,7 @@
 			$this->object = $this->GetProject()->GetCommit($this->GetHash());
 			$this->commit = $this->object;
 			$this->type = 'commit';
-			GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+			GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 			return;
 		}
 
@@ -538,7 +538,7 @@
 			}
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**

--- a/include/git/Tree.class.php
+++ b/include/git/Tree.class.php
@@ -114,13 +114,13 @@
 	{
 		$this->contentsRead = true;
 
-		if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
+		if ($this->GetProject()->GetCompat()) {
 			$this->ReadContentsGit();
 		} else {
 			$this->ReadContentsRaw();
 		}
 
-		GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+		GitPHP_Cache::GetObjectCacheInstance()->Set($this->GetCacheKey(), $this);
 	}
 
 	/**

file:a/index.php -> file:b/index.php
--- a/index.php
+++ b/index.php
@@ -25,6 +25,8 @@
 define('GITPHP_CONTROLLERDIR', GITPHP_INCLUDEDIR . 'controller/');
 define('GITPHP_CACHEDIR', GITPHP_INCLUDEDIR . 'cache/');
 define('GITPHP_LOCALEDIR', GITPHP_BASEDIR . 'locale/');
+
+define('GITPHP_CACHE', GITPHP_BASEDIR . 'cache/');
 
 include_once(GITPHP_INCLUDEDIR . 'version.php');
 
@@ -184,7 +186,7 @@
 if (GitPHP_Log::GetInstance()->GetEnabled()) {
 	$entries = GitPHP_Log::GetInstance()->GetEntries();
 	foreach ($entries as $logline) {
-		echo "\n" . $logline;
+		echo "<br />\n" . $logline;
 	}
 }
 

comments