Don't do redundant regexes when reading commit data
Don't do redundant regexes when reading commit data

<?php <?php
/** /**
* GitPHP Commit * GitPHP Commit
* *
* Represents a single commit * Represents a single commit
* *
* @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 . 'GitObject.class.php'); require_once(GITPHP_GITOBJECTDIR . 'GitObject.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Tree.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Tree.class.php');
require_once(GITPHP_GITOBJECTDIR . 'TreeDiff.class.php'); require_once(GITPHP_GITOBJECTDIR . 'TreeDiff.class.php');
   
/** /**
* Commit class * Commit class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_Commit extends GitPHP_GitObject class GitPHP_Commit extends GitPHP_GitObject
{ {
   
/** /**
* dataRead * dataRead
* *
* Indicates whether data for this commit has been read * Indicates whether data for this commit has been read
* *
* @access protected * @access protected
*/ */
protected $dataRead = false; protected $dataRead = false;
   
/** /**
* parents * parents
* *
* Array of parent commits * Array of parent commits
* *
* @access protected * @access protected
*/ */
protected $parents = array(); protected $parents = array();
   
/** /**
* tree * tree
* *
* Tree object for this commit * Tree object for this commit
* *
* @access protected * @access protected
*/ */
protected $tree; protected $tree;
   
/** /**
* author * author
* *
* Author for this commit * Author for this commit
* *
* @access protected * @access protected
*/ */
protected $author; protected $author;
   
/** /**
* authorEpoch * authorEpoch
* *
* Author's epoch * Author's epoch
* *
* @access protected * @access protected
*/ */
protected $authorEpoch; protected $authorEpoch;
   
/** /**
* authorTimezone * authorTimezone
* *
* Author's timezone * Author's timezone
* *
* @access protected * @access protected
*/ */
protected $authorTimezone; protected $authorTimezone;
   
/** /**
* committer * committer
* *
* Committer for this commit * Committer for this commit
* *
* @access protected * @access protected
*/ */
protected $committer; protected $committer;
   
/** /**
* committerEpoch * committerEpoch
* *
* Committer's epoch * Committer's epoch
* *
* @access protected * @access protected
*/ */
protected $committerEpoch; protected $committerEpoch;
   
/** /**
* committerTimezone * committerTimezone
* *
* Committer's timezone * Committer's timezone
* *
* @access protected * @access protected
*/ */
protected $committerTimezone; protected $committerTimezone;
   
/** /**
* title * title
* *
* Stores the commit title * Stores the commit title
* *
* @access protected * @access protected
*/ */
protected $title; protected $title;
   
/** /**
* comment * comment
* *
* Stores the commit comment * Stores the commit comment
* *
* @access protected * @access protected
*/ */
protected $comment = array(); protected $comment = array();
   
/** /**
* readTree * readTree
* *
* Stores whether tree filenames have been read * Stores whether tree filenames have been read
* *
* @access protected * @access protected
*/ */
protected $readTree = false; protected $readTree = false;
   
/** /**
* blobPaths * blobPaths
* *
* Stores blob hash to path mappings * Stores blob hash to path mappings
* *
* @access protected * @access protected
*/ */
protected $blobPaths = array(); protected $blobPaths = array();
   
/** /**
* treePaths * treePaths
* *
* Stores tree hash to path mappings * Stores tree hash to path mappings
* *
* @access protected * @access protected
*/ */
protected $treePaths = array(); protected $treePaths = array();
   
/** /**
* hashPathsRead * hashPathsRead
* *
* Stores whether hash paths have been read * Stores whether hash paths have been read
* *
* @access protected * @access protected
*/ */
protected $hashPathsRead = false; protected $hashPathsRead = false;
   
/** /**
* containingTag * containingTag
* *
* Stores the tag containing the changes in this commit * Stores the tag containing the changes in this commit
* *
* @access protected * @access protected
*/ */
protected $containingTag = null; protected $containingTag = null;
   
/** /**
* containingTagRead * containingTagRead
* *
* Stores whether the containing tag has been looked up * Stores whether the containing tag has been looked up
* *
* @access public * @access public
*/ */
protected $containingTagRead = false; protected $containingTagRead = false;
   
