Object cache support for tags
Also improves performance by not requiring the entire tag list to be
loaded if a single tag object is needed. The full list is loaded if all
tags are needed (and it doesn't reload already loaded tags).
--- a/include/controller/Controller_Tag.class.php
+++ b/include/controller/Controller_Tag.class.php
@@ -112,7 +112,7 @@
$head = $this->project->GetHeadCommit();
$this->tpl->assign('head', $head);
- $tag = new GitPHP_Tag($this->project, $this->params['hash']);
+ $tag = $this->project->GetTag($this->params['hash']);
$this->tpl->assign("tag", $tag);
}
--- a/include/git/Project.class.php
+++ b/include/git/Project.class.php
@@ -734,13 +734,34 @@
if (empty($tag))
return null;
- if (!$this->readTags)
- $this->ReadTagList();
-
- if (isset($this->tags[$tag]))
- return $this->tags[$tag];
-
- return null;
+ if (!isset($this->tags[$tag])) {
+ $this->LoadTag($tag);
+ }
+
+ return $this->tags[$tag];
+ }
+
+ /**
+ * LoadTag
+ *
+ * Attempts to load a cached tag, or creates a new object
+ *
+ * @access private
+ * @param string $tag tag to find
+ * @return mixed tag object
+ */
+ private function LoadTag($tag)
+ {
+ if (empty($tag))
+ return;
+
+ $cacheKey = 'project|' . $this->project . '|tag|' . $tag;
+ $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
+ if ($cached) {
+ $this->tags[$tag] = $cached;
+ } else {
+ $this->tags[$tag] = new GitPHP_Tag($this, $tag);
+ }
}
/**
@@ -772,8 +793,15 @@
$this->tags[$regs[2]]->SetCommit($derefCommit);
}
- } else {
- $this->tags[$regs[2]] = new GitPHP_Tag($this, $regs[2], $regs[1]);
+ } else if (!isset($this->tags[$regs[2]])) {
+ $this->LoadTag($regs[2]);
+ if (isset($this->tags[$regs[2]])) {
+ $tagHash = $this->tags[$regs[2]]->GetHash();
+ if (empty($tagHash)) {
+ // New non-cached tag object
+ $this->tags[$regs[2]]->SetHash($regs[1]);
+ }
+ }
}
} catch (Exception $e) {
}
--- a/include/git/Ref.class.php
+++ b/include/git/Ref.class.php
@@ -18,7 +18,7 @@
* @package GitPHP
* @subpackage Git
*/
-class GitPHP_Ref extends GitPHP_GitObject
+abstract class GitPHP_Ref extends GitPHP_GitObject
{
/**
@@ -59,9 +59,23 @@
$this->refName = $refName;
if (!empty($refHash)) {
$this->SetHash($refHash);
- } else {
+ }
+ }
+
+ /**
+ * GetHash
+ *
+ * Gets the hash for this ref (overrides base)
+ *
+ * @access public
+ * @return string object hash
+ */
+ public function GetHash()
+ {
+ if (empty($this->hash))
$this->FindHash();
- }
+
+ return $this->hash;
}
/**
@@ -74,7 +88,7 @@
*/
protected function FindHash()
{
- $exe = new GitPHP_GitExe($this->project);
+ $exe = new GitPHP_GitExe($this->GetProject());
$args = array();
$args[] = '--hash';
$args[] = '--verify';
@@ -88,7 +102,7 @@
}
/**
- * GetName()
+ * GetName
*
* Gets the ref name
*
@@ -136,7 +150,22 @@
*/
public function GetFullPath()
{
- return $this->project->GetPath() . '/' . $this->GetRefPath();
+ return $this->GetProject()->GetPath() . '/' . $this->GetRefPath();
+ }
+
+ /**
+ * __sleep
+ *
+ * Called to prepare the object for serialization
+ *
+ * @access public
+ * @return array list of properties to serialize
+ */
+ public function __sleep()
+ {
+ $properties = array('refName', 'refDir');
+
+ return array_merge($properties, parent::__sleep());
}
}
--- a/include/git/Tag.class.php
+++ b/include/git/Tag.class.php
@@ -95,6 +95,24 @@
protected $comment = array();
/**
+ * objectReferenced
+ *
+ * Stores whether the object has been referenced into a pointer
+ *
+ * @access private
+ */
+ private $objectReferenced = false;
+
+ /**
+ * commitReferenced
+ *
+ * Stores whether the commit has been referenced into a pointer
+ *
+ * @access private
+ */
+ private $commitReferenced = false;
+
+ /**
* __construct
*
* Instantiates tag
@@ -124,6 +142,9 @@
if (!$this->dataRead)
$this->ReadData();
+ if ($this->objectReferenced)
+ $this->DereferenceObject();
+
return $this->object;
}
@@ -140,8 +161,11 @@
if (!$this->dataRead)
$this->ReadData();
+ if ($this->commitReferenced)
+ $this->DereferenceCommit();
+
if (!$this->commit)
- $this->LoadCommit();
+ $this->ReadCommit();
return $this->commit;
}
@@ -156,7 +180,11 @@
*/
public function SetCommit($commit)
{
- $this->commit = $commit;
+ if ($this->commitReferenced)
+ $this->DereferenceCommit();
+
+ if (!$this->commit)
+ $this->commit = $commit;
}
/**
@@ -271,6 +299,9 @@
if (!$this->dataRead)
$this->ReadData();
+ if ($this->objectReferenced)
+ $this->DereferenceObject();
+
if (!$this->object)
return true;
@@ -288,7 +319,7 @@
{
$this->dataRead = true;
- $exe = new GitPHP_GitExe($this->project);
+ $exe = new GitPHP_GitExe($this->GetProject());
$args = array();
$args[] = '-t';
$args[] = $this->hash;
@@ -296,9 +327,10 @@
if ($ret === 'commit') {
/* light tag */
- $this->object = $this->project->GetCommit($this->hash);
+ $this->object = $this->GetProject()->GetCommit($this->hash);
$this->commit = $this->object;
$this->type = 'commit';
+ GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
return;
}
@@ -347,13 +379,13 @@
switch ($this->type) {
case 'commit':
try {
- $this->object = $this->project->GetCommit($objectHash);
- $this->commit = $this->GetObject();
+ $this->object = $this->GetProject()->GetCommit($objectHash);
+ $this->commit = $this->object;
} catch (Exception $e) {
}
break;
case 'tag':
- $exe = new GitPHP_GitExe($this->project);
+ $exe = new GitPHP_GitExe($this->GetProject());
$args = array();
$args[] = 'tag';
$args[] = $objectHash;
@@ -363,12 +395,16 @@
foreach ($lines as $i => $line) {
if (preg_match('/^tag (.+)$/', $line, $regs)) {
$name = trim($regs[1]);
- $this->object = new GitPHP_Tag($this->project, $name, $objectHash);
+ $this->object = $this->GetProject()->GetTag($name);
+ if ($this->object) {
+ $this->object->SetHash($objectHash);
+ }
}
}
break;
}
+ GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
}
/**
@@ -392,10 +428,155 @@
foreach ($lines as $line) {
if (preg_match('/^([0-9a-fA-F]{40}) refs\/tags\/' . preg_quote($this->refName) . '(\^{})$/', $line, $regs)) {
- $this->commit = $this->project->GetCommit($regs[1]);
+ $this->commit = $this->GetProject()->GetCommit($regs[1]);
+ return;
}
}
- }
+
+ GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
+ }
+
+ /**
+ * ReferenceObject
+ *
+ * Turns the object into a reference pointer
+ *
+ * @access private
+ */
+ private function ReferenceObject()
+ {
+ if ($this->objectReferenced)
+ return;
+
+ if (!$this->object)
+ return;
+
+ if ($this->type == 'commit') {
+ $this->object = $this->object->GetHash();
+ } else if ($this->type == 'tag') {
+ $this->object = $this->object->GetName();
+ }
+
+ $this->objectReferenced = true;
+ }
+
+ /**
+ * DereferenceObject
+ *
+ * Turns the object pointer back into an object
+ *
+ * @access private
+ */
+ private function DereferenceObject()
+ {
+ if (!$this->objectReferenced)
+ return;
+
+ if (empty($this->object))
+ return;
+
+ if ($this->type == 'commit') {
+ $this->object = $this->GetProject()->GetCommit($this->object);
+ } else if ($this->type == 'tag') {
+ $this->object = $this->GetProject()->GetTag($this->object);
+ }
+
+ $this->objectReferenced = false;
+ }
+
+ /**
+ * ReferenceCommit
+ *
+ * Turns the commit into a reference pointer
+ *
+ * @access private
+ */
+ private function ReferenceCommit()
+ {
+ if ($this->commitReferenced)
+ return;
+
+ if (!$this->commit)
+ return;
+
+ $this->commit = $this->commit->GetHash();
+
+ $this->commitReferenced = true;
+ }
+
+ /**
+ * DereferenceCommit
+ *
+ * Turns the commit pointer back into an object
+ *
+ * @access private
+ */
+ private function DereferenceCommit()
+ {
+ if (!$this->commitReferenced)
+ return;
+
+ if (empty($this->commit))
+ return;
+
+ if ($this->type == 'commit') {
+ $obj = $this->GetObject();
+ if ($obj && ($obj->GetHash() == $this->commit)) {
+ /*
+ * Light tags are type commit and the commit
+ * and object are the same, in which case
+ * no need to fetch the object again
+ */
+ $this->commit = $obj;
+ $this->commitReferenced = false;
+ return;
+ }
+ }
+
+ $this->commit = $this->GetProject()->GetCommit($this->commit);
+
+ $this->commitReferenced = false;
+ }
+
+ /**
+ * __sleep
+ *
+ * Called to prepare the object for serialization
+ *
+ * @access public
+ * @return array list of properties to serialize
+ */
+ public function __sleep()
+ {
+ if (!$this->objectReferenced)
+ $this->ReferenceObject();
+
+ if (!$this->commitReferenced)
+ $this->ReferenceCommit();
+
+ $properties = array('dataRead', 'object', 'commit', 'type', 'tagger', 'taggerEpoch', 'taggerTimezone', 'comment', 'objectReferenced', 'commitReferenced');
+ return array_merge($properties, parent::__sleep());
+ }
+
+ /**
+ * GetCacheKey
+ *
+ * Gets the cache key to use for this object
+ *
+ * @access public
+ * @return string cache key
+ */
+ public function GetCacheKey()
+ {
+ $key = parent::GetCacheKey();
+ if (!empty($key))
+ $key .= '|';
+
+ $key .= 'tag|' . $this->refName;
+
+ return $key;
+ }
+
/**
* CompareAge