From 8050463a12f462bd4efebb8cef94bb778c2526ef Mon Sep 17 00:00:00 2001 From: Mehdi Kabab Date: Fri, 6 Nov 2009 16:12:54 +0100 Subject: [PATCH] Added ticket 271, support of the Git tags. --- AUTHORS | 2 +- src/IDF/Scm.php | 18 +++ src/IDF/Scm/Git.php | 128 ++++++++++++++---- src/IDF/Views/Source.php | 47 +++++-- src/IDF/templates/idf/source/base.html | 2 +- src/IDF/templates/idf/source/commit.html | 24 ++-- .../templates/idf/source/git/changelog.html | 13 +- src/IDF/templates/idf/source/git/file.html | 16 ++- src/IDF/templates/idf/source/git/tree.html | 16 ++- 9 files changed, 201 insertions(+), 65 deletions(-) diff --git a/AUTHORS b/AUTHORS index cf35cf7..ba9f544 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,7 +10,7 @@ Much appreciated contributors: Julien Issler Manuel Eidenberger Ciaran Gultnieks - Mehdi Kabab + Mehdi Kabab Sindre R. Myren Patrick Georgi Adrien Bustany diff --git a/src/IDF/Scm.php b/src/IDF/Scm.php index 9a8cbac..1483615 100644 --- a/src/IDF/Scm.php +++ b/src/IDF/Scm.php @@ -202,6 +202,24 @@ class IDF_Scm throw new Pluf_Exception_NotImplemented(); } + /** + * Returns in which tags a commit/path is. + * + * A commit can be in several tags and some of the SCMs are + * managing tags using subfolders (like Subversion). + * + * This means that to know in which tag we are at the moment, + * one needs to have both the path and the commit. + * + * @param string Commit + * @param string Path + * @return array Tags + */ + public function inTags($commit, $path) + { + throw new Pluf_Exception_NotImplemented(); + } + /** * Returns the main branch. * diff --git a/src/IDF/Scm/Git.php b/src/IDF/Scm/Git.php index e1e94a4..add57a1 100644 --- a/src/IDF/Scm/Git.php +++ b/src/IDF/Scm/Git.php @@ -91,14 +91,15 @@ class IDF_Scm_Git extends IDF_Scm public function getMainBranch() { - $possible = array('master', 'main', 'trunk', 'local'); - $branches = array_keys($this->getBranches()); - foreach ($possible as $p) { - if (in_array($p, $branches)) { - return $p; - } + $branches = $this->getBranches(); + if (array_key_exists('master', $branches)) + return 'master'; + static $possible = array('main', 'trunk', 'local'); + for ($i = 0; 3 > $i; ++$i) { + if (array_key_exists($possible[$i], $branches)) + return $possible[$i]; } - return (isset($branches[0])) ? $branches[0] : 'master'; + return key($branches); } /** @@ -109,10 +110,75 @@ class IDF_Scm_Git extends IDF_Scm */ public function inBranches($commit, $path) { - return (in_array($commit, array_keys($this->getBranches()))) - ? array($commit) : array(); + return $this->_inObject($commit, 'branch'); } + /** + * @see IDF_Scm::getTags() + **/ + public function getTags() + { + if (isset($this->cache['tags'])) { + return $this->cache['tags']; + } + $cmd = Pluf::f('idf_exec_cmd_prefix', '') + .sprintf('GIT_DIR=%s %s tag', + escapeshellarg($this->repo), + Pluf::f('git_path', 'git')); + exec($cmd, $out, $return); + if (0 != $return) { + throw new IDF_Scm_Exception(sprintf($this->error_tpl, + $cmd, + $return, + implode("\n", $out))); + } + $res = array(); + foreach ($out as $b) { + if (false !== strpos($b, '/')) { + $res[$this->getCommit($b)->commit] = $b; + } else { + $res[$b] = ''; + } + } + $this->cache['tags'] = $res; + return $res; + } + + /** + * @see IDF_Scm::inTags() + **/ + public function inTags($commit, $path) + { + return $this->_inObject($commit, 'tag'); + } + + /** + * Returns in which branches or tags a commit is. + * + * @param string Commit + * @param string Object's type: 'branch' or 'tag'. + * @return array + */ + private function _inObject($commit, $object) + { + $object = strtolower($object); + if ('branch' === $object) { + $objects = $this->getBranches(); + } else if ('tag' === $object) { + $objects = $this->getTags(); + } else { + throw new InvalidArgumentException(sprintf(__('Invalid value for the parameter %1$s: %2$s. Use %3$s.'), + '$object', + $object, + '\'branch\' or \'tag\'')); + } + unset($object); + $result = array(); + if (array_key_exists($commit, $objects)) { + $result[] = $commit; + } + return $result; + } /** * Git "tree" is not the same as the tree we get here. @@ -226,14 +292,15 @@ class IDF_Scm_Git extends IDF_Scm public function isValidRevision($commit) { - return ('commit' == $this->testHash($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' + * @return mixed false if not valid or 'blob', 'tree', 'commit', 'tag' */ public function testHash($hash) { @@ -257,7 +324,7 @@ class IDF_Scm_Git extends IDF_Scm */ public function getTreeInfo($tree, $folder='') { - if (!in_array($this->testHash($tree), array('tree', 'commit'))) { + 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'; @@ -276,7 +343,6 @@ class IDF_Scm_Git extends IDF_Scm return $res; } - /** * Get the file info. * @@ -315,7 +381,6 @@ class IDF_Scm_Git extends IDF_Scm return ($cmd_only) ? $cmd : shell_exec($cmd); } - /** * Get commit details. * @@ -342,21 +407,26 @@ class IDF_Scm_Git extends IDF_Scm if ($ret != 0 or count($out) == 0) { return false; } - $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; + 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 = ''; } - $out = self::parseLog($log, 4); - $out[0]->changes = implode("\n", $change); return $out[0]; } @@ -408,7 +478,7 @@ class IDF_Scm_Git extends IDF_Scm $out = array(); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; exec($cmd, $out); - return self::parseLog($out, 4); + return self::parseLog($out); } /** @@ -478,7 +548,7 @@ class IDF_Scm_Git extends IDF_Scm * Specific Git Commands * ===================================================== */ - + /** * Get submodule details. * diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php index 2b1a979..70b2874 100644 --- a/src/IDF/Views/Source.php +++ b/src/IDF/Views/Source.php @@ -62,8 +62,6 @@ class IDF_Views_Source public $changeLog_precond = array('IDF_Precondition::accessSource'); public function changeLog($request, $match) { - $title = sprintf(__('%1$s %2$s Change Log'), (string) $request->project, - $this->getScmType($request)); $scm = IDF_Scm::get($request->project); if (!$scm->isAvailable()) { $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help', @@ -85,6 +83,8 @@ class IDF_Views_Source $scm->getMainBranch())); return new Pluf_HTTP_Response_Redirect($url); } + $title = sprintf(__('%1$s %2$s Change Log'), (string) $request->project, + $this->getScmType($request)); $changes = $scm->getChangeLog($commit, 25); $rchanges = array(); // Sync with the database @@ -94,6 +94,8 @@ class IDF_Views_Source $rchanges = new Pluf_Template_ContextVars($rchanges); $scmConf = $request->conf->getVal('scm', 'git'); $in_branches = $scm->inBranches($commit, ''); + $tags = $scm->getTags(); + $in_tags = $scm->inTags($commit, ''); return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/changelog.html', array( 'page_title' => $title, @@ -102,6 +104,8 @@ class IDF_Views_Source 'commit' => $commit, 'branches' => $branches, 'tree_in' => $in_branches, + 'tags' => $tags, + 'tags_in' => $in_tags, 'scm' => $scmConf, ), $request); @@ -110,8 +114,6 @@ class IDF_Views_Source public $treeBase_precond = array('IDF_Precondition::accessSource'); public function treeBase($request, $match) { - $title = sprintf(__('%1$s %2$s Source Tree'), - $request->project, $this->getScmType($request)); $scm = IDF_Scm::get($request->project); if (!$scm->isAvailable()) { $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help', @@ -126,8 +128,12 @@ class IDF_Views_Source $scm->getMainBranch())); return new Pluf_HTTP_Response_Redirect($url); } + $title = sprintf(__('%1$s %2$s Source Tree'), + $request->project, $this->getScmType($request)); $branches = $scm->getBranches(); $in_branches = $scm->inBranches($commit, ''); + $tags = $scm->getTags(); + $in_tags = $scm->inTags($commit, ''); $cache = Pluf_Cache::factory(); $key = sprintf('Project:%s::IDF_Views_Source::treeBase:%s::', $request->project->id, $commit); @@ -146,6 +152,8 @@ class IDF_Views_Source 'commit' => $commit, 'tree_in' => $in_branches, 'branches' => $branches, + 'tags' => $tags, + 'tags_in' => $in_tags, 'props' => $props, ), $request); @@ -154,21 +162,18 @@ class IDF_Views_Source public $tree_precond = array('IDF_Precondition::accessSource'); public function tree($request, $match) { - $title = sprintf(__('%1$s %2$s Source Tree'), - $request->project, $this->getScmType($request)); $scm = IDF_Scm::get($request->project); $commit = $match[2]; - $request_file = $match[3]; if (!$scm->isAvailable()) { $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help', array($request->project->shortname)); return new Pluf_HTTP_Response_Redirect($url); } - $branches = $scm->getBranches(); $fburl = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $scm->getMainBranch())); + $request_file = $match[3]; if (substr($request_file, -1) == '/') { $request_file = substr($request_file, 0, -1); $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree', @@ -185,11 +190,12 @@ class IDF_Views_Source // Redirect to the first branch return new Pluf_HTTP_Response_Redirect($fburl); } + $branches = $scm->getBranches(); + $tags = $scm->getTags(); if ($request_file_info->type != 'tree') { - $info = self::getRequestedFileMimeType($request_file_info, + $info = self::getRequestedFileMimeType($request_file_info, $commit, $scm); if (!self::isText($info)) { - $rep = new Pluf_HTTP_Response($scm->getFile($request_file_info), $info[0]); $rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"'; @@ -197,6 +203,7 @@ class IDF_Views_Source } else { // We want to display the content of the file as text $extra = array('branches' => $branches, + 'tags' => $tags, 'commit' => $commit, 'request_file' => $request_file, 'request_file_info' => $request_file_info, @@ -207,6 +214,8 @@ class IDF_Views_Source } $bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->fullpath); + $title = sprintf(__('%1$s %2$s Source Tree'), + $request->project, $this->getScmType($request)); $page_title = $bc.' - '.$title; $cobject = $scm->getCommit($commit); @@ -218,6 +227,7 @@ class IDF_Views_Source return new Pluf_HTTP_Response_Redirect($url); } $in_branches = $scm->inBranches($commit, $request_file); + $in_tags = $scm->inTags($commit, $request_file); $cache = Pluf_Cache::factory(); $key = sprintf('Project:%s::IDF_Views_Source::tree:%s::%s', $request->project->id, $commit, $request_file); @@ -243,6 +253,8 @@ class IDF_Views_Source 'prev' => $previous, 'tree_in' => $in_branches, 'branches' => $branches, + 'tags' => $tags, + 'tags_in' => $in_tags, 'props' => $props, ), $request); @@ -270,7 +282,6 @@ class IDF_Views_Source { $scm = IDF_Scm::get($request->project); $commit = $match[2]; - $branches = $scm->getBranches(); if (!$scm->isValidRevision($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', @@ -293,7 +304,10 @@ class IDF_Views_Source $diff = new IDF_Diff($cobject->changes); $diff->parse(); $scmConf = $request->conf->getVal('scm', 'git'); - $in_branches = $scm->inBranches($commit, ''); + $branches = $scm->getBranches(); + $in_branches = $scm->inBranches($cobject->commit, ''); + $tags = $scm->getTags(); + $in_tags = $scm->inTags($cobject->commit, ''); return Pluf_Shortcuts_RenderToResponse('idf/source/commit.html', array( 'page_title' => $page_title, @@ -303,6 +317,8 @@ class IDF_Views_Source 'commit' => $commit, 'branches' => $branches, 'tree_in' => $in_branches, + 'tags' => $tags, + 'tags_in' => $in_tags, 'scm' => $scmConf, 'rcommit' => $rcommit, 'large_commit' => $large, @@ -315,7 +331,6 @@ class IDF_Views_Source { $scm = IDF_Scm::get($request->project); $commit = $match[2]; - $branches = $scm->getBranches(); if (!$scm->isValidRevision($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', @@ -338,6 +353,7 @@ class IDF_Views_Source $this->getScmType($request)); $scm = IDF_Scm::get($request->project); $branches = $extra['branches']; + $tags = $extra['tags']; $commit = $extra['commit']; $request_file = $extra['request_file']; $request_file_info = $extra['request_file_info']; @@ -345,6 +361,7 @@ class IDF_Views_Source $page_title = $bc.' - '.$title; $cobject = $scm->getCommit($commit); $in_branches = $scm->inBranches($commit, $request_file); + $in_tags = $scm->inTags($commit, ''); // try to find the previous level if it exists. $prev = explode('/', $request_file); $l = array_pop($prev); @@ -365,6 +382,8 @@ class IDF_Views_Source 'prev' => $previous, 'tree_in' => $in_branches, 'branches' => $branches, + 'tags' => $tags, + 'tags_in' => $in_tags, 'props' => $props, ), $request); @@ -378,7 +397,6 @@ class IDF_Views_Source public function getFile($request, $match) { $scm = IDF_Scm::get($request->project); - $branches = $scm->getBranches(); $commit = $match[2]; $request_file = $match[3]; if (!$scm->isValidRevision($commit)) { @@ -413,7 +431,6 @@ class IDF_Views_Source { $commit = trim($match[2]); $scm = IDF_Scm::get($request->project); - $branches = $scm->getBranches(); if (!$scm->isValidRevision($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', diff --git a/src/IDF/templates/idf/source/base.html b/src/IDF/templates/idf/source/base.html index 534e605..322f6ce 100644 --- a/src/IDF/templates/idf/source/base.html +++ b/src/IDF/templates/idf/source/base.html @@ -1,7 +1,7 @@ {extends "idf/base.html"} {block tabsource} class="active"{/block} {block subtabs} -{if !$inHelp and array_key_exists($commit, $branches)}{assign $currentCommit = $commit}{else}{assign $currentCommit = $project.getScmRoot()}{/if} +{if !$inHelp and (in_array($commit, $tree_in) or (in_array($commit, $tags_in)))}{assign $currentCommit = $commit}{else}{assign $currentCommit = $project.getScmRoot()}{/if}
{trans 'Source Tree'} | {trans 'Change Log'} diff --git a/src/IDF/templates/idf/source/commit.html b/src/IDF/templates/idf/source/commit.html index 7905eb4..a3c0f51 100644 --- a/src/IDF/templates/idf/source/commit.html +++ b/src/IDF/templates/idf/source/commit.html @@ -1,5 +1,5 @@ {extends "idf/source/base.html"} -{block extraheader}{/block} +{block extraheader}{/block} {block docclass}yui-t1{assign $inCommit=true}{/block} {block body} @@ -13,7 +13,7 @@ - + {if count($diff.files)} @@ -21,7 +21,7 @@ @@ -33,24 +33,32 @@ {$diff.as_html()} {/if}{if count($diff.files) or $large_commit} {aurl 'url', 'IDF_Views_Source::downloadDiff', array($project.shortname, $commit)} -