/** /**
* parentsReferenced * parentsReferenced
* *
* Stores whether the parents have been referenced into pointers * Stores whether the parents have been referenced into pointers
* *
* @access private * @access private
*/ */
private $parentsReferenced = false; private $parentsReferenced = false;
   
/** /**
* treeReferenced * treeReferenced
* *
* Stores whether the tree has been referenced into a pointer * Stores whether the tree has been referenced into a pointer
* *
* @access private * @access private
*/ */
private $treeReferenced = false; private $treeReferenced = false;
   
/** /**
* __construct * __construct
* *
* Instantiates object * Instantiates object
* *
* @access public * @access public
* @param mixed $project the project * @param mixed $project the project
* @param string $hash object hash * @param string $hash object hash
* @return mixed git object * @return mixed git 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);
} }
   
/** /**
* GetParent * GetParent
* *
* Gets the main parent of this commit * Gets the main parent of this commit
* *
* @access public * @access public
* @return mixed commit object for parent * @return mixed commit object for parent
*/ */
public function GetParent() public function GetParent()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
if ($this->parentsReferenced) if ($this->parentsReferenced)
$this->DereferenceParents(); $this->DereferenceParents();
   
if (isset($this->parents[0])) if (isset($this->parents[0]))
return $this->parents[0]; return $this->parents[0];
return null; return null;
} }
   
/** /**
* GetParents * GetParents
* *
* Gets an array of parent objects for this commit * Gets an array of parent objects for this commit
* *
* @access public * @access public
* @return mixed array of commit objects * @return mixed array of commit objects
*/ */
public function GetParents() public function GetParents()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
if ($this->parentsReferenced) if ($this->parentsReferenced)
$this->DereferenceParents(); $this->DereferenceParents();
   
return $this->parents; return $this->parents;
} }
   
/** /**
* GetTree * GetTree
* *
* Gets the tree for this commit * Gets the tree for this commit
* *
* @access public * @access public
* @return mixed tree object * @return mixed tree object
*/ */
public function GetTree() public function GetTree()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
if ($this->treeReferenced) if ($this->treeReferenced)
$this->DereferenceTree(); $this->DereferenceTree();
   
return $this->tree; return $this->tree;
} }
   
/** /**
* GetAuthor * GetAuthor
* *
* Gets the author for this commit * Gets the author for this commit
* *
* @access public * @access public
* @return string author * @return string author
*/ */
public function GetAuthor() public function GetAuthor()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->author; return $this->author;
} }
   
/** /**
* GetAuthorName * GetAuthorName
* *
* Gets the author's name only * Gets the author's name only
* *
* @access public * @access public
* @return string author name * @return string author name
*/ */
public function GetAuthorName() public function GetAuthorName()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return preg_replace('/ <.*/', '', $this->author); return preg_replace('/ <.*/', '', $this->author);
} }
   
/** /**
* GetAuthorEpoch * GetAuthorEpoch
* *
* Gets the author's epoch * Gets the author's epoch
* *
* @access public * @access public
* @return string author epoch * @return string author epoch
*/ */
public function GetAuthorEpoch() public function GetAuthorEpoch()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->authorEpoch; return $this->authorEpoch;
} }
   
/** /**
* GetAuthorLocalEpoch * GetAuthorLocalEpoch
* *
* Gets the author's local epoch * Gets the author's local epoch
* *
* @access public * @access public
* @return string author local epoch * @return string author local epoch
*/ */
public function GetAuthorLocalEpoch() public function GetAuthorLocalEpoch()
{ {
$epoch = $this->GetAuthorEpoch(); $epoch = $this->GetAuthorEpoch();
$tz = $this->GetAuthorTimezone(); $tz = $this->GetAuthorTimezone();
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;
} }
   
/** /**
* GetAuthorTimezone * GetAuthorTimezone
* *
* Gets the author's timezone * Gets the author's timezone
* *
* @access public * @access public
* @return string author timezone * @return string author timezone
*/ */
public function GetAuthorTimezone() public function GetAuthorTimezone()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->authorTimezone; return $this->authorTimezone;
} }
   
/** /**
* GetCommitter * GetCommitter
* *
* Gets the author for this commit * Gets the author for this commit
* *
* @access public * @access public
* @return string author * @return string author
*/ */
public function GetCommitter() public function GetCommitter()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->committer; return $this->committer;
} }
   
