5fac7301bc785c4e195871898ea0681b6d70294a
[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     
110     public $repopath = '';
111     /*
112     static function factory(&$repopath) {
113         // [ / owner type rest ] 
114         $bits = explode('/', $repopath, 4);
115         //  print_r($bits);
116         if (count($bits) < 3) {
117           throw new Exception("Invalid repo $repopath");
118         }
119         array_shift($bits);
120         list($owner, $type) = $bits;
121         $repo = "$owner/$type";
122         
123         $r = MTrack_Repo::loadByName($repo);
124         if (!$r) {
125           throw new Exception("invalid repo $repo");
126         }
127         $repopath = isset($bits[2]) ? $bits[2] : '';
128         $r->repopath = $repopath;
129         return $r;
130     }
131     */
132     
133     /** Returns an array keyed by possible branch names.
134     * The data associated with the branches is implementation
135     * defined.
136     * If the SCM does not have a concept of first-class branch
137     * objects, this function returns null */
138     abstract public function getBranches();
139     
140     /** Returns an array keyed by possible tag names.
141     * The data associated with the tags is implementation
142     * defined.
143     * If the SCM does not have a concept of first-class tag
144     * objects, this function returns null */
145     abstract public function getTags();
146     
147     /** Enumerates the files/dirs that are present in the specified
148     * location of the repository that match the specified revision,
149     * branch or tag information.  If no revision, branch or tag is
150     * specified, then the appropriate default is assumed.
151     *
152     * The second and third parameters are optional; the second
153     * parameter is one of 'rev', 'branch', or 'tag', and if specifed
154     * the third parameter must be the corresponding revision, branch
155     * or tag identifier.
156     *
157     * The return value is an array of MTrackSCMFile objects present
158     * at that location/revision of the repository.
159     */
160     abstract public function readdir($path, $object = null, $ident = null);
161     
162     /** Queries information on a specific file in the repository.
163     *
164     * Parameters are as for readdir() above.
165     *
166     * This function returns a single MTrackSCMFile for the location
167     * in question.
168     */
169     abstract public function file($path, $object = null, $ident = null);
170     
171     /** Queries history for a particular location in the repo.
172     *
173     * Parameters are as for readdir() above, except that path can be
174     * left unspecified to query the history for the entire repo.
175     *
176     * The limit parameter limits the number of entries returned; it it is
177     * a number, it specifies the number of events, otherwise it is assumed
178     * to be a date in the past; only events since that date will be returned.
179     *
180     * Returns an array of MTrackSCMEvent objects.
181     */
182     abstract public function history($path, $limit = null, $object = null,
183     $ident = null);
184     
185     /** Obtain the diff text representing a change to a file.
186     *
187     * You may optionally provide one or two revisions as context.
188     *
189     * If no revisions are passed in, then the change associated
190     * with the location will be assumed.
191     *
192     * If one revision is passed, then the change associated with
193     * that event will be assumed.
194     *
195     * If two revisions are passed, then the difference between
196     * the two events will be assumed.
197     */
198     abstract public function diff($path, $from = null, $to = null);
199     
200     /** Determine the next and previous revisions for a given
201     * changeset.
202     *
203     * Returns an array: the 0th element is an array of prior revisions,
204     * and the 1st element is an array of successor revisions.
205     *
206     * There will usually be one prior and one successor revision for a
207     * given change, but some SCMs will return multiples in the case of
208     * merges.
209     */
210     abstract public function getRelatedChanges($revision);
211     
212     /** Returns a working copy object for the repo
213     *
214     * The intended purpose is to support wiki page modifications, and
215     * as such, is not meant to be an especially efficient means to do so.
216     */
217     abstract public function getWorkingCopy();
218     
219     /** Returns meta information about the SCM type; this is used in the
220     * UI and tooling to let the user know their options.
221     *
222     * Returns an array with the following keys:
223     * 'name' => 'Mercurial', // human displayable name
224     * 'tools' => array('hg'), // list of tools to find during setup
225     */
226     abstract public function getSCMMetaData();
227     
228     /** Returns the default 'root' location in the repository.
229     * For SCMs that have a concept of branches, this is the empty string.
230     * For SCMs like SVN, this is the trunk dir */
231     public function getDefaultRoot() {
232     return '';
233     }
234
235   
236     
237   
238   /* takes an MTrackSCM as a parameter because in some bootstrapping
239    * cases, we're actually MTrack_Repo and not the end-class.
240    * MTrack_Repo calls the end-class method and passes itself in for
241    * context */
242   public function reconcileRepoSettings(MTrackSCM $r) {
243     throw new Exception(
244       "Creating/updating a repo of type $this->scmtype is not implemented");
245   }
246
247
248
249
250   public function getBrowseRootName() {
251     return self::makeDisplayName($this);
252   }
253
254     public function resolveRevision($rev, $object, $ident)
255     {
256         if ($rev !== null) {
257           return $rev;
258         }
259         
260         if ($object === null) {
261           return null;
262         }
263         
264         switch ($object) {
265             case 'rev':
266                 $rev = $ident;
267                 break;
268             
269             case 'branch':
270                 $branches = $this->getBranches();
271                 $rev = isset($branches[$ident]) ? $branches[$ident] : null;
272                 break;
273             
274             case 'tag':
275               $tags = $this->getTags();
276               $rev = isset($tags[$ident]) ? $tags[$ident] : null;
277               break;
278         }
279         if ($rev === null) {
280           throw new Exception(
281             "don't know which revision to use ($rev,$object,$ident)");
282         }
283       return $rev;
284     }
285
286      
287 }