{trans 'Archive'} {trans 'Download the corresponding diff file'}

+

{trans 'Archive'} {trans 'Download the corresponding diff file'}

{/if} {/block} {block context} {if $scm != 'svn'} -

{trans 'Branches:'}
+

{trans 'Branches:'}
{foreach $branches as $branch => $path} {aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)} -{$branch}
+{$branch}
{/foreach}

+{if $tags} +

{trans 'Tags:'}
+{foreach $tags as $tag => $path} +{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)} +{if $path}{$path}{else}{$tag}{/if}
+{/foreach} +

+{/if} {else}

{trans 'Revision:'} {$commit}

- - + +

{/if} diff --git a/src/IDF/templates/idf/source/git/changelog.html b/src/IDF/templates/idf/source/git/changelog.html index c384025..fa92f24 100644 --- a/src/IDF/templates/idf/source/git/changelog.html +++ b/src/IDF/templates/idf/source/git/changelog.html @@ -1,10 +1,17 @@ {extends "idf/source/changelog.html"} {block context} -

{trans 'Branches:'}
+

{trans 'Branches:'}
{foreach $branches as $branch => $path} {aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)} -{if $path}{$path}{else}{$branch}{/if}
+{if $path}{$path}{else}{$branch}{/if}
{/foreach}

