From 9fd4334dec58e292e9a3c4e3c6ab776a6ee8d07c Mon Sep 17 00:00:00 2001
From: Thomas Keller {trans 'Branches:'} {trans 'Tags:'} {trans 'Branches:'} {trans 'Tags:'} {blocktrans}The team behind {$project} is using
+the git software to manage the source
+code.{/blocktrans} git clone {$project.getSourceAccessUrl($user)} {blocktrans}You may need to provide your SSH key. The synchronization of your SSH key can take a couple of minutes. You can learn more about SSH key authentification.{/blocktrans} {blocktrans}To make a first commit in the repository, perform the following steps:{/blocktrans} {blocktrans}Find here more details on how to access {$project} source code.{/blocktrans} {trans 'Branches:'} {trans 'Tags:'} {blocktrans}The team behind {$project} is using
-the git software to manage the source
+the monotone software to manage the source
code.{/blocktrans} git clone {$project.getSourceAccessUrl($user)} {blocktrans}You may need to provide your SSH key. The synchronization of your SSH key can take a couple of minutes. You can learn more about SSH key authentification.{/blocktrans} mtn clone {$project.getSourceAccessUrl()}
+{foreach $branches as $branch => $path}
+{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)}
+{if $path}{$path}{else}{$branch}{/if}
+{/foreach}
+
+{foreach $tags as $tag => $path}
+{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $tag)}
+
+{/foreach}
+{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
+
+
+{if !$tree_in and !$tags_in}
+{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
+
+
+{aurl 'url', 'IDF_Views_Source::getFile', array($project.shortname, $commit, $fullpath)}
+
+
+{/block}
+{block context}
+
+
+{/if}
+
+{$file}
+
+{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
+{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}
+
+{foreach $branches as $branch => $path}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
+{if $path}{$path}{else}{$branch}{/if}
+{/foreach}
+
+{foreach $tags as $tag => $path}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
+
+{/foreach}
+{trans 'Command-Line Access'}
+
+{trans 'First Commit'}
+
+
+git init
+git add .
+git commit -m "initial import"
+git remote add origin {$project.getWriteRemoteAccessUrl($url)}
+git push origin master
+
+
+{/if}
+
+{/block}
+{block context}
+{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
+
+
+
+
+{aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)}
+
+
+{if !$tree_in and !$tags_in}
+{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
+
+{trans 'File'}
+{trans 'Age'}
+{trans 'Message'}
+{trans 'Size'}
+
+
+{/if}
+{if $base}
+{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
+{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}
+
+
+{/if}
+{foreach $files as $file}
+{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, $commit, $file.efullpath)}
+
+
+..
+
+
+
+{/foreach}
+
+
+{if $file.type != 'extern'}
+
{$file.file} {else}{$file.file} {/if}
+{if $file.type == 'blob'}
+{if isset($file.date) and $file.log != '----'}
+{$file.date|dateago:"wihtout"}
+{$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false}
+{else}{/if}
+ {$file.size|size} {/if}
+{if $file.type == 'extern'}
+{$file.extern}
+{/if}
+
{trans 'Download this version'} {trans 'or'} git clone {$project.getSourceAccessUrl($user)} 
+{foreach $branches as $branch => $path}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
+{if $path}{$path}{else}{$branch}{/if}
+{/foreach}
+
+{foreach $tags as $tag => $path}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
+
+{/foreach}
+
+
{$form.f.mtn_master_branch.labelTag}:
+{if $form.f.mtn_master_branch.errors}{$form.f.mtn_master_branch.fieldErrors}{/if}
+{$form.f.mtn_master_branch|unsafe}
+
+{$form.f.mtn_master_branch.help_text}
+
{$form.f.owners.labelTag}:
@@ -76,7 +83,7 @@
@@ -112,12 +119,22 @@ $(document).ready(function() {
if ($("#id_scm option:selected").val() != "svn") {
$(".svn-form").hide();
}
+ // Hide if not mtn
+ if ($("#id_scm option:selected").val() != "mtn") {
+ $(".mtn-form").hide();
+ }
$("#id_scm").change(function () {
if ($("#id_scm option:selected").val() == "svn") {
$(".svn-form").show();
} else {
$(".svn-form").hide();
}
+ if ($("#id_scm option:selected").val() == "mtn") {
+ $(".mtn-form").show();
+ } else {
+ $(".mtn-form").hide();
+ }
+
});
});
diff --git a/src/IDF/templates/idf/source/mtn/help.html b/src/IDF/templates/idf/source/mtn/help.html
index 9e72ba9..34d0e83 100644
--- a/src/IDF/templates/idf/source/mtn/help.html
+++ b/src/IDF/templates/idf/source/mtn/help.html
@@ -3,15 +3,12 @@
{block body}
-
+
{trans 'Command-Line Access'}
-{trans 'First Commit'}
@@ -19,11 +16,9 @@ code.{/blocktrans}
{blocktrans}To make a first commit in the repository, perform the following steps:{/blocktrans}
-git init
-git add .
-git commit -m "initial import"
-git remote add origin {$project.getWriteRemoteAccessUrl($url)}
-git push origin master
+mtn add -R .
+mtn commit -m "initial import"
+mtn push {$project.getSourceAccessUrl()}
{/if}
From 94a5464155aa03030712a95ca25fd8e50e8b810a Mon Sep 17 00:00:00 2001
From: Thomas Keller {blocktrans}To make a first commit in the repository, perform the following steps:{/blocktrans}
+mtn db init -d project.mtn
+mtn setup -d project.mtn -b {$project.getConf().getVal('mtn_master_branch', 'your-branch')} .
mtn add -R .
mtn commit -m "initial import"
mtn push {$project.getSourceAccessUrl()}
From 5954cd0ad115397804ab48b9a9553c12266e500d Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Wed, 28 Apr 2010 00:14:19 +0200
Subject: [PATCH 05/40] default to the master branch for the head / tip / main
revision
---
src/IDF/Project.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/IDF/Project.php b/src/IDF/Project.php
index 762bbde..b1dfb76 100644
--- a/src/IDF/Project.php
+++ b/src/IDF/Project.php
@@ -436,7 +436,7 @@ class IDF_Project extends Pluf_Model
'git' => 'master',
'svn' => 'HEAD',
'mercurial' => 'tip',
- 'mtn' => 'h:',
+ 'mtn' => 'h:'.$conf->getVal('mtn_master_branch', '*'),
);
$scm = $conf->getVal('scm', 'git');
return $roots[$scm];
From f8012c37d1d8b105d4cd91ad741b0ba86082a137 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Wed, 28 Apr 2010 01:12:29 +0200
Subject: [PATCH 06/40] from monotone 0.48 onwards the setup command creates
its own internal database
---
src/IDF/templates/idf/source/mtn/help.html | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/IDF/templates/idf/source/mtn/help.html b/src/IDF/templates/idf/source/mtn/help.html
index e915cb5..6b2fd0c 100644
--- a/src/IDF/templates/idf/source/mtn/help.html
+++ b/src/IDF/templates/idf/source/mtn/help.html
@@ -16,8 +16,7 @@ code.{/blocktrans}
{blocktrans}To make a first commit in the repository, perform the following steps:{/blocktrans}
-mtn db init -d project.mtn
-mtn setup -d project.mtn -b {$project.getConf().getVal('mtn_master_branch', 'your-branch')} .
+mtn setup -b {$project.getConf().getVal('mtn_master_branch', 'your-branch')} .
mtn add -R .
mtn commit -m "initial import"
mtn push {$project.getSourceAccessUrl()}
From af4f5aaeb09abff77df8dd671bd74cefcbdcb109 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Thu, 29 Apr 2010 01:37:28 +0200
Subject: [PATCH 07/40] make the path to the monotone executable configurable
---
src/IDF/conf/idf.php-dist | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/IDF/conf/idf.php-dist b/src/IDF/conf/idf.php-dist
index 6f46f17..49f7e2d 100644
--- a/src/IDF/conf/idf.php-dist
+++ b/src/IDF/conf/idf.php-dist
@@ -75,6 +75,7 @@ $cfg['svn_remote_url'] = 'http://localhost/svn/%s';
# Same as for git, you can have multiple repositories, one for each
# project or a single one for all the projects.
+$cfg['mtn_path'] = 'mtn';
$cfg['mtn_repositories'] = '/home/mtn/repositories/%s.mtn';
$cfg['mtn_remote_url'] = 'mtn://localhost/~%s/%s';
From 02603fd8fd8b951ee7b6296b29cf877f5e28919d Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Thu, 29 Apr 2010 01:42:50 +0200
Subject: [PATCH 08/40] disable archive generation for now, this is not
possible with monotone as it is implemented now in IDF
---
src/IDF/templates/idf/source/mtn/tree.html | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/IDF/templates/idf/source/mtn/tree.html b/src/IDF/templates/idf/source/mtn/tree.html
index 859a249..8dfdb18 100644
--- a/src/IDF/templates/idf/source/mtn/tree.html
+++ b/src/IDF/templates/idf/source/mtn/tree.html
@@ -12,7 +12,7 @@
{trans 'Size'}
{if !$tree_in and !$tags_in}
-{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
+{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}
@@ -35,7 +35,7 @@
{$file.file} {else}{$file.file} {/if}
{if $file.type == 'blob'}
{if isset($file.date) and $file.log != '----'}
-{$file.date|dateago:"wihtout"}
+{$file.date|dateago:"wihtout"}
{$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false}
{else} {/if}
{$file.size|size} {/if}
@@ -47,7 +47,10 @@
{aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)}
-
{trans 'Download this version'} {trans 'or'} git clone {$project.getSourceAccessUrl($user)} 
+
+{*
{trans 'Download this version'} {trans 'or'} *}
+mtn clone {$project.getSourceAccessUrl($user)}
+
{/block}
From cf22909722bb54602bc8bca2e71737f17351ee5e Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Thu, 29 Apr 2010 01:44:34 +0200
Subject: [PATCH 09/40] * 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
---
src/IDF/Scm/Monotone.php | 524 +++++++++++++++++++++------------------
1 file changed, 283 insertions(+), 241 deletions(-)
diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php
index 160bf75..d93bce1 100644
--- a/src/IDF/Scm/Monotone.php
+++ b/src/IDF/Scm/Monotone.php
@@ -27,7 +27,7 @@
*/
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;
/* ============================================== *
* *
@@ -56,12 +56,19 @@ class IDF_Scm_Monotone extends IDF_Scm
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()
@@ -112,7 +119,7 @@ class IDF_Scm_Monotone extends IDF_Scm
* @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",
@@ -132,6 +139,9 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
private static function _parseBasicIO($in)
{
+ if (substr($in, -1) != "\n")
+ $in .= "\n";
+
$pos = 0;
$stanzas = array();
@@ -192,49 +202,102 @@ class IDF_Scm_Monotone extends IDF_Scm
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");
}
/**
@@ -252,14 +315,21 @@ class IDF_Scm_Monotone extends IDF_Scm
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;
}
}
@@ -274,9 +344,9 @@ class IDF_Scm_Monotone extends IDF_Scm
**/
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");
}
/**
@@ -284,13 +354,10 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
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', '')
@@ -301,7 +368,7 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
@@ -319,22 +386,33 @@ class IDF_Scm_Monotone extends IDF_Scm
$file['efullpath'] = self::smartEncode($path);
if ($stanza[0]['key'] == "dir")
- $file['type'] == "tree";
- 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'] = "tree";
+ $file['size'] = 0;
}
- $file['author'] = (string) $entry->commit->author;
- */
- $file['perm'] = '';
+ else
+ {
+ $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);
+ }
+
$files[] = (object) $file;
}
return $files;
@@ -349,9 +427,9 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
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) {
@@ -364,17 +442,22 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
);
}
@@ -391,61 +474,14 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
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;
}
/**
@@ -455,38 +491,121 @@ class IDF_Scm_Monotone extends IDF_Scm
* @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.
*
@@ -496,44 +615,27 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
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;
}
/**
@@ -542,29 +644,35 @@ class IDF_Scm_Monotone extends IDF_Scm
* @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;
}
/**
@@ -586,70 +694,4 @@ class IDF_Scm_Monotone extends IDF_Scm
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));
- }
-}
\ No newline at end of file
+}
From 995f1a13c3b17aac73aa7f0dcdbe5ddb021b404f Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Thu, 29 Apr 2010 23:35:57 +0200
Subject: [PATCH 10/40] Add a new view modifier which allows the shortening of
long strings such as branch or tag names. Use that in the tree view and
display the full name in a title tag.
---
src/IDF/Middleware.php | 5 +++--
src/IDF/Views/Source.php | 25 ++++++++++++++++------
src/IDF/templates/idf/source/mtn/tree.html | 8 +++++--
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/src/IDF/Middleware.php b/src/IDF/Middleware.php
index a4060af..e0436b6 100644
--- a/src/IDF/Middleware.php
+++ b/src/IDF/Middleware.php
@@ -92,6 +92,7 @@ class IDF_Middleware
array(
'size' => 'IDF_Views_Source_PrettySize',
'ssize' => 'IDF_Views_Source_PrettySizeSimple',
+ 'shorten' => 'IDF_Views_Source_ShortenString',
));
}
}
@@ -104,9 +105,9 @@ function IDF_Middleware_ContextPreProcessor($request)
$c['isAdmin'] = ($request->user->administrator or $request->user->staff);
if (isset($request->project)) {
$c['project'] = $request->project;
- $c['isOwner'] = $request->user->hasPerm('IDF.project-owner',
+ $c['isOwner'] = $request->user->hasPerm('IDF.project-owner',
$request->project);
- $c['isMember'] = $request->user->hasPerm('IDF.project-member',
+ $c['isMember'] = $request->user->hasPerm('IDF.project-member',
$request->project);
$c = array_merge($c, $request->rights);
}
diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php
index e979084..9a4791f 100644
--- a/src/IDF/Views/Source.php
+++ b/src/IDF/Views/Source.php
@@ -35,10 +35,10 @@ class IDF_Views_Source
* Extension supported by the syntax highlighter.
*/
public static $supportedExtenstions = array(
- 'ascx', 'ashx', 'asmx', 'aspx', 'browser', 'bsh', 'c', 'cc',
- 'config', 'cpp', 'cs', 'csh', 'csproj', 'css', 'cv', 'cyc',
- 'html', 'html', 'java', 'js', 'master', 'perl', 'php', 'pl',
- 'pm', 'py', 'rb', 'sh', 'sitemap', 'skin', 'sln', 'svc', 'vala',
+ 'ascx', 'ashx', 'asmx', 'aspx', 'browser', 'bsh', 'c', 'cc',
+ 'config', 'cpp', 'cs', 'csh', 'csproj', 'css', 'cv', 'cyc',
+ 'html', 'html', 'java', 'js', 'master', 'perl', 'php', 'pl',
+ 'pm', 'py', 'rb', 'sh', 'sitemap', 'skin', 'sln', 'svc', 'vala',
'vb', 'vbproj', 'wsdl', 'xhtml', 'xml', 'xsd', 'xsl', 'xslt');
/**
@@ -415,7 +415,7 @@ class IDF_Views_Source
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
- $info = self::getRequestedFileMimeType($request_file_info,
+ $info = self::getRequestedFileMimeType($request_file_info,
$commit, $scm);
$rep = new Pluf_HTTP_Response($scm->getFile($request_file_info),
$info[0]);
@@ -476,7 +476,7 @@ class IDF_Views_Source
public static function getMimeTypeFromContent($file, $filedata)
{
$info = pathinfo($file);
- $res = array('application/octet-stream',
+ $res = array('application/octet-stream',
$info['basename'],
isset($info['extension']) ? $info['extension'] : 'bin');
if (function_exists('finfo_open')) {
@@ -597,3 +597,16 @@ function IDF_Views_Source_PrettySizeSimple($size)
return Pluf_Utils::prettySize($size);
}
+function IDF_Views_Source_ShortenString($string, $length)
+{
+ $ellipse = "...";
+ $length = max(strlen($ellipse) + 2, $length);
+ $preflen = ceil($length / 10);
+
+ if (mb_strlen($string) < $length)
+ return $string;
+
+ return substr($string, 0, $preflen).$ellipse.
+ substr($string, -($length - $preflen - mb_strlen($ellipse)));
+}
+
diff --git a/src/IDF/templates/idf/source/mtn/tree.html b/src/IDF/templates/idf/source/mtn/tree.html
index 8dfdb18..933f7b0 100644
--- a/src/IDF/templates/idf/source/mtn/tree.html
+++ b/src/IDF/templates/idf/source/mtn/tree.html
@@ -58,14 +58,18 @@
{trans 'Branches:'}
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-{if $path}{$path}{else}{$branch}{/if}
+
+ {if $path}{$path|shorten:25}{else}{$branch|shorten:25}{/if}
+
{/foreach}
{if $tags}
{trans 'Tags:'}
{foreach $tags as $tag => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
-
+
{/foreach}
{/if}
From b7ced5fa69a838280a2bb49c2be8b821de982a84 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Thu, 29 Apr 2010 23:38:28 +0200
Subject: [PATCH 11/40] Do not shorten the changelog in the SCM model - thats a
task for the view.
---
src/IDF/Scm/Monotone.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php
index d93bce1..5d71efe 100644
--- a/src/IDF/Scm/Monotone.php
+++ b/src/IDF/Scm/Monotone.php
@@ -410,7 +410,7 @@ class IDF_Scm_Monotone extends IDF_Scm
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['log'] = implode("\n---\n", $certs['changelog']);
}
$files[] = (object) $file;
@@ -552,7 +552,7 @@ class IDF_Scm_Monotone extends IDF_Scm
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['log'] = implode("\n---\n", $certs['changelog']);
}
return (object) $file;
@@ -629,7 +629,7 @@ class IDF_Scm_Monotone extends IDF_Scm
$dates[] = gmdate('Y-m-d H:i:s', strtotime($date));
$res['date'] = implode(', ', $dates);
- $res['title'] = implode("\n---\n, ", $certs['changelog']);
+ $res['title'] = implode("\n---\n", $certs['changelog']);
$res['commit'] = $revs[0];
From 445c90fefe75c4e8a601f0e038309d3057b881f4 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Fri, 30 Apr 2010 02:03:58 +0200
Subject: [PATCH 12/40] Create a separate class which handles command streaming
over mtn automate stdio. Use that everywhere instead of the direct system
calls.
---
src/IDF/Scm/Monotone.php | 397 ++++++++++++++++++++++++++++-----------
1 file changed, 289 insertions(+), 108 deletions(-)
diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php
index 5d71efe..02287d2 100644
--- a/src/IDF/Scm/Monotone.php
+++ b/src/IDF/Scm/Monotone.php
@@ -25,10 +25,248 @@
* 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 *
@@ -39,6 +277,7 @@ class IDF_Scm_Monotone extends IDF_Scm
{
$this->repo = $repo;
$this->project = $project;
+ $this->stdio = new IDF_Scm_Monotone_Stdio($repo);
}
public function getRepositorySize()
@@ -56,19 +295,14 @@ class IDF_Scm_Monotone extends IDF_Scm
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()
@@ -77,25 +311,18 @@ class IDF_Scm_Monotone extends IDF_Scm
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;
}
@@ -121,14 +348,8 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
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);
}
/**
@@ -139,9 +360,6 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
private static function _parseBasicIO($in)
{
- if (substr($in, -1) != "\n")
- $in .= "\n";
-
$pos = 0;
$stanzas = array();
@@ -208,15 +426,9 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
{
@@ -264,16 +476,11 @@ class IDF_Scm_Monotone extends IDF_Scm
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
@@ -305,17 +512,15 @@ class IDF_Scm_Monotone extends IDF_Scm
**/
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;
@@ -360,15 +565,12 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
@@ -501,15 +703,12 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
{
@@ -562,13 +761,13 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
@@ -594,16 +793,10 @@ class IDF_Scm_Monotone extends IDF_Scm
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])
+ );
}
/**
@@ -655,16 +848,12 @@ class IDF_Scm_Monotone extends IDF_Scm
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)
{
@@ -682,16 +871,8 @@ class IDF_Scm_Monotone extends IDF_Scm
* @param int Number of changes (10).
* @return array Changes.
*/
- public function getChangeLog($commit='HEAD', $n=10)
+ public function getChangeLog($commit=null, $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);
+ throw new Pluf_Exception_NotImplemented();
}
}
From 601e894935f66257cd7c0969c141f898d99a1e23 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Fri, 30 Apr 2010 02:11:40 +0200
Subject: [PATCH 13/40] Use the branch / tag name shortener in two other
templates as well.
---
src/IDF/templates/idf/source/mtn/changelog.html | 12 ++++++++----
src/IDF/templates/idf/source/mtn/file.html | 11 +++++++----
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/IDF/templates/idf/source/mtn/changelog.html b/src/IDF/templates/idf/source/mtn/changelog.html
index fa92f24..bb3dbc5 100644
--- a/src/IDF/templates/idf/source/mtn/changelog.html
+++ b/src/IDF/templates/idf/source/mtn/changelog.html
@@ -2,15 +2,19 @@
{block context}
{trans 'Branches:'}
{foreach $branches as $branch => $path}
-{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)}
-{if $path}{$path}{else}{$branch}{/if}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
+
+ {if $path}{$path|shorten:25}{else}{$branch|shorten:25}{/if}
+
{/foreach}
{if $tags}
{trans 'Tags:'}
{foreach $tags as $tag => $path}
-{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $tag)}
-
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
+
{/foreach}
{/if}
diff --git a/src/IDF/templates/idf/source/mtn/file.html b/src/IDF/templates/idf/source/mtn/file.html
index 77b33d8..a8f4e43 100644
--- a/src/IDF/templates/idf/source/mtn/file.html
+++ b/src/IDF/templates/idf/source/mtn/file.html
@@ -6,7 +6,7 @@
{if !$tree_in and !$tags_in}
-{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
+{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}
@@ -25,19 +25,22 @@
{trans 'Branches:'}
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-{if $path}{$path}{else}{$branch}{/if}
+
+ {if $path}{$path|shorten:25}{else}{$branch|shorten:25}{/if}
+
{/foreach}
{if $tags}
{trans 'Tags:'}
{foreach $tags as $tag => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
-
+
{/foreach}
{/if}
{/block}
-
{block javascript}
From c49a8204e00ca385032098120000e2b1647fbaef Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Fri, 30 Apr 2010 02:38:45 +0200
Subject: [PATCH 14/40] Properly activate the correct branches / tags for the
currently viewed revision
---
.../templates/idf/source/mtn/changelog.html | 24 +++++++++++--------
src/IDF/templates/idf/source/mtn/file.html | 24 +++++++++++--------
src/IDF/templates/idf/source/mtn/tree.html | 24 +++++++++++--------
3 files changed, 42 insertions(+), 30 deletions(-)
diff --git a/src/IDF/templates/idf/source/mtn/changelog.html b/src/IDF/templates/idf/source/mtn/changelog.html
index bb3dbc5..5a6fc23 100644
--- a/src/IDF/templates/idf/source/mtn/changelog.html
+++ b/src/IDF/templates/idf/source/mtn/changelog.html
@@ -1,20 +1,24 @@
{extends "idf/source/changelog.html"}
{block context}
{trans 'Branches:'}
-{foreach $branches as $branch => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-
- {if $path}{$path|shorten:25}{else}{$branch|shorten:25}{/if}
-
+{foreach $branches as $selector => $branch}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $selector)}
+
+
+ {$branch|shorten:25}
+
+
{/foreach}
{if $tags}
{trans 'Tags:'}
-{foreach $tags as $tag => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
-
+{foreach $tags as $selector => $tag}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $selector)}
+
{/foreach}
{/if}
diff --git a/src/IDF/templates/idf/source/mtn/file.html b/src/IDF/templates/idf/source/mtn/file.html
index a8f4e43..6190557 100644
--- a/src/IDF/templates/idf/source/mtn/file.html
+++ b/src/IDF/templates/idf/source/mtn/file.html
@@ -23,20 +23,24 @@
{/block}
{block context}
{trans 'Branches:'}
-{foreach $branches as $branch => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-
- {if $path}{$path|shorten:25}{else}{$branch|shorten:25}{/if}
-
+{foreach $branches as $selector => $branch}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $selector)}
+
+
+ {$branch|shorten:25}
+
+
{/foreach}
{if $tags}
{trans 'Tags:'}
-{foreach $tags as $tag => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
-
+{foreach $tags as $selector => $tag}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $selector)}
+
{/foreach}
{/if}
diff --git a/src/IDF/templates/idf/source/mtn/tree.html b/src/IDF/templates/idf/source/mtn/tree.html
index 933f7b0..0250b01 100644
--- a/src/IDF/templates/idf/source/mtn/tree.html
+++ b/src/IDF/templates/idf/source/mtn/tree.html
@@ -56,20 +56,24 @@
{/block}
{block context}
{trans 'Branches:'}
-{foreach $branches as $branch => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-
- {if $path}{$path|shorten:25}{else}{$branch|shorten:25}{/if}
-
+{foreach $branches as $selector => $branch}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $selector)}
+
+
+ {$branch|shorten:25}
+
+
{/foreach}
{if $tags}
{trans 'Tags:'}
-{foreach $tags as $tag => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
-
+{foreach $tags as $selector => $tag}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $selector)}
+
{/foreach}
{/if}
From 15a2bd90b35a629e7f3551fe120efe1c02bbf20d Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sat, 1 May 2010 00:56:48 +0200
Subject: [PATCH 15/40] Add support for monotone's diff header
---
src/IDF/Diff.php | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/src/IDF/Diff.php b/src/IDF/Diff.php
index 817885d..71e8019 100644
--- a/src/IDF/Diff.php
+++ b/src/IDF/Diff.php
@@ -51,7 +51,7 @@ class IDF_Diff
$i = 0; // Used to skip the end of a git patch with --\nversion number
foreach ($this->lines as $line) {
$i++;
- if (0 === strpos($line, '--') and isset($this->lines[$i])
+ if (0 === strpos($line, '--') and isset($this->lines[$i])
and preg_match('/^\d+\.\d+\.\d+\.\d+$/', $this->lines[$i])) {
break;
}
@@ -71,6 +71,14 @@ class IDF_Diff
$current_chunk = 0;
$indiff = true;
continue;
+ } else if (0 === strpos($line, '=========')) {
+ $current_file = self::getMtnFile($this->lines[$i+1]);
+ $files[$current_file] = array();
+ $files[$current_file]['chunks'] = array();
+ $files[$current_file]['chunks_def'] = array();
+ $current_chunk = 0;
+ $indiff = true;
+ continue;
} else if (0 === strpos($line, 'Index: ')) {
$current_file = self::getSvnFile($line);
$files[$current_file] = array();
@@ -133,6 +141,12 @@ class IDF_Diff
return substr(trim($line), 7);
}
+ public static function getMtnFile($line)
+ {
+ preg_match("/^[+-]{3} ([^\t]+)/", $line, $m);
+ return $m[1];
+ }
+
/**
* Return the html version of a parsed diff.
*/
@@ -215,14 +229,14 @@ class IDF_Diff
* @param int Number of lines before/after the chunk to be displayed (10)
* @return Pluf_Template_SafeString The table body
*/
- public function fileCompare($orig, $chunks, $filename, $context=10)
+ public function fileCompare($orig, $chunks, $filename, $context=10)
{
$orig_lines = preg_split("/\015\012|\015|\012/", $orig);
$new_chunks = $this->mergeChunks($orig_lines, $chunks, $context);
return $this->renderCompared($new_chunks, $filename);
}
- public function mergeChunks($orig_lines, $chunks, $context=10)
+ public function mergeChunks($orig_lines, $chunks, $context=10)
{
$spans = array();
$new_chunks = array();
@@ -250,7 +264,7 @@ class IDF_Diff
for ($lc=$spans[$i][0];$lc<$chunk[0][0];$lc++) {
$exists = false;
foreach ($chunk_lines as $line) {
- if ($lc == $line[0]
+ if ($lc == $line[0]
or ($chunk[0][1]-$chunk[0][0]+$lc) == $line[1]) {
$exists = true;
break;
@@ -259,7 +273,7 @@ class IDF_Diff
if (!$exists) {
$orig = isset($orig_lines[$lc-1]) ? $orig_lines[$lc-1] : '';
$n_chunk[] = array(
- $lc,
+ $lc,
$chunk[0][1]-$chunk[0][0]+$lc,
$orig
);
@@ -283,7 +297,7 @@ class IDF_Diff
}
if (!$exists) {
$n_chunk[] = array(
- $lc,
+ $lc,
$lline[1]-$lline[0]+$lc,
$orig_lines[$lc-1]
);
@@ -305,7 +319,7 @@ class IDF_Diff
foreach ($chunk as $line) {
if ($line[0] > $lline[0] or empty($line[0])) {
$nnew_chunks[$i-1][] = $line;
- }
+ }
}
} else {
$nnew_chunks[] = $chunk;
From 3b53ceedcd60fe116e3f2844b0b5b7fd3b0eaa60 Mon Sep 17 00:00:00 2001
From: Thomas Keller
Date: Sat, 1 May 2010 01:05:54 +0200
Subject: [PATCH 16/40] * Monotone.php (IDF_Scm_Monotone): basic_io values need
to be unescaped; implement getChangeLog() * Monotone.php
(IDF_Scm_Monotone_Stdio): add support for multiple, equally named options *
Source.php, commit.html: split-off the global commit template (which had some
separate code already for SVN) and adapt the left blocks for mtn to shorten
branch and tag names just like we do everywhere else
---
src/IDF/Scm/Monotone.php | 73 +++++++++++++++++--
src/IDF/Views/Source.php | 2 +-
src/IDF/templates/idf/source/commit.html | 27 -------
src/IDF/templates/idf/source/git/commit.html | 18 +++++
.../idf/source/mercurial/commit.html | 18 +++++
src/IDF/templates/idf/source/mtn/commit.html | 26 +++++++
src/IDF/templates/idf/source/svn/commit.html | 11 +++
7 files changed, 142 insertions(+), 33 deletions(-)
create mode 100644 src/IDF/templates/idf/source/git/commit.html
create mode 100644 src/IDF/templates/idf/source/mercurial/commit.html
create mode 100644 src/IDF/templates/idf/source/mtn/commit.html
create mode 100644 src/IDF/templates/idf/source/svn/commit.html
diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php
index 02287d2..b35c6b4 100644
--- a/src/IDF/Scm/Monotone.php
+++ b/src/IDF/Scm/Monotone.php
@@ -109,10 +109,16 @@ class IDF_Scm_Monotone_Stdio
if (count($options) > 0)
{
$cmd = "o";
- foreach ($options as $k => $v)
+ foreach ($options as $k => $vals)
{
- $cmd .= strlen((string)$k) . ":" . (string)$k;
- $cmd .= strlen((string)$v) . ":" . (string)$v;
+ if (!is_array($vals))
+ $vals = array($vals);
+
+ foreach ($vals as $v)
+ {
+ $cmd .= strlen((string)$k) . ":" . (string)$k;
+ $cmd .= strlen((string)$v) . ":" . (string)$v;
+ }
}
$cmd .= "e ";
}
@@ -409,6 +415,15 @@ class IDF_Scm_Monotone extends IDF_Scm
++$valCount;
}
}
+
+ for ($i = 0; $i <= $valCount; $i++)
+ {
+ $stanzaLine['values'][$i] = str_replace(
+ array("\\\\", "\\\""),
+ array("\\", "\""),
+ $stanzaLine['values'][$i]
+ );
+ }
}
$stanza[] = $stanzaLine;
@@ -795,7 +810,7 @@ class IDF_Scm_Monotone extends IDF_Scm
return $this->stdio->exec(
array("content_diff"),
- array("r" => $sources[0], "r" => $targets[0])
+ array("r" => array($sources[0], $targets[0]))
);
}
@@ -873,6 +888,54 @@ class IDF_Scm_Monotone extends IDF_Scm
*/
public function getChangeLog($commit=null, $n=10)
{
- throw new Pluf_Exception_NotImplemented();
+ $horizont = $this->_resolveSelector($commit);
+ $initialBranches = array();
+ $logs = array();
+
+ while (!empty($horizont) && $n > 0)
+ {
+ if (count($horizont) > 1)
+ {
+ $out = $this->stdio->exec(array("toposort") + $horizont);
+ $horizont = preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ $rev = array_shift($horizont);
+ $certs = $this->_getCerts($rev);
+
+ // read in the initial branches we should follow
+ if (count($initialBranches) == 0)
+ {
+ $initialBranches = $certs['branch'];
+ }
+
+ // only add it to our log if it is on one of the initial branches
+ if (count(array_intersect($initialBranches, $certs['branch'])) > 0)
+ {
+ --$n;
+
+ $log = array();
+ $log['author'] = implode(", ", $certs['author']);
+
+ $dates = array();
+ foreach ($certs['date'] as $date)
+ $dates[] = gmdate('Y-m-d H:i:s', strtotime($date));
+ $log['date'] = implode(', ', $dates);
+
+ $combinedChangelog = implode("\n---\n", $certs['changelog']);
+ $split = preg_split("/[\n\r]/", $combinedChangelog, 2);
+ $log['title'] = $split[0];
+ $log['full_message'] = (isset($split[1])) ? trim($split[1]) : '';
+
+ $log['commit'] = $rev;
+
+ $logs[] = (object)$log;
+ }
+
+ $out = $this->stdio->exec(array("parents", $rev));
+ $horizont += preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ return $logs;
}
}
diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php
index 9a4791f..37d9ca9 100644
--- a/src/IDF/Views/Source.php
+++ b/src/IDF/Views/Source.php
@@ -308,7 +308,7 @@ class IDF_Views_Source
$in_branches = $scm->inBranches($cobject->commit, '');
$tags = $scm->getTags();
$in_tags = $scm->inTags($cobject->commit, '');
- return Pluf_Shortcuts_RenderToResponse('idf/source/commit.html',
+ return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/commit.html',
array(
'page_title' => $page_title,
'title' => $title,
diff --git a/src/IDF/templates/idf/source/commit.html b/src/IDF/templates/idf/source/commit.html
index a3c0f51..c9c1051 100644
--- a/src/IDF/templates/idf/source/commit.html
+++ b/src/IDF/templates/idf/source/commit.html
@@ -37,33 +37,6 @@
{/if}
{/block}
-{block context}
-{if $scm != 'svn'}
-{trans 'Branches:'}
-{foreach $branches as $branch => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-{$branch}
-{/foreach}
-
-{if $tags}
-{trans 'Tags:'}
-{foreach $tags as $tag => $path}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)}
-
-{/foreach}
-
-{/if}
-{else}
-
-{/if}
-{/block}
-
{block javascript}