# ***** END LICENSE BLOCK ***** */␊ |
␊ |
/**␊ |
* Monotone utils.␊ |
* Monotone stdio class␊ |
*␊ |
* Connects to a monotone process and executes commands via its␊ |
* stdio interface␊ |
*␊ |
* @author Thomas Keller <me@thomaskeller.biz>␊ |
*/␊ |
␊ |
class IDF_Scm_Monotone_Stdio␊ |
{␊ |
/** this is the most recent STDIO version. The number is output␊ |
at the protocol start. Older versions of monotone (prior 0.47)␊ |
do not output it and are therefor incompatible */␊ |
public static $SUPPORTED_STDIO_VERSION = 2;␊ |
␊ |
private $repo;␊ |
|
private $cmdnum;␊ |
private $lastcmd;␊ |
␊ |
/**␊ |
* Constructor - starts the stdio process␊ |
*␊ |
* @param string Repository path␊ |
*/␊ |
public function __construct($repo)␊ |
{␊ |
$this->repo = $repo;␊ |
$this->start();␊ |
}␊ |
␊ |
/**␊ |
* Destructor - stops the stdio process␊ |
*/␊ |
public function __destruct()␊ |
{␊ |
$this->stop();␊ |
}␊ |
␊ |
/**␊ |
* Starts the stdio process and resets the command counter␊ |
*/␊ |
public function start()␊ |
{␊ |
if (is_resource($this->proc))␊ |
|
$this->cmdnum = -1;␊ |
}␊ |
␊ |
/**␊ |
* Stops the stdio process and closes all pipes␊ |
*/␊ |
public function stop()␊ |
{␊ |
if (!is_resource($this->proc))␊ |
|
$this->proc = null;␊ |
}␊ |
␊ |
/**␊ |
* select()'s on stdout and returns true as soon as we got new␊ |
* data to read, false if the select() timed out␊ |
*␊ |
* @return boolean␊ |
* @throws IDF_Scm_Exception␊ |
*/␊ |
private function _waitForReadyRead()␊ |
{␊ |
if (!is_resource($this->pipes[1]))␊ |
|
return true;␊ |
}␊ |
␊ |
/**␊ |
* Checks the version of the used stdio protocol␊ |
*␊ |
* @throws IDF_Scm_Exception␊ |
*/␊ |
private function _checkVersion()␊ |
{␊ |
$this->_waitForReadyRead();␊ |
|
fgets($this->pipes[1]);␊ |
}␊ |
␊ |
private function _write($args, $options = array())␊ |
/**␊ |
* Writes a command to stdio␊ |
*␊ |
* @param array␊ |
* @param array␊ |
* @throws IDF_Scm_Exception␊ |
*/␊ |
private function _write(array $args, array $options = array())␊ |
{␊ |
$cmd = "";␊ |
if (count($options) > 0)␊ |
|
$this->cmdnum++;␊ |
}␊ |
␊ |
/**␊ |
* Reads the last output from the stdio process, parses and returns it␊ |
*␊ |
* @return string␊ |
* @throws IDF_Scm_Exception␊ |
*/␊ |
private function _read()␊ |
{␊ |
$this->oob = array('w' => array(),␊ |
|
return $output;␊ |
}␊ |
␊ |
public function exec($args, $options = array())␊ |
/**␊ |
* Executes a command over stdio and returns its result␊ |
*␊ |
* @param array Array of arguments␊ |
* @param array Array of options as key-value pairs. Multiple options␊ |
* can be defined in sub-arrays, like␊ |
* "r" => array("123...", "456...")␊ |
* @return string␊ |
*/␊ |
public function exec(array $args, array $options = array())␊ |
{␊ |
$this->_write($args, $options);␊ |
return $this->_read();␊ |
}␊ |
␊ |
public function getLastWarnings()␊ |
{␊ |
return array_key_exists('w', $this->oob) ?␊ |
$this->oob['w'] : array();␊ |
}␊ |
␊ |
public function getLastProgress()␊ |
{␊ |
return array_key_exists('p', $this->oob) ?␊ |
$this->oob['p'] : array();␊ |
}␊ |
␊ |
public function getLastTickers()␊ |
{␊ |
return array_key_exists('t', $this->oob) ?␊ |
$this->oob['t'] : array();␊ |
}␊ |
␊ |
public function getLastErrors()␊ |
/**␊ |
* Returns the last out-of-band output for a previously executed␊ |
* command as associative array with 'e' (error), 'w' (warning),␊ |
* 'p' (progress) and 't' (ticker, unparsed) as keys␊ |
*␊ |
* @return array␊ |
*/␊ |
public function getLastOutOfBandOutput()␊ |
{␊ |
return array_key_exists('e', $this->oob) ?␊ |
$this->oob['e'] : array();␊ |
return $this->oob;␊ |
}␊ |
}␊ |
␊ |
/**␊ |
* Monotone scm class␊ |
*␊ |
* @author Thomas Keller <me@thomaskeller.biz>␊ |
*/␊ |
class IDF_Scm_Monotone extends IDF_Scm␊ |
{␊ |
/** the minimum supported interface version */␊ |
public static $MIN_INTERFACE_VERSION = 12.0;␊ |
␊ |
private $stdio;␊ |
␊ |
/* ============================================== *␊ |
* *␊ |
* Common Methods Implemented By All The SCMs *␊ |
* *␊ |
* ============================================== */␊ |
␊ |
/**␊ |
* @see IDF_Scm::__construct()␊ |
*/␊ |
public function __construct($repo, $project=null)␊ |
{␊ |
$this->repo = $repo;␊ |
|
$this->stdio = new IDF_Scm_Monotone_Stdio($repo);␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::getRepositorySize()␊ |
*/␊ |
public function getRepositorySize()␊ |
{␊ |
if (!file_exists($this->repo)) {␊ |
return 0;␊ |
}␊ |
␊ |
// FIXME: this won't work with remote databases - upstream␊ |
// needs to implement mtn db info in automate at first␊ |
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '␊ |
.escapeshellarg($this->repo);␊ |
$out = explode(' ',␊ |
|
return (int) $out[0]*1024;␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::isAvailable()␊ |
*/␊ |
public function isAvailable()␊ |
{␊ |
try␊ |
|
return false;␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::getBranches()␊ |
*/␊ |
public function getBranches()␊ |
{␊ |
if (isset($this->cache['branches'])) {␊ |
return $this->cache['branches'];␊ |
}␊ |
// FIXME: introduce handling of suspended branches␊ |
// FIXME: we could / should introduce handling of suspended␊ |
// (i.e. dead) branches here by hiding them from the user's eye...␊ |
$out = $this->stdio->exec(array("branches"));␊ |
␊ |
// FIXME: we could expand each branch with one of its head revisions␊ |
// note: we could expand each branch with one of its head revisions␊ |
// here, but these would soon become bogus anyway and we cannot␊ |
// map multiple head revisions here either, so we just use the␊ |
// selector as placeholder␊ |
|
␊ |
/**␊ |
* monotone has no concept of a "main" branch, so just return␊ |
* the first one (the branch list is already sorted)␊ |
* the confiured one␊ |
*␊ |
* @return string␊ |
* @see IDF_Scm::getMainBranch()␊ |
*/␊ |
public function getMainBranch()␊ |
{␊ |
$branches = $this->getBranches();␊ |
return key($branches);␊ |
$conf = $this->project->getConf();␊ |
if (false === ($branch = $conf->getVal('mtn_master_branch', false))␊ |
|| empty($branch)) {␊ |
$branch = "*";␊ |
}␊ |
return $branch;␊ |
}␊ |
␊ |
/**␊ |
|
return $stanzas;␊ |
}␊ |
␊ |
/**␊ |
* Queries the certs for a given revision and returns them in an␊ |
* associative array array("branch" => array("branch1", ...), ...)␊ |
*␊ |
* @param string␊ |
* @param array␊ |
*/␊ |
private function _getCerts($rev)␊ |
{␊ |
static $certCache = array();␊ |
|
return $certCache[$rev];␊ |
}␊ |
␊ |
/**␊ |
* Returns unique certificate values for the given revs and the specific␊ |
* cert name␊ |
*␊ |
* @param array␊ |
* @param string␊ |
* @return array␊ |
*/␊ |
private function _getUniqueCertValuesFor($revs, $certName)␊ |
{␊ |
$certValues = array();␊ |
|
return array_unique($certValues);␊ |
}␊ |
␊ |
/**␊ |
* Returns the revision in which the file has been last changed,␊ |
* starting from the start rev␊ |
*␊ |
* @param string␊ |
* @param string␊ |
* @return string␊ |
*/␊ |
private function _getLastChangeFor($file, $startrev)␊ |
{␊ |
$out = $this->stdio->exec(array(␊ |
|
$stanzas = self::_parseBasicIO($out);␊ |
␊ |
// FIXME: we only care about the first returned content mark␊ |
// everything else seem to be very rare cases␊ |
// everything else seem to be very, very rare cases␊ |
foreach ($stanzas as $stanza)␊ |
{␊ |
foreach ($stanza as $stanzaline)␊ |
|
␊ |
/**␊ |
* @see IDF_Scm::inBranches()␊ |
**/␊ |
*/␊ |
public function inBranches($commit, $path)␊ |
{␊ |
$revs = $this->_resolveSelector($commit);␊ |
|
␊ |
/**␊ |
* @see IDF_Scm::getTags()␊ |
**/␊ |
*/␊ |
public function getTags()␊ |
{␊ |
if (isset($this->cache['tags']))␊ |
|
␊ |
/**␊ |
* @see IDF_Scm::inTags()␊ |
**/␊ |
*/␊ |
public function inTags($commit, $path)␊ |
{␊ |
$revs = $this->_resolveSelector($commit);␊ |
|
}␊ |
␊ |
/**␊ |
* Given the string describing the author from the log find the␊ |
* author in the database.␊ |
*␊ |
* @param string Author␊ |
* @return mixed Pluf_User or null␊ |
* @see IDF_Scm::findAuthor()␊ |
*/␊ |
public function findAuthor($author)␊ |
{␊ |
|
return null;␊ |
}␊ |
␊ |
private static function _getMasterBranch($project)␊ |
{␊ |
$conf = $project->getConf();␊ |
if (false === ($branch = $conf->getVal('mtn_master_branch', false))␊ |
|| empty($branch)) {␊ |
$branch = "*";␊ |
}␊ |
return $branch;␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::getAnonymousAccessUrl()␊ |
*/␊ |
public static function getAnonymousAccessUrl($project, $commit = null)␊ |
{␊ |
$branch = self::_getMasterBranch($project);␊ |
$scm = IDF_Scm::get($project);␊ |
$branch = $scm->getMainBranch();␊ |
␊ |
if (!empty($commit))␊ |
{␊ |
$scm = IDF_Scm::get($project);␊ |
$revs = $scm->_resolveSelector($commit);␊ |
if (count($revs) > 0)␊ |
{␊ |
|
)." ".$branch;␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::getAuthAccessUrl()␊ |
*/␊ |
public static function getAuthAccessUrl($project, $user, $commit = null)␊ |
{␊ |
return self::getAnonymousAccessUrl($project, $commit);␊ |
|
return new IDF_Scm_Monotone($rep, $project);␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::isValidRevision()␊ |
*/␊ |
public function isValidRevision($commit)␊ |
{␊ |
$revs = $this->_resolveSelector($commit);␊ |
|
}␊ |
␊ |
/**␊ |
* Get the file info.␊ |
*␊ |
* @param string File␊ |
* @param string Commit ('HEAD')␊ |
* @return false Information␊ |
* @see IDF_Scm::getPathInfo()␊ |
*/␊ |
public function getPathInfo($file, $commit = null)␊ |
{␊ |
if ($commit === null) {␊ |
$commit = 'h:' . self::_getMasterBranch($this->project);␊ |
$commit = 'h:' . $this->getMainBranch();␊ |
}␊ |
␊ |
$revs = $this->_resolveSelector($commit);␊ |
|
return false;␊ |
}␊ |
␊ |
/**␊ |
* @see IDF_Scm::getFile()␊ |
*/␊ |
public function getFile($def, $cmd_only=false)␊ |
{␊ |
// this won't work with remote databases␊ |
|
return $this->stdio->exec(array("get_file", $def->hash));␊ |
}␊ |
␊ |
/**␊ |
* Returns the differences between two revisions as unified diff␊ |
*␊ |
* @param string The target of the diff␊ |
* @param string The source of the diff, if not given, the first␊ |
* parent of the target is used␊ |
* @return string␊ |
*/␊ |
private function _getDiff($target, $source = null)␊ |
{␊ |
if (empty($source))␊ |
|
}␊ |
␊ |
/**␊ |
* Get commit details.␊ |
*␊ |
* @param string Commit␊ |
* @param bool Get commit diff (false)␊ |
* @return array Changes␊ |
* @see IDF_Scm::getCommit()␊ |
*/␊ |
public function getCommit($commit, $getdiff=false)␊ |
{␊ |
|
}␊ |
␊ |
/**␊ |
* Check if a commit is big.␊ |
*␊ |
* @param string Commit ('HEAD')␊ |
* @return bool The commit is big␊ |
* @see IDF_Scm::isCommitLarge()␊ |
*/␊ |
public function isCommitLarge($commit=null)␊ |
{␊ |
if (empty($commit))␊ |
{␊ |
$commit = "h:"+self::_getMasterBranch($this->project);␊ |
$commit = "h:"+$this->getMainBranch();␊ |
}␊ |
␊ |
$revs = $this->_resolveSelector($commit);␊ |
|
}␊ |
␊ |
/**␊ |
* Get latest changes.␊ |
*␊ |
* @param string Commit ('HEAD').␊ |
* @param int Number of changes (10).␊ |
* @return array Changes.␊ |
* @see IDF_Scm::getChangeLog()␊ |
*/␊ |
public function getChangeLog($commit=null, $n=10)␊ |
{␊ |
|
return $logs;␊ |
}␊ |
}␊ |
␊ |