+{if $tags} +

{trans 'Tags:'}
+{foreach $tags as $tag => $path} +{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $tag)} +{if $path}{$path}{else}{$tag}{/if}
+{/foreach} +

+{/if} {/block} - diff --git a/src/IDF/templates/idf/source/git/file.html b/src/IDF/templates/idf/source/git/file.html index 76e9b75..77b33d8 100644 --- a/src/IDF/templates/idf/source/git/file.html +++ b/src/IDF/templates/idf/source/git/file.html @@ -5,10 +5,10 @@

{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}

{trans 'Commit:'}{$cobject.commit}
{trans 'Message:'}{issuetext $cobject.title, $request}{if isset($cobject.full_message)}

{issuetext $cobject.full_message, $request, true, false, true, true, true}{/if}
{trans 'Message:'}{issuetext $cobject.title, $request}{if isset($cobject.full_message)}

{issuetext $cobject.full_message, $request, true, false, true, true, true}{/if}
{foreach $diff.files as $filename=>$diffdef} {assign $ndiff = count($diffdef['chunks'])} -{$filename} ({blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans})
+{$filename} ({blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans})
{/foreach}
-{if !$tree_in} +{if !$tree_in and !$tags_in} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} - @@ -22,12 +22,20 @@ {/block} {block context} -

{trans 'Branches:'}
+

{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}{else}{$branch}{/if}
{/foreach}

+{if $tags} +

{trans 'Tags:'}
+{foreach $tags as $tag => $path} +{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)} +{if $path}{$path}{else}{$tag}{/if}
+{/foreach} +

+{/if} {/block} {block javascript} diff --git a/src/IDF/templates/idf/source/git/tree.html b/src/IDF/templates/idf/source/git/tree.html index 8bc8a68..200907c 100644 --- a/src/IDF/templates/idf/source/git/tree.html +++ b/src/IDF/templates/idf/source/git/tree.html @@ -11,10 +11,10 @@ -{if !$tree_in} +{if !$tree_in and !$tags_in} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} - @@ -52,10 +52,18 @@ {/block} {block context} -

{trans 'Branches:'}
+

{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}{else}{$branch}{/if}
{/foreach}

+{if $tags} +

{trans 'Tags:'}
+{foreach $tags as $tag => $path} +{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tag)} +{if $path}{$path}{else}{$tag}{/if}
+{/foreach} +

+{/if} {/block}
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
+
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}
{trans 'Message'} {trans 'Size'}
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
+
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}