parent; $name = $data->shortname; } else if (is_array($data)) { $parent = $data['parent']; $name = $data['shortname']; } if ($parent) { list($type, $owner) = explode(':', $parent); return "$owner/$name"; } return "default/$name"; } /** * was run tool... */ static function run($toolname, $mode, $args = null) { global $FORKS; //??? why? static $tools; // we cache the lookups... //- we could use the config for this... (as per original..) // but that would only be needed or realy heavily loaded sites. require_once 'System.php'; $tool = isset($tools[$toolname]) ? $tools[$toolname] : System::which($toolname); $tools[$toolname] = $tool; if (empty($tool)) { throw new Exception("Could not find '$toolname'"); } $cmd = $tool; $args = is_array($args) ? $args : array(); foreach ($args as $arg) { if (!is_array($arg)) { $cmd .= ' ' . MTrackSCM::escapeshellarg($arg); continue; } foreach ($arg as $a) { $cmd .= ' ' . MTrackSCM::escapeshellarg($a); } } if (!isset($FORKS[$cmd])) { $FORKS[$cmd] = 0; } $FORKS[$cmd]++; //echo $cmd; // debugging.... if (false) { if (php_sapi_name() == 'cli') { echo $cmd, "\n"; } else { error_log($cmd); echo htmlentities($cmd) . "
\n"; } } switch ($mode) { case 'read': return popen($cmd, 'r'); case 'write': return popen($cmd, 'w'); case 'string': return stream_get_contents(popen($cmd, 'r')); case 'proc': $pipedef = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'), ); $proc = proc_open($cmd, $pipedef, $pipes); return array($proc, $pipes); } } static function escapeshellarg($a) { if (preg_match('/\-[a-z0-9]+$/i', $a)) { return $a; // raw -a } if (preg_match('/\-\-[a-z0-9]+=/i', $a)) { $lr = explode('=', $a,2); return $lr[0].'=' . escapeshellarg($lr[1]); // raw -a } return escapeshellarg($a); } public $repopath = ''; /* static function factory(&$repopath) { // [ / owner type rest ] $bits = explode('/', $repopath, 4); // print_r($bits); if (count($bits) < 3) { throw new Exception("Invalid repo $repopath"); } array_shift($bits); list($owner, $type) = $bits; $repo = "$owner/$type"; $r = MTrack_Repo::loadByName($repo); if (!$r) { throw new Exception("invalid repo $repo"); } $repopath = isset($bits[2]) ? $bits[2] : ''; $r->repopath = $repopath; return $r; } */ /** Returns an array keyed by possible branch names. * The data associated with the branches is implementation * defined. * If the SCM does not have a concept of first-class branch * objects, this function returns null */ abstract public function getBranches(); /** Returns an array keyed by possible tag names. * The data associated with the tags is implementation * defined. * If the SCM does not have a concept of first-class tag * objects, this function returns null */ abstract public function getTags(); /** Enumerates the files/dirs that are present in the specified * location of the repository that match the specified revision, * branch or tag information. If no revision, branch or tag is * specified, then the appropriate default is assumed. * * The second and third parameters are optional; the second * parameter is one of 'rev', 'branch', or 'tag', and if specifed * the third parameter must be the corresponding revision, branch * or tag identifier. * * The return value is an array of MTrackSCMFile objects present * at that location/revision of the repository. */ abstract public function readdir($path, $object = null, $ident = null); /** Queries information on a specific file in the repository. * * Parameters are as for readdir() above. * * This function returns a single MTrackSCMFile for the location * in question. */ abstract public function file($path, $object = null, $ident = null); /** Queries history for a particular location in the repo. * * Parameters are as for readdir() above, except that path can be * left unspecified to query the history for the entire repo. * * The limit parameter limits the number of entries returned; it it is * a number, it specifies the number of events, otherwise it is assumed * to be a date in the past; only events since that date will be returned. * * Returns an array of MTrackSCMEvent objects. */ abstract public function history($path, $limit = null, $object = null, $ident = null); /** Obtain the diff text representing a change to a file. * * You may optionally provide one or two revisions as context. * * If no revisions are passed in, then the change associated * with the location will be assumed. * * If one revision is passed, then the change associated with * that event will be assumed. * * If two revisions are passed, then the difference between * the two events will be assumed. */ abstract public function diff($path, $from = null, $to = null); /** Determine the next and previous revisions for a given * changeset. * * Returns an array: the 0th element is an array of prior revisions, * and the 1st element is an array of successor revisions. * * There will usually be one prior and one successor revision for a * given change, but some SCMs will return multiples in the case of * merges. */ abstract public function getRelatedChanges($revision); /** Returns a working copy object for the repo * * The intended purpose is to support wiki page modifications, and * as such, is not meant to be an especially efficient means to do so. */ abstract public function getWorkingCopy(); /** Returns meta information about the SCM type; this is used in the * UI and tooling to let the user know their options. * * Returns an array with the following keys: * 'name' => 'Mercurial', // human displayable name * 'tools' => array('hg'), // list of tools to find during setup */ abstract public function getSCMMetaData(); /** Returns the default 'root' location in the repository. * For SCMs that have a concept of branches, this is the empty string. * For SCMs like SVN, this is the trunk dir */ public function getDefaultRoot() { return ''; } /* takes an MTrackSCM as a parameter because in some bootstrapping * cases, we're actually MTrack_Repo and not the end-class. * MTrack_Repo calls the end-class method and passes itself in for * context */ public function reconcileRepoSettings(MTrackSCM $r) { throw new Exception( "Creating/updating a repo of type $this->scmtype is not implemented"); } public function getBrowseRootName() { return self::makeDisplayName($this); } public function resolveRevision($rev, $object, $ident) { if ($rev !== null) { return $rev; } if ($object === null) { return null; } switch ($object) { case 'rev': $rev = $ident; break; case 'branch': $branches = $this->getBranches(); $rev = isset($branches[$ident]) ? $branches[$ident] : null; break; case 'tag': $tags = $this->getTags(); $rev = isset($tags[$ident]) ? $tags[$ident] : null; break; } if ($rev === null) { throw new Exception( "don't know which revision to use ($rev,$object,$ident)"); } return $rev; } }