Clean up how we handle instantiation of the File object.
Remove all PEAR_Error code - use Exceptions instead.
Move valid revision determination into base class.
protected $_branches = false;
/**
+ * @var integer
+ */
+ protected $_cacheVersion = 3;
+
+ /**
* Attempts to return a concrete Horde_Vcs instance based on $driver.
*
* @param mixed $driver The type of concrete Horde_Vcs subclass to return.
return new $class($params);
}
- return PEAR::raiseError($class . ' not found.');
+ throw new Horde_Vcs_Exception($class . ' not found.');
}
/**
}
/**
- * Return the source root for this repository, with no trailing /
+ * Return the source root for this repository, with no trailing /.
*
* @return string Source root for this repository.
*/
}
/**
- * Validation function to ensure that a revision number is of the right
+ * Validation function to ensure that a revision string is of the right
* form.
*
- * @param mixed $rev The purported revision number.
+ * @param mixed $rev The purported revision string.
*
- * @return boolean True if it is a revision number.
+ * @return boolean True if a valid revision string.
*/
public function isValidRevision($rev)
{
- $rev_ob = $this->getRevisionObject();
- return $rev_ob->valid($rev);
+ return true;
}
/**
* $opts:
* 'cache' - (boolean)
* 'quicklog' - (boolean)
+ * 'branch' - (string)
*/
public function getFileObject($filename, $opts = array())
{
$class = 'Horde_Vcs_File_' . $this->_driver;
- $vc_file = new $class($this, $filename, $opts);
- return $vc_file->getFileObject();
+
+ /* The version of the cached data. Increment this whenever the
+ * internal storage format changes, such that we must
+ * invalidate prior cached data. */
+ sort($opts);
+ $cacheId = implode('|', array($class, $this->sourceroot(), $filename, serialize($opts), $this->_cacheVersion));
+
+ $ctime = time() - filemtime($filename);
+ if (!empty($opts['cache']) &&
+ $opts['cache']->exists($cacheId, $ctime)) {
+ $fileOb = unserialize($opts['cache']->get($cacheId, $ctime));
+ $fileOb->setRepository($this);
+ } else {
+ $fileOb = new $class($this, $filename, $opts);
+ $fileOb->applySort(self::SORT_AGE);
+
+ if (!empty($opts['cache'])) {
+ $opts['cache']->set($cacheId, serialize($fileOb));
+ }
+ }
+
+ return $fileOb;
}
public function getAnnotateObject($filename)
public $branches = array();
public $revob;
- public function getFileObject()
- {
- }
-
/**
* TODO
*/
public function queryRevision()
{
if (!isset($this->revs[0])) {
- return PEAR::raiseError('No revisions');
+ throw new Horde_Vcs_Exception('No revisions');
}
return $this->revs[0];
}
public function queryLastLog()
{
if (!isset($this->revs[0]) || !isset($this->logs[$this->revs[0]])) {
- return PEAR::raiseError('No revisions');
+ throw new Horde_Vcs_Exception('No revisions');
}
return $this->logs[$this->revs[0]];
}
class Horde_Vcs_Revision
{
/**
- * Validation function to ensure that a revision string is of the right
- * form.
- *
- * @param mixed $rev The purported revision string.
- *
- * @return boolean True if it is a valid revision string.
- */
- public function valid($rev)
- {
- return true;
- }
-
- /**
* Given a revision string, remove a given number of portions from
* it. For example, if we remove 2 portions of 1.2.3.4, we are
* left with 1.2.
if (substr($filename, 0, 1) != '/') {
$filename = '/' . $filename;
}
- return parent::getFileObject($this->sourceroot() . $filename, $opts);
+
+ $filename = $this->sourceroot() . $filename;
+
+ /* Assume file is in the Attic if it doesn't exist. */
+ $fname = $filename . ',v';
+ if (!@is_file($fname)) {
+ $fname = dirname($filename) . '/Attic/' . basename($filename) . ',v';
+ }
+ return parent::getFileObject($fname, $opts);
}
/**
*/
public function doAnnotate($rev)
{
- if (is_a($this->_file, 'PEAR_Error') ||
- is_a($this->_rep, 'PEAR_Error') ||
- !$this->_rep->isValidRevision($rev)) {
+ if (!$this->_rep->isValidRevision($rev)) {
return false;
}
}
if (!$line) {
- return PEAR::raiseError('Unable to annotate; server said: ' . $line);
+ throw new Horde_Vcs_Exception('Unable to annotate; server said: ' . $line);
}
$lineno = 1;
* to checkout.
* @param string $rev Revision number to check out.
*
- * @return resource|object Either a PEAR_Error object, or a stream
- * pointer to the head of the checkout
+ * @return resource A stream pointer to the head of the checkout.
*/
public function get($rep, $fullname, $rev)
{
if (!$rep->isValidRevision($rev)) {
- return PEAR::raiseError('Invalid revision number');
- }
-
- if (VC_WINDOWS) {
- $mode = 'rb';
- $q_name = '"' . escapeshellcmd(str_replace('\\', '/', $fullname)) . '"';
- } else {
- $mode = 'r';
- $q_name = escapeshellarg($fullname);
+ throw new Horde_Vcs_Exception('Invalid revision number');
}
- if (!($RCS = popen($rep->getPath('co') . " -p$rev $q_name 2>&1", $mode))) {
- return PEAR::raiseError('Couldn\'t perform checkout of the requested file');
+ if (!($RCS = popen($rep->getPath('co') . " -p$rev " . escapeshellarg($fullname) . " 2>&1", VC_WINDOWS ? 'rb' : 'r'))) {
+ throw new Horde_Vcs_Exception('Couldn\'t perform checkout of the requested file');
}
/* First line from co should be of the form :
$co = fgets($RCS, 1024);
if (!preg_match('/^([\S ]+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/', $co, $regs) ||
($regs[1] != $fullname)) {
- return PEAR::raiseError('Unexpected output from checkout: ' . $co);
+ throw new Horde_Vcs_Exception('Unexpected output from checkout: ' . $co);
}
/* Next line from co is of the form:
public function get($rep, $file, $rev1, $rev2, $type = 'context',
$num = 3, $ws = true)
{
- /* Make sure that the file parameter is valid. */
- if (is_a($file, 'PEAR_Error')) {
- return false;
- }
-
/* Check that the revision numbers are valid. */
$rev1 = $rep->isValidRevision($rev1) ? $rev1 : '1.1';
$rev2 = $rep->isValidRevision($rev1) ? $rev2 : '1.1';
* retrieve a list of all the objects in there. It then populates
* the file/directory stack and makes it available for retrieval.
*
- * @return boolean|object PEAR_Error object on an error, true on success.
+ * @return boolean True on success.
*/
public function browseDir($cache = null, $quicklog = true,
$showattic = false)
{
/* Make sure we are trying to list a directory */
if (!@is_dir($this->_dirName)) {
- return PEAR::raiseError('Unable to find directory ' . $this->_dirName);
+ throw new Horde_Vcs_Exception('Unable to find directory ' . $this->_dirName);
}
/* Open the directory for reading its contents */
if (!($DIR = @opendir($this->_dirName))) {
- return PEAR::raiseError(empty($php_errormsg) ? 'Permission denied' : $php_errormsg);
+ throw new Horde_Vcs_Exception(empty($php_errormsg) ? 'Permission denied' : $php_errormsg);
}
/* Create two arrays - one of all the files, and the other of
}
} elseif (@is_file($path) && (substr($name, -2) == ',v')) {
/* Spawn a new file object to represent this file. */
- $fl = $this->_rep->getFileObject(substr($path, strlen($this->_rep->sourceroot()), -2), array('cache' => $cache, 'quicklog' => $quicklog));
- if (is_a($fl, 'PEAR_Error')) {
- return $fl;
- } else {
- $this->_files[] = $fl;
- }
+ $this->_files[] = $this->_rep->getFileObject(substr($path, strlen($this->_rep->sourceroot()), -2), array('cache' => $cache, 'quicklog' => $quicklog));
}
}
/* If we want to merge the attic, add it in here. */
if ($showattic) {
- $atticDir = new Horde_Vcs_Directory_Cvs($this->_rep, $this->_moduleName . '/Attic', $this);
- if (!is_a($atticDir->browseDir($cache, $quicklog), 'PEAR_Error')) {
+ try {
+ $atticDir = new Horde_Vcs_Directory_Cvs($this->_rep, $this->_moduleName . '/Attic', $this);
$this->_atticFiles = $atticDir->queryFileList();
$this->_mergedFiles = array_merge($this->_files, $this->_atticFiles);
- }
+ } catch (Horde_Vcs_Exception $e) {}
}
return true;
$this->name = basename($fl);
$this->dir = dirname($fl);
$this->filename = $fl;
- $this->cache = empty($opts['cache']) ? null : $opts['cache'];
$this->quicklog = !empty($opts['quicklog']);
if (!empty($opts['branch'])) {
$this->_branch = $opts['branch'];
}
- }
-
- public function &getFileObject()
- {
- /* Assume file is in the Attic if it doesn't exist. */
- $filename = $this->filename . ',v';
- if (!@is_file($filename)) {
- $filename = dirname($this->filename) . '/Attic/' . basename($this->filename) . ',v';
- }
-
- /* The version of the cached data. Increment this whenever the
- * internal storage format changes, such that we must
- * invalidate prior cached data. */
- $cacheVersion = 2;
- $cacheId = $this->rep->sourceroot() . '_n' . $filename . '_f' . (int)$this->quicklog . '_v' . $cacheVersion;
-
- $ctime = time() - filemtime($filename);
- if ($this->cache &&
- $this->cache->exists($cacheId, $ctime)) {
- $fileOb = unserialize($this->cache->get($cacheId, $ctime));
- $fileOb->setRepository($this->rep);
- } else {
- if (is_a(($result = $this->getBrowseInfo()), 'PEAR_Error')) {
- return $result;
- }
- $this->applySort(Horde_Vcs::SORT_AGE);
-
- if ($this->cache) {
- $this->cache->set($cacheId, serialize($this));
- }
-
- $fileOb = $this;
- }
-
- return $fileOb;
- }
-
- /**
- * If this file is present in an Attic directory, this indicates it.
- *
- * @return boolean True if file is in the Attic, and false otherwise
- */
- public function isDeleted()
- {
- return (substr($this->dir, -5) == 'Attic');
- }
-
- /**
- * Returns name of the current file without the repository
- * extensions (usually ,v)
- *
- * @return string Filename without repository extension
- */
- public function queryName()
- {
- return preg_replace('/,v$/', '', $this->name);
- }
-
- public function queryPreviousRevision($rev)
- {
- $ob = $this->rep->getRevisionObject();
- return $ob->prev($rev);
- }
-
- /**
- * Return the HEAD (most recent) revision number for this file.
- *
- * @return string HEAD revision number
- */
- public function queryHead()
- {
- return $this->head;
- }
- /**
- * Populate the object with information about the revisions logs and dates
- * of the file.
- *
- * @return boolean|object PEAR_Error object on error, or true on success
- */
- public function getBrowseInfo()
- {
/* Check that we are actually in the filesystem. */
- $file = $this->queryFullPath() . ',v';
+ $file = $this->queryFullPath();
if (!is_file($file)) {
- return PEAR::raiseError('File Not Found: ' . $file);
+ throw new Horde_Vcs_Exception('File Not Found: ' . $file);
}
/* Call the RCS rlog command to retrieve the file
* information. */
$flag = $this->quicklog ? ' -r ' : ' ';
- $q_file = VC_WINDOWS ? '"' . escapeshellcmd($file) . '"' : escapeshellarg($file);
- $cmd = $this->rep->getPath('rlog') . $flag . $q_file;
+ $cmd = $this->rep->getPath('rlog') . $flag . escapeshellarg($file);
exec($cmd, $return_array, $retval);
if ($retval) {
- return PEAR::raiseError('Failed to spawn rlog to retrieve file log information for ' . $file);
+ throw new Horde_Vcs_Exception('Failed to spawn rlog to retrieve file log information for ' . $file);
}
$accum = $revsym = $symrev = array();
}
}
- return true;
+ }
+
+ /**
+ * If this file is present in an Attic directory, this indicates it.
+ *
+ * @return boolean True if file is in the Attic, and false otherwise
+ */
+ public function isDeleted()
+ {
+ return (substr($this->dir, -5) == 'Attic');
+ }
+
+ /**
+ * Returns name of the current file without the repository
+ * extensions (usually ,v)
+ *
+ * @return string Filename without repository extension
+ */
+ public function queryName()
+ {
+ return preg_replace('/,v$/', '', $this->name);
+ }
+
+ /**
+ * TODO
+ */
+ public function queryPreviousRevision($rev)
+ {
+ $ob = $this->rep->getRevisionObject();
+ return $ob->prev($rev);
+ }
+
+ /**
+ * Return the HEAD (most recent) revision number for this file.
+ *
+ * @return string HEAD revision number
+ */
+ public function queryHead()
+ {
+ return $this->head;
}
/**
public function toBranch($rev)
{
/* Check if we have a valid revision number */
- $rev_ob = $this->rep->getRevisionObject();
- if (!$rev_ob->valid($rev)) {
- return false;
- }
-
- if (($end = strrpos($rev, '.')) === false) {
+ if (!$this->rep->isValidRevision($rev) ||
+ (($end = strrpos($rev, '.')) === false)) {
return false;
}
* Populate the object with information about the patchsets that
* this file is involved in.
*
- * @return boolean|object PEAR_Error object on error, or true on success.
+ * @return boolean True on success.
*/
public function getPatchsets()
{
/* Check that we are actually in the filesystem. */
if (!is_file($this->getFullPath() . ',v')) {
- return PEAR::raiseError('File Not Found');
+ throw new Horde_Vcs_Exception('File Not Found');
}
/* Call cvsps to retrieve all patchsets for this file. */
- $q_root = $this->_rep->sourceroot();
- $q_root = VC_WINDOWS ? '"' . escapeshellcmd($q_root) . '"' : escapeshellarg($q_root);
-
$cvsps_home = $this->_rep->getPath('cvsps_home');
$HOME = !empty($cvsps_home) ?
'HOME=' . $cvsps_home . ' ' :
'';
- $cmd = $HOME . $this->_rep->getPath('cvsps') . ' -u --cvs-direct --root ' . $q_root . ' -f ' . escapeshellarg($this->_name) . ' ' . escapeshellarg($this->_dir);
+ $cmd = $HOME . $this->_rep->getPath('cvsps') . ' -u --cvs-direct --root ' . escapeshellarg($this->_rep->sourceroot) . ' -f ' . escapeshellarg($this->_name) . ' ' . escapeshellarg($this->_dir);
exec($cmd, $return_array, $retval);
if ($retval) {
- return PEAR::raiseError('Failed to spawn cvsps to retrieve patchset information');
+ throw new Horde_Vcs_Exception('Failed to spawn cvsps to retrieve patchset information');
}
$this->_patchsets = array();
/**
* TODO
*/
+ public function isValidRevision($rev)
+ {
+ return $rev && preg_match('/^[a-f0-9]+$/i', $rev);
+ }
+
+ /**
+ * TODO
+ */
public function isFile($where)
{
- $command = $this->_rep->getCommand() . ' ls-tree master ' . escapeshellarg($where) . ' 2>&1';
+ $where = str_replace($this->sourceroot() . '/', '', $where);
+ $command = $this->getCommand() . ' ls-tree master ' . escapeshellarg($where) . ' 2>&1';
$entry = array();
exec($command, $entry);
$data = explode(' ', $entry[0]);
*/
class Horde_Vcs_Annotate_Git extends Horde_Vcs_Annotate
{
- public function __construct($rep, $file)
- {
- if (is_a($file, 'PEAR_Error')) {
- throw new Horde_Vcs_Exception($file->getMessage());
- }
- parent::__construct($rep, $file);
- }
-
/**
* TODO
*/
* to checkout
* @param string $rev Revision number to check out
*
- * @return resource|object Either a PEAR_Error object, or a stream
- * pointer to the head of the checkout.
+ * @return resource A stream pointer to the head of the checkout.
*/
function get($rep, $file, $rev)
{
public function get($rep, $file, $rev1, $rev2, $type = 'context',
$num = 3, $ws = true)
{
- /* Make sure that the file parameter is valid */
- if (is_a($file, 'PEAR_Error')) {
- return false;
- }
-
/* Check that the revision numbers are valid */
$rev1 = $rep->isValidRevision($rev1) ? $rev1 : 0;
$rev2 = $rep->isValidRevision($rev1) ? $rev2 : 0;
* retrieve a list of all the objects in there. It then populates
* the file/directory stack and makes it available for retrieval.
*
- * @return PEAR_Error object on an error, 1 on success.
+ * @return integer 1 on success.
*/
public function browseDir($cache = null, $quicklog = true,
$showattic = false)
{
/* @TODO */
protected $_branch;
+ public $cache;
/**
* Create a repository file object, and give it information about
if (!empty($opts['branch'])) {
$this->_branch = $opts['branch'];
}
- }
- /**
- * TODO
- */
- public function getFileObject()
- {
// Get the list of revisions that touch this path
$this->revs = $this->_getRevList($this->_branch);
$line = explode(' ', trim($val), 2);
$this->branches[substr($line[1], strrpos($line[1], '/') + 1)] = $line[0];
}
-
- return $this;
- }
-
- protected function _getRevList($branch)
- {
- $cmd = $this->rep->getCommand() . ' rev-list ' . (empty($branch) ? '--branches' : $branch) . ' -- ' . escapeshellarg($this->fullname) . ' 2>&1';
-
- $revisions = shell_exec($cmd);
- if (substr($revisions, 5) == 'fatal') {
- throw new Horde_Vcs_Exception($revisions);
- } elseif (!strlen($revisions)) {
- throw new Horde_Vcs_Exception('No revisions found');
- }
-
- return explode("\n", trim($revisions));
}
/**
return $revs;
}
+ /**
+ * TODO
+ */
+ protected function _getRevList($branch)
+ {
+ $cmd = $this->rep->getCommand() . ' rev-list ' . (empty($branch) ? '--branches' : $branch) . ' -- ' . escapeshellarg($this->fullname) . ' 2>&1';
+
+ $revisions = shell_exec($cmd);
+ if (substr($revisions, 5) == 'fatal') {
+ throw new Horde_Vcs_Exception($revisions);
+ } elseif (!strlen($revisions)) {
+ throw new Horde_Vcs_Exception('No revisions found');
+ }
+
+ return explode("\n", trim($revisions));
+ }
+
}
/**
*
* @return boolean True if it is a revision number.
*/
- public function valid($rev)
- {
- return preg_match('/^[a-f0-9]+$/i', $rev);
- }
/**
* Returns an abbreviated form of the revision, for display.
class Horde_Vcs_Rcs extends Horde_Vcs
{
/**
+ * TODO
+ */
+ public function isValidRevision($rev)
+ {
+ return $rev && preg_match('/^[\d\.]+$/', $rev);
+ }
+
+ /**
* Checks an RCS file in with a specified change log.
*
* @param string $filepath Location of file to check in.
* @param string $user The user name to use for the check in.
* @param boolean $newBinary Does the change involve binary data?
*
- * @return string|object The new revision number on success, or a
- * PEAR_Error object on failure.
+ * @return string The new revision number on success.
*/
public function ci($filepath, $message, $user = null, $newBinary = false)
{
putenv('LOGNAME=guest');
}
- $Q = VC_WINDOWS ? '"' : "'" ;
-
- $ci_cmd = $this->getPath('ci') . ' ' . $Q . $filepath . $Q.' 2>&1';
- $rcs_cmd = $this->getPath('rcs') . ' -i -kb ' . $Q . $filepath . $Q.' 2>&1';
+ $ci_cmd = $this->getPath('ci') . ' ' . escapeshellarg($filepath) . ' 2>&1';
+ $rcs_cmd = $this->getPath('rcs') . ' -i -kb ' . escapeshellarg($filepath) . ' 2>&1';
$output = '';
$message_lines = explode("\n", $message);
fclose($pipes[1]);
proc_close($process);
} else {
- return PEAR::raiseError('Failed to open pipe in ci()');
+ throw new Horde_Vcs_Exception('Failed to open pipe in ci()');
}
if ($newBinary) {
exec($ci_cmd . ' 2>&1', $return_array, $retval);
if ($retval) {
- return PEAR::raiseError("Unable to spawn ci on $filepath from ci()");
+ throw new Horde_Vcs_Exception("Unable to spawn ci on $filepath from ci()");
} else {
foreach ($return_array as $line) {
$output .= $line;
unlock($filepath);
$temp_pos = strpos($output, 'file is unchanged');
if ($temp_pos !== false) {
- return PEAR::raiseError('Check-in Failure: ' . basename($filepath) . ' has not been modified');
+ throw new Horde_Vcs_Exception('Check-in Failure: ' . basename($filepath) . ' has not been modified');
} else {
- return PEAR::raiseError("Failed to checkin $filepath, $ci_cmd, $output");
+ throw new Horde_Vcs_Exception("Failed to checkin $filepath, $ci_cmd, $output");
}
}
}
* @param string $filepath Location of file.
* @param string &$locked_by Returns the username holding the lock.
*
- * @return boolean|object True on success, or a PEAR_Error on failure.
+ * @return boolean True on success.
*/
public function isLocked($filepath, &$locked_by)
{
- $rlog_cmd = $this->getPath('rlog');
- $rlog_flag = ' -L ';
-
- $Q = VC_WINDOWS ? '"' : "'";
-
- $cmd = $rlog_cmd . $rlog_flag . $Q . $filepath . $Q;
+ $cmd = $this->getPath('rlog') . ' -L ' . escapeshellarg($filepath);
- exec($cmd.' 2>&1', $return_array, $retval);
+ exec($cmd . ' 2>&1', $return_array, $retval);
if ($retval) {
- return PEAR::raiseError("Unable to spawn rlog on $filepath from isLocked()");
+ throw new Horde_Vcs_Exception("Unable to spawn rlog on $filepath from isLocked()");
} else {
$output = '';
} elseif (strlen($output) == 0) {
return false;
} else {
- return PEAR::raiseError('Failure running rlog in isLocked()');
+ throw new Horde_Vcs_Exception('Failure running rlog in isLocked()');
}
}
}
* @param string $filepath Location of file.
* @param string $user User name to lock the file with
*
- * @return boolean|object True on success, or a PEAR_Error on failure.
+ * @return boolean True on success.
*/
public function lock($filepath, $user = null)
{
putenv('LOGNAME=guest');
}
- $rcs_cmd = $this->getPath('rcs');
- $rcs_flag = ' -l ';
-
- $Q = VC_WINDOWS ? '"' : "'" ;
- $cmd = $rcs_cmd . $rcs_flag . $Q . $filepath . $Q;
- exec($cmd.' 2>&1', $return_array, $retval);
+ $cmd = $this->getPath('rcs') . ' -l ' . escapeshellarg($filepath);
+ exec($cmd . ' 2>&1', $return_array, $retval);
if ($retval) {
- return PEAR::raiseError('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
+ throw new Horde_Vcs_Exception('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
} else {
$output = '';
foreach ($return_array as $line) {
if ($locked_pos !== false) {
return true;
} else {
- return PEAR::raiseError('Failed to lock "' . $filepath . '" (Ran "' . $cmd . '", got return code ' . $retval . ', output: ' . $output . ')');
+ throw new Horde_Vcs_Exception('Failed to lock "' . $filepath . '" (Ran "' . $cmd . '", got return code ' . $retval . ', output: ' . $output . ')');
}
}
}
* @param string $filepath Location of file.
* @param string $user User name to unlock the file with
*
- * @return boolean|object True on success, or a PEAR_Error on failure.
+ * @return boolean True on success.
*/
public function unlock($filepath, $user = null)
{
putenv('LOGNAME=guest');
}
- $rcs_cmd = $this->getPath('rcs');
- $rcs_flag = ' -u ';
-
- $Q = VC_WINDOWS ? '"' : "'" ;
- $cmd = $rcs_cmd . $rcs_flag . $Q . $filepath . $Q;
+ $cmd = $this->getPath('rcs') . ' -u ' . escapeshellarg($filepath);
exec($cmd . ' 2>&1', $return_array, $retval);
if ($retval) {
- return PEAR::raiseError('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
+ throw new Horde_Vcs_Exception('Failed to spawn rcs ("' . $cmd . '") on "' . $filepath . '" (returned ' . $retval . ')');
} else {
$output = '';
class Horde_Vcs_Revision_Rcs extends Horde_Vcs_Revision
{
/**
- * Validation function to ensure that a revision number is of the right
- * form.
- *
- * @param mixed $rev The purported revision number.
- *
- * @return boolean True if it is a revision number.
- */
- public function valid($rev)
- {
- return $rev && preg_match('/^[\d\.]+$/', $rev);
- }
-
- /**
* Given a revision number, remove a given number of portions from
* it. For example, if we remove 2 portions of 1.2.3.4, we are
* left with 1.2.
return $command;
}
+
+ public function isValidRevision($rev)
+ {
+ return $rev && is_numeric($rev);
+ }
}
/**
*/
public function doAnnotate($rev)
{
- if (is_a($this->_file, 'PEAR_Error') ||
- !$this->_rep->isValidRevision($rev)) {
+ if (!$this->_rep->isValidRevision($rev)) {
return false;
}
$command = $this->_rep->getCommand() . ' annotate -r 1:' . $rev . ' ' . escapeshellarg($this->_file->queryFullPath()) . ' 2>&1';
$pipe = popen($command, 'r');
if (!$pipe) {
- return PEAR::raiseError('Failed to execute svn annotate: ' . $command);
+ throw new Horde_Vcs_Exception('Failed to execute svn annotate: ' . $command);
}
$lines = array();
* Function which returns a file pointing to the head of the requested
* revision of a file.
*
- * @param Horde_Vcs $rep A repository object
+ * @param Horde_Vcs $rep A repository object
* @param string $fullname Fully qualified pathname of the desired file
* to checkout
* @param string $rev Revision number to check out
*
- * @return resource|object Either a PEAR_Error object, or a stream
- * pointer to the head of the checkout.
+ * @return resource A stream pointer to the head of the checkout.
*/
public function get($rep, $fullname, $rev)
{
if (!$rep->isValidRevision($rev)) {
- return PEAR::raiseError('Invalid revision number');
+ throw new Horde_Vcs_Exception('Invalid revision number');
}
return ($RCS = popen($rep->getCommand() . ' cat -r ' . $rev . ' ' . escapeshellarg($fullname) . ' 2>&1', VC_WINDOWS ? 'rb' : 'r'))
? $RCS
- : PEAR::raiseError('Couldn\'t perform checkout of the requested file');
+ : throw new Horde_Vcs_Exception('Couldn\'t perform checkout of the requested file');
}
}
*
* @param Horde_Vcs $rep A repository object.
* @param Horde_Vcs_File $file The desired file.
- * @param string $rev1 Original revision number to compare from.
- * @param string $rev2 New revision number to compare against.
- * @param string $type The type of diff (e.g. 'unified').
- * @param integer $num Number of lines to be used in context and
- * unified diffs.
- * @param boolean $ws Show whitespace in the diff?
+ * @param string $rev1 Original revision number to compare from.
+ * @param string $rev2 New revision number to compare against.
+ * @param string $type The type of diff (e.g. 'unified').
+ * @param integer $num Number of lines to be used in context and
+ * unified diffs.
+ * @param boolean $ws Show whitespace in the diff?
*
* @return string|boolean False on failure, or a string containing the
* diff on success.
public function get($rep, $file, $rev1, $rev2, $type = 'context',
$num = 3, $ws = true)
{
- /* Make sure that the file parameter is valid */
- if (is_a($file, 'PEAR_Error')) {
- return false;
- }
-
/* Check that the revision numbers are valid */
$rev1 = $rep->isValidRevision($rev1) ? $rev1 : 0;
$rev2 = $rep->isValidRevision($rev1) ? $rev2 : 0;
* retrieve a list of all the objects in there. It then populates
* the file/directory stack and makes it available for retrieval.
*
- * @return PEAR_Error object on an error, 1 on success.
+ * @return integer 1 on success.
*/
public function browseDir($cache = null, $quicklog = true,
$showattic = false)
$dir = popen($cmd, 'r');
if (!$dir) {
- return PEAR::raiseError('Failed to execute svn ls: ' . $cmd);
+ throw new Horde_Vcs_Exception('Failed to execute svn ls: ' . $cmd);
}
/* Create two arrays - one of all the files, and the other of
} elseif (substr($line, -1) == '/') {
$this->_dirs[] = substr($line, 0, -1);
} else {
- $fl = $this->_rep->getFileObject($this->queryDir() . "/$line", array('cache' => $cache, 'quicklog' => $quicklog));
- if (is_a($fl, 'PEAR_Error')) {
- return $fl;
- } else {
- $this->_files[] = $fl;
- }
+ $this->_files[] = $this->_rep->getFileObject($this->queryDir() . "/$line", array('cache' => $cache, 'quicklog' => $quicklog));
}
}
pclose($dir);
return $errors
- ? PEAR::raiseError(implode("\n", $errors))
+ ? throw new Horde_Vcs_Exception(implode("\n", $errors))
: true;
}
$this->name = basename($fl);
$this->dir = dirname($fl);
$this->filename = $fl;
- $this->cache = empty($opts['cache']) ? null : $opts['cache'];
$this->quicklog = !empty($opts['quicklog']);
- }
-
- public function getFileObject()
- {
- /* The version of the cached data. Increment this whenever the
- * internal storage format changes, such that we must
- * invalidate prior cached data. */
- $cacheVersion = 2;
- $cacheId = $this->rep->sourceroot() . '_n' . $this->filename . '_f' . (int)$this->quicklog . '_v' . $cacheVersion;
-
- if ($this->cache &&
- // The file is cached for one hour no matter what, because
- // there is no way to determine with Subversion the time
- // the file last changed.
- $this->cache->exists($cacheId, 3600)) {
- $fileOb = unserialize($this->cache->get($cacheId, 3600));
- $fileOb->setRepository($rep);
- } else {
- $fileOb = new Horde_Vcs_File_Svn($this->rep, $this->filename, $this->cache, $this->quicklog);
- if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
- return $result;
- }
- $fileOb->applySort(Horde_Vcs::SORT_AGE);
-
- if ($this->cache) {
- $this->cache->set($cacheId, serialize($fileOb));
- }
- }
-
- return $fileOb;
- }
- /**
- * Returns name of the current file without the repository
- * extensions (usually ,v)
- *
- * @return Filename without repository extension
- */
- function queryName()
- {
- return preg_replace('/,v$/', '', $this->name);
- }
-
- /**
- * Populate the object with information about the revisions logs
- * and dates of the file.
- *
- * @return mixed boolean True on success,
- * PEAR_Error On error.
- */
- function getBrowseInfo()
- {
/* This doesn't work; need to find another way to simply
* request the most recent revision:
*
* $flag = $this->quicklog ? '-r HEAD ' : ''; */
$flag = '';
- $Q = VC_WINDOWS ? '"' : "'";
- $cmd = $this->rep->getCommand() . ' log -v ' . $flag . $Q . str_replace($Q, '\\' . $Q, $this->queryFullPath()) . $Q . ' 2>&1';
+ $cmd = $this->rep->getCommand() . ' log -v ' . $flag . escapeshellarg($this->queryFullPath()) . ' 2>&1';
$pipe = popen($cmd, 'r');
if (!$pipe) {
- return PEAR::raiseError('Failed to execute svn log: ' . $cmd);
+ throw new Horde_Vcs_Exception('Failed to execute svn log: ' . $cmd);
}
$header = fgets($pipe);
if (!strspn($header, '-')) {
- return PEAR::raiseError('Error executing svn log: ' . $header);
+ throw new Horde_Vcs_Exception('Error executing svn log: ' . $header);
}
while (!feof($pipe)) {
}
pclose($pipe);
- return true;
+ }
+
+ /**
+ * Returns name of the current file without the repository
+ * extensions (usually ,v)
+ *
+ * @return Filename without repository extension
+ */
+ function queryName()
+ {
+ return preg_replace('/,v$/', '', $this->name);
}
}
* Populate the object with information about the patchsets that
* this file is involved in.
*
- * @return mixed PEAR_Error object on error, or true on success.
+ * @return boolean True on success.
*/
function getPatchsets()
{
$fileOb = new Horde_Vcs_File_Svn($this->_rep, $this->_file);
- if (is_a(($result = $fileOb->getBrowseInfo()), 'PEAR_Error')) {
- return $result;
- }
$this->_patchsets = array();
foreach ($fileOb->logs as $rev => $log) {
}
}
-
-class Horde_Vcs_Revision_Svn extends Horde_Vcs_Revision_Rcs
-{
- /**
- * Validation function to ensure that a revision number is of the right
- * form.
- *
- * @param mixed $rev The purported revision number.
- *
- * @return boolean True if it is a revision number.
- */
- public function valid($rev)
- {
- return is_numeric($rev);
- }
-}