*/␊ |
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));␊ |
}␊ |
} |
}␊ |