Load tree data from pack
[gitphp.git] / include / git / Tree.class.php
blob:a/include/git/Tree.class.php -> blob:b/include/git/Tree.class.php
<?php <?php
/** /**
* GitPHP Tree * GitPHP Tree
* *
* Represents a single tree * Represents a single tree
* *
* @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 . 'FilesystemObject.class.php'); require_once(GITPHP_GITOBJECTDIR . 'FilesystemObject.class.php');
require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php'); require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
   
/** /**
* Tree class * Tree class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_Tree extends GitPHP_FilesystemObject class GitPHP_Tree extends GitPHP_FilesystemObject
{ {
   
/** /**
* contents * contents
* *
* Tree contents * Tree contents
* *
* @access protected * @access protected
*/ */
protected $contents = array(); protected $contents = array();
   
/** /**
* contentsRead * contentsRead
* *
* Stores whether contents were read * Stores whether contents were read
* *
* @access protected * @access protected
*/ */
protected $contentsRead = false; protected $contentsRead = false;
   
/** /**
* contentsReferenced * contentsReferenced
* *
* Stores whether contents have been referenced into pointers * Stores whether contents have been referenced into pointers
* *
* @access private * @access private
*/ */
private $contentsReferenced = false; private $contentsReferenced = false;
   
/** /**
* __construct * __construct
* *
* Instantiates object * Instantiates object
* *
* @access public * @access public
* @param mixed $project the project * @param mixed $project the project
* @param string $hash tree hash * @param string $hash tree hash
* @return mixed tree object * @return mixed tree object
* @throws Exception exception on invalid hash * @throws Exception exception on invalid hash
*/ */
public function __construct($project, $hash) public function __construct($project, $hash)
{ {
parent::__construct($project, $hash); parent::__construct($project, $hash);
} }
   
/** /**
* SetCommit * SetCommit
* *
* Sets the commit for this tree (overrides base) * Sets the commit for this tree (overrides base)
* *
* @access public * @access public
* @param mixed $commit commit object * @param mixed $commit commit object
*/ */
public function SetCommit($commit) public function SetCommit($commit)
{ {
parent::SetCommit($commit); parent::SetCommit($commit);
   
if ($this->contentsRead && !$this->contentsReferenced) { if ($this->contentsRead && !$this->contentsReferenced) {
foreach ($this->contents as $obj) { foreach ($this->contents as $obj) {
$obj->SetCommit($commit); $obj->SetCommit($commit);
} }
} }
} }
   
/** /**
* GetContents * GetContents
* *
* Gets the tree contents * Gets the tree contents
* *
* @access public * @access public
* @return array array of objects for contents * @return array array of objects for contents
*/ */
public function GetContents() public function GetContents()
{ {
if (!$this->contentsRead) if (!$this->contentsRead)
$this->ReadContents(); $this->ReadContents();
   
if ($this->contentsReferenced) if ($this->contentsReferenced)
$this->DereferenceContents(); $this->DereferenceContents();
   
return $this->contents; return $this->contents;
} }
   
/** /**
* ReadContents * ReadContents
* *
* Reads the tree contents * Reads the tree contents
* *
* @access protected * @access protected
*/ */
protected function ReadContents() protected function ReadContents()
{ {
$this->contentsRead = true; $this->contentsRead = true;
   
  if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
  $this->ReadContentsGit();
  } else {
  $this->ReadContentsRaw();
  }
   
  GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
  }
   
  /**
  * ReadContentsGit
  *
  * Reads the tree contents using the git executable
  *
  * @access private
  */
  private function ReadContentsGit()
  {
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
   
$args = array(); $args = array();
$args[] = '--full-name'; $args[] = '--full-name';
if ($exe->CanShowSizeInTree()) if ($exe->CanShowSizeInTree())
$args[] = '-l'; $args[] = '-l';
$args[] = '-t'; $args[] = '-t';
$args[] = $this->hash; $args[] = $this->hash;
$lines = explode("\n", $exe->Execute(GIT_LS_TREE, $args)); $lines = explode("\n", $exe->Execute(GIT_LS_TREE, $args));
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match("/^([0-9]+) (.+) ([0-9a-fA-F]{40})(\s+[0-9]+|\s+-)?\t(.+)$/", $line, $regs)) { if (preg_match("/^([0-9]+) (.+) ([0-9a-fA-F]{40})(\s+[0-9]+|\s+-)?\t(.+)$/", $line, $regs)) {
switch($regs[2]) { switch($regs[2]) {
case 'tree': case 'tree':
$t = $this->GetProject()->GetTree($regs[3]); $t = $this->GetProject()->GetTree($regs[3]);
$t->SetMode($regs[1]); $t->SetMode($regs[1]);
$path = $regs[5]; $path = $regs[5];
if (!empty($this->path)) if (!empty($this->path))
$path = $this->path . '/' . $path; $path = $this->path . '/' . $path;
$t->SetPath($path); $t->SetPath($path);
if ($this->commit) if ($this->commit)
$t->SetCommit($this->commit); $t->SetCommit($this->commit);
$this->contents[] = $t; $this->contents[] = $t;
break; break;
case 'blob': case 'blob':
$b = $this->GetProject()->GetBlob($regs[3]); $b = $this->GetProject()->GetBlob($regs[3]);
$b->SetMode($regs[1]); $b->SetMode($regs[1]);
$path = $regs[5]; $path = $regs[5];
if (!empty($this->path)) if (!empty($this->path))
$path = $this->path . '/' . $path; $path = $this->path . '/' . $path;
$b->SetPath($path); $b->SetPath($path);
$size = trim($regs[4]); $size = trim($regs[4]);
if (!empty($size)) if (!empty($size))
$b->SetSize($regs[4]); $b->SetSize($regs[4]);
if ($this->commit) if ($this->commit)
$b->SetCommit($this->commit); $b->SetCommit($this->commit);
$this->contents[] = $b; $this->contents[] = $b;
break; break;
} }
} }
} }
   
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this); }
   
  /**
  * ReadContentsRaw
  *
  * Reads the tree contents using the raw git object
  *
  * @access private
  */
  private function ReadContentsRaw()
  {
  $treeData = $this->GetProject()->GetObject($this->hash);
   
  $start = 0;
  $len = strlen($treeData);
  while ($start < $len) {
  $pos = strpos($treeData, "\0", $start);
   
  list($mode, $path) = explode(' ', substr($treeData, $start, $pos-$start), 2);
  $mode = str_pad($mode, 6, '0', STR_PAD_LEFT);
  $hash = bin2hex(substr($treeData, $pos+1, 20));
  $start = $pos + 21;
   
  $octmode = octdec($mode);
   
  if ($octmode == 57344) {
  // submodules not currently supported
  continue;
  }
   
  if (!empty($this->path))
  $path = $this->path . '/' . $path;
   
  $obj = null;
  if ($octmode & 0x4000) {
  // tree
  $obj = $this->GetProject()->GetTree($hash);
  } else {
  // blob
  $obj = $this->GetProject()->GetBlob($hash);
  }
   
  if (!$obj) {
  continue;
  }
   
  $obj->SetMode($mode);
  $obj->SetPath($path);
  if ($this->commit)
  $obj->SetCommit($this->commit);
  $this->contents[] = $obj;
  }
} }
   