/** /**
* GetCommitterName * GetCommitterName
* *
* Gets the author's name only * Gets the author's name only
* *
* @access public * @access public
* @return string author name * @return string author name
*/ */
public function GetCommitterName() public function GetCommitterName()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return preg_replace('/ <.*/', '', $this->committer); return preg_replace('/ <.*/', '', $this->committer);
} }
   
/** /**
* GetCommitterEpoch * GetCommitterEpoch
* *
* Gets the committer's epoch * Gets the committer's epoch
* *
* @access public * @access public
* @return string committer epoch * @return string committer epoch
*/ */
public function GetCommitterEpoch() public function GetCommitterEpoch()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->committerEpoch; return $this->committerEpoch;
} }
   
/** /**
* GetCommitterLocalEpoch * GetCommitterLocalEpoch
* *
* Gets the committer's local epoch * Gets the committer's local epoch
* *
* @access public * @access public
* @return string committer local epoch * @return string committer local epoch
*/ */
public function GetCommitterLocalEpoch() public function GetCommitterLocalEpoch()
{ {
$epoch = $this->GetCommitterEpoch(); $epoch = $this->GetCommitterEpoch();
$tz = $this->GetCommitterTimezone(); $tz = $this->GetCommitterTimezone();
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;
} }
   
/** /**
* GetCommitterTimezone * GetCommitterTimezone
* *
* Gets the author's timezone * Gets the author's timezone
* *
* @access public * @access public
* @return string author timezone * @return string author timezone
*/ */
public function GetCommitterTimezone() public function GetCommitterTimezone()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->committerTimezone; return $this->committerTimezone;
} }
   
/** /**
* GetTitle * GetTitle
* *
* Gets the commit title * Gets the commit title
* *
* @access public * @access public
* @param integer $trim length to trim to (0 for no trim) * @param integer $trim length to trim to (0 for no trim)
* @return string title * @return string title
*/ */
public function GetTitle($trim = 0) public function GetTitle($trim = 0)
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
if (($trim > 0) && (strlen($this->title) > $trim)) { if (($trim > 0) && (strlen($this->title) > $trim)) {
return substr($this->title, 0, $trim) . '…'; return substr($this->title, 0, $trim) . '…';
} }
   
return $this->title; return $this->title;
} }
   
/** /**
* GetComment * GetComment
* *
* Gets the lines of comment * Gets the lines of comment
* *
* @access public * @access public
* @return array lines of comment * @return array lines of comment
*/ */
public function GetComment() public function GetComment()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return $this->comment; return $this->comment;
} }
   
/** /**
* SearchComment * SearchComment
* *
* Gets the lines of the comment matching the given pattern * Gets the lines of the comment matching the given pattern
* *
* @access public * @access public
* @param string $pattern pattern to find * @param string $pattern pattern to find
* @return array matching lines of comment * @return array matching lines of comment
*/ */
public function SearchComment($pattern) public function SearchComment($pattern)
{ {
if (empty($pattern)) if (empty($pattern))
return $this->GetComment(); return $this->GetComment();
   
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
return preg_grep('/' . $pattern . '/i', $this->comment); return preg_grep('/' . $pattern . '/i', $this->comment);
} }
   
/** /**
* GetAge * GetAge
* *
* Gets the age of the commit * Gets the age of the commit
* *
* @access public * @access public
* @return string age * @return string age
*/ */
public function GetAge() public function GetAge()
{ {
if (!$this->dataRead) if (!$this->dataRead)
$this->ReadData(); $this->ReadData();
   
if (!empty($this->committerEpoch)) if (!empty($this->committerEpoch))
return time() - $this->committerEpoch; return time() - $this->committerEpoch;
   
return ''; return '';
} }
   
