Load tag data from pack
Load tag data from pack

<?php <?php
/** /**
* GitPHP Tag * GitPHP Tag
* *
* Represents a single tag object * Represents a single tag object
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php'); require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Ref.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Ref.class.php');
   
/** /**
* Tag class * Tag class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_Tag extends GitPHP_Ref class GitPHP_Tag extends GitPHP_Ref
{ {
/** /**
* dataRead * dataRead
* *
* Indicates whether data for this tag has been read * Indicates whether data for this tag has been read
* *
* @access protected * @access protected
*/ */
protected $dataRead = false; protected $dataRead = false;
   
/** /**
* object * object
* *
* Stores the object internally * Stores the object internally
* *
* @access protected * @access protected
*/ */
protected $object; protected $object;
   
/** /**
* commit * commit
* *
* Stores the commit internally * Stores the commit internally
* *
* @access protected * @access protected
*/ */
protected $commit; protected $commit;
   
/** /**
* type * type
* *
* Stores the type internally * Stores the type internally
* *
* @access protected * @access protected
*/ */
protected $type; protected $type;
   
/** /**
* tagger * tagger
* *
* Stores the tagger internally * Stores the tagger internally
* *
* @access protected * @access protected
*/ */
protected $tagger; protected $tagger;
   
/** /**
* taggerEpoch * taggerEpoch
* *
* Stores the tagger epoch internally * Stores the tagger epoch internally
* *
* @access protected * @access protected
*/ */
protected $taggerEpoch; protected $taggerEpoch;
   
/** /**
* taggerTimezone * taggerTimezone
* *
* Stores the tagger timezone internally * Stores the tagger timezone internally
* *
* @access protected * @access protected
*/ */
protected $taggerTimezone; protected $taggerTimezone;
   
/** /**
* comment * comment
* *
* Stores the tag comment internally * Stores the tag comment internally
* *
* @access protected * @access protected
*/ */
protected $comment = array(); protected $comment = array();
   
/** /**
* objectReferenced * objectReferenced
* *
* Stores whether the object has been referenced into a pointer * Stores whether the object has been referenced into a pointer
* *
* @access private * @access private
*/ */
private $objectReferenced = false; private $objectReferenced = false;
   
/** /**
* commitReferenced * commitReferenced
* *
* Stores whether the commit has been referenced into a pointer * Stores whether the commit has been referenced into a pointer
* *
* @access private * @access private
*/ */
private $commitReferenced = false; private $commitReferenced = false;
   
/** /**
* __construct * __construct
* *
* Instantiates tag * Instantiates tag
* *
* @access public * @access public
* @param mixed $project the project * @param mixed $project the project
* @param string $tag tag name * @param string $tag tag name
* @param string $tagHash tag hash * @param string $tagHash tag hash
* @return mixed tag object * @return mixed tag object
* @throws Exception exception on invalid tag or hash * @throws Exception exception on invalid tag or hash
*/ */
public function __construct($project, $tag, $tagHash = '') public function __construct($project, $tag, $tagHash = '')
{ {
parent::__construct($project, 'tags', $tag, $tagHash); parent::__construct($project, 'tags', $tag, $tagHash);
} }
   
/** /**
* GetObject * GetObject
* *
* Gets the object this tag points to * Gets the object this tag points to
* *
* @access public * @access public
* @return mixed object for this tag * @return mixed object for this tag
*/ */
public function GetObject() public function GetObject()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
if ($this->objectReferenced) if ($this->objectReferenced)
$this->DereferenceObject(); $this->DereferenceObject();
   
return $this->object; return $this->object;
} }
   
/** /**
* GetCommit * GetCommit
* *
* Gets the commit this tag points to * Gets the commit this tag points to
* *
* @access public * @access public
* @return mixed commit for this tag * @return mixed commit for this tag
*/ */
public function GetCommit() public function GetCommit()
{ {
if ($this->commitReferenced) if ($this->commitReferenced)
$this->DereferenceCommit(); $this->DereferenceCommit();
   
if ($this->commit) if ($this->commit)
return $this->commit; return $this->commit;
   
if (!$this->dataRead) { if (!$this->dataRead) {
$this->ReadData(); $this->ReadData();
if ($this->commitReferenced) if ($this->commitReferenced)
$this->DereferenceCommit(); $this->DereferenceCommit();
} }
   
return $this->commit; return $this->commit;
} }
   
