1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 /* Mercurial SCM browsing */
6 class MTrackSCMFileHg extends MTrackSCMFile {
12 function __construct(MTrackSCM $repo, $name, $rev, $is_dir = false)
17 $this->is_dir = $is_dir;
20 public function _determineFileChangeEvent($repoid, $filename, $rev)
22 $repo = MTrackRepo::loadById($repoid);
23 $ents = $repo->history($filename, 1, 'rev', "$rev:0");
25 throw new Exception("$filename is invalid");
30 public function getChangeEvent()
33 array('MTrackSCMFileHg', '_determineFileChangeEvent'),
34 array($this->repo->repoid, $this->name, $this->rev),
40 return $this->repo->hg('cat', '-r', $this->rev, $this->name);
43 function annotate($include_line_content = false)
47 $fp = $this->repo->hg('annotate', '-r', $this->rev, '-uvc', $this->name);
48 while ($line = fgets($fp)) {
49 preg_match("/^\s*([^:]*)\s+([0-9a-fA-F]+): (.*)$/", $line, $M);
50 $A = new MTrackSCMAnnotation;
53 if ($include_line_content) {
62 class MTrackWCHg extends MTrackSCMWorkingCopy {
65 function __construct(MTrackRepo $repo) {
66 $this->dir = mtrack_make_temp_dir();
69 stream_get_contents($this->hg('init', $this->dir));
70 stream_get_contents($this->hg('pull', $this->repo->repopath));
71 stream_get_contents($this->hg('up'));
74 function __destruct() {
76 $a = array("-y", "--cwd", $this->dir, 'push', $this->repo->repopath);
78 list($proc, $pipes) = mtrack_run_tool('hg', 'proc', $a);
80 $out = stream_get_contents($pipes[1]);
81 $err = stream_get_contents($pipes[2]);
82 $st = proc_close($proc);
85 throw new Exception("push failed with status $st: $err $out");
87 mtrack_rmdir($this->dir);
90 function getFile($path)
92 return $this->repo->file($path);
95 function addFile($path)
97 // nothing to do; we use --addremove
100 function delFile($path)
102 // we use --addremove when we commit for this to take effect
103 unlink($this->dir . DIRECTORY_SEPARATOR . $path);
106 function commit(MTrackChangeset $CS)
108 $hg_date = (int)strtotime($CS->when) . ' 0';
109 $reason = trim($CS->reason);
110 if (!strlen($reason)) {
113 $out = $this->hg('ci', '--addremove',
117 $data = stream_get_contents($out);
120 throw new Exception("commit failed $st $data");
126 $args = func_get_args();
127 $a = array("-y", "--cwd", $this->dir);
128 foreach ($args as $arg) {
132 return mtrack_run_tool('hg', 'read', $a);
136 class MTrackSCMHg extends MTrackRepo {
137 protected $hg = 'hg';
138 protected $branches = null;
139 protected $tags = null;
141 public function getSCMMetaData() {
143 'name' => 'Mercurial',
144 'tools' => array('hg'),
148 public function reconcileRepoSettings(MTrackSCM $r = null) {
152 $description = substr(preg_replace("/\r?\n/m", ' ', $r->description), 0, 64);
153 $description = trim($description);
154 if (!is_dir($r->repopath)) {
155 if ($r->clonedfrom) {
156 $S = MTrackRepo::loadById($r->clonedfrom);
157 $stm = mtrack_run_tool('hg', 'read', array(
158 'clone', $S->repopath, $r->repopath));
160 $stm = mtrack_run_tool('hg', 'read', array('init', $r->repopath));
162 $out = stream_get_contents($stm);
165 throw new Exception("hg: failed $out");
169 $php = MTrackConfig::get('tools', 'php');
170 $conffile = realpath(MTrackConfig::getLocation());
172 $install = realpath(dirname(__FILE__) . '/../../');
177 "changegroup.mtrack" =>
178 "$php $install/bin/hg-commit-hook changegroup $conffile",
180 "$php $install/bin/hg-commit-hook commit $conffile",
181 "pretxncommit.mtrack" =>
182 "$php $install/bin/hg-commit-hook pretxncommit $conffile",
183 "pretxnchangegroup.mtrack" =>
184 "$php $install/bin/hg-commit-hook pretxnchangegroup $conffile",
187 "description" => $description,
191 $cfg = @file_get_contents("$r->repopath/.hg/hgrc");
194 foreach ($apply as $sect => $opts) {
195 foreach ($opts as $name => $value) {
196 if (preg_match("/^$name\s*=/m", $cfg)) {
197 $cfg = preg_replace("/^$name\s*=.*$/m", "$name = $value", $cfg);
199 $adds[$sect][$name] = $value;
204 foreach ($adds as $sect => $opts) {
206 foreach ($opts as $name => $value) {
207 $cfg .= "$name = $value\n";
210 file_put_contents("$r->repopath/.hg/hgrc", $cfg, LOCK_EX);
211 system("chmod -R 02777 $r->repopath");
218 function getServerURL() {
219 $url = parent::getServerURL();
220 if ($url) return $url;
221 $url = MTrackConfig::get('repos', 'serverurl');
223 return "ssh://$url/" . $this->getBrowseRootName();
228 public function getBranches()
230 if ($this->branches !== null) {
231 return $this->branches;
233 $this->branches = array();
234 $fp = $this->hg('branches');
235 while ($line = fgets($fp)) {
236 list($branch, $revstr) = preg_split('/\s+/', $line);
237 list($num, $rev) = explode(':', $revstr, 2);
238 $this->branches[$branch] = $rev;
241 return $this->branches;
244 public function getTags()
246 if ($this->tags !== null) {
249 $this->tags = array();
250 $fp = $this->hg('tags');
251 while ($line = fgets($fp)) {
252 list($tag, $revstr) = preg_split('/\s+/', $line);
253 list($num, $rev) = explode(':', $revstr, 2);
254 $this->tags[$tag] = $rev;
260 public function readdir($path, $object = null, $ident = null)
264 if ($object === null) {
268 $rev = $this->resolveRevision(null, $object, $ident);
270 $fp = $this->hg('manifest', '-r', $rev);
275 $plen = strlen($path);
280 while ($line = fgets($fp)) {
283 if (!strncmp($name, $path, $plen)) {
285 $ent = substr($name, $plen);
286 if (strpos($ent, '/') === false) {
287 $res[] = new MTrackSCMFileHg($this, "$path$ent", $rev);
289 list($d) = explode('/', $ent, 2);
290 if (!isset($dirs[$d])) {
292 $res[] = new MTrackSCMFileHg($this, "$path$d", $rev, true);
299 throw new Exception("location $path does not exist");
304 public function file($path, $object = null, $ident = null)
306 if ($object == null) {
307 $branches = $this->getBranches();
308 if (isset($branches['default'])) {
317 $rev = $this->resolveRevision(null, $object, $ident);
318 return new MTrackSCMFileHg($this, $path, $rev);
321 public function history($path, $limit = null, $object = null, $ident = null)
326 if ($object !== null) {
327 $rev = $this->resolveRevision(null, $object, $ident);
331 if ($limit !== null) {
332 if (is_int($limit)) {
336 $t = strtotime($limit);
343 $fp = $this->hg('log',
344 '--template', $sep . '\n{node|short}\n{branches}\n{tags}\n{file_adds}\n{file_copies}\n{file_mods}\n{file_dels}\n{author|email}\n{date|hgdate}\n{desc}\n', $args,
347 fgets($fp); # discard leading $sep
349 // corresponds to the file_adds, file_copies, file_modes, file_dels
350 // in the template above
351 static $file_status_order = array('A', 'C', 'M', 'D');
354 $ent = new MTrackSCMEvent;
355 $ent->rev = trim(fgets($fp));
356 if (!strlen($ent->rev)) {
360 $ent->branches = array();
361 foreach (preg_split('/\s+/', trim(fgets($fp))) as $b) {
363 $ent->branches[] = $b;
366 if (!count($ent->branches)) {
367 $ent->branches[] = 'default';
370 $ent->tags = array();
371 foreach (preg_split('/\s+/', trim(fgets($fp))) as $t) {
377 $ent->files = array();
379 foreach ($file_status_order as $status) {
380 foreach (preg_split('/\s+/', trim(fgets($fp))) as $t) {
382 $f = new MTrackSCMFileEvent;
384 $f->status = $status;
390 $ent->changeby = trim(fgets($fp));
391 list($ts) = preg_split('/\s+/', fgets($fp));
392 $ent->ctime = MTrackDB::unixtime((int)$ts);
393 $changelog = array();
394 while (($line = fgets($fp)) !== false) {
395 $line = rtrim($line, "\r\n");
399 $changelog[] = $line;
401 $ent->changelog = join("\n", $changelog);
405 if ($line === false) {
413 public function diff($path, $from = null, $to = null)
415 if ($path instanceof MTrackSCMFile) {
416 if ($from === null) {
422 return $this->hg('diff', '-r', $from, '-r', $to,
425 return $this->hg('diff', '-c', $from, '--git', $path);
428 public function getWorkingCopy()
430 return new MTrackWCHg($this);
433 public function getRelatedChanges($revision)
438 foreach (preg_split('/\s+/',
439 stream_get_contents($this->hg('parents', '-r', $revision,
440 '--template', '{node|short}\n'))) as $p) {
446 foreach (preg_split('/\s+/',
447 stream_get_contents($this->hg('--config',
448 'extensions.children=',
449 'children', '-r', $revision,
450 '--template', '{node|short}\n'))) as $p) {
455 return array($parents, $kids);
460 $args = func_get_args();
461 $a = array("-y", "-R", $this->repopath, "--cwd", $this->repopath);
462 foreach ($args as $arg) {
466 return mtrack_run_tool('hg', 'read', $a);
470 MTrackRepo::registerSCM('hg', 'MTrackSCMHg');