/** /**
* ReadData * ReadData
* *
* Read the data for the commit * Read the data for the commit
* *
* @access protected * @access protected
*/ */
protected function ReadData() protected function ReadData()
{ {
$this->dataRead = true; $this->dataRead = true;
   
$lines = null; $lines = null;
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
   
/* get data from git_rev_list */ /* get data from git_rev_list */
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
$args = array(); $args = array();
$args[] = '--header'; $args[] = '--header';
$args[] = '--parents'; $args[] = '--parents';
$args[] = '--max-count=1'; $args[] = '--max-count=1';
$args[] = $this->hash; $args[] = $this->hash;
$ret = $exe->Execute(GIT_REV_LIST, $args); $ret = $exe->Execute(GIT_REV_LIST, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
if (!isset($lines[0])) if (!isset($lines[0]))
return; return;
   
/* In case we returned something unexpected */ /* In case we returned something unexpected */
$tok = strtok($lines[0], ' '); $tok = strtok($lines[0], ' ');
if ($tok != $this->hash) if ($tok != $this->hash)
return; return;
   
array_shift($lines); array_shift($lines);
   
} else { } else {
$data = $this->GetProject()->GetObject($this->hash); $data = $this->GetProject()->GetObject($this->hash);
if (empty($data)) if (empty($data))
return; return;
   
$lines = explode("\n", $data); $lines = explode("\n", $data);
   
} }
   
  $header = true;
   
foreach ($lines as $i => $line) { foreach ($lines as $i => $line) {
if (preg_match('/^tree ([0-9a-fA-F]{40})$/', $line, $regs)) { if ($header && preg_match('/^tree ([0-9a-fA-F]{40})$/', $line, $regs)) {
/* Tree */ /* Tree */
try { try {
$tree = $this->GetProject()->GetTree($regs[1]); $tree = $this->GetProject()->GetTree($regs[1]);
if ($tree) { if ($tree) {
$tree->SetCommit($this); $tree->SetCommit($this);
$this->tree = $tree; $this->tree = $tree;
} }
} catch (Exception $e) { } catch (Exception $e) {
} }
} else if (preg_match('/^parent ([0-9a-fA-F]{40})$/', $line, $regs)) { } else if ($header && preg_match('/^parent ([0-9a-fA-F]{40})$/', $line, $regs)) {
/* Parent */ /* Parent */
try { try {
$this->parents[] = $this->GetProject()->GetCommit($regs[1]); $this->parents[] = $this->GetProject()->GetCommit($regs[1]);
} catch (Exception $e) { } catch (Exception $e) {
} }
} else if (preg_match('/^author (.*) ([0-9]+) (.*)$/', $line, $regs)) { } else if ($header && preg_match('/^author (.*) ([0-9]+) (.*)$/', $line, $regs)) {
/* author data */ /* author data */
$this->author = $regs[1]; $this->author = $regs[1];
$this->authorEpoch = $regs[2]; $this->authorEpoch = $regs[2];
$this->authorTimezone = $regs[3]; $this->authorTimezone = $regs[3];
} else if (preg_match('/^committer (.*) ([0-9]+) (.*)$/', $line, $regs)) { } else if ($header && preg_match('/^committer (.*) ([0-9]+) (.*)$/', $line, $regs)) {
/* committer data */ /* committer data */
$this->committer = $regs[1]; $this->committer = $regs[1];
$this->committerEpoch = $regs[2]; $this->committerEpoch = $regs[2];
$this->committerTimezone = $regs[3]; $this->committerTimezone = $regs[3];
} else { } else {
/* commit comment */ /* commit comment */
  $header = false;
$trimmed = trim($line); $trimmed = trim($line);
if (empty($this->title) && (strlen($trimmed) > 0)) if (empty($this->title) && (strlen($trimmed) > 0))
$this->title = $trimmed; $this->title = $trimmed;
if (!empty($this->title)) { if (!empty($this->title)) {
if ((strlen($trimmed) > 0) || ($i < (count($lines)-1))) if ((strlen($trimmed) > 0) || ($i < (count($lines)-1)))
$this->comment[] = $trimmed; $this->comment[] = $trimmed;
} }
} }
} }
   
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this); GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
} }
   
/** /**
* GetHeads * GetHeads
* *
* Gets heads that point to this commit * Gets heads that point to this commit
* *
* @access public * @access public
* @return array array of heads * @return array array of heads
*/ */
public function GetHeads() public function GetHeads()
{ {
$heads = array(); $heads = array();
   
$projectRefs = $this->GetProject()->GetRefs('heads'); $projectRefs = $this->GetProject()->GetRefs('heads');
   
foreach ($projectRefs as $ref) { foreach ($projectRefs as $ref) {
if ($ref->GetHash() == $this->hash) { if ($ref->GetHash() == $this->hash) {
$heads[] = $ref; $heads[] = $ref;
} }
} }
   
return $heads; return $heads;
} }
   
