Indefero

Indefero Commit Details


Date:2010-04-29 19:03:58 (14 years 7 months ago)
Author:Thomas Keller
Branch:develop, feature-issue_links, feature.better-home, feature.content-md5, feature.diff-whitespace, feature.download-md5, feature.issue-links, feature.issue-of-others, feature.issue-summary, feature.search-filter, feature.webrepos, feature.wiki-default-page, master, release-1.1, release-1.2, release-1.3
Commit:445c90fefe75c4e8a601f0e038309d3057b881f4
Parents: b7ced5fa69a838280a2bb49c2be8b821de982a84
Message:Create a separate class which handles command streaming over mtn automate stdio. Use that everywhere instead of the direct system calls.

Changes:

File differences

src/IDF/Scm/Monotone.php
2525
2626
2727
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
28264
29265
30266
31267
268
269
32270
33271
34272
......
39277
40278
41279
280
42281
43282
44283
......
56295
57296
58297
59
60
61
62
63
64
65
66
67
68
298
299
300
301
69302
303
70304
71
305
72306
73307
74308
......
77311
78312
79313
80
81
82
83
84
85
86
87
88
89
90
91
314
315
92316
93317
94318
95319
96
320
321
322
97323
98324
325
99326
100327
101328
......
121348
122349
123350
124
125
126
127
128
129
130
131
351
352
132353
133354
134355
......
139360
140361
141362
142
143
144
145363
146364
147365
......
208426
209427
210428
211
212
213
214
215
216
217
429
218430
219
431
220432
221433
222434
......
264476
265477
266478
267
268
269
270
271
272
273
274
479
480
481
275482
276
483
277484
278485
279486
......
305512
306513
307514
308
515
516
309517
310518
311
312
313
314
315
519
520
316521
317522
318
523
319524
320525
321526
......
360565
361566
362567
363
364
365
366
367
368
568
569
570
369571
370572
371
573
372574
373575
374576
......
501703
502704
503705
504
505
506
507
508
509
706
707
708
510709
511710
512
711
513712
514713
515714
......
562761
563762
564763
565
566
567
568
569
570
571
764
765
766
767
768
769
770
572771
573772
574773
......
594793
595794
596795
597
598
599
600
601
602
603
604
605
606
796
797
798
799
607800
608801
609802
......
655848
656849
657850
658
659
660
661
662
663
664
851
852
853
665854
666855
667
856
668857
669858
670859
......
682871
683872
684873
685
686
687
688
689
690
691
692
693
694
695
874
875
876
696877
697878
* Monotone utils.
*
*/
class IDF_Scm_Monotone_Stdio
{
public static $SUPPORTED_STDIO_VERSION = 2;
private $repo;
private $proc;
private $pipes;
private $oob;
private $cmdnum;
private $lastcmd;
public function __construct($repo)
{
$this->repo = $repo;
$this->start();
}
public function __destruct()
{
$this->stop();
}
public function start()
{
if (is_resource($this->proc))
$this->stop();
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate stdio --no-workspace --norc",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo));
$descriptors = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "r")
);
$this->proc = proc_open($cmd, $descriptors, $this->pipes);
if (!is_resource($this->proc))
{
throw new IDF_Scm_Exception("could not start stdio process");
}
$this->_checkVersion();
$this->cmdnum = -1;
}
public function stop()
{
if (!is_resource($this->proc))
return;
fclose($this->pipes[0]);
fclose($this->pipes[1]);
fclose($this->pipes[2]);
proc_close($this->proc);
$this->proc = null;
}
private function _checkVersion()
{
$version = fgets($this->pipes[1]);
if (!preg_match('/^format-version: (\d+)$/', $version, $m) ||
$m[1] != self::$SUPPORTED_STDIO_VERSION)
{
throw new IDF_Scm_Exception(
"stdio format version mismatch, expected '".
self::$SUPPORTED_STDIO_VERSION."', got '".@$m[1]."'"
);
}
fgets($this->pipes[1]);
}
private function _write($args, $options = array())
{
$cmd = "";
if (count($options) > 0)
{
$cmd = "o";
foreach ($options as $k => $v)
{
$cmd .= strlen((string)$k) . ":" . (string)$k;
$cmd .= strlen((string)$v) . ":" . (string)$v;
}
$cmd .= "e ";
}
$cmd .= "l";
foreach ($args as $arg)
{
$cmd .= strlen((string)$arg) . ":" . (string)$arg;
}
$cmd .= "e\n";
if (!fwrite($this->pipes[0], $cmd))
{
throw new IDF_Scm_Exception("could not write '$cmd' to process");
}
$this->lastcmd = $cmd;
$this->cmdnum++;
}
private function _read()
{
$this->oob = array('w' => array(),
'p' => array(),
't' => array(),
'e' => array());
$output = "";
$errcode = 0;
while (true)
{
$read = array($this->pipes[1]);
$write = null;
$except = null;
$streamsChanged = stream_select(
$read, $write, $except, 0, 20000
);
if ($streamsChanged === false)
{
throw new IDF_Scm_Exception(
"Could not select() on read pipe"
);
}
if ($streamsChanged == 0)
{
continue;
}
$data = array(0,"",0);
$idx = 0;
while (true)
{
$c = fgetc($this->pipes[1]);
if ($c == ':')
{
if ($idx == 2)
break;
++$idx;
continue;
}
if (is_numeric($c))
$data[$idx] = $data[$idx] * 10 + $c;
else
$data[$idx] .= $c;
}
// sanity
if ($this->cmdnum != $data[0])
{
throw new IDF_Scm_Exception(
"command numbers out of sync; ".
"expected {$this->cmdnum}, got {$data[0]}"
);
}
$toRead = $data[2];
$buffer = "";
while ($toRead > 0)
{
$buffer .= fread($this->pipes[1], $toRead);
$toRead = $data[2] - strlen($buffer);
}
switch ($data[1])
{
case 'w':
case 'p':
case 't':
case 'e':
$this->oob[$data[1]][] = $buffer;
continue;
case 'm':
$output .= $buffer;
continue;
case 'l':
$errcode = $buffer;
break 2;
}
}
if ($errcode != 0)
{
throw new IDF_Scm_Exception(
"command '{$this->lastcmd}' returned error code $errcode: ".
implode(" ", $this->oob['e'])
);
}
return $output;
}
public function exec($args, $options = array())
{
$this->_write($args, $options);
return $this->_read();
}
public function getLastWarnings()
{
return array_key_exists('w', $this->oob) ?
$this->oob['w'] : array();
}
public function getLastProgress()
{
return array_key_exists('p', $this->oob) ?
$this->oob['p'] : array();
}
public function getLastTickers()
{
return array_key_exists('t', $this->oob) ?
$this->oob['t'] : array();
}
public function getLastErrors()
{
return array_key_exists('e', $this->oob) ?
$this->oob['e'] : array();
}
}
class IDF_Scm_Monotone extends IDF_Scm
{
public static $MIN_INTERFACE_VERSION = 12.0;
private $stdio;
/* ============================================== *
* *
* Common Methods Implemented By All The SCMs *
{
$this->repo = $repo;
$this->project = $project;
$this->stdio = new IDF_Scm_Monotone_Stdio($repo);
}
public function getRepositorySize()
public function isAvailable()
{
$out = array();
try {
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate interface_version",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo));
self::exec('IDF_Scm_Monotone::isAvailable',
$cmd, $out, $return);
} catch (IDF_Scm_Exception $e) {
return false;
try
{
$out = $this->stdio->exec(array("interface_version"));
return floatval($out) >= self::$MIN_INTERFACE_VERSION;
}
catch (IDF_Scm_Exception $e) {}
return count($out) > 0 && floatval($out[0]) >= self::$MIN_INTERFACE_VERSION;
return false;
}
public function getBranches()
return $this->cache['branches'];
}
// FIXME: introduce handling of suspended branches
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate branches",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo));
self::exec('IDF_Scm_Monotone::getBranches',
$cmd, $out, $return);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf($this->error_tpl,
$cmd, $return,
implode("\n", $out)));
}
$res = array();
$out = $this->stdio->exec(array("branches"));
// FIXME: we could expand each branch with one of its head revisions
// here, but these would soon become bogus anyway and we cannot
// map multiple head revisions here either, so we just use the
// selector as placeholder
foreach ($out as $b) {
$res = array();
foreach (preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY) as $b)
{
$res["h:$b"] = $b;
}
$this->cache['branches'] = $res;
return $res;
}
*/
private function _resolveSelector($selector)
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate select %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($selector));
self::exec('IDF_Scm_Monotone::_resolveSelector',
$cmd, $out, $return);
return $out;
$out = $this->stdio->exec(array("select", $selector));
return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
}
/**
*/
private static function _parseBasicIO($in)
{
if (substr($in, -1) != "\n")
$in .= "\n";
$pos = 0;
$stanzas = array();
if (!array_key_exists($rev, $certCache))
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate certs %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($rev));
self::exec('IDF_Scm_Monotone::_getCerts',
$cmd, $out, $return);
$out = $this->stdio->exec(array("certs", $rev));
$stanzas = self::_parseBasicIO(implode("\n", $out));
$stanzas = self::_parseBasicIO($out);
$certs = array();
foreach ($stanzas as $stanza)
{
private function _getLastChangeFor($file, $startrev)
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate get_content_changed %s %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($startrev),
escapeshellarg($file));
self::exec('IDF_Scm_Monotone::_getLastChangeFor',
$cmd, $out, $return);
$out = $this->stdio->exec(array(
"get_content_changed", $startrev, $file
));
$stanzas = self::_parseBasicIO(implode("\n", $out));
$stanzas = self::_parseBasicIO($out);
// FIXME: we only care about the first returned content mark
// everything else seem to be very rare cases
**/
public function getTags()
{
if (isset($this->cache['tags'])) {
if (isset($this->cache['tags']))
{
return $this->cache['tags'];
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate tags",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo));
self::exec('IDF_Scm_Monotone::getTags', $cmd, $out, $return);
$out = $this->stdio->exec(array("tags"));
$tags = array();
$stanzas = self::_parseBasicIO(implode("\n", $out));
$stanzas = self::_parseBasicIO($out);
foreach ($stanzas as $stanza)
{
$tagname = null;
return array();
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate get_manifest_of %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($revs[0]));
self::exec('IDF_Scm_Monotone::getTree', $cmd, $out, $return);
$out = $this->stdio->exec(array(
"get_manifest_of", $revs[0]
));
$files = array();
$stanzas = self::_parseBasicIO(implode("\n", $out));
$stanzas = self::_parseBasicIO($out);
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
foreach ($stanzas as $stanza)
if (count($revs) == 0)
return false;
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate get_manifest_of %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($revs[0]));
self::exec('IDF_Scm_Monotone::getPathInfo', $cmd, $out, $return);
$out = $this->stdio->exec(array(
"get_manifest_of", $revs[0]
));
$files = array();
$stanzas = self::_parseBasicIO(implode("\n", $out));
$stanzas = self::_parseBasicIO($out);
foreach ($stanzas as $stanza)
{
public function getFile($def, $cmd_only=false)
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate get_file %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($def->hash));
return ($cmd_only)
? $cmd : self::shell_exec('IDF_Scm_Monotone::getFile', $cmd);
// this won't work with remote databases
if ($cmd_only)
{
throw new Pluf_Exception_NotImplemented();
}
return $this->stdio->exec(array("get_file", $def->hash));
}
private function _getDiff($target, $source = null)
return "";
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate content_diff -r %s -r %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($sources[0]),
escapeshellarg($targets[0]));
self::exec('IDF_Scm_Monotone::_getDiff',
$cmd, $out, $return);
return implode("\n", $out);
return $this->stdio->exec(
array("content_diff"),
array("r" => $sources[0], "r" => $targets[0])
);
}
/**
if (count($revs) == 0)
return false;
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate get_revision %s",
Pluf::f('mtn_path', 'mtn'),
escapeshellarg($this->repo),
escapeshellarg($revs[0]));
self::exec('IDF_Scm_Monotone::isCommitLarge',
$cmd, $out, $return);
$out = $this->stdio->exec(array(
"get_revision", $revs[0]
));
$newAndPatchedFiles = 0;
$stanzas = self::_parseBasicIO(implode("\n", $out));
$stanzas = self::_parseBasicIO($out);
foreach ($stanzas as $stanza)
{
* @param int Number of changes (10).
* @return array Changes.
*/
public function getChangeLog($commit='HEAD', $n=10)
{
if ($n === null) $n = '';
else $n = ' -'.$n;
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log%s --date=iso --pretty=format:\'%s\' %s',
escapeshellarg($this->repo), $n, $this->mediumtree_fmt,
escapeshellarg($commit));
$out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Monotone::getChangeLog', $cmd, $out);
return self::parseLog($out);
public function getChangeLog($commit=null, $n=10)
{
throw new Pluf_Exception_NotImplemented();
}
}

Archive Download the corresponding diff file

Page rendered in 0.08668s using 13 queries.