Implement pagination interface on history
Implement pagination interface on history

<?php <?php
/** /**
* Controller for displaying file history * Controller for displaying file history
* *
* @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 Controller * @subpackage Controller
*/ */
class GitPHP_Controller_History extends GitPHP_ControllerBase class GitPHP_Controller_History extends GitPHP_ControllerBase
{ {
   
/** /**
* Initialize controller * Initialize controller
*/ */
public function Initialize() public function Initialize()
{ {
parent::Initialize(); parent::Initialize();
   
if (empty($this->params['hash'])) if (empty($this->params['hash']))
$this->params['hash'] = 'HEAD'; $this->params['hash'] = 'HEAD';
} }
   
/** /**
* Gets the template for this controller * Gets the template for this controller
* *
* @return string template filename * @return string template filename
*/ */
protected function GetTemplate() protected function GetTemplate()
{ {
return 'history.tpl'; return 'history.tpl';
} }
   
/** /**
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @return string cache key * @return string cache key
*/ */
protected function GetCacheKey() protected function GetCacheKey()
{ {
return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : ''); return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? sha1($this->params['file']) : '');
} }
   
/** /**
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public function GetName($local = false) public function GetName($local = false)
{ {
if ($local && $this->resource) { if ($local && $this->resource) {
return $this->resource->translate('history'); return $this->resource->translate('history');
} }
return 'history'; return 'history';
} }
   
/** /**
* Loads data for this template * Loads data for this template
*/ */
protected function LoadData() protected function LoadData()
{ {
$co = $this->GetProject()->GetCommit($this->params['hash']); $co = $this->GetProject()->GetCommit($this->params['hash']);
$this->tpl->assign('commit', $co); $this->tpl->assign('commit', $co);
$tree = $co->GetTree(); $tree = $co->GetTree();
$this->tpl->assign('tree', $co->GetTree()); $this->tpl->assign('tree', $co->GetTree());
   
$blobhash = $tree->PathToHash($this->params['file']); $blobhash = $tree->PathToHash($this->params['file']);
$blob = $this->GetProject()->GetObjectManager()->GetBlob($blobhash); $blob = $this->GetProject()->GetObjectManager()->GetBlob($blobhash);
$blob->SetCommit($co); $blob->SetCommit($co);
$blob->SetPath($this->params['file']); $blob->SetPath($this->params['file']);
$this->tpl->assign('blob', $blob); $this->tpl->assign('blob', $blob);
   
$history = new GitPHP_FileHistory($this->GetProject(), $co, $this->params['file'], $this->exe); $history = new GitPHP_FileHistory($this->GetProject(), $this->params['file'], $this->exe, $co);
$this->tpl->assign('history', $history); $this->tpl->assign('history', $history);
} }
   
} }
   
<?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;
  }
} }
   
comments