/** /**
* GetTags * GetTags
* *
* Gets tags that point to this commit * Gets tags that point to this commit
* *
* @access public * @access public
* @return array array of tags * @return array array of tags
*/ */
public function GetTags() public function GetTags()
{ {
$tags = array(); $tags = array();
   
$projectRefs = $this->GetProject()->GetRefs('tags'); $projectRefs = $this->GetProject()->GetRefs('tags');
   
foreach ($projectRefs as $ref) { foreach ($projectRefs as $ref) {
if ($ref->GetCommit()->GetHash() === $this->hash) { if ($ref->GetCommit()->GetHash() === $this->hash) {
$tags[] = $ref; $tags[] = $ref;
} }
} }
   
return $tags; return $tags;
} }
   
/** /**
* GetContainingTag * GetContainingTag
* *
* Gets the tag that contains the changes in this commit * Gets the tag that contains the changes in this commit
* *
* @access public * @access public
* @return tag object * @return tag object
*/ */
public function GetContainingTag() public function GetContainingTag()
{ {
if (!$this->containingTagRead) if (!$this->containingTagRead)
$this->ReadContainingTag(); $this->ReadContainingTag();
   
return $this->containingTag; return $this->containingTag;
} }
   
/** /**
* ReadContainingTag * ReadContainingTag
* *
* Looks up the tag that contains the changes in this commit * Looks up the tag that contains the changes in this commit
* *
* @access private * @access private
*/ */
public function ReadContainingTag() public function ReadContainingTag()
{ {
$this->containingTagRead = true; $this->containingTagRead = true;
   
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
$args = array(); $args = array();
$args[] = '--tags'; $args[] = '--tags';
$args[] = $this->hash; $args[] = $this->hash;
$revs = explode("\n", $exe->Execute(GIT_NAME_REV, $args)); $revs = explode("\n", $exe->Execute(GIT_NAME_REV, $args));
   
foreach ($revs as $revline) { foreach ($revs as $revline) {
if (preg_match('/^([0-9a-fA-F]{40})\s+tags\/(.+)(\^[0-9]+|\~[0-9]+)$/', $revline, $regs)) { if (preg_match('/^([0-9a-fA-F]{40})\s+tags\/(.+)(\^[0-9]+|\~[0-9]+)$/', $revline, $regs)) {
if ($regs[1] == $this->hash) { if ($regs[1] == $this->hash) {
$this->containingTag = $this->GetProject()->GetTag($regs[2]); $this->containingTag = $this->GetProject()->GetTag($regs[2]);
break; break;
} }
} }
} }
   
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this); GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
} }
   
/** /**
* DiffToParent * DiffToParent
* *
* Diffs this commit with its immediate parent * Diffs this commit with its immediate parent
* *
* @access public * @access public
* @return mixed Tree diff * @return mixed Tree diff
*/ */
public function DiffToParent() public function DiffToParent()
{ {
return new GitPHP_TreeDiff($this->GetProject(), $this->hash); return new GitPHP_TreeDiff($this->GetProject(), $this->hash);
} }
   
/** /**
* PathToHash * PathToHash
* *
* Given a filepath, get its hash * Given a filepath, get its hash
* *
* @access public * @access public
* @param string $path path * @param string $path path
* @return string hash * @return string hash
*/ */
public function PathToHash($path) public function PathToHash($path)
{ {
if (empty($path)) if (empty($path))
return ''; return '';
   
if (!$this->hashPathsRead) if (!$this->hashPathsRead)
$this->ReadHashPaths(); $this->ReadHashPaths();
   
if (isset($this->blobPaths[$path])) { if (isset($this->blobPaths[$path])) {
return $this->blobPaths[$path]; return $this->blobPaths[$path];
} }
   
if (isset($this->treePaths[$path])) { if (isset($this->treePaths[$path])) {
return $this->treePaths[$path]; return $this->treePaths[$path];
} }
   
return ''; return '';
} }
   