/** /**
* SetCommit * SetCommit
* *
* Sets the commit this tag points to * Sets the commit this tag points to
* *
* @access public * @access public
* @param mixed $commit commit object * @param mixed $commit commit object
*/ */
public function SetCommit($commit) public function SetCommit($commit)
{ {
if ($this->commitReferenced) if ($this->commitReferenced)
$this->DereferenceCommit(); $this->DereferenceCommit();
   
if (!$this->commit) if (!$this->commit)
$this->commit = $commit; $this->commit = $commit;
} }
   
/** /**
* GetType * GetType
* *
* Gets the tag type * Gets the tag type
* *
* @access public * @access public
* @return string tag type * @return string tag type
*/ */
public function GetType() public function GetType()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->type; return $this->type;
} }
   
/** /**
* GetTagger * GetTagger
* *
* Gets the tagger * Gets the tagger
* *
* @access public * @access public
* @return string tagger * @return string tagger
*/ */
public function GetTagger() public function GetTagger()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->tagger; return $this->tagger;
} }
   
/** /**
* GetTaggerEpoch * GetTaggerEpoch
* *
* Gets the tagger epoch * Gets the tagger epoch
* *
* @access public * @access public
* @return string tagger epoch * @return string tagger epoch
*/ */
public function GetTaggerEpoch() public function GetTaggerEpoch()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->taggerEpoch; return $this->taggerEpoch;
} }
   
/** /**
* GetTaggerLocalEpoch * GetTaggerLocalEpoch
* *
* Gets the tagger local epoch * Gets the tagger local epoch
* *
* @access public * @access public
* @return string tagger local epoch * @return string tagger local epoch
*/ */
public function GetTaggerLocalEpoch() public function GetTaggerLocalEpoch()
{ {
$epoch = $this->GetTaggerEpoch(); $epoch = $this->GetTaggerEpoch();
$tz = $this->GetTaggerTimezone(); $tz = $this->GetTaggerTimezone();
if (preg_match('/^([+\-][0-9][0-9])([0-9][0-9])$/', $tz, $regs)) { if (preg_match('/^([+\-][0-9][0-9])([0-9][0-9])$/', $tz, $regs)) {
$local = $epoch + ((((int)$regs[1]) + ($regs[2]/60)) * 3600); $local = $epoch + ((((int)$regs[1]) + ($regs[2]/60)) * 3600);
return $local; return $local;
} }
return $epoch; return $epoch;
} }
   
/** /**
* GetTaggerTimezone * GetTaggerTimezone
* *
* Gets the tagger timezone * Gets the tagger timezone
* *
* @access public * @access public
* @return string tagger timezone * @return string tagger timezone
*/ */
public function GetTaggerTimezone() public function GetTaggerTimezone()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->taggerTimezone; return $this->taggerTimezone;
} }
   
/** /**
* GetComment * GetComment
* *
* Gets the tag comment * Gets the tag comment
* *
* @access public * @access public
* @return array comment lines * @return array comment lines
*/ */
public function GetComment() public function GetComment()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->comment; return $this->comment;
} }
   
/** /**
* LightTag * LightTag
* *
* Tests if this is a light tag (tag without tag object) * Tests if this is a light tag (tag without tag object)
* *
* @access public * @access public
* @return boolean true if tag is light (has no object) * @return boolean true if tag is light (has no object)
*/ */
public function LightTag() public function LightTag()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
if ($this->objectReferenced) if ($this->objectReferenced)
$this->DereferenceObject(); $this->DereferenceObject();
   
if (!$this->object) if (!$this->object)
return true; return true;
   
return $this->object->GetHash() === $this->GetHash(); return $this->object->GetHash() === $this->GetHash();
} }
   
/** /**
* ReadData * ReadData
* *
* Reads the tag data * Reads the tag data
* *
* @access protected * @access protected
*/ */
protected function ReadData() protected function ReadData()
{ {
$this->dataRead = true; $this->dataRead = true;
   
  if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
  $this->ReadDataGit();
  } else {
  $this->ReadDataRaw();
  }
   
  GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
  }
   
  /**
  * ReadDataGit
  *
  * Reads the tag data using the git executable
  *
  * @access private
  */
  private function ReadDataGit()
  {
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
$args = array(); $args = array();
$args[] = '-t'; $args[] = '-t';
$args[] = $this->GetHash(); $args[] = $this->GetHash();
$ret = trim($exe->Execute(GIT_CAT_FILE, $args)); $ret = trim($exe->Execute(GIT_CAT_FILE, $args));
if ($ret === 'commit') { if ($ret === 'commit') {
/* light tag */ /* light tag */
$this->object = $this->GetProject()->GetCommit($this->GetHash()); $this->object = $this->GetProject()->GetCommit($this->GetHash());
$this->commit = $this->object; $this->commit = $this->object;
$this->type = 'commit'; $this->type = 'commit';
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this); GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
return; return;
} }
   
