<?php |
<?php |
/** |
/** |
* Class to load a file's history |
* Class to load a file's history |
* |
* |
* @author Christopher Han <xiphux@gmail.com> |
* @author Christopher Han <xiphux@gmail.com> |
* @copyright Copyright (c) 2012 Christopher Han |
* @copyright Copyright (c) 2012 Christopher Han |
* @package GitPHP |
* @package GitPHP |
* @subpackage Git |
* @subpackage Git |
*/ |
*/ |
class GitPHP_FileHistory implements Iterator |
class GitPHP_FileHistory implements Iterator, GitPHP_Pagination_Interface |
{ |
{ |
/** |
/** |
* The project |
* The project |
* |
* |
* @var GitPHP_Project |
* @var GitPHP_Project |
*/ |
*/ |
protected $project; |
protected $project = null; |
|
|
/** |
/** |
* The commit hash |
* History |
|
* |
|
* @var GitPHP_FileDiff[] |
|
*/ |
|
protected $history = array(); |
|
|
|
/** |
|
* The path |
* |
* |
* @var string |
* @var string |
*/ |
*/ |
protected $commitHash; |
protected $path; |
|
|
/** |
/** |
* The path |
* The limit of objects to load |
|
* |
|
* @var int |
|
*/ |
|
protected $limit = 50; |
|
|
|
/** |
|
* The number of objects to skip |
|
* |
|
* @var int |
|
*/ |
|
protected $skip = 0; |
|
|
|
/** |
|
* The hash to walk back from |
* |
* |
* @var string |
* @var string |
*/ |
*/ |
protected $path; |
protected $hash = false; |
|
|
/** |
|
* The history |
|
* |
|
* @var GitPHP_FileDiff[] |
|
*/ |
|
protected $history = array(); |
|
|
|
/** |
/** |
* Whether data has been loaded |
* Whether data has been loaded |
* |
* |
* @var boolean |
* @var boolean |
*/ |
*/ |
protected $dataLoaded = false; |
protected $dataLoaded = false; |
|
|
/** |
/** |
* Executable |
* Executable |
* |
* |
* @var GitPHP_GitExe |
* @var GitPHP_GitExe |
*/ |
*/ |
protected $exe; |
protected $exe; |
|
|
/** |
/** |
* Constructor |
* Constructor |
* |
* |
* @param GitPHP_Project $project project |
* @param GitPHP_Project $project project |
* @param GitPHP_Commit $commit commit to start history from |
|
* @param string $path file path to trace history of |
* @param string $path file path to trace history of |
* @param GitPHP_GitExe $exe git exe |
* @param GitPHP_GitExe $exe git exe |
*/ |
* @param GitPHP_Commit $head commit to start history from |
public function __construct($project, $commit, $path, $exe) |
*/ |
{ |
public function __construct($project, $path, $exe, $head = null, $limit = 0, $skip = 0) |
if (!$project) |
{ |
|
if (!$project) { |
throw new Exception('Project is required'); |
throw new Exception('Project is required'); |
|
} |
if (!$commit) |
|
throw new Exception('Commit is required'); |
$this->project = $project; |
|
$this->limit = $limit; |
|
$this->skip = $skip; |
|
|
|
if (!$head) |
|
$head = $this->project->GetHeadCommit(); |
|
|
|
if ($head) { |
|
$this->hash = $head->GetHash(); |
|
} |
if (empty($path)) |
if (empty($path)) |
throw new Exception('Path is required'); |
throw new Exception('Path is required'); |
|
|
if (!$exe) |
if (!$exe) |
throw new Exception('Git exe is required'); |
throw new Exception('Git exe is required'); |
|
|
$this->project = $project; |
|
|
|
$this->commitHash = $commit->GetHash(); |
|
|
|
$this->path = $path; |
$this->path = $path; |
|
|
$this->exe = $exe; |
$this->exe = $exe; |
} |
} |
|
|
/** |
/** |
* Gets the project for this file history |
* Gets the path for this file history |
|
* |
|
* @return string path |
|
*/ |
|
public function GetPath() |
|
{ |
|
return $this->path; |
|
} |
|
|
|
/** |
|
* Gets the project |
* |
* |
* @return GitPHP_Project project |
* @return GitPHP_Project project |
*/ |
*/ |
public function GetProject() |
public function GetProject() |
{ |
{ |
return $this->project; |
return $this->project; |
} |
} |
|
|
/** |
/** |
* Gets the commit for this file history |
* Gets the count |
* |
* |
* @return GitPHP_Commit commit |
* @return int count |
*/ |
*/ |
public function GetCommit() |
public function GetCount() |
{ |
{ |
return $this->project->GetCommit($this->commitHash); |
if (!$this->dataLoaded) { |
} |
$this->LoadData(); |
|
} |
/** |
|
* Gets the path for this file history |
return count($this->history); |
* |
} |
* @return string path |
|
*/ |
/** |
public function GetPath() |
* Gets the limit |
{ |
* |
return $this->path; |
* @return int limit |
} |
*/ |
|
public function GetLimit() |
/** |
{ |
* Gets the history |
return $this->limit; |
* |
} |
* @return GitPHP_FileDiff[] history data |
|
*/ |
/** |
public function GetHistory() |
* Sets the limit |
{ |
* |
if (!$this->dataLoaded) |
* @param int $limit limit |
$this->LoadData(); |
*/ |
|
public function SetLimit($limit) |
return $this->history; |
{ |
|
if ($this->limit == $limit) |
|
return; |
|
|
|
if ($this->dataLoaded) { |
|
if (($limit < $this->limit) && ($limit > 0)) { |
|
/* want less data, just trim the array */ |
|
$this->history = array_slice($this->history, 0, $limit); |
|
} else if (($limit > $this->limit) || ($limit < 1)) { |
|
/* want more data, have to reload */ |
|
$this->Clear(); |
|
} |
|
} |
|
|
|
$this->limit = $limit; |
|
} |
|
|
|
/** |
|
* Gets the skip number |
|
* |
|
* @return int skip number |
|
*/ |
|
public function GetSkip() |
|
{ |
|
return $this->skip; |
|
} |
|
|
|
/** |
|
* Sets the skip number |
|
* |
|
* @param int $skip skip number |
|
*/ |
|
public function SetSkip($skip) |
|
{ |
|
if ($skip == $this->skip) |
|
return; |
|
|
|
if ($this->dataLoaded) { |
|
$this->Clear(); |
|
} |
|
|
|
$this->skip = $skip; |
|
} |
|
|
|
/** |
|
* Gets the head this log will walk from |
|
* |
|
* @return GitPHP_Commit head commit |
|
*/ |
|
public function GetHead() |
|
{ |
|
return $this->project->GetCommit($this->hash); |
|
} |
|
|
|
/** |
|
* Sets the head this log will walk from |
|
* |
|
* @param GitPHP_Commit $head head commit |
|
*/ |
|
public function SetHead($head) |
|
{ |
|
if ($head) |
|
$this->SetHeadHash($head->GetHash()); |
|
else |
|
$this->SetHeadHash(null); |
|
} |
|
|
|
/** |
|
* Gets the head hash this log will walk from |
|
* |
|
* @return string hash |
|
*/ |
|
public function GetHeadHash() |
|
{ |
|
return $this->hash; |
|
} |
|
|
|
/** |
|
* Sets the head hash this log will walk from |
|
* |
|
* @param string $hash head commit hash |
|
*/ |
|
public function SetHeadHash($hash) |
|
{ |
|
if (empty($hash)) { |
|
$head = $this->project->GetHeadCommit(); |
|
if ($head) |
|
$hash = $head->GetHash(); |
|
} |
|
|
|
if ($hash != $this->hash) { |
|
$this->Clear(); |
|
$this->hash = $hash; |
|
} |
} |
} |
|
|
/** |
/** |
* Loads the history data |
* Loads the history data |
*/ |
*/ |
private function LoadData() |
protected function LoadData() |
{ |
{ |
$this->dataLoaded = true; |
$this->dataLoaded = true; |
|
|
$args = array(); |
$args = array(); |
$args[] = $this->commitHash; |
$args[] = $this->hash; |
|
|
|
$canSkip = true; |
|
if ($this->skip > 0) |
|
$canSkip = $this->exe->CanSkip(); |
|
|
|
if ($canSkip) { |
|
if ($this->limit > 0) { |
|
$args[] = '--max-count=' . $this->limit; |
|
} |
|
if ($this->skip > 0) { |
|
$args[] = '--skip=' . $skip; |
|
} |
|
} else { |
|
if ($this->limit > 0) { |
|
$args[] = '--max-count=' . ($this->limit + $this->skip); |
|
} |
|
} |
|
|
|
$args[] = '--'; |
|
$args[] = $this->path; |
$args[] = '|'; |
$args[] = '|'; |
$args[] = $this->exe->GetBinary(); |
$args[] = $this->exe->GetBinary(); |
$args[] = '--git-dir=' . $this->project->GetPath(); |
$args[] = '--git-dir=' . $this->project->GetPath(); |
$args[] = GIT_DIFF_TREE; |
$args[] = GIT_DIFF_TREE; |
$args[] = '-r'; |
$args[] = '-r'; |
$args[] = '--stdin'; |
$args[] = '--stdin'; |
$args[] = '--'; |
$args[] = '--'; |
$args[] = $this->path; |
$args[] = $this->path; |
|
|
$historylines = explode("\n", $this->exe->Execute($this->project->GetPath(), GIT_REV_LIST, $args)); |
$historylines = explode("\n", $this->exe->Execute($this->project->GetPath(), GIT_REV_LIST, $args)); |
|
|
$commitHash = null; |
$commitHash = null; |
foreach ($historylines as $line) { |
foreach ($historylines as $line) { |
if (preg_match('/^([0-9a-fA-F]{40})/', $line, $regs)) { |
if (preg_match('/^([0-9a-fA-F]{40})/', $line, $regs)) { |
$commitHash = $regs[1]; |
$commitHash = $regs[1]; |
} else if ($commitHash) { |
} else if ($commitHash) { |
try { |
try { |
$history = $this->GetProject()->GetObjectManager()->GetFileDiff($line); |
$history = $this->GetProject()->GetObjectManager()->GetFileDiff($line); |
$history->SetCommitHash($commitHash); |
$history->SetCommitHash($commitHash); |
$this->history[] = $history; |
$this->history[] = $history; |
} catch (Exception $e) { |
} catch (Exception $e) { |
} |
} |
$commitHash = null; |
$commitHash = null; |
} |
} |
} |
} |
|
|
|
if (($this->skip > 0) && (!$canSkip)) { |
|
if ($this->limit > 0) { |
|
$this->history = array_slice($this->history, $this->skip, $this->limit); |
|
} else { |
|
$this->history = array_slice($this->history, $this->skip); |
|
} |
|
} |
|
|
} |
} |
|
|
/** |
/** |
* Rewinds the iterator |
* Rewinds the iterator |
* |
|
* @return GitPHP_FileDiff |
|
*/ |
*/ |
function rewind() |
function rewind() |
{ |
{ |
if (!$this->dataLoaded) { |
if (!$this->dataLoaded) { |
$this->LoadData(); |
$this->LoadData(); |
} |
} |
|
|
return reset($this->history); |
return reset($this->history); |
} |
} |
|
|
/** |
/** |
* Returns the current revision |
* Returns the current revision |
* |
* |
* @return GitPHP_FileDiff |
* @return GitPHP_Commit |
*/ |
*/ |
function current() |
function current() |
{ |
{ |
if (!$this->dataLoaded) { |
if (!$this->dataLoaded) { |
$this->LoadData(); |
$this->LoadData(); |
} |
} |
|
|
return current($this->history); |
return current($this->history); |
} |
} |
|
|
/** |
/** |
* Returns the current key |
* Returns the current key |
* |
|
* @return int |
|
*/ |
*/ |
function key() |
function key() |
{ |
{ |
if (!$this->dataLoaded) { |
if (!$this->dataLoaded) { |
$this->LoadData(); |
$this->LoadData(); |
} |
} |
|
|
return key($this->history); |
return key($this->history); |
} |
} |
|
|
/** |
/** |
* Advance the pointer |
* Advance the pointer |
* |
|
* @return GitPHP_FileDiff|boolean |
|
*/ |
*/ |
function next() |
function next() |
{ |
{ |
if (!$this->dataLoaded) { |
if (!$this->dataLoaded) { |
$this->LoadData(); |
$this->LoadData(); |
} |
} |
|
|
return next($this->history); |
return next($this->history); |
} |
} |
|
|
/** |
/** |
* Test for a valid pointer |
* Test for a valid pointer |
* |
* |
* @return boolean |
* @return boolean |
*/ |
*/ |
function valid() |
function valid() |
{ |
{ |
if (!$this->dataLoaded) { |
if (!$this->dataLoaded) { |
$this->LoadData(); |
$this->LoadData(); |
} |
} |
|
|
return key($this->history) !== null; |
return key($this->history) !== null; |
} |
} |
|
|
|
/** |
|
* Clears the loaded data |
|
*/ |
|
public function Clear() |
|
{ |
|
if (!$this->dataLoaded) |
|
return; |
|
|
|
$this->history = array(); |
|
reset($this->history); |
|
|
|
$this->dataLoaded = false; |
|
} |
} |
} |
|
|