3 require_once 'MTrack/Repo.php';
5 abstract class MTrackSCM
9 static $repos = array();
12 static function makeBreadcrumbs($pi) {
19 $crumbs = explode('/', $pi);
24 static function makeDisplayName($data) {
27 if (is_object($data)) {
28 $parent = $data->parent;
29 $name = $data->shortname;
30 } else if (is_array($data)) {
31 $parent = $data['parent'];
32 $name = $data['shortname'];
35 list($type, $owner) = explode(':', $parent);
36 return "$owner/$name";
38 return "default/$name";
45 static function run($toolname, $mode, $args = null)
47 global $FORKS; //??? why?
49 static $tools; // we cache the lookups...
50 //- we could use the config for this... (as per original..)
51 // but that would only be needed or realy heavily loaded sites.
54 require_once 'System.php';
55 $tool = isset($tools[$toolname]) ? $tools[$toolname] : System::which($toolname);
56 $tools[$toolname] = $tool;
60 throw new Exception("Could not find '$toolname'");
64 $args = is_array($args) ? $args : array();
66 foreach ($args as $arg) {
67 if (!is_array($arg)) {
68 $cmd .= ' ' . MTrackSCM::escapeshellarg($arg);
72 foreach ($arg as $a) {
73 $cmd .= ' ' . MTrackSCM::escapeshellarg($a);
77 if (!isset($FORKS[$cmd])) {
84 if (php_sapi_name() == 'cli') {
88 echo htmlentities($cmd) . "<br>\n";
93 case 'read': return popen($cmd, 'r');
94 case 'write': return popen($cmd, 'w');
95 case 'string': return stream_get_contents(popen($cmd, 'r'));
98 0 => array('pipe', 'r'),
99 1 => array('pipe', 'w'),
100 2 => array('pipe', 'w'),
102 $proc = proc_open($cmd, $pipedef, $pipes);
103 return array($proc, $pipes);
107 static function escapeshellarg($a) {
108 if (preg_match('/\-[a-z0-9]+$/i', $a)) {
111 if (preg_match('/\-\-[a-z0-9]+=/i', $a)) {
112 $lr = explode('=', $a,2);
113 return $lr[0].'=' . escapeshellarg($lr[1]); // raw -a
115 return escapeshellarg($a);
120 public $repopath = '';
122 static function factory(&$repopath) {
123 // [ / owner type rest ]
124 $bits = explode('/', $repopath, 4);
126 if (count($bits) < 3) {
127 throw new Exception("Invalid repo $repopath");
130 list($owner, $type) = $bits;
131 $repo = "$owner/$type";
133 $r = MTrack_Repo::loadByName($repo);
135 throw new Exception("invalid repo $repo");
137 $repopath = isset($bits[2]) ? $bits[2] : '';
138 $r->repopath = $repopath;
143 /** Returns an array keyed by possible branch names.
144 * The data associated with the branches is implementation
146 * If the SCM does not have a concept of first-class branch
147 * objects, this function returns null */
148 abstract public function getBranches();
150 /** Returns an array keyed by possible tag names.
151 * The data associated with the tags is implementation
153 * If the SCM does not have a concept of first-class tag
154 * objects, this function returns null */
155 abstract public function getTags();
157 /** Enumerates the files/dirs that are present in the specified
158 * location of the repository that match the specified revision,
159 * branch or tag information. If no revision, branch or tag is
160 * specified, then the appropriate default is assumed.
162 * The second and third parameters are optional; the second
163 * parameter is one of 'rev', 'branch', or 'tag', and if specifed
164 * the third parameter must be the corresponding revision, branch
167 * The return value is an array of MTrackSCMFile objects present
168 * at that location/revision of the repository.
170 abstract public function readdir($path, $object = null, $ident = null);
172 /** Queries information on a specific file in the repository.
174 * Parameters are as for readdir() above.
176 * This function returns a single MTrackSCMFile for the location
179 abstract public function file($path, $object = null, $ident = null);
181 /** Queries history for a particular location in the repo.
183 * Parameters are as for readdir() above, except that path can be
184 * left unspecified to query the history for the entire repo.
186 * The limit parameter limits the number of entries returned; it it is
187 * a number, it specifies the number of events, otherwise it is assumed
188 * to be a date in the past; only events since that date will be returned.
190 * Returns an array of MTrackSCMEvent objects.
192 abstract public function history($path, $limit = null, $object = null,
195 /** Obtain the diff text representing a change to a file.
197 * You may optionally provide one or two revisions as context.
199 * If no revisions are passed in, then the change associated
200 * with the location will be assumed.
202 * If one revision is passed, then the change associated with
203 * that event will be assumed.
205 * If two revisions are passed, then the difference between
206 * the two events will be assumed.
208 abstract public function diff($path, $from = null, $to = null);
210 /** Determine the next and previous revisions for a given
213 * Returns an array: the 0th element is an array of prior revisions,
214 * and the 1st element is an array of successor revisions.
216 * There will usually be one prior and one successor revision for a
217 * given change, but some SCMs will return multiples in the case of
220 abstract public function getRelatedChanges($revision);
222 /** Returns a working copy object for the repo
224 * The intended purpose is to support wiki page modifications, and
225 * as such, is not meant to be an especially efficient means to do so.
227 abstract public function getWorkingCopy();
229 /** Returns meta information about the SCM type; this is used in the
230 * UI and tooling to let the user know their options.
232 * Returns an array with the following keys:
233 * 'name' => 'Mercurial', // human displayable name
234 * 'tools' => array('hg'), // list of tools to find during setup
236 abstract public function getSCMMetaData();
238 /** Returns the default 'root' location in the repository.
239 * For SCMs that have a concept of branches, this is the empty string.
240 * For SCMs like SVN, this is the trunk dir */
241 public function getDefaultRoot() {
248 /* takes an MTrackSCM as a parameter because in some bootstrapping
249 * cases, we're actually MTrack_Repo and not the end-class.
250 * MTrack_Repo calls the end-class method and passes itself in for
252 public function reconcileRepoSettings(MTrackSCM $r) {
254 "Creating/updating a repo of type $this->scmtype is not implemented");
260 public function getBrowseRootName() {
261 return self::makeDisplayName($this);
264 public function resolveRevision($rev, $object, $ident)
270 if ($object === null) {
280 $branches = $this->getBranches();
281 $rev = isset($branches[$ident]) ? $branches[$ident] : null;
285 $tags = $this->getTags();
286 $rev = isset($tags[$ident]) ? $tags[$ident] : null;
291 "don't know which revision to use ($rev,$object,$ident)");