Indefero

Indefero Commit Details


Date:2010-04-28 18:44:34 (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:cf22909722bb54602bc8bca2e71737f17351ee5e
Parents: 02603fd8fd8b951ee7b6296b29cf877f5e28919d
Message:* isAvailable(): check monotone's interface version and mark the interface as available if it matches (we might see later on if this alone is actually a good idea especially if we browse an empty database...) * _getCerts(): implement a cert cache and make multiple cert values easily available * getCommit(), getCommitLarge(), getFile(), getPathInfo(), testHash(): implement * getTags(): save the first found revision id for a tag as key in the associative array to make tags actually browsable

Changes:

File differences

src/IDF/Scm/Monotone.php
2727
2828
2929
30
30
3131
3232
3333
......
5656
5757
5858
59
5960
60
61
62
63
64
65
66
6167
6268
6369
64
70
71
6572
6673
6774
......
112119
113120
114121
115
122
116123
117124
118125
......
132139
133140
134141
142
143
144
135145
136146
137147
......
192202
193203
194204
195
205
196206
197
198
207
208
209
199210
200211
201212
202213
203214
204215
205
216
206217
207218
208
219
220
209221
210222
223
211224
212225
213226
214
215
227
216228
217
229
230
218231
232
219233
220234
221
235
236
237
238
239
240
222241
223242
224243
225244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
226261
227262
228263
229264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
230293
231294
232295
233296
234297
235
298
236299
237
300
238301
239302
240303
......
252315
253316
254317
255
318
256319
257320
321
258322
259323
324
260325
261326
262
327
328
329
330
331
332
263333
264334
265335
......
274344
275345
276346
277
347
278348
279
349
280350
281351
282352
......
284354
285355
286356
287
288
357
358
289359
290
291
292
293
360
294361
295362
296363
......
301368
302369
303370
304
371
305372
306373
307374
......
319386
320387
321388
322
389
390
391
392
323393
324
325
326
327
328
329
330
331
332
333
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
334414
335
336
337
415
338416
339417
340418
......
349427
350428
351429
352
430
353431
354
432
355433
356434
357435
......
364442
365443
366444
367
445
368446
369447
370448
371449
372450
373451
452
453
454
455
456
374457
375458
376459
377
460
378461
379462
380463
......
391474
392475
393476
394
477
395478
396479
397480
398481
399482
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
483
484
449485
450486
451487
......
455491
456492
457493
458
494
459495
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
475537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
476559
477560
478561
479562
480563
481564
482
483
484
485
565
566
567
568
569
486570
487571
488572
489573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
490609
491610
492611
......
496615
497616
498617
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
537639
538640
539641
......
542644
543645
544646
545
647
546648
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
649
650
651
566652
567
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
568676
569677
570678
......
586694
587695
588696
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
697
*/
class IDF_Scm_Monotone extends IDF_Scm
{
public $mediumtree_fmt = 'commit %H%nAuthor: %an <%ae>%nTree: %T%nDate: %ai%n%n%s%n%n%b';
public static $MIN_INTERFACE_VERSION = 12.0;
/* ============================================== *
* *
public function isAvailable()
{
$out = array();
try {
$branches = $this->getBranches();
$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;
}
return (count($branches) > 0);
return count($out) > 0 && floatval($out[0]) >= self::$MIN_INTERFACE_VERSION;
}
public function getBranches()
* @param string $selector
* @return array
*/
private static function _resolveSelector($selector)
private function _resolveSelector($selector)
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf("%s -d %s automate select %s",
*/
private static function _parseBasicIO($in)
{
if (substr($in, -1) != "\n")
$in .= "\n";
$pos = 0;
$stanzas = array();
return $stanzas;
}
private static function _getUniqueCertValuesFor($revs, $certName)
private function _getCerts($rev)
{
$certValues = array();
foreach ($revs as $rev)
static $certCache = 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::inBranches',
self::exec('IDF_Scm_Monotone::_getCerts',
$cmd, $out, $return);
$stanzas = self::_parseBasicIO(implode('\n', $out));
$stanzas = self::_parseBasicIO(implode("\n", $out));
$certs = array();
foreach ($stanzas as $stanza)
{
$certname = null;
foreach ($stanza as $stanzaline)
{
// luckily, name always comes before value
if ($stanzaline['key'] == "name" &&
$stanzaline['values'][0] != $certName)
if ($stanzaline['key'] == "name")
{
break;
$certname = $stanzaline['values'][0];
continue;
}
if ($stanzaline['key'] == "value")
{
$certValues[] = $stanzaline['values'][0];
if (!array_key_exists($certname, $certs))
{
$certs[$certname] = array();
}
$certs[$certname][] = $stanzaline['values'][0];
break;
}
}
}
$certCache[$rev] = $certs;
}
return $certCache[$rev];
}
private function _getUniqueCertValuesFor($revs, $certName)
{
$certValues = array();
foreach ($revs as $rev)
{
$certs = $this->_getCerts($rev);
if (!array_key_exists($certName, $certs))
continue;
$certValues = array_merge($certValues, $certs[$certName]);
}
return array_unique($certValues);
}
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);
$stanzas = self::_parseBasicIO(implode("\n", $out));
// FIXME: we only care about the first returned content mark
// everything else seem to be very rare cases
foreach ($stanzas as $stanza)
{
foreach ($stanza as $stanzaline)
{
if ($stanzaline['key'] == "content_mark")
{
return $stanzaline['hash'];
}
}
}
return null;
}
/**
* @see IDF_Scm::inBranches()
**/
public function inBranches($commit, $path)
{
$revs = self::_resolveSelector($commit);
$revs = $this->_resolveSelector($commit);
if (count($revs) == 0) return array();
return self::_getUniqueCertValuesFor($revs, "branch");
return $this->_getUniqueCertValuesFor($revs, "branch");
}
/**
self::exec('IDF_Scm_Monotone::getTags', $cmd, $out, $return);
$tags = array();
$stanzas = self::parseBasicIO(implode('\n', $out));
$stanzas = self::_parseBasicIO(implode("\n", $out));
foreach ($stanzas as $stanza)
{
$tagname = null;
foreach ($stanza as $stanzaline)
{
// revision comes directly after the tag stanza
if ($stanzaline['key'] == "tag")
{
$tags[] = $stanzaline['values'][0];
$tagname = $stanzaline['values'][0];
continue;
}
if ($stanzaline['key'] == "revision")
{
$tags[$stanzaline['hash']] = $tagname;
break;
}
}
**/
public function inTags($commit, $path)
{
$revs = self::_resolveSelector($commit);
$revs = $this->_resolveSelector($commit);
if (count($revs) == 0) return array();
return self::_getUniqueCertValuesFor($revs, "tag");
return $this->_getUniqueCertValuesFor($revs, "tag");
}
/**
*/
public function getTree($commit, $folder='/', $branch=null)
{
$revs = self::_resolveSelector($commit);
if ($revs != 1)
$revs = $this->_resolveSelector($commit);
if (count($revs) == 0)
{
throw new Exception(sprintf(
__('Commit %1$s does not (uniquely) identify a revision.'),
$commit
));
return array();
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
self::exec('IDF_Scm_Monotone::getTree', $cmd, $out, $return);
$files = array();
$stanzas = self::parseBasicIO(implode('\n', $out));
$stanzas = self::_parseBasicIO(implode("\n", $out));
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
foreach ($stanzas as $stanza)
$file['efullpath'] = self::smartEncode($path);
if ($stanza[0]['key'] == "dir")
$file['type'] == "tree";
{
$file['type'] = "tree";
$file['size'] = 0;
}
else
$file['type'] == "blob";
/*
$file['date'] = gmdate('Y-m-d H:i:s',
strtotime((string) $entry->commit->date));
$file['rev'] = (string) $entry->commit['revision'];
$file['log'] = $this->getCommitMessage($file['rev']);
// Get the size if the type is blob
if ($file['type'] == 'blob') {
$file['size'] = (string) $entry->size;
{
$file['type'] = "blob";
$file['hash'] = $stanza[1]['hash'];
$file['size'] = strlen($this->getFile((object)$file));
}
$rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);
if ($rev !== null)
{
$file['rev'] = $rev;
$certs = $this->_getCerts($rev);
// FIXME: this assumes that author, date and changelog are always given
$file['author'] = implode(", ", $certs['author']);
$dates = array();
foreach ($certs['date'] as $date)
$dates[] = gmdate('Y-m-d H:i:s', strtotime($date));
$file['date'] = implode(', ', $dates);
$file['log'] = substr(implode("; ", $certs['changelog']), 0, 80);
}
$file['author'] = (string) $entry->commit->author;
*/
$file['perm'] = '';
$files[] = (object) $file;
}
return $files;
*/
public function findAuthor($author)
{
// We extract the email.
// We extract anything which looks like an email.
$match = array();
if (!preg_match('/<(.*)>/', $author, $match)) {
if (!preg_match('/([^ ]+@[^ ]+)/', $author, $match)) {
return null;
}
foreach (array('email', 'login') as $what) {
return null;
}
public static function getAnonymousAccessUrl($project)
private static function _getMasterBranch($project)
{
$conf = $project->getConf();
if (false === ($branch = $conf->getVal('mtn_master_branch', false))
|| empty($branch)) {
$branch = "*";
}
return $branch;
}
public static function getAnonymousAccessUrl($project)
{
return sprintf(
Pluf::f('mtn_remote_url'),
$project->shortname,
$branch
self::_getMasterBranch($project)
);
}
*/
public static function factory($project)
{
$rep = sprintf(Pluf::f('git_repositories'), $project->shortname);
$rep = sprintf(Pluf::f('mtn_repositories'), $project->shortname);
return new IDF_Scm_Monotone($rep, $project);
}
public function isValidRevision($commit)
{
$type = $this->testHash($commit);
return ('commit' == $type || 'tag' == $type);
}
/**
* Test a given object hash.
*
* @param string Object hash.
* @return mixed false if not valid or 'blob', 'tree', 'commit', 'tag'
*/
public function testHash($hash)
{
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file -t %s',
escapeshellarg($this->repo),
escapeshellarg($hash));
$ret = 0; $out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Monotone::testHash', $cmd, $out, $ret);
if ($ret != 0) return false;
return trim($out[0]);
}
/**
* Get the tree info.
*
* @param string Tree hash
* @param bool Do we recurse in subtrees (true)
* @param string Folder in which we want to get the info ('')
* @return array Array of file information.
*/
public function getTreeInfo($tree, $folder='')
{
if (!in_array($this->testHash($tree), array('tree', 'commit', 'tag'))) {
throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree));
}
$cmd_tmpl = 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' ls-tree -l %s %s';
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf($cmd_tmpl, escapeshellarg($this->repo),
escapeshellarg($tree), escapeshellarg($folder));
$out = array();
$res = array();
self::exec('IDF_Scm_Monotone::getTreeInfo', $cmd, $out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
$res[] = (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'file' => $file);
}
return $res;
$revs = $this->_resolveSelector($commit);
return count($revs) == 1;
}
/**
* @param string Commit ('HEAD')
* @return false Information
*/
public function getPathInfo($totest, $commit='HEAD')
public function getPathInfo($file, $commit = null)
{
$cmd_tmpl = 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' ls-tree -r -t -l %s';
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
escapeshellarg($commit));
$out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Monotone::getPathInfo', $cmd, $out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
if ($totest == $file) {
$pathinfo = pathinfo($file);
return (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'fullpath' => $file,
'file' => $pathinfo['basename']);
if ($commit === null) {
$commit = 'h:' . self::_getMasterBranch($this->project);
}
$revs = $this->_resolveSelector($commit);
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);
$files = array();
$stanzas = self::_parseBasicIO(implode("\n", $out));
foreach ($stanzas as $stanza)
{
if ($stanza[0]['key'] == "format_version")
continue;
$path = $stanza[0]['values'][0];
if (!preg_match('#^'.$file.'$#', $path, $m))
continue;
$file = array();
$file['fullpath'] = $path;
if ($stanza[0]['key'] == "dir")
{
$file['type'] = "tree";
$file['hash'] = null;
$file['size'] = 0;
}
else
{
$file['type'] = "blob";
$file['hash'] = $stanza[1]['hash'];
$file['size'] = strlen($this->getFile((object)$file));
}
$pathinfo = pathinfo($file['fullpath']);
$file['file'] = $pathinfo['basename'];
$rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);
if ($rev !== null)
{
$file['rev'] = $rev;
$certs = $this->_getCerts($rev);
// FIXME: this assumes that author, date and changelog are always given
$file['author'] = implode(", ", $certs['author']);
$dates = array();
foreach ($certs['date'] as $date)
$dates[] = gmdate('Y-m-d H:i:s', strtotime($date));
$file['date'] = implode(', ', $dates);
$file['log'] = substr(implode("; ", $certs['changelog']), 0, 80);
}
return (object) $file;
}
return false;
}
public function getFile($def, $cmd_only=false)
{
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file blob %s',
escapeshellarg($this->repo),
escapeshellarg($def->hash));
$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);
}
private function _getDiff($target, $source = null)
{
if (empty($source))
{
$source = "p:$target";
}
// FIXME: add real support for merge revisions here which have
// two distinct diff sets
$targets = $this->_resolveSelector($target);
$sources = $this->_resolveSelector($source);
if (count($targets) == 0 || count($sources) == 0)
{
return "";
}
// if target contains a root revision, we cannot produce a diff
if (empty($sources[0]))
{
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);
}
/**
* Get commit details.
*
*/
public function getCommit($commit, $getdiff=false)
{
if ($getdiff) {
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' show --date=iso --pretty=format:%s %s',
escapeshellarg($this->repo),
"'".$this->mediumtree_fmt."'",
escapeshellarg($commit));
} else {
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log -1 --date=iso --pretty=format:%s %s',
escapeshellarg($this->repo),
"'".$this->mediumtree_fmt."'",
escapeshellarg($commit));
}
$out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Monotone::getCommit', $cmd, $out, $ret);
if ($ret != 0 or count($out) == 0) {
return false;
}
if ($getdiff) {
$log = array();
$change = array();
$inchange = false;
foreach ($out as $line) {
if (!$inchange and 0 === strpos($line, 'diff --git a')) {
$inchange = true;
}
if ($inchange) {
$change[] = $line;
} else {
$log[] = $line;
}
}
$out = self::parseLog($log);
$out[0]->changes = implode("\n", $change);
} else {
$out = self::parseLog($out);
$out[0]->changes = '';
}
return $out[0];
$revs = $this->_resolveSelector($commit);
if (count($revs) == 0)
return array();
$certs = $this->_getCerts($revs[0]);
// FIXME: this assumes that author, date and changelog are always given
$res['author'] = implode(", ", $certs['author']);
$dates = array();
foreach ($certs['date'] as $date)
$dates[] = gmdate('Y-m-d H:i:s', strtotime($date));
$res['date'] = implode(', ', $dates);
$res['title'] = implode("\n---\n, ", $certs['changelog']);
$res['commit'] = $revs[0];
$res['changes'] = ($getdiff) ? $this->_getDiff($revs[0]) : '';
return (object) $res;
}
/**
* @param string Commit ('HEAD')
* @return bool The commit is big
*/
public function isCommitLarge($commit='HEAD')
public function isCommitLarge($commit=null)
{
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log --numstat -1 --pretty=format:%s %s',
escapeshellarg($this->repo),
"'commit %H%n'",
escapeshellarg($commit));
$out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Monotone::isCommitLarge', $cmd, $out);
$affected = count($out) - 2;
$added = 0;
$removed = 0;
$c=0;
foreach ($out as $line) {
$c++;
if ($c < 3) {
continue;
}
list($a, $r, $f) = preg_split("/[\s]+/", $line, 3, PREG_SPLIT_NO_EMPTY);
$added+=$a;
$removed+=$r;
if (empty($commit))
{
$commit = "h:"+self::_getMasterBranch($this->project);
}
return ($affected > 100 or ($added + $removed) > 20000);
$revs = $this->_resolveSelector($commit);
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);
$newAndPatchedFiles = 0;
$stanzas = self::_parseBasicIO(implode("\n", $out));
foreach ($stanzas as $stanza)
{
if ($stanza[0]['key'] == "patch" || $stanza[0]['key'] == "add_file")
$newAndPatchedFiles++;
}
return $newAndPatchedFiles > 100;
}
/**
self::exec('IDF_Scm_Monotone::getChangeLog', $cmd, $out);
return self::parseLog($out);
}
/**
* Parse the log lines of a --pretty=medium log output.
*
* @param array Lines.
* @return array Change log.
*/
public static function parseLog($lines)
{
$res = array();
$c = array();
$inheads = true;
$next_is_title = false;
foreach ($lines as $line) {
if (preg_match('/^commit (\w{40})$/', $line)) {
if (count($c) > 0) {
$c['full_message'] = trim($c['full_message']);
$c['full_message'] = IDF_Commit::toUTF8($c['full_message']);
$c['title'] = IDF_Commit::toUTF8($c['title']);
$res[] = (object) $c;
}
$c = array();
$c['commit'] = trim(substr($line, 7, 40));
$c['full_message'] = '';
$inheads = true;
$next_is_title = false;
continue;
}
if ($next_is_title) {
$c['title'] = trim($line);
$next_is_title = false;
continue;
}
$match = array();
if ($inheads and preg_match('/(\S+)\s*:\s*(.*)/', $line, $match)) {
$match[1] = strtolower($match[1]);
$c[$match[1]] = trim($match[2]);
if ($match[1] == 'date') {
$c['date'] = gmdate('Y-m-d H:i:s', strtotime($match[2]));
}
continue;
}
if ($inheads and !$next_is_title and $line == '') {
$next_is_title = true;
$inheads = false;
}
if (!$inheads) {
$c['full_message'] .= trim($line)."\n";
continue;
}
}
$c['full_message'] = !empty($c['full_message']) ? trim($c['full_message']) : '';
$c['full_message'] = IDF_Commit::toUTF8($c['full_message']);
$c['title'] = IDF_Commit::toUTF8($c['title']);
$res[] = (object) $c;
return $res;
}
public function getArchiveCommand($commit, $prefix='repository/')
{
return sprintf(Pluf::f('idf_exec_cmd_prefix', '').
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' archive --format=zip --prefix=%s %s',
escapeshellarg($this->repo),
escapeshellarg($prefix),
escapeshellarg($commit));
}
}
}

Archive Download the corresponding diff file

Page rendered in 0.09617s using 13 queries.