/* get data from tag object */ /* get data from tag object */
$args = array(); $args = array();
$args[] = 'tag'; $args[] = 'tag';
$args[] = $this->GetName(); $args[] = $this->GetName();
$ret = $exe->Execute(GIT_CAT_FILE, $args); $ret = $exe->Execute(GIT_CAT_FILE, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
if (!isset($lines[0])) if (!isset($lines[0]))
return; return;
   
$objectHash = null; $objectHash = null;
   
$readInitialData = false; $readInitialData = false;
foreach ($lines as $i => $line) { foreach ($lines as $i => $line) {
if (!$readInitialData) { if (!$readInitialData) {
if (preg_match('/^object ([0-9a-fA-F]{40})$/', $line, $regs)) { if (preg_match('/^object ([0-9a-fA-F]{40})$/', $line, $regs)) {
$objectHash = $regs[1]; $objectHash = $regs[1];
continue; continue;
} else if (preg_match('/^type (.+)$/', $line, $regs)) { } else if (preg_match('/^type (.+)$/', $line, $regs)) {
$this->type = $regs[1]; $this->type = $regs[1];
continue; continue;
} else if (preg_match('/^tag (.+)$/', $line, $regs)) { } else if (preg_match('/^tag (.+)$/', $line, $regs)) {
continue; continue;
} else if (preg_match('/^tagger (.*) ([0-9]+) (.*)$/', $line, $regs)) { } else if (preg_match('/^tagger (.*) ([0-9]+) (.*)$/', $line, $regs)) {
$this->tagger = $regs[1]; $this->tagger = $regs[1];
$this->taggerEpoch = $regs[2]; $this->taggerEpoch = $regs[2];
$this->taggerTimezone = $regs[3]; $this->taggerTimezone = $regs[3];
continue; continue;
} }
} }
   
$trimmed = trim($line); $trimmed = trim($line);
   
if ((strlen($trimmed) > 0) || ($readInitialData === true)) { if ((strlen($trimmed) > 0) || ($readInitialData === true)) {
$this->comment[] = $line; $this->comment[] = $line;
} }
$readInitialData = true; $readInitialData = true;
   
} }
   
switch ($this->type) { switch ($this->type) {
case 'commit': case 'commit':
try { try {
$this->object = $this->GetProject()->GetCommit($objectHash); $this->object = $this->GetProject()->GetCommit($objectHash);
$this->commit = $this->object; $this->commit = $this->object;
} catch (Exception $e) { } catch (Exception $e) {
} }
break; break;
case 'tag': case 'tag':
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
$args = array(); $args = array();
$args[] = 'tag'; $args[] = 'tag';
$args[] = $objectHash; $args[] = $objectHash;
$ret = $exe->Execute(GIT_CAT_FILE, $args); $ret = $exe->Execute(GIT_CAT_FILE, $args);
unset($exe); unset($exe);
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
foreach ($lines as $i => $line) { foreach ($lines as $i => $line) {
if (preg_match('/^tag (.+)$/', $line, $regs)) { if (preg_match('/^tag (.+)$/', $line, $regs)) {
$name = trim($regs[1]); $name = trim($regs[1]);
$this->object = $this->GetProject()->GetTag($name); $this->object = $this->GetProject()->GetTag($name);
if ($this->object) { if ($this->object) {
$this->object->SetHash($objectHash); $this->object->SetHash($objectHash);
} }
} }
} }
break; break;
} }
  }
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);  
  /**
  * ReadDataRaw
  *
  * Reads the tag data using the raw git object
  *
  * @access private
  */
  private function ReadDataRaw()
  {
  $data = $this->GetProject()->GetObject($this->GetHash(), $type);
   
  if ($type == GitPHP_Pack::OBJ_COMMIT) {
  /* light tag */
  $this->object = $this->GetProject()->GetCommit($this->GetHash());
  $this->commit = $this->object;
  $this->type = 'commit';
  GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
  return;
  }
   
  $lines = explode("\n", $data);
   
  if (!isset($lines[0]))
  return;
   
  $objectHash = null;
   
  $readInitialData = false;
  foreach ($lines as $i => $line) {
  if (!$readInitialData) {
  if (preg_match('/^object ([0-9a-fA-F]{40})$/', $line, $regs)) {
  $objectHash = $regs[1];
  continue;
  } else if (preg_match('/^type (.+)$/', $line, $regs)) {
  $this->type = $regs[1];
  continue;
  } else if (preg_match('/^tag (.+)$/', $line, $regs)) {
  continue;
  } else if (preg_match('/^tagger (.*) ([0-9]+) (.*)$/', $line, $regs)) {
  $this->tagger = $regs[1];
  $this->taggerEpoch = $regs[2];
  $this->taggerTimezone = $regs[3];
  continue;
  }
  }
   
  $trimmed = trim($line);
   
  if ((strlen($trimmed) > 0) || ($readInitialData === true)) {
  $this->comment[] = $line;
  }
  $readInitialData = true;
  }
   
  switch ($this->type) {
  case 'commit':
  try {
  $this->object = $this->GetProject()->GetCommit($objectHash);
  $this->commit = $this->object;
  } catch (Exception $e) {
  }
  break;
  case 'tag':
  $objectData = $this->GetProject()->GetObject($objectHash);
  $lines = explode("\n", $objectData);
  foreach ($lines as $i => $line) {
  if (preg_match('/^tag (.+)$/', $line, $regs)) {
  $name = trim($regs[1]);
  $this->object = $this->GetProject()->GetTag($name);
  if ($this->object) {
  $this->object->SetHash($objectHash);
  }
  }
  }
  break;
  }
} }
   
