| // FIXME: this obviously won't work with remote databases - upstream␊ | 
| // needs to implement mtn db info in automate at first␊ | 
| $repo = sprintf(Pluf::f('mtn_repositories'), $this->project->shortname);␊ | 
| if (!file_exists($repo))␊ | 
| {␊ | 
| if (!file_exists($repo)) {␊ | 
| return 0;␊ | 
| }␊ | 
| ␊ | 
|  | 
| {␊ | 
| try␊ | 
| {␊ | 
| $out = $this->stdio->exec(array("interface_version"));␊ | 
| $out = $this->stdio->exec(array('interface_version'));␊ | 
| return floatval($out) >= self::$MIN_INTERFACE_VERSION;␊ | 
| }␊ | 
| catch (IDF_Scm_Exception $e) {}␊ | 
|  | 
| }␊ | 
| // 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"));␊ | 
| $out = $this->stdio->exec(array('branches'));␊ | 
| ␊ | 
| // 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␊ | 
| $res = array();␊ | 
| foreach (preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY) as $b)␊ | 
| {␊ | 
| foreach (preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY) as $b) {␊ | 
| $res["h:$b"] = $b;␊ | 
| }␊ | 
| ␊ | 
|  | 
| $branch = "*";␊ | 
| }␊ | 
| ␊ | 
| if (count($this->_resolveSelector("h:$branch")) == 0)␊ | 
| {␊ | 
| if (count($this->_resolveSelector("h:$branch")) == 0) {␊ | 
| throw new IDF_Scm_Exception(␊ | 
| "Branch $branch is empty"␊ | 
| );␊ | 
|  | 
| */␊ | 
| private function _resolveSelector($selector)␊ | 
| {␊ | 
| $out = $this->stdio->exec(array("select", $selector));␊ | 
| $out = $this->stdio->exec(array('select', $selector));␊ | 
| return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);␊ | 
| }␊ | 
| ␊ | 
|  | 
| $pos = 0;␊ | 
| $stanzas = array();␊ | 
| ␊ | 
| while ($pos < strlen($in))␊ | 
| {␊ | 
| while ($pos < strlen($in)) {␊ | 
| $stanza = array();␊ | 
| while ($pos < strlen($in))␊ | 
| {␊ | 
| while ($pos < strlen($in)) {␊ | 
| if ($in[$pos] == "\n") break;␊ | 
| ␊ | 
| $stanzaLine = array("key" => "", "values" => array(), "hash" => null);␊ | 
| while ($pos < strlen($in))␊ | 
| {␊ | 
| $stanzaLine = array('key' => '', 'values' => array(), 'hash' => null);␊ | 
| while ($pos < strlen($in)) {␊ | 
| $ch = $in[$pos];␊ | 
| if ($ch == '"' || $ch == '[') break;␊ | 
| ++$pos;␊ | 
|  | 
| $stanzaLine['key'] .= $ch;␊ | 
| }␊ | 
| ␊ | 
| if ($in[$pos] == '[')␊ | 
| {␊ | 
| if ($in[$pos] == '[') {␊ | 
| ++$pos; // opening square bracket␊ | 
| $stanzaLine['hash'] = substr($in, $pos, 40);␊ | 
| $pos += 40;␊ | 
|  | 
| else␊ | 
| {␊ | 
| $valCount = 0;␊ | 
| while ($in[$pos] == '"')␊ | 
| {␊ | 
| while ($in[$pos] == '"') {␊ | 
| ++$pos; // opening quote␊ | 
| $stanzaLine['values'][$valCount] = "";␊ | 
| while ($pos < strlen($in))␊ | 
| {␊ | 
| $stanzaLine['values'][$valCount] = '';␊ | 
| while ($pos < strlen($in)) {␊ | 
| $ch = $in[$pos]; $pr = $in[$pos-1];␊ | 
| if ($ch == '"' && $pr != '\\') break;␊ | 
| ++$pos;␊ | 
|  | 
| }␊ | 
| ++$pos; // closing quote␊ | 
| ␊ | 
| if ($in[$pos] == ' ')␊ | 
| {␊ | 
| if ($in[$pos] == ' ') {␊ | 
| ++$pos; // space␊ | 
| ++$valCount;␊ | 
| }␊ | 
| }␊ | 
| ␊ | 
| for ($i = 0; $i <= $valCount; $i++)␊ | 
| {␊ | 
| for ($i = 0; $i <= $valCount; $i++) {␊ | 
| $stanzaLine['values'][$i] = str_replace(␊ | 
| array("\\\\", "\\\""),␊ | 
| array("\\", "\""),␊ | 
|  | 
| {␊ | 
| static $certCache = array();␊ | 
| ␊ | 
| if (!array_key_exists($rev, $certCache))␊ | 
| {␊ | 
| $out = $this->stdio->exec(array("certs", $rev));␊ | 
| if (!array_key_exists($rev, $certCache)) {␊ | 
| $out = $this->stdio->exec(array('certs', $rev));␊ | 
| ␊ | 
| $stanzas = self::_parseBasicIO($out);␊ | 
| $certs = array();␊ | 
| foreach ($stanzas as $stanza)␊ | 
| {␊ | 
| foreach ($stanzas as $stanza) {␊ | 
| $certname = null;␊ | 
| foreach ($stanza as $stanzaline)␊ | 
| {␊ | 
| foreach ($stanza as $stanzaline) {␊ | 
| // luckily, name always comes before value␊ | 
| if ($stanzaline['key'] == "name")␊ | 
| {␊ | 
| if ($stanzaline['key'] == 'name') {␊ | 
| $certname = $stanzaline['values'][0];␊ | 
| continue;␊ | 
| }␊ | 
| ␊ | 
| if ($stanzaline['key'] == "value")␊ | 
| {␊ | 
| if (!array_key_exists($certname, $certs))␊ | 
| {␊ | 
| if ($stanzaline['key'] == 'value') {␊ | 
| if (!array_key_exists($certname, $certs)) {␊ | 
| $certs[$certname] = array();␊ | 
| }␊ | 
| ␊ | 
|  | 
| private function _getUniqueCertValuesFor($revs, $certName, $prefix)␊ | 
| {␊ | 
| $certValues = array();␊ | 
| foreach ($revs as $rev)␊ | 
| {␊ | 
| foreach ($revs as $rev) {␊ | 
| $certs = $this->_getCerts($rev);␊ | 
| if (!array_key_exists($certName, $certs))␊ | 
| continue;␊ | 
| foreach ($certs[$certName] as $certValue)␊ | 
| {␊ | 
| foreach ($certs[$certName] as $certValue) {␊ | 
| $certValues[] = "$prefix$certValue";␊ | 
| }␊ | 
| }␊ | 
|  | 
| private function _getLastChangeFor($file, $startrev)␊ | 
| {␊ | 
| $out = $this->stdio->exec(array(␊ | 
| "get_content_changed", $startrev, $file␊ | 
| 'get_content_changed', $startrev, $file␊ | 
| ));␊ | 
| ␊ | 
| $stanzas = self::_parseBasicIO($out);␊ | 
| ␊ | 
| // FIXME: we only care about the first returned content mark␊ | 
| // everything else seem to be very, very rare cases␊ | 
| foreach ($stanzas as $stanza)␊ | 
| {␊ | 
| foreach ($stanza as $stanzaline)␊ | 
| {␊ | 
| if ($stanzaline['key'] == "content_mark")␊ | 
| {␊ | 
| foreach ($stanzas as $stanza) {␊ | 
| foreach ($stanza as $stanzaline) {␊ | 
| if ($stanzaline['key'] == 'content_mark') {␊ | 
| return $stanzaline['hash'];␊ | 
| }␊ | 
| }␊ | 
|  | 
| {␊ | 
| $revs = $this->_resolveSelector($commit);␊ | 
| if (count($revs) == 0) return array();␊ | 
| return $this->_getUniqueCertValuesFor($revs, "branch", "h:");␊ | 
| return $this->_getUniqueCertValuesFor($revs, 'branch', 'h:');␊ | 
| }␊ | 
| ␊ | 
| /**␊ | 
|  | 
| */␊ | 
| public function getTags()␊ | 
| {␊ | 
| if (isset($this->cache['tags']))␊ | 
| {␊ | 
| if (isset($this->cache['tags'])) {␊ | 
| return $this->cache['tags'];␊ | 
| }␊ | 
| ␊ | 
| $out = $this->stdio->exec(array("tags"));␊ | 
| $out = $this->stdio->exec(array('tags'));␊ | 
| ␊ | 
| $tags = array();␊ | 
| $stanzas = self::_parseBasicIO($out);␊ | 
| foreach ($stanzas as $stanza)␊ | 
| {␊ | 
| foreach ($stanzas as $stanza) {␊ | 
| $tagname = null;␊ | 
| foreach ($stanza as $stanzaline)␊ | 
| {␊ | 
| foreach ($stanza as $stanzaline) {␊ | 
| // revision comes directly after the tag stanza␊ | 
| if ($stanzaline['key'] == "tag")␊ | 
| {␊ | 
| if ($stanzaline['key'] == 'tag') {␊ | 
| $tagname = $stanzaline['values'][0];␊ | 
| continue;␊ | 
| }␊ | 
| if ($stanzaline['key'] == "revision")␊ | 
| {␊ | 
| if ($stanzaline['key'] == 'revision') {␊ | 
| // FIXME: warn if multiple revisions have␊ | 
| // equally named tags␊ | 
| if (!array_key_exists("t:$tagname", $tags))␊ | 
| {␊ | 
| if (!array_key_exists("t:$tagname", $tags)) {␊ | 
| $tags["t:$tagname"] = $tagname;␊ | 
| }␊ | 
| break;␊ | 
|  | 
| {␊ | 
| $revs = $this->_resolveSelector($commit);␊ | 
| if (count($revs) == 0) return array();␊ | 
| return $this->_getUniqueCertValuesFor($revs, "tag", "t:");␊ | 
| return $this->_getUniqueCertValuesFor($revs, 'tag', 't:');␊ | 
| }␊ | 
| ␊ | 
| /**␊ | 
|  | 
| public function getTree($commit, $folder='/', $branch=null)␊ | 
| {␊ | 
| $revs = $this->_resolveSelector($commit);␊ | 
| if (count($revs) == 0)␊ | 
| {␊ | 
| if (count($revs) == 0) {␊ | 
| return array();␊ | 
| }␊ | 
| ␊ | 
| $out = $this->stdio->exec(array(␊ | 
| "get_manifest_of", $revs[0]␊ | 
| 'get_manifest_of', $revs[0]␊ | 
| ));␊ | 
| ␊ | 
| $files = array();␊ | 
| $stanzas = self::_parseBasicIO($out);␊ | 
| $folder = $folder == '/' || empty($folder) ? '' : $folder.'/';␊ | 
| ␊ | 
| foreach ($stanzas as $stanza)␊ | 
| {␊ | 
| if ($stanza[0]['key'] == "format_version")␊ | 
| foreach ($stanzas as $stanza) {␊ | 
| if ($stanza[0]['key'] == 'format_version')␊ | 
| continue;␊ | 
| ␊ | 
| $path = $stanza[0]['values'][0];␊ | 
|  | 
| $file['fullpath'] = $path;␊ | 
| $file['efullpath'] = self::smartEncode($path);␊ | 
| ␊ | 
| if ($stanza[0]['key'] == "dir")␊ | 
| {␊ | 
| $file['type'] = "tree";␊ | 
| if ($stanza[0]['key'] == 'dir') {␊ | 
| $file['type'] = 'tree';␊ | 
| $file['size'] = 0;␊ | 
| }␊ | 
| else␊ | 
| {␊ | 
| $file['type'] = "blob";␊ | 
| $file['type'] = 'blob';␊ | 
| $file['hash'] = $stanza[1]['hash'];␊ | 
| $file['size'] = strlen($this->getFile((object)$file));␊ | 
| }␊ | 
| ␊ | 
| $rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);␊ | 
| if ($rev !== null)␊ | 
| {␊ | 
| if ($rev !== null) {␊ | 
| $file['rev'] = $rev;␊ | 
| $certs = $this->_getCerts($rev);␊ | 
| ␊ | 
|  | 
| $scm = IDF_Scm::get($project);␊ | 
| $branch = $scm->getMainBranch();␊ | 
| ␊ | 
| if (!empty($commit))␊ | 
| {␊ | 
| if (!empty($commit)) {␊ | 
| $revs = $scm->_resolveSelector($commit);␊ | 
| if (count($revs) > 0)␊ | 
| {␊ | 
| if (count($revs) > 0) {␊ | 
| $certs = $scm->_getCerts($revs[0]);␊ | 
| // for the very seldom case that a revision␊ | 
| // has no branch certificate␊ | 
| if (count($certs['branch']) == 0)␊ | 
| {␊ | 
| $branch = "*";␊ | 
| if (count($certs['branch']) == 0) {␊ | 
| $branch = '*';␊ | 
| }␊ | 
| else␊ | 
| {␊ | 
|  | 
| }␊ | 
| ␊ | 
| $remote_url = Pluf::f('mtn_remote_url', '');␊ | 
| if (empty($remote_url))␊ | 
| {␊ | 
| if (empty($remote_url)) {␊ | 
| return '';␊ | 
| }␊ | 
| ␊ | 
| return sprintf($remote_url, $project->shortname)."?".$branch;␊ | 
| return sprintf($remote_url, $project->shortname).'?'.$branch;␊ | 
| }␊ | 
| ␊ | 
| /**␊ | 
|  | 
| */␊ | 
| public static function factory($project)␊ | 
| {␊ | 
| if (!array_key_exists($project->shortname, self::$instances))␊ | 
| {␊ | 
| if (!array_key_exists($project->shortname, self::$instances)) {␊ | 
| self::$instances[$project->shortname] =␊ | 
| new IDF_Scm_Monotone($project);␊ | 
| }␊ | 
|  | 
| return false;␊ | 
| ␊ | 
| $out = $this->stdio->exec(array(␊ | 
| "get_manifest_of", $revs[0]␊ | 
| 'get_manifest_of', $revs[0]␊ | 
| ));␊ | 
| ␊ | 
| $files = array();␊ | 
| $stanzas = self::_parseBasicIO($out);␊ | 
| ␊ | 
| foreach ($stanzas as $stanza)␊ | 
| {␊ | 
| if ($stanza[0]['key'] == "format_version")␊ | 
| foreach ($stanzas as $stanza) {␊ | 
| if ($stanza[0]['key'] == 'format_version')␊ | 
| continue;␊ | 
| ␊ | 
| $path = $stanza[0]['values'][0];␊ | 
|  | 
| $file = array();␊ | 
| $file['fullpath'] = $path;␊ | 
| ␊ | 
| if ($stanza[0]['key'] == "dir")␊ | 
| {␊ | 
| if ($stanza[0]['key'] == "dir") {␊ | 
| $file['type'] = "tree";␊ | 
| $file['hash'] = null;␊ | 
| $file['size'] = 0;␊ | 
| }␊ | 
| else␊ | 
| {␊ | 
| $file['type'] = "blob";␊ | 
| $file['type'] = 'blob';␊ | 
| $file['hash'] = $stanza[1]['hash'];␊ | 
| $file['size'] = strlen($this->getFile((object)$file));␊ | 
| }␊ | 
|  | 
| $file['file'] = $pathinfo['basename'];␊ | 
| ␊ | 
| $rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);␊ | 
| if ($rev !== null)␊ | 
| {␊ | 
| if ($rev !== null) {␊ | 
| $file['rev'] = $rev;␊ | 
| $certs = $this->_getCerts($rev);␊ | 
| ␊ | 
|  | 
| public function getFile($def, $cmd_only=false)␊ | 
| {␊ | 
| // this won't work with remote databases␊ | 
| if ($cmd_only)␊ | 
| {␊ | 
| if ($cmd_only) {␊ | 
| throw new Pluf_Exception_NotImplemented();␊ | 
| }␊ | 
| ␊ | 
| return $this->stdio->exec(array("get_file", $def->hash));␊ | 
| return $this->stdio->exec(array('get_file', $def->hash));␊ | 
| }␊ | 
| ␊ | 
| /**␊ | 
|  | 
| */␊ | 
| private function _getDiff($target, $source = null)␊ | 
| {␊ | 
| if (empty($source))␊ | 
| {␊ | 
| if (empty($source)) {␊ | 
| $source = "p:$target";␊ | 
| }␊ | 
| ␊ | 
|  | 
| $targets = $this->_resolveSelector($target);␊ | 
| $sources = $this->_resolveSelector($source);␊ | 
| ␊ | 
| if (count($targets) == 0 || count($sources) == 0)␊ | 
| {␊ | 
| return "";␊ | 
| if (count($targets) == 0 || count($sources) == 0) {␊ | 
| return '';␊ | 
| }␊ | 
| ␊ | 
| // if target contains a root revision, we cannot produce a diff␊ | 
| if (empty($sources[0]))␊ | 
| {␊ | 
| return "";␊ | 
| if (empty($sources[0])) {␊ | 
| return '';␊ | 
| }␊ | 
| ␊ | 
| return $this->stdio->exec(␊ | 
| array("content_diff"),␊ | 
| array("r" => array($sources[0], $targets[0]))␊ | 
| array('content_diff'),␊ | 
| array('r' => array($sources[0], $targets[0]))␊ | 
| );␊ | 
| }␊ | 
| ␊ | 
|  | 
| $certs = $this->_getCerts($revs[0]);␊ | 
| ␊ | 
| // FIXME: this assumes that author, date and changelog are always given␊ | 
| $res['author'] = implode(", ", $certs['author']);␊ | 
| $res['author'] = implode(', ', $certs['author']);␊ | 
| ␊ | 
| $dates = array();␊ | 
| foreach ($certs['date'] as $date)␊ | 
|  | 
| */␊ | 
| public function isCommitLarge($commit=null)␊ | 
| {␊ | 
| if (empty($commit))␊ | 
| {␊ | 
| $commit = "h:"+$this->getMainBranch();␊ | 
| if (empty($commit)) {␊ | 
| $commit = 'h:'.$this->getMainBranch();␊ | 
| }␊ | 
| ␊ | 
| $revs = $this->_resolveSelector($commit);␊ | 
|  | 
| return false;␊ | 
| ␊ | 
| $out = $this->stdio->exec(array(␊ | 
| "get_revision", $revs[0]␊ | 
| 'get_revision', $revs[0]␊ | 
| ));␊ | 
| ␊ | 
| $newAndPatchedFiles = 0;␊ | 
| $stanzas = self::_parseBasicIO($out);␊ | 
| ␊ | 
| foreach ($stanzas as $stanza)␊ | 
| {␊ | 
| if ($stanza[0]['key'] == "patch" || $stanza[0]['key'] == "add_file")␊ | 
| foreach ($stanzas as $stanza) {␊ | 
| if ($stanza[0]['key'] == 'patch' || $stanza[0]['key'] == 'add_file')␊ | 
| $newAndPatchedFiles++;␊ | 
| }␊ | 
| ␊ | 
|  | 
| $initialBranches = array();␊ | 
| $logs = array();␊ | 
| ␊ | 
| while (!empty($horizont) && $n > 0)␊ | 
| {␊ | 
| if (count($horizont) > 1)␊ | 
| {␊ | 
| $out = $this->stdio->exec(array("toposort") + $horizont);␊ | 
| while (!empty($horizont) && $n > 0) {␊ | 
| if (count($horizont) > 1) {␊ | 
| $out = $this->stdio->exec(array('toposort') + $horizont);␊ | 
| $horizont = preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);␊ | 
| }␊ | 
| ␊ | 
|  | 
| $certs = $this->_getCerts($rev);␊ | 
| ␊ | 
| // read in the initial branches we should follow␊ | 
| if (count($initialBranches) == 0)␊ | 
| {␊ | 
| if (count($initialBranches) == 0) {␊ | 
| $initialBranches = $certs['branch'];␊ | 
| }␊ | 
| ␊ | 
| // only add it to our log if it is on one of the initial branches␊ | 
| if (count(array_intersect($initialBranches, $certs['branch'])) > 0)␊ | 
| {␊ | 
| if (count(array_intersect($initialBranches, $certs['branch'])) > 0) {␊ | 
| --$n;␊ | 
| ␊ | 
| $log = array();␊ | 
|  | 
| $logs[] = (object)$log;␊ | 
| }␊ | 
| ␊ | 
| $out = $this->stdio->exec(array("parents", $rev));␊ | 
| $out = $this->stdio->exec(array('parents', $rev));␊ | 
| $horizont += preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);␊ | 
| }␊ | 
| ␊ |