/** /**
* ReadHashPaths * ReadHashPaths
* *
* Read hash to path mappings * Read hash to path mappings
* *
* @access private * @access private
*/ */
private function ReadHashPaths() private function ReadHashPaths()
{ {
$this->hashPathsRead = true; $this->hashPathsRead = true;
   
if (GitPHP_Config::GetInstance()->GetValue('compat', false)) { if (GitPHP_Config::GetInstance()->GetValue('compat', false)) {
$this->ReadHashPathsGit(); $this->ReadHashPathsGit();
} else { } else {
$this->ReadHashPathsRaw($this->GetTree()); $this->ReadHashPathsRaw($this->GetTree());
} }
   
GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this); GitPHP_Cache::GetInstance()->Set($this->GetCacheKey(), $this);
} }
   
/** /**
* ReadHashPathsGit * ReadHashPathsGit
* *
* Reads hash to path mappings using git exe * Reads hash to path mappings using git exe
* *
* @access private * @access private
*/ */
private function ReadHashPathsGit() private function ReadHashPathsGit()
{ {
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
   
$args = array(); $args = array();
$args[] = '--full-name'; $args[] = '--full-name';
$args[] = '-r'; $args[] = '-r';
$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})\t(.+)$/", $line, $regs)) { if (preg_match("/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/", $line, $regs)) {
switch ($regs[2]) { switch ($regs[2]) {
case 'tree': case 'tree':
$this->treePaths[trim($regs[4])] = $regs[3]; $this->treePaths[trim($regs[4])] = $regs[3];
break; break;
case 'blob'; case 'blob';
$this->blobPaths[trim($regs[4])] = $regs[3]; $this->blobPaths[trim($regs[4])] = $regs[3];
break; break;
} }
} }
} }
} }
   
/** /**
* ReadHashPathsRaw * ReadHashPathsRaw
* *
* Reads hash to path mappings using raw objects * Reads hash to path mappings using raw objects
* *
* @access private * @access private
*/ */
private function ReadHashPathsRaw($tree) private function ReadHashPathsRaw($tree)
{ {
if (!$tree) { if (!$tree) {
return; return;
} }
   
$contents = $tree->GetContents(); $contents = $tree->GetContents();
   
foreach ($contents as $obj) { foreach ($contents as $obj) {
if ($obj instanceof GitPHP_Blob) { if ($obj instanceof GitPHP_Blob) {
$hash = $obj->GetHash(); $hash = $obj->GetHash();
$path = $obj->GetPath(); $path = $obj->GetPath();
$this->blobPaths[trim($path)] = $hash; $this->blobPaths[trim($path)] = $hash;
} else if ($obj instanceof GitPHP_Tree) { } else if ($obj instanceof GitPHP_Tree) {
$hash = $obj->GetHash(); $hash = $obj->GetHash();
$path = $obj->GetPath(); $path = $obj->GetPath();
$this->treePaths[trim($path)] = $hash; $this->treePaths[trim($path)] = $hash;
$this->ReadHashPathsRaw($obj); $this->ReadHashPathsRaw($obj);
} }
} }
} }
   
/** /**
* SearchFilenames * SearchFilenames
* *
* Returns array of objects matching pattern * Returns array of objects matching pattern
* *
* @access public * @access public
* @param string $pattern pattern to find * @param string $pattern pattern to find
* @return array array of objects * @return array array of objects
*/ */
public function SearchFilenames($pattern) public function SearchFilenames($pattern)
{ {
if (empty($pattern)) if (empty($pattern))
return; return;
   
if (!$this->hashPathsRead) if (!$this->hashPathsRead)
$this->ReadHashPaths(); $this->ReadHashPaths();
   
$results = array(); $results = array();
   
foreach ($this->treePaths as $path => $hash) { foreach ($this->treePaths as $path => $hash) {
if (preg_match('/' . $pattern . '/i', $path)) { if (preg_match('/' . $pattern . '/i', $path)) {
$obj = $this->GetProject()->GetTree($hash); $obj = $this->GetProject()->GetTree($hash);
$obj->SetCommit($this); $obj->SetCommit($this);
$results[$path] = $obj; $results[$path] = $obj;
} }
} }
   
foreach ($this->blobPaths as $path => $hash) { foreach ($this->blobPaths as $path => $hash) {
if (preg_match('/' . $pattern . '/i', $path)) { if (preg_match('/' . $pattern . '/i', $path)) {
$obj = $this->GetProject()->GetBlob($hash); $obj = $this->GetProject()->GetBlob($hash);
$obj->SetCommit($this); $obj->SetCommit($this);
$results[$path] = $obj; $results[$path] = $obj;
} }
} }
   
ksort($results); ksort($results);
   
return $results; return $results;
} }
   