/** /**
* ReadCommit * ReadCommit
* *
* Attempts to dereference the commit for this tag * Attempts to dereference the commit for this tag
* *
* @access private * @access private
*/ */
private function ReadCommit() private function ReadCommit()
{ {
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
$args = array(); $args = array();
$args[] = '--tags'; $args[] = '--tags';
$args[] = '--dereference'; $args[] = '--dereference';
$args[] = $this->refName; $args[] = $this->refName;
$ret = $exe->Execute(GIT_SHOW_REF, $args); $ret = $exe->Execute(GIT_SHOW_REF, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match('/^([0-9a-fA-F]{40}) refs\/tags\/' . preg_quote($this->refName) . '(\^{})$/', $line, $regs)) { if (preg_match('/^([0-9a-fA-F]{40}) refs\/tags\/' . preg_quote($this->refName) . '(\^{})$/', $line, $regs)) {
$this->commit = $this->GetProject()->GetCommit($regs[1]); $this->commit = $this->GetProject()->GetCommit($regs[1]);
return; return;
} }
} }
   
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this); GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
} }
   
/** /**
* ReferenceObject * ReferenceObject
* *
* Turns the object into a reference pointer * Turns the object into a reference pointer
* *
* @access private * @access private
*/ */
private function ReferenceObject() private function ReferenceObject()
{ {
if ($this->objectReferenced) if ($this->objectReferenced)
return; return;
   
if (!$this->object) if (!$this->object)
return; return;
   
if ($this->type == 'commit') { if ($this->type == 'commit') {
$this->object = $this->object->GetHash(); $this->object = $this->object->GetHash();
} else if ($this->type == 'tag') { } else if ($this->type == 'tag') {
$this->object = $this->object->GetName(); $this->object = $this->object->GetName();
} }
   
$this->objectReferenced = true; $this->objectReferenced = true;
} }
   
/** /**
* DereferenceObject * DereferenceObject
* *
* Turns the object pointer back into an object * Turns the object pointer back into an object
* *
* @access private * @access private
*/ */
private function DereferenceObject() private function DereferenceObject()
{ {
if (!$this->objectReferenced) if (!$this->objectReferenced)
return; return;
   
if (empty($this->object)) if (empty($this->object))
return; return;
   
if ($this->type == 'commit') { if ($this->type == 'commit') {
$this->object = $this->GetProject()->GetCommit($this->object); $this->object = $this->GetProject()->GetCommit($this->object);
} else if ($this->type == 'tag') { } else if ($this->type == 'tag') {
$this->object = $this->GetProject()->GetTag($this->object); $this->object = $this->GetProject()->GetTag($this->object);
} }
   
$this->objectReferenced = false; $this->objectReferenced = false;
} }
   
/** /**
* ReferenceCommit * ReferenceCommit
* *
* Turns the commit into a reference pointer * Turns the commit into a reference pointer
* *
* @access private * @access private
*/ */
private function ReferenceCommit() private function ReferenceCommit()
{ {
if ($this->commitReferenced) if ($this->commitReferenced)
return; return;
   
if (!$this->commit) if (!$this->commit)
return; return;
   
$this->commit = $this->commit->GetHash(); $this->commit = $this->commit->GetHash();
   
$this->commitReferenced = true; $this->commitReferenced = true;
} }
   
