repo = $repo; $this->name = $name; $this->rev = $rev; $this->is_dir = $is_dir; } public function _determineFileChangeEvent($reponame, $filename, $rev) { $repo = MTrack_Repo::loadByName($reponame); list($ent) = $repo->history($filename, 1, 'rev', $rev); return $ent; } public function getChangeEvent() { //FIXME - this should do something similar to git, // and use the database rather than caching with expiry.. return MTrackSCMFileSVN::_determineFileChangeEvent($this->repo->getBrowseRootName(), $this->name, $this->rev); //return mtrack_cache( // array('MTrack_SCM_Hg_File', '_determineFileChangeEvent'), // array($this->repo->id, $this->name, $this->rev), // 864000); } function cat() { return $this->repo->svn('cat', '-r', $this->rev, 'file://' . $this->repo->repopath . '/' . $this->name . "@$this->rev"); } function annotate($include_line_content = false) { $xml = stream_get_contents($this->repo->svn('annotate', '--xml', 'file://' . $this->repo->repopath . '/' . $this->name . "@$this->rev")); $ann = array(); $xml = @simplexml_load_string($xml); if (!is_object($xml)) { return 'DELETED'; } if ($include_line_content) { $cat = $this->cat(); } foreach ($xml->target->entry as $ent) { $A = new MTrackSCMAnnotation; $A->rev = (int)$ent->commit['revision']; $A->changeby = (string)$ent->commit->author; if ($include_line_content) { $A->line = fgets($cat); } $ann[(int)$ent['line-number']] = $A; } return $ann; } } class MTrackWCSVN extends MTrackSCMWorkingCopy { public $repo; function __construct(MTrack_Repo $repo) { $this->dir = mtrack_make_temp_dir(); $this->repo = $repo; stream_get_contents($this->repo->svn('checkout', 'file://' . $this->repo->repopath . '/trunk', $this->dir)); } function getFile($path) { return $this->repo->file('trunk/' . $path); } function addFile($path) { stream_get_contents( $this->repo->svn('add', $this->dir . DIRECTORY_SEPARATOR . $path)); } function delFile($path) { stream_get_contents( $this->repo->svn('rm', $this->dir . DIRECTORY_SEPARATOR . $path)); } function commit(MTrackChangeset $CS) { list($proc, $pipes) = MTrackSCM::run('svn', 'proc', array('ci', '--non-interactive', '--username', $CS->who, '-m', $CS->reason, $this->dir)); /* $svn = MTrackConfig::get('tools', 'svn'); if (!strlen($svn)) $svn = 'svn'; $proc = proc_open( "$svn ci --non-interactive " . ' --username ' . escapeshellarg($CS->who) . ' -m ' . escapeshellarg($CS->reason) . ' ' . $this->dir, array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'), ), $pipes, $this->dir); */ $pipes[0] = null; $output = stream_get_contents($pipes[1]); $err = stream_get_contents($pipes[2]); if (strlen($err)) { throw new Exception($err); } if (preg_match("/Committed revision (\d+)/", $output, $M)) { $rev = $M[1]; stream_get_contents( $this->repo->svn('propset', 'svn:date', '--revprop', '-r', $rev, $CS->when, $this->dir )); } } } class MTrackSCMSVN extends MTrack_Repo { protected $svn = 'svn'; static $debug = false; public function getSCMMetaData() { return array( 'name' => 'Subversion', 'tools' => array('svn', 'svnlook', 'svnadmin'), ); } function getServerURL() { $url = parent::getServerURL(); if ($url) return $url; $url = MTrackConfig::get('repos', 'serverurl'); if ($url) { return "svn+ssh://$url/" . $this->getBrowseRootName() . '/BRANCHNAME'; } return null; } public function reconcileRepoSettings(MTrackSCM $r = null) { if ($r == null) { $r = $this; } if (!is_dir($r->repopath)) { $stm = MTrackSCM::run('svnadmin', 'read', array('create', $r->repopath)); $out = stream_get_contents($stm); if (pclose($stm)) { throw new Exception("failed to create repo: $out"); } file_put_contents("$r->repopath/hooks/pre-revprop-change", "#!/bin/sh\nexit 0\n"); chmod("$r->repopath/hooks/pre-revprop-change", 0755); $me = mtrack_canon_username(MTrackAuth::whoami()); $stm = MTrackSCM::run('svn', 'read', array('mkdir', '-m', 'init', '--username', $me, "file://$r->repopath/trunk")); $out = stream_get_contents($stm); if (pclose($stm)) { throw new Exception("failed to create trunk: $out"); } system("chmod -R 02777 $r->repopath/db $r->repopath/locks"); $authzname = MTrackConfig::get('core', 'vardir') . '/svn.authz'; $svnserve = "[general]\nauthz-db = $authzname\n"; file_put_contents("$r->repopath/conf/svnserve.conf", $svnserve); } } public function getDefaultRoot() { return 'trunk/'; } public function getBranches() { return null; } public function getTags() { return null; } public function readdir($path, $object = null, $ident = null) { $res = array(); if ($object === null) { $object = 'rev'; $ident = 'HEAD'; } $rev = $this->resolveRevision(null, $object, $ident); $rpath = $this->repopath; if (strlen($path)) { $rpath .= "/$path"; } $fp = $this->svn('ls', '--xml', '-r', $rev, "file://" . $rpath); $ls = stream_get_contents($fp); $doc = simplexml_load_string($ls); if (!is_object($doc)) { echo '
', htmlentities($ls, ENT_QUOTES, 'utf-8'), ''; } if (isset($doc->list)) foreach ($doc->list->entry as $le) { $name = $path; $name .= '/'; $name .= $le->name; if ($name[0] == '/') { $name = substr($name, 1); } /* Use the revision passed in to readdir rather than the revision * in the entry, as svn can return a revision number that pre-dates * that of the containing tag, and this causes the subsequent * lookup of commit data to fail */ $res[] = new MTrackSCMFileSVN($this, $name, //$le->commit['revision'], $rev, $le['kind'] == 'dir'); } return $res; } public function file($path, $object = null, $ident = null) { if ($object == null) { $object = 'rev'; $ident = 'HEAD'; } $rev = $this->resolveRevision(null, $object, $ident); return new MTrackSCMFileSVN($this, $path, $rev); } public function history($path, $limit = null, $object = null, $ident = null) { $res = array(); $args = array(); $limit_date = null; if ($limit !== null) { if (!is_int($limit)) { $limit_date = strtotime($limit); $limit = null; $limit_date = date('c', $limit_date); } } $use_at_rev = false; if ($object !== null) { $rev = $this->resolveRevision(null, $object, $ident); if ($limit_date != null) { $args[] = '-r'; $args[] = $rev . ':{' . $limit_date . '}'; } else if ($rev == 'HEAD') { $args[] = '-r'; $args[] = "$rev:1"; } else { $use_at_rev = true; } } if ($limit !== null) { $args[] = '--limit'; $args[] = $limit; } else if ($limit_date !== null) { $args[] = '-r'; $args[] = '{' . $limit_date . '}:head'; } $rpath = $this->repopath; if (strlen($path)) { if ($path[0] != '/') { $rpath .= '/'; } $rpath .= $path; } $spath = $rpath; if ($use_at_rev) { $spath .= "@$rev"; } $fp = $this->svn('log', '--xml', '-v', $args, "file://$spath"); $xml = stream_get_contents($fp); $doc = @simplexml_load_string($xml); if (!is_object($doc)) { /* try looking at the parent */ $spath = dirname($spath); if ($use_at_rev) { $spath .= "@$rev"; } $fp = $this->svn('log', '--xml', '-v', $args, "file://$spath"); $xml = stream_get_contents($fp); $doc = @simplexml_load_string($xml); } if (!is_object($doc)) { // echo '
', htmlentities($xml, ENT_QUOTES, 'utf-8'), ''; return null; } if (self::$debug) { if (php_sapi_name() == 'cli') { echo $xml, "\n"; } else { echo htmlentities(var_export($xml, true)) . "