/** /**
* SearchFileContents * SearchFileContents
* *
* Searches for a pattern in file contents * Searches for a pattern in file contents
* *
* @access public * @access public
* @param string $pattern pattern to search for * @param string $pattern pattern to search for
* @return array multidimensional array of results * @return array multidimensional array of results
*/ */
public function SearchFileContents($pattern) public function SearchFileContents($pattern)
{ {
if (empty($pattern)) if (empty($pattern))
return; return;
   
$exe = new GitPHP_GitExe($this->GetProject()); $exe = new GitPHP_GitExe($this->GetProject());
   
$args = array(); $args = array();
$args[] = '-I'; $args[] = '-I';
$args[] = '--full-name'; $args[] = '--full-name';
$args[] = '--ignore-case'; $args[] = '--ignore-case';
$args[] = '-n'; $args[] = '-n';
$args[] = '-e'; $args[] = '-e';
$args[] = $pattern; $args[] = $pattern;
$args[] = $this->hash; $args[] = $this->hash;
   
$lines = explode("\n", $exe->Execute(GIT_GREP, $args)); $lines = explode("\n", $exe->Execute(GIT_GREP, $args));
   
$results = array(); $results = array();
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match('/^[^:]+:([^:]+):([0-9]+):(.+)$/', $line, $regs)) { if (preg_match('/^[^:]+:([^:]+):([0-9]+):(.+)$/', $line, $regs)) {
if (!isset($results[$regs[1]]['object'])) { if (!isset($results[$regs[1]]['object'])) {
$hash = $this->PathToHash($regs[1]); $hash = $this->PathToHash($regs[1]);
if (!empty($hash)) { if (!empty($hash)) {
$obj = $this->GetProject()->GetBlob($hash); $obj = $this->GetProject()->GetBlob($hash);
$obj->SetCommit($this); $obj->SetCommit($this);
$results[$regs[1]]['object'] = $obj; $results[$regs[1]]['object'] = $obj;
} }
} }
$results[$regs[1]]['lines'][(int)($regs[2])] = $regs[3]; $results[$regs[1]]['lines'][(int)($regs[2])] = $regs[3];
} }
} }
   
return $results; return $results;
} }
   
/** /**
* SearchFiles * SearchFiles
* *
* Searches filenames and file contents for a pattern * Searches filenames and file contents for a pattern
* *
* @access public * @access public
* @param string $pattern pattern to search * @param string $pattern pattern to search
* @param integer $count number of results to get * @param integer $count number of results to get
* @param integer $skip number of results to skip * @param integer $skip number of results to skip
* @return array array of results * @return array array of results
*/ */
public function SearchFiles($pattern, $count = 100, $skip = 0) public function SearchFiles($pattern, $count = 100, $skip = 0)
{ {
if (empty($pattern)) if (empty($pattern))
return; return;
   
$grepresults = $this->SearchFileContents($pattern); $grepresults = $this->SearchFileContents($pattern);
   
$nameresults = $this->SearchFilenames($pattern); $nameresults = $this->SearchFilenames($pattern);
   
/* Merge the results together */ /* Merge the results together */
foreach ($nameresults as $path => $obj) { foreach ($nameresults as $path => $obj) {
if (!isset($grepresults[$path]['object'])) { if (!isset($grepresults[$path]['object'])) {
$grepresults[$path]['object'] = $obj; $grepresults[$path]['object'] = $obj;
} }
} }
   
ksort($grepresults); ksort($grepresults);
   
return array_slice($grepresults, $skip, $count, true); return array_slice($grepresults, $skip, $count, true);
} }
   
/** /**
* ReferenceParents * ReferenceParents
* *
* Turns the list of parents into reference pointers * Turns the list of parents into reference pointers
* *
* @access private * @access private
*/ */
private function ReferenceParents() private function ReferenceParents()
{ {
if ($this->parentsReferenced) if ($this->parentsReferenced)
return; return;
   
if ((!isset($this->parents)) || (count($this->parents) < 1)) if ((!isset($this->parents)) || (count($this->parents) < 1))
return; return;
   
for ($i = 0; $i < count($this->parents); $i++) { for ($i = 0; $i < count($this->parents); $i++) {
$this->parents[$i] = $this->parents[$i]->GetHash(); $this->parents[$i] = $this->parents[$i]->GetHash();
} }
   
$this->parentsReferenced = true; $this->parentsReferenced = true;
} }
   
