Improve performance of tag listing
Improve performance of tag listing

This changes the tag / head list functions GetTags and GetHeads to use
for-each-ref to let git do the age sorting internally (which is much
faster) and changes them to build off of the data already loaded by the
main ref list loading function

--- a/include/git/Head.class.php
+++ b/include/git/Head.class.php
@@ -58,7 +58,7 @@
 	public function GetCommit()
 	{
 		if (!$this->commit) {
-			$this->commit = $this->project->GetCommit($this->hash);
+			$this->commit = $this->project->GetCommit($this->GetHash());
 		}
 
 		return $this->commit;

--- a/include/git/Project.class.php
+++ b/include/git/Project.class.php
@@ -132,42 +132,6 @@
 	 * @access protected
 	 */
 	protected $readRefs = false;
-
-	/**
-	 * heads
-	 *
-	 * Stores the heads for the project
-	 *
-	 * @access protected
-	 */
-	protected $heads = array();
-
-	/**
-	 * readHeads
-	 *
-	 * Stores whether heads have been read yet
-	 *
-	 * @access protected
-	 */
-	protected $readHeads = false;
-
-	/**
-	 * tags
-	 *
-	 * Stores the tags for the project
-	 *
-	 * @access protected
-	 */
-	protected $tags = array();
-
-	/**
-	 * readTags
-	 *
-	 * Stores whether tags have been read yet
-	 *
-	 * @access protected
-	 */
-	protected $readTags = false;
 
 	/**
 	 * cloneUrl
@@ -784,17 +748,40 @@
 	/**
 	 * GetTags
 	 *
-	 * Gets list of tags for this project
-	 *
-	 * @access public
+	 * Gets list of tags for this project by age descending
+	 *
+	 * @access public
+	 * @param integer $count number of tags to load
 	 * @return array array of tags
 	 */
-	public function GetTags()
-	{
-		if (!$this->readTags)
-			$this->ReadTagList();
-
-		return $this->tags;
+	public function GetTags($count = 0)
+	{
+		if (!$this->readRefs)
+			$this->ReadRefList();
+
+		$exe = new GitPHP_GitExe($this);
+		$args = array();
+		$args[] = '--sort=-creatordate';
+		$args[] = '--format="%(refname)"';
+		if ($count > 0) {
+			$args[] = '--count=' . $count;
+		}
+		$args[] = '--';
+		$args[] = 'refs/tags';
+		$ret = $exe->Execute(GIT_FOR_EACH_REF, $args);
+		unset($exe);
+
+		$lines = explode("\n", $ret);
+
+		$tags = array();
+
+		foreach ($lines as $ref) {
+			if (isset($this->refs[$ref])) {
+				$tags[] = $this->refs[$ref];
+			}
+		}
+
+		return $tags;
 	}
 
 	/**
@@ -811,11 +798,13 @@
 		if (empty($tag))
 			return null;
 
-		if (!isset($this->tags[$tag])) {
-			$this->tags[$tag] = $this->LoadTag($tag);
-		}
-
-		return $this->tags[$tag];
+		$key = 'refs/tags/' . $tag;
+
+		if (!isset($this->refs[$key])) {
+			$this->refs[$key] = $this->LoadTag($key);
+		}
+
+		return $this->refs[$key];
 	}
 
 	/**
@@ -842,59 +831,42 @@
 	}
 
 	/**
-	 * ReadTagList
-	 *
-	 * Reads tag list
-	 *
-	 * @access protected
-	 */
-	protected function ReadTagList()
-	{
-		$this->readTags = true;
+	 * GetHeads
+	 *
+	 * Gets list of heads for this project by age descending
+	 *
+	 * @access public
+	 * @param integer $count number of tags to load
+	 * @return array array of heads
+	 */
+	public function GetHeads($count = 0)
+	{
+		if (!$this->readRefs)
+			$this->ReadRefList();
 
 		$exe = new GitPHP_GitExe($this);
 		$args = array();
-		$args[] = '--tags';
-		$args[] = '--dereference';
-		$ret = $exe->Execute(GIT_SHOW_REF, $args);
+		$args[] = '--sort=-committerdate';
+		$args[] = '--format="%(refname)"';
+		if ($count > 0) {
+			$args[] = '--count=' . $count;
+		}
+		$args[] = '--';
+		$args[] = 'refs/heads';
+		$ret = $exe->Execute(GIT_FOR_EACH_REF, $args);
 		unset($exe);
 
 		$lines = explode("\n", $ret);
 
-		foreach ($lines as $line) {
-			if (preg_match('/^([0-9a-fA-F]{40}) refs\/tags\/([^^]+)(\^{})?$/', $line, $regs)) {
-				try {
-					if ((!empty($regs[3])) && ($regs[3] == '^{}')) {
-						$derefCommit = $this->GetCommit($regs[1]);
-						if ($derefCommit && isset($this->tags[$regs[2]])) {
-							$this->tags[$regs[2]]->SetCommit($derefCommit);
-						}
-							
-					} else if (!isset($this->tags[$regs[2]])) {
-							$this->tags[$regs[2]] = $this->LoadTag($regs[2], $regs[1]);
-					}
-				} catch (Exception $e) {
-				}
+		$heads = array();
+
+		foreach ($lines as $ref) {
+			if (isset($this->refs[$ref])) {
+				$heads[] = $this->refs[$ref];
 			}
 		}
 
-		uasort($this->tags, array('GitPHP_Tag', 'CompareAge'));
-	}
-
-	/**
-	 * GetHeads
-	 *
-	 * Gets list of heads for this project
-	 *
-	 * @access public
-	 * @return array array of heads
-	 */
-	public function GetHeads()
-	{
-		if (!$this->readHeads)
-			$this->ReadHeadList();
-
-		return $this->heads;
+		return $heads;
 	}
 
 	/**
@@ -911,48 +883,13 @@
 		if (empty($head))
 			return null;
 
-		if (!$this->readHeads)
-			$this->ReadHeadList();
-
-		foreach ($this->heads as $h) {
-			if ($h->GetName() === $head) {
-				return $h;
-			}
-		}
-		
-		return null;
-	}
-
-	/**
-	 * ReadHeadList
-	 *
-	 * Reads head list
-	 *
-	 * @access protected
-	 */
-	protected function ReadHeadList()
-	{
-		$this->readHeads = true;
-
-		$exe = new GitPHP_GitExe($this);
-		$args = array();
-		$args[] = '--heads';
-		$ret = $exe->Execute(GIT_SHOW_REF, $args);
-		unset($exe);
-
-		$lines = explode("\n", $ret);
-
-		foreach ($lines as $line) {
-			if (preg_match('/^([0-9a-fA-F]{40}) refs\/heads\/(.+)$/', $line, $regs)) {
-				try {
-					$this->heads[] = new GitPHP_Head($this, $regs[2], $regs[1]);
-				} catch (Exception $e) {
-				}
-			}
-		}
-
-		usort($this->heads, array('GitPHP_Head', 'CompareAge'));
-
+		$key = 'refs/heads/' . $head;
+
+		if (!isset($this->refs[$key])) {
+			$this->refs[$key] = new GitPHP_Head($this, $head);
+		}
+
+		return $this->refs[$key];
 	}
 
 	/**

--- a/include/git/Tag.class.php
+++ b/include/git/Tag.class.php
@@ -305,7 +305,7 @@
 		if (!$this->object)
 			return true;
 
-		return $this->object->GetHash() === $this->hash;
+		return $this->object->GetHash() === $this->GetHash();
 	}
 
 	/**
@@ -322,12 +322,12 @@
 		$exe = new GitPHP_GitExe($this->GetProject());
 		$args = array();
 		$args[] = '-t';
-		$args[] = $this->hash;
+		$args[] = $this->GetHash();
 		$ret = trim($exe->Execute(GIT_CAT_FILE, $args));
 		
 		if ($ret === 'commit') {
 			/* light tag */
-			$this->object = $this->GetProject()->GetCommit($this->hash);
+			$this->object = $this->GetProject()->GetCommit($this->GetHash());
 			$this->commit = $this->object;
 			$this->type = 'commit';
 			GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);

comments