/** /**
* DereferenceCommit * DereferenceCommit
* *
* Turns the commit pointer back into an object * Turns the commit pointer back into an object
* *
* @access private * @access private
*/ */
private function DereferenceCommit() private function DereferenceCommit()
{ {
if (!$this->commitReferenced) if (!$this->commitReferenced)
return; return;
   
if (empty($this->commit)) if (empty($this->commit))
return; return;
   
if ($this->type == 'commit') { if ($this->type == 'commit') {
$obj = $this->GetObject(); $obj = $this->GetObject();
if ($obj && ($obj->GetHash() == $this->commit)) { if ($obj && ($obj->GetHash() == $this->commit)) {
/* /*
* Light tags are type commit and the commit * Light tags are type commit and the commit
* and object are the same, in which case * and object are the same, in which case
* no need to fetch the object again * no need to fetch the object again
*/ */
$this->commit = $obj; $this->commit = $obj;
$this->commitReferenced = false; $this->commitReferenced = false;
return; return;
} }
} }
   
$this->commit = $this->GetProject()->GetCommit($this->commit); $this->commit = $this->GetProject()->GetCommit($this->commit);
   
$this->commitReferenced = false; $this->commitReferenced = false;
} }
   
/** /**
* __sleep * __sleep
* *
* Called to prepare the object for serialization * Called to prepare the object for serialization
* *
* @access public * @access public
* @return array list of properties to serialize * @return array list of properties to serialize
*/ */
public function __sleep() public function __sleep()
{ {
if (!$this->objectReferenced) if (!$this->objectReferenced)
$this->ReferenceObject(); $this->ReferenceObject();
   
if (!$this->commitReferenced) if (!$this->commitReferenced)
$this->ReferenceCommit(); $this->ReferenceCommit();
   
$properties = array('dataRead', 'object', 'commit', 'type', 'tagger', 'taggerEpoch', 'taggerTimezone', 'comment', 'objectReferenced', 'commitReferenced'); $properties = array('dataRead', 'object', 'commit', 'type', 'tagger', 'taggerEpoch', 'taggerTimezone', 'comment', 'objectReferenced', 'commitReferenced');
return array_merge($properties, parent::__sleep()); return array_merge($properties, parent::__sleep());
} }
   
/** /**
* GetCacheKey * GetCacheKey
* *
* Gets the cache key to use for this object * Gets the cache key to use for this object
* *
* @access public * @access public
* @return string cache key * @return string cache key
*/ */
public function GetCacheKey() public function GetCacheKey()
{ {
$key = parent::GetCacheKey(); $key = parent::GetCacheKey();
if (!empty($key)) if (!empty($key))
$key .= '|'; $key .= '|';
   
$key .= 'tag|' . $this->refName; $key .= 'tag|' . $this->refName;
return $key; return $key;
} }
   
   
/** /**
* CompareAge * CompareAge
* *
* Compares two tags by age * Compares two tags by age
* *
* @access public * @access public
* @static * @static
* @param mixed $a first tag * @param mixed $a first tag
* @param mixed $b second tag * @param mixed $b second tag
* @return integer comparison result * @return integer comparison result
*/ */
public static function CompareAge($a, $b) public static function CompareAge($a, $b)
{ {
$aObj = $a->GetObject(); $aObj = $a->GetObject();
$bObj = $b->GetObject(); $bObj = $b->GetObject();
if (($aObj instanceof GitPHP_Commit) && ($bObj instanceof GitPHP_Commit)) { if (($aObj instanceof GitPHP_Commit) && ($bObj instanceof GitPHP_Commit)) {
if ($aObj->GetAge() === $bObj->GetAge()) if ($aObj->GetAge() === $bObj->GetAge())
return 0; return 0;
return ($aObj->GetAge() < $bObj->GetAge() ? -1 : 1); return ($aObj->GetAge() < $bObj->GetAge() ? -1 : 1);
} }
   
if ($aObj instanceof GitPHP_Commit) if ($aObj instanceof GitPHP_Commit)
return 1; return 1;
   
if ($bObj instanceof GitPHP_Commit) if ($bObj instanceof GitPHP_Commit)
return -1; return -1;
   
return strcmp($a->GetName(), $b->GetName()); return strcmp($a->GetName(), $b->GetName());
} }
   
} }
   
comments