2 require_once 'MTrack/SCM.php';
3 require_once 'MTrack/DB.php';
4 require_once 'MTrack/Config.php';
5 require_once 'MTrack/Project.php';
6 require_once 'MTrack/SCMFileEvent.php';
7 require_once 'MTrack/ACL.php';
8 require_once 'MTrack/Changeset.php';
9 require_once 'MTrack/Wiki.php';
12 class MTrack_Repo extends MTrackSCM
14 public $repoid = null;
15 public $shortname = null;
16 public $scmtype = null;
17 public $repopath = null;
18 public $browserurl = null;
19 public $browsertype = null;
20 public $description = null;
22 public $clonedfrom = null;
23 public $serverurl = null;
26 private $links_to_add = array();
27 private $links_to_remove = array();
28 private $links = null;
29 static $scms = array();
31 *load class and create instance using array as properties
34 static function factory($ar)
37 $type = ucfirst($ar['scmtype']);
38 $fn = 'MTrack/SCM/'.$type .'/Repo.php';
39 $cls = 'MTrack_SCM_'.$type .'_Repo';
49 static function getAvailableSCMs()
52 $ar = scandir(dirname(__FILE__).'/SCM');
55 if (empty($a) || $a[0] == '.') {
58 $fn = dirname(__FILE__).'/SCM/'.$a.'/Repo.php';
59 if (!file_exists($fn)) {
62 $ret[$a] = MTrack_Repo::factory(array('scmtype'=> $a));
69 /*static function loadById($id) {
70 list($row) = MTrackDB::q(
71 'select repoid, scmtype from repos where repoid = ?',
75 if (isset(self::$scms[$type])) {
76 $class = self::$scms[$type];
77 return new $class($row[0]);
79 throw new Exception("unsupported repo type $type");
83 static function loadByName($name) {
84 $bits = explode('/', $name);
85 if (count($bits) > 1 && $bits[0] == 'default') {
89 if (count($bits) > 1) {
90 // wez/reponame -> per user repo
92 $p = "project:$bits[0]";
94 'select repoid, scmtype from repos where shortname = ? and (parent = ? OR parent = ?)',
95 $bits[1], $u, $p)->fetchAll();
98 "select repoid, scmtype from repos where shortname = ? and parent =''",
101 if (is_array($rows) && isset($rows[0])) {
103 if (isset($row[0])) {
105 if (isset(self::$scms[$type])) {
106 $class = self::$scms[$type];
107 return new $class($row[0]);
109 throw new Exception("unsupported repo type $type");
114 static function loadByLocation($path)
117 // we have magic configuration - end users commit into SVN
118 // backend is really git... - so pre-commit hooks have to be from svn
119 list($row) = MTrackDB::q('select repoid, scmtype from repos where repopath = ?', $path)->fetchAll();
120 if (isset($row[0])) {
122 if (isset(self::$scms[$type])) {
123 $class = self::$scms[$type];
124 return new $class($row[0]);
126 throw new Exception("unsupported repo type $type");
131 static function loadByChangeSet($cs)
134 static $re = array();
135 if (isset($re[$cs])) {
146 project_repo_link l on r.repoid = l.repoid
148 projects p on p.projid = r.projectid
150 (parent is null or length(parent) = 0)
153 ( ? like CONCAT(proj), '%')
155 ( ? like CONCAT(repo), '%')
158 $ar = $q->fetchAll(PDO::FETCH_ASSOC);
160 $re[$cs] = self::loadByName($ar['repo']);
169 function __construct($ar = null) {
171 if (!is_array($ar)) {
172 return; // can accept empty ctrs
174 foreach($ar as $k=>$v) {
180 function reconcileRepoSettings()
182 $c = self::Factory(array('scmtype'=>$this->scmtype));
183 $s->reconcileRepoSettings($this);
185 // these are needed just to implement the abstract interface..
186 function getSCMMetaData() {
190 function getServerURL()
192 if ($this->serverurl) {
193 return $this->serverurl;
196 $url = MTrackConfig::get('repos', "$this->scmtype.serverurl");
198 return $url . $this->getBrowseRootName();
204 function getCheckoutCommand() {
205 $url = $this->getServerURL();
207 return $this->scmtype . ' clone ' . $this->getServerURL();
216 function getWorkingCopy() {
217 throw new Exception("cannot getWorkingCopy from a generic repo object");
220 function deleteRepo(MTrackChangeset $CS) {
221 MTrackDB::q('delete from repos where repoid = ?', $this->repoid);
222 mtrack_rmdir($this->repopath);
229 if ($this->links === null) {
230 $this->links = array();
231 foreach (MTrackDB::q('select linkid, projid, repopathregex
232 from project_repo_link where repoid = ? order by repopathregex',
233 $this->repoid)->fetchAll() as $row) {
234 $this->links[$row[0]] = array($row[1], $row[2]);
240 function addLink($proj, $regex)
242 if ($proj instanceof MTrackProject) {
243 $this->links_to_add[] = array($proj->projid, $regex);
245 $this->links_to_add[] = array($proj, $regex);
249 function removeLink($linkid)
251 $this->links_to_remove[$linkid] = $linkid;
254 function getBranches() {}
255 function getTags() {}
256 function readdir($path, $object = null, $ident = null) {}
257 function file($path, $object = null, $ident = null) {}
258 function history($path, $limit = null, $object = null, $ident = null){}
259 function diff($path, $from = null, $to = null) {}
260 function getRelatedChanges($revision) {}
262 function projectFromPath($filename)
264 static $links = array();
265 if (!isset($links[$this->repoid]) || $links[$this->repoid] === null) {
266 $links[$this->repoid] = array();
267 foreach (MTrackDB::q(
268 'select projid, repopathregex from project_repo_link where repoid = ?',
269 $this->repoid) as $row) {
270 $re = str_replace('/', '\\/', $row[1]);
271 $links[$this->repoid][] = array($row[0], "/$re/");
274 if (is_array($filename)) {
275 $proj_incidence = array();
276 foreach ($filename as $file) {
277 $proj = $this->projectFromPath($file);
278 if ($proj === null) continue;
279 if (isset($proj_incidence[$proj])) {
280 $proj_incidence[$proj]++;
282 $proj_incidence[$proj] = 1;
287 foreach ($proj_incidence as $proj => $count) {
288 if ($count > $the_proj_count) {
289 $the_proj_count = $count;
296 if ($filename instanceof MTrackSCMFileEvent) {
297 $filename = $filename->name;
300 // walk through the regexes; take the longest match as definitive
303 if ($filename[0] != '/') {
304 $filename = '/' . $filename;
306 foreach ($links[$this->repoid] as $link) {
307 if (preg_match($link[1], $filename, $M)) {
308 if (strlen($M[0]) > strlen($longest)) {
310 $longest_id = $link[0];
317 function historyWithChangelog($path, $limit = null, $object = null, $ident = null)
320 $ents = $this->history($path, $limit, $object, $ident);
321 $data = new StdClass;
329 // Determine project from the file list
330 $the_proj = $this->projectFromPath($ent->files);
332 $proj = MTrackProject::loadById($the_proj);
333 $changelog = $proj->adjust_links($ent->changelog, true);
335 $changelog = $ent->changelog;
337 $data->changelog = $changelog;
339 //if (is_array($ent->files)) foreach ($ent->files as $file) {
340 // $file->diff = mtrack_diff($repo->diff($file, $ent->rev));
346 function historyWithChangelogAndDiff($path, $limit = null, $object = null, $ident = null)
348 $ret = $this->historyWithChangelog($path, $limit, $object, $ident);
352 if (!is_array($ret->ent->files)) {
355 foreach ($ret->ent->files as $file) {
356 // where is mtrack_diff...
357 $file->diff = mtrack_diff($this->diff($file, $ret->ent->rev));
365 function displayName()
367 // fixme - this code needs to be in here.. rather than in SCM?
368 return MTrackSCM::makeDisplayName($this);
370 function descriptionToHtml()
372 return MTrack_Wiki::format_to_html($this->description);
375 static function defaultRepo($cfg = null)
377 static $defrepo = null;
378 if ($defrepo !== null) {
379 return $defrepo; // already to it..
383 if ($defrepo !== null) {
384 $defrepo = strpos($defrepo, '/') === false ? 'default/' . $defrepo : $defrepo;
389 $q = MTrackDB::q( 'select parent, shortname from repos order by shortname');
390 foreach($q->fetchAll() as $row) {
391 $defrepo = MTrackSCM::makeDisplayName($row);