MTrack/SCM.php
[web.mtrack] / MTrack / SCM.php
1 <?php
2
3 require_once 'MTrack/Repo.php';
4
5 abstract class MTrackSCM
6 {
7
8   // statics...
9     static $repos = array();
10
11
12     static function makeBreadcrumbs($pi) {
13         if (!strlen($pi)) {
14           $pi = '/';
15         }
16         if ($pi == '/') {
17           $crumbs = array('');
18         } else {
19           $crumbs = explode('/', $pi);
20         }
21         return $crumbs;
22     }
23
24     static function makeDisplayName($data) {
25         $parent = '';
26         $name = '';
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'];
33         }
34         if ($parent) {
35           list($type, $owner) = explode(':', $parent);
36           return "$owner/$name";
37         }
38         return "default/$name";
39     }
40
41     /**
42      * was run tool...
43      */
44
45     static function run($toolname, $mode, $args = null)
46     {
47         global $FORKS; //??? why?
48         
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.
52                 
53         
54         require_once 'System.php';
55         $tool = isset($tools[$toolname]) ? $tools[$toolname] :  System::which($toolname);
56         $tools[$toolname] = $tool;
57         
58         
59         if (empty($tool)) {
60             throw new Exception("Could not find '$toolname'");
61         }
62          
63         $cmd = $tool;
64         $args = is_array($args) ? $args : array();
65         
66         foreach ($args as $arg) {
67             if (!is_array($arg)) {
68                 $cmd .= ' ' . escapeshellarg($arg);
69                 continue;
70             }
71             
72             foreach ($arg as $a) {
73                 $cmd .= ' ' . escapeshellarg($a);
74             }
75         }
76         
77         if (!isset($FORKS[$cmd])) {
78             $FORKS[$cmd] = 0;
79         }
80         $FORKS[$cmd]++;
81         //echo  $cmd;
82         // debugging....
83         if (true) {
84             if (php_sapi_name() == 'cli') {
85                 echo $cmd, "\n";
86             } else {
87                 error_log($cmd);
88                 echo htmlentities($cmd) . "<br>\n";
89             }
90         }
91
92         switch ($mode) {
93             case 'read':   return popen($cmd, 'r');
94             case 'write':  return popen($cmd, 'w');
95             case 'string': return stream_get_contents(popen($cmd, 'r'));
96             case 'proc':
97               $pipedef = array(
98                 0 => array('pipe', 'r'),
99                 1 => array('pipe', 'w'),
100                 2 => array('pipe', 'w'),
101               );
102               $proc = proc_open($cmd, $pipedef, $pipes);
103               return array($proc, $pipes);
104         }
105     }
106     
107     
108     
109     public $repopath = '';
110     /*
111     static function factory(&$repopath) {
112         // [ / owner type rest ] 
113         $bits = explode('/', $repopath, 4);
114         //  print_r($bits);
115         if (count($bits) < 3) {
116           throw new Exception("Invalid repo $repopath");
117         }
118         array_shift($bits);
119         list($owner, $type) = $bits;
120         $repo = "$owner/$type";
121         
122         $r = MTrack_Repo::loadByName($repo);
123         if (!$r) {
124           throw new Exception("invalid repo $repo");
125         }
126         $repopath = isset($bits[2]) ? $bits[2] : '';
127         $r->repopath = $repopath;
128         return $r;
129     }
130     */
131     
132     /** Returns an array keyed by possible branch names.
133     * The data associated with the branches is implementation
134     * defined.
135     * If the SCM does not have a concept of first-class branch
136     * objects, this function returns null */
137     abstract public function getBranches();
138     
139     /** Returns an array keyed by possible tag names.
140     * The data associated with the tags is implementation
141     * defined.
142     * If the SCM does not have a concept of first-class tag
143     * objects, this function returns null */
144     abstract public function getTags();
145     
146     /** Enumerates the files/dirs that are present in the specified
147     * location of the repository that match the specified revision,
148     * branch or tag information.  If no revision, branch or tag is
149     * specified, then the appropriate default is assumed.
150     *
151     * The second and third parameters are optional; the second
152     * parameter is one of 'rev', 'branch', or 'tag', and if specifed
153     * the third parameter must be the corresponding revision, branch
154     * or tag identifier.
155     *
156     * The return value is an array of MTrackSCMFile objects present
157     * at that location/revision of the repository.
158     */
159     abstract public function readdir($path, $object = null, $ident = null);
160     
161     /** Queries information on a specific file in the repository.
162     *
163     * Parameters are as for readdir() above.
164     *
165     * This function returns a single MTrackSCMFile for the location
166     * in question.
167     */
168     abstract public function file($path, $object = null, $ident = null);
169     
170     /** Queries history for a particular location in the repo.
171     *
172     * Parameters are as for readdir() above, except that path can be
173     * left unspecified to query the history for the entire repo.
174     *
175     * The limit parameter limits the number of entries returned; it it is
176     * a number, it specifies the number of events, otherwise it is assumed
177     * to be a date in the past; only events since that date will be returned.
178     *
179     * Returns an array of MTrackSCMEvent objects.
180     */
181     abstract public function history($path, $limit = null, $object = null,
182     $ident = null);
183     
184     /** Obtain the diff text representing a change to a file.
185     *
186     * You may optionally provide one or two revisions as context.
187     *
188     * If no revisions are passed in, then the change associated
189     * with the location will be assumed.
190     *
191     * If one revision is passed, then the change associated with
192     * that event will be assumed.
193     *
194     * If two revisions are passed, then the difference between
195     * the two events will be assumed.
196     */
197     abstract public function diff($path, $from = null, $to = null);
198     
199     /** Determine the next and previous revisions for a given
200     * changeset.
201     *
202     * Returns an array: the 0th element is an array of prior revisions,
203     * and the 1st element is an array of successor revisions.
204     *
205     * There will usually be one prior and one successor revision for a
206     * given change, but some SCMs will return multiples in the case of
207     * merges.
208     */
209     abstract public function getRelatedChanges($revision);
210     
211     /** Returns a working copy object for the repo
212     *
213     * The intended purpose is to support wiki page modifications, and
214     * as such, is not meant to be an especially efficient means to do so.
215     */
216     abstract public function getWorkingCopy();
217     
218     /** Returns meta information about the SCM type; this is used in the
219     * UI and tooling to let the user know their options.
220     *
221     * Returns an array with the following keys:
222     * 'name' => 'Mercurial', // human displayable name
223     * 'tools' => array('hg'), // list of tools to find during setup
224     */
225     abstract public function getSCMMetaData();
226     
227     /** Returns the default 'root' location in the repository.
228     * For SCMs that have a concept of branches, this is the empty string.
229     * For SCMs like SVN, this is the trunk dir */
230     public function getDefaultRoot() {
231     return '';
232     }
233
234   
235     
236   
237   /* takes an MTrackSCM as a parameter because in some bootstrapping
238    * cases, we're actually MTrack_Repo and not the end-class.
239    * MTrack_Repo calls the end-class method and passes itself in for
240    * context */
241   public function reconcileRepoSettings(MTrackSCM $r) {
242     throw new Exception(
243       "Creating/updating a repo of type $this->scmtype is not implemented");
244   }
245
246
247
248
249   public function getBrowseRootName() {
250     return self::makeDisplayName($this);
251   }
252
253     public function resolveRevision($rev, $object, $ident)
254     {
255         if ($rev !== null) {
256           return $rev;
257         }
258         
259         if ($object === null) {
260           return null;
261         }
262         
263         switch ($object) {
264             case 'rev':
265                 $rev = $ident;
266                 break;
267             
268             case 'branch':
269                 $branches = $this->getBranches();
270                 $rev = isset($branches[$ident]) ? $branches[$ident] : null;
271                 break;
272             
273             case 'tag':
274               $tags = $this->getTags();
275               $rev = isset($tags[$ident]) ? $tags[$ident] : null;
276               break;
277         }
278         if ($rev === null) {
279           throw new Exception(
280             "don't know which revision to use ($rev,$object,$ident)");
281         }
282       return $rev;
283     }
284
285      
286 }