/** /**
* ReferenceContents * ReferenceContents
* *
* Turns the contents objects into reference pointers * Turns the contents objects into reference pointers
* *
* @access private * @access private
*/ */
private function ReferenceContents() private function ReferenceContents()
{ {
if ($this->contentsReferenced) if ($this->contentsReferenced)
return; return;
   
if (!(isset($this->contents) && (count($this->contents) > 0))) if (!(isset($this->contents) && (count($this->contents) > 0)))
return; return;
   
for ($i = 0; $i < count($this->contents); ++$i) { for ($i = 0; $i < count($this->contents); ++$i) {
$obj = $this->contents[$i]; $obj = $this->contents[$i];
$data = array(); $data = array();
   
$data['hash'] = $obj->GetHash(); $data['hash'] = $obj->GetHash();
$data['mode'] = $obj->GetMode(); $data['mode'] = $obj->GetMode();
$data['path'] = $obj->GetPath(); $data['path'] = $obj->GetPath();
   
if ($obj instanceof GitPHP_Tree) { if ($obj instanceof GitPHP_Tree) {
$data['type'] = 'tree'; $data['type'] = 'tree';
} else if ($obj instanceof GitPHP_Blob) { } else if ($obj instanceof GitPHP_Blob) {
$data['type'] = 'blob'; $data['type'] = 'blob';
$data['size'] = $obj->GetSize(); $data['size'] = $obj->GetSize();
} }
   
$this->contents[$i] = $data; $this->contents[$i] = $data;
} }
   
$this->contentsReferenced = true; $this->contentsReferenced = true;
} }
   
/** /**
* DereferenceContents * DereferenceContents
* *
* Turns the contents pointers back into objects * Turns the contents pointers back into objects
* *
* @access private * @access private
*/ */
private function DereferenceContents() private function DereferenceContents()
{ {
if (!$this->contentsReferenced) if (!$this->contentsReferenced)
return; return;
   
if (!(isset($this->contents) && (count($this->contents) > 0))) if (!(isset($this->contents) && (count($this->contents) > 0)))
return; return;
   
for ($i = 0; $i < count($this->contents); ++$i) { for ($i = 0; $i < count($this->contents); ++$i) {
$data = $this->contents[$i]; $data = $this->contents[$i];
$obj = null; $obj = null;
   
if (!isset($data['hash']) || empty($data['hash'])) if (!isset($data['hash']) || empty($data['hash']))
continue; continue;
   
if ($data['type'] == 'tree') { if ($data['type'] == 'tree') {
$obj = $this->GetProject()->GetTree($data['hash']); $obj = $this->GetProject()->GetTree($data['hash']);
} else if ($data['type'] == 'blob') { } else if ($data['type'] == 'blob') {
$obj = $this->GetProject()->GetBlob($data['hash']); $obj = $this->GetProject()->GetBlob($data['hash']);
if (isset($data['size']) && !empty($data['size'])) if (isset($data['size']) && !empty($data['size']))
$obj->SetSize($data['size']); $obj->SetSize($data['size']);
} else { } else {
continue; continue;
} }
   
if (isset($data['mode']) && !empty($data['mode'])) if (isset($data['mode']) && !empty($data['mode']))
$obj->SetMode($data['mode']); $obj->SetMode($data['mode']);
   
if (isset($data['path']) && !empty($data['path'])) if (isset($data['path']) && !empty($data['path']))
$obj->SetPath($data['path']); $obj->SetPath($data['path']);
   
if ($this->commit) if ($this->commit)
$obj->SetCommit($this->commit); $obj->SetCommit($this->commit);
   
$this->contents[$i] = $obj; $this->contents[$i] = $obj;
} }
   
$this->contentsReferenced = false; $this->contentsReferenced = 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->contentsReferenced) if (!$this->contentsReferenced)
$this->ReferenceContents(); $this->ReferenceContents();
   
$properties = array('contents', 'contentsRead', 'contentsReferenced'); $properties = array('contents', 'contentsRead', 'contentsReferenced');
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 .= 'tree|' . $this->hash; $key .= 'tree|' . $this->hash;
   
return $key; return $key;
} }
   
} }
   
comments