/** /**
* DereferenceParents * DereferenceParents
* *
* Turns the list of parent pointers back into objects * Turns the list of parent pointers back into objects
* *
* @access private * @access private
*/ */
private function DereferenceParents() private function DereferenceParents()
{ {
if (!$this->parentsReferenced) if (!$this->parentsReferenced)
return; return;
   
if ((!$this->parents) || (count($this->parents) < 1)) if ((!$this->parents) || (count($this->parents) < 1))
return; return;
   
for ($i = 0; $i < count($this->parents); $i++) { for ($i = 0; $i < count($this->parents); $i++) {
$this->parents[$i] = $this->GetProject()->GetCommit($this->parents[$i]); $this->parents[$i] = $this->GetProject()->GetCommit($this->parents[$i]);
} }
   
$this->parentsReferenced = false; $this->parentsReferenced = false;
} }
   
/** /**
* ReferenceTree * ReferenceTree
* *
* Turns the tree into a reference pointer * Turns the tree into a reference pointer
* *
* @access private * @access private
*/ */
private function ReferenceTree() private function ReferenceTree()
{ {
if ($this->treeReferenced) if ($this->treeReferenced)
return; return;
   
if (!$this->tree) if (!$this->tree)
return; return;
   
$this->tree = $this->tree->GetHash(); $this->tree = $this->tree->GetHash();
   
$this->treeReferenced = true; $this->treeReferenced = true;
} }
   
/** /**
* DereferenceTree * DereferenceTree
* *
* Turns the tree pointer back into an object * Turns the tree pointer back into an object
* *
* @access private * @access private
*/ */
private function DereferenceTree() private function DereferenceTree()
{ {
if (!$this->treeReferenced) if (!$this->treeReferenced)
return; return;
   
if (empty($this->tree)) if (empty($this->tree))
return; return;
   
$this->tree = $this->GetProject()->GetTree($this->tree); $this->tree = $this->GetProject()->GetTree($this->tree);
   
if ($this->tree) if ($this->tree)
$this->tree->SetCommit($this); $this->tree->SetCommit($this);
   
$this->treeReferenced = false; $this->treeReferenced = 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->parentsReferenced) if (!$this->parentsReferenced)
$this->ReferenceParents(); $this->ReferenceParents();
   
if (!$this->treeReferenced) if (!$this->treeReferenced)
$this->ReferenceTree(); $this->ReferenceTree();
   
$properties = array('dataRead', 'parents', 'tree', 'author', 'authorEpoch', 'authorTimezone', 'committer', 'committerEpoch', 'committerTimezone', 'title', 'comment', 'readTree', 'blobPaths', 'treePaths', 'hashPathsRead', 'parentsReferenced', 'treeReferenced'); $properties = array('dataRead', 'parents', 'tree', 'author', 'authorEpoch', 'authorTimezone', 'committer', 'committerEpoch', 'committerTimezone', 'title', 'comment', 'readTree', 'blobPaths', 'treePaths', 'hashPathsRead', 'parentsReferenced', 'treeReferenced');
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 .= 'commit|' . $this->hash; $key .= 'commit|' . $this->hash;
   
return $key; return $key;
} }
   
/** /**
* CompareAge * CompareAge
* *
* Compares two commits by age * Compares two commits by age
* *
* @access public * @access public
* @static * @static
* @param mixed $a first commit * @param mixed $a first commit
* @param mixed $b second commit * @param mixed $b second commit
* @return integer comparison result * @return integer comparison result
*/ */
public static function CompareAge($a, $b) public static function CompareAge($a, $b)
{ {
if ($a->GetAge() === $b->GetAge()) if ($a->GetAge() === $b->GetAge())
return 0; return 0;
return ($a->GetAge() < $b->GetAge() ? -1 : 1); return ($a->GetAge() < $b->GetAge() ? -1 : 1);
} }
   
} }
   
comments