From 7c502b1745a2f7188299dac7c754f21690c6e858 Mon Sep 17 00:00:00 2001
From: Loic d'Anterroches
+ * array('master' => '',
+ * 'foo-branch' => '',
+ * 'design/feature1' => '')
+ *
+ *
+ * But with Subversion, as the branches are managed as subfolder
+ * with a special folder for trunk, you would get something like:
+ *
+ *
+ * array('trunk' => 'trunk',
+ * 'foo-branch' => 'branches/foo-branch',)
+ *
+ *
+ * @return array Branches
*/
public function getBranches()
{
@@ -116,7 +189,11 @@ class IDF_Scm
/**
* Returns the list of tags.
*
- * @return array For example array('v0.9', 'v1.0')
+ * The format is the same as for the branches.
+ *
+ * @see self::getBranches()
+ *
+ * @return array Tags
*/
public function getTags()
{
@@ -201,67 +278,59 @@ class IDF_Scm
/**
* Given a revision and a file path, retrieve the file content.
*
- * The third parameter is to only request the command that is used
- * to get the file content. This is used when downloading a file
- * at a given revision as it can be passed to a
+ * The $cmd_only parameter is to only request the command that is
+ * used to get the file content. This is used when downloading a
+ * file at a given revision as it can be passed to a
* Pluf_HTTP_Response_CommandPassThru reponse. This allows to
* stream a large response without buffering it in memory.
*
- * The file definition can be a hash or a path depending on the
- * SCM.
+ * The file definition is coming from getPathInfo().
*
- * @param string File definition
- * @param string Revision ('')
+ * @see self::getPathInfo()
+ *
+ * @param stdClass File definition
* @param bool Returns command only (false)
* @return string File content
*/
- public function getFile($def, $rev='', $cmd_only=false)
+ public function getFile($def, $cmd_only=false)
{
throw new Pluf_Exception_NotImplemented();
}
-
/**
- * Equivalent to exec but with caching.
+ * Get information about a file or a path.
*
- * @param string Command
- * @param &array Output
- * @param &int Return value
- * @return string Last line of the output
+ * @param string File or path
+ * @param string Revision (null)
+ * @return mixed False or stdClass with info
*/
- public static function exec($command, &$output=array(), &$return=0)
+ public function getPathInfo($file, $rev=null)
{
- $command = Pluf::f('idf_exec_cmd_prefix', '').$command;
- $key = md5($command);
- $cache = Pluf_Cache::factory();
- if (null === ($res=$cache->get($key))) {
- $ll = exec($command, $output, $return);
- if ($return != 0 and Pluf::f('debug_scm', false)) {
- throw new IDF_Scm_Exception(sprintf('Error when running command: "%s", return code: %d', $command, $return));
- }
- $cache->set($key, array($ll, $return, $output));
- } else {
- list($ll, $return, $output) = $res;
- }
- return $ll;
+ throw new Pluf_Exception_NotImplemented();
}
/**
- * Equivalent to shell_exec but with caching.
+ * Given a revision and possible path returns additional properties.
*
- * @param string Command
- * @return string Output of the command
+ * @param string Revision
+ * @param string Path ('')
+ * @return mixed null or array of properties
*/
- public static function shell_exec($command)
+ public function getProperties($rev, $path='')
{
- $command = Pluf::f('idf_exec_cmd_prefix', '').$command;
- $key = md5($command);
- $cache = Pluf_Cache::factory();
- if (null === ($res=$cache->get($key))) {
- $res = shell_exec($command);
- $cache->set($key, $res);
- }
- return $res;
+ return null;
+ }
+
+ /**
+ * Generate the command to create a zip archive at a given commit.
+ *
+ * @param string Commit
+ * @param string Prefix ('repository/')
+ * @return string Command
+ */
+ public function getArchiveCommand($commit, $prefix='repository/')
+ {
+ throw new Pluf_Exception_NotImplemented();
}
/**
diff --git a/src/IDF/Scm/Git.php b/src/IDF/Scm/Git.php
index b89b44c..2361b9c 100644
--- a/src/IDF/Scm/Git.php
+++ b/src/IDF/Scm/Git.php
@@ -61,7 +61,7 @@ class IDF_Scm_Git extends IDF_Scm
}
$res = array();
foreach ($out as $b) {
- $res[] = substr($b, 2);
+ $res[substr($b, 2)] = '';
}
$this->cache['branches'] = $res;
return $res;
@@ -69,9 +69,29 @@ class IDF_Scm_Git extends IDF_Scm
public function getMainBranch()
{
- return 'master';
+ $possible = array('master', 'main', 'trunk', 'local');
+ $branches = array_keys($this->getBranches());
+ foreach ($possible as $p) {
+ if (in_array($p, $branches)) {
+ return $p;
+ }
+ }
+ return $branches[0];
}
+ /**
+ * Note: Running the `git branch --contains $commit` is
+ * theoritically the best way to do it, until you figure out that
+ * you cannot cache the result and that it takes several seconds
+ * to execute on a big tree.
+ */
+ public function inBranches($commit, $path)
+ {
+ return (in_array($commit, array_keys($this->getBranches())))
+ ? array($commit) : array();
+ }
+
+
/**
* Git "tree" is not the same as the tree we get here.
*
@@ -152,25 +172,12 @@ class IDF_Scm_Git extends IDF_Scm
return ($users->count() > 0) ? $users[0] : null;
}
-
- /**
- * Returns the URL of the git daemon.
- *
- * @param IDF_Project
- * @return string URL
- */
- public static function getRemoteAccessUrl($project)
+ public static function getAnonymousAccessUrl($project)
{
return sprintf(Pluf::f('git_remote_url'), $project->shortname);
}
- /**
- * Returns the URL for SSH access
- *
- * @param IDF_Project
- * @return string URL
- */
- public static function getWriteRemoteAccessUrl($project)
+ public static function getAuthAccessUrl($project, $user)
{
return sprintf(Pluf::f('git_write_remote_url'), $project->shortname);
}
@@ -187,20 +194,25 @@ class IDF_Scm_Git extends IDF_Scm
return new IDF_Scm_Git($rep, $project);
}
+
+ public function isValidRevision($commit)
+ {
+ return ('commit' == $this->testHash($commit));
+ }
+
/**
* Test a given object hash.
*
* @param string Object hash.
- * @param null to be svn client compatible
* @return mixed false if not valid or 'blob', 'tree', 'commit'
*/
- public function testHash($hash, $dummy=null)
+ 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();
- IDF_Scm::exec($cmd, $out, $ret);
+ exec($cmd, $out, $ret);
if ($ret != 0) return false;
return trim($out[0]);
}
@@ -257,14 +269,14 @@ class IDF_Scm_Git extends IDF_Scm
* @param string Commit ('HEAD')
* @return false Information
*/
- public function getFileInfo($totest, $commit='HEAD')
+ public function getPathInfo($totest, $commit='HEAD')
{
$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();
- IDF_Scm::exec($cmd, $out);
+ exec($cmd, $out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
if ($totest == $file) {
@@ -276,19 +288,13 @@ class IDF_Scm_Git extends IDF_Scm
return false;
}
- /**
- * Get a blob.
- *
- * @param string request_file_info
- * @param null to be svn client compatible
- * @return string Raw blob
- */
- public function getBlob($request_file_info, $dummy=null)
+ public function getFile($def, $cmd_only=false)
{
- return shell_exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
- 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file blob %s',
- escapeshellarg($this->repo),
- escapeshellarg($request_file_info->hash)));
+ $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));
+ return ($cmd_only) ? $cmd : shell_exec($cmd);
}
@@ -345,7 +351,7 @@ class IDF_Scm_Git extends IDF_Scm
"'commit %H%n'",
escapeshellarg($commit));
$out = array();
- IDF_Scm::exec($cmd, $out);
+ exec($cmd, $out);
$affected = count($out) - 2;
$added = 0;
$removed = 0;
@@ -377,7 +383,7 @@ class IDF_Scm_Git extends IDF_Scm
escapeshellarg($this->repo), $n, $this->mediumtree_fmt,
escapeshellarg($commit));
$out = array();
- IDF_Scm::exec($cmd, $out);
+ exec($cmd, $out);
return self::parseLog($out, 4);
}
@@ -436,14 +442,7 @@ class IDF_Scm_Git extends IDF_Scm
return $res;
}
- /**
- * Generate the command to create a zip archive at a given commit.
- *
- * @param string Commit
- * @param string Prefix ('git-repo-dump')
- * @return string Command
- */
- public function getArchiveCommand($commit, $prefix='git-repo-dump/')
+ 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',
@@ -471,11 +470,11 @@ class IDF_Scm_Git extends IDF_Scm
{
$file->type = 'extern';
$file->extern = '';
- $info = $this->getFileInfo('.gitmodules', $commit);
+ $info = $this->getPathInfo('.gitmodules', $commit);
if ($info == false) {
return $file;
}
- $gitmodules = $this->getBlob($info);
+ $gitmodules = $this->getFile($info);
if (preg_match('#\[submodule\s+\"'.$file->fullpath.'\"\]\s+path\s=\s(\S+)\s+url\s=\s(\S+)#mi', $gitmodules, $matches)) {
$file->extern = $matches[2];
}
diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php
index c1344c4..074bea2 100644
--- a/src/IDF/Views/Source.php
+++ b/src/IDF/Views/Source.php
@@ -66,7 +66,7 @@ class IDF_Views_Source
$scm = IDF_Scm::get($request->project);
$branches = $scm->getBranches();
$commit = $match[2];
- if ('commit' != $scm->testHash($commit)) {
+ if (!$scm->isValidRevision($commit)) {
if (count($branches) == 0) {
// Redirect to the project source help
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help',
@@ -119,6 +119,7 @@ class IDF_Views_Source
return new Pluf_HTTP_Response_Redirect($url);
}
$branches = $scm->getBranches();
+ $in_branches = $scm->inBranches($commit, '');
$cache = Pluf_Cache::factory();
$key = sprintf('Project:%s::IDF_Views_Source::treeBase:%s::',
$request->project->id, $commit);
@@ -126,13 +127,9 @@ class IDF_Views_Source
$res = new Pluf_Template_ContextVars($scm->getTree($commit));
$cache->set($key, $res);
}
-
- $tree_in = in_array($commit, $branches);
+ //$tree_in = in_array($commit, $branches);
$scmConf = $request->conf->getVal('scm', 'git');
- $props = null;
- if ($scmConf === 'svn') {
- $props = $scm->getProperties($commit);
- }
+ $props = $scm->getProperties($commit);
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/tree.html',
array(
'page_title' => $title,
@@ -140,7 +137,7 @@ class IDF_Views_Source
'files' => $res,
'cobject' => $cobject,
'commit' => $commit,
- 'tree_in' => $tree_in,
+ 'tree_in' => $in_branches,
'branches' => $branches,
'props' => $props,
),
@@ -174,12 +171,12 @@ class IDF_Views_Source
return new Pluf_HTTP_Response_Redirect($url, 301);
}
- if ('commit' != $scm->testHash($commit, $request_file)) {
+ if (!$scm->isValidRevision($commit, $request_file)) {
// Redirect to the first branch
return new Pluf_HTTP_Response_Redirect($fburl);
}
- $request_file_info = $scm->getFileInfo($request_file, $commit);
+ $request_file_info = $scm->getPathInfo($request_file, $commit);
if (!$request_file_info) {
// Redirect to the first branch
return new Pluf_HTTP_Response_Redirect($fburl);
@@ -190,7 +187,7 @@ class IDF_Views_Source
$commit, $scm);
if (!self::isText($info)) {
- $rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
+ $rep = new Pluf_HTTP_Response($scm->getFile($request_file_info),
$info[0]);
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
return $rep;
@@ -210,7 +207,7 @@ class IDF_Views_Source
$page_title = $bc.' - '.$title;
$cobject = $scm->getCommit($commit);
- $tree_in = in_array($commit, $branches);
+ $in_branches = $scm->inBranches($commit, $request_file);
try {
$cache = Pluf_Cache::factory();
$key = sprintf('Project:%s::IDF_Views_Source::tree:%s::%s',
@@ -243,7 +240,7 @@ class IDF_Views_Source
'cobject' => $cobject,
'base' => $request_file_info->file,
'prev' => $previous,
- 'tree_in' => $tree_in,
+ 'tree_in' => $in_branches,
'branches' => $branches,
'props' => $props,
),
@@ -273,7 +270,7 @@ class IDF_Views_Source
$scm = IDF_Scm::get($request->project);
$commit = $match[2];
$branches = $scm->getBranches();
- if ('commit' != $scm->testHash($commit)) {
+ if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
@@ -309,7 +306,7 @@ class IDF_Views_Source
$scm = IDF_Scm::get($request->project);
$commit = $match[2];
$branches = $scm->getBranches();
- if ('commit' != $scm->testHash($commit)) {
+ if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
@@ -347,7 +344,7 @@ class IDF_Views_Source
if ($scmConf === 'svn') {
$props = $scm->getProperties($commit, $request_file);
}
- $content = self::highLight($extra['mime'], $scm->getBlob($request_file_info, $commit));
+ $content = self::highLight($extra['mime'], $scm->getFile($request_file_info));
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/file.html',
array(
'page_title' => $page_title,
@@ -377,14 +374,14 @@ class IDF_Views_Source
$branches = $scm->getBranches();
$commit = $match[2];
$request_file = $match[3];
- if ('commit' != $scm->testHash($commit, $request_file)) {
+ if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
return new Pluf_HTTP_Response_Redirect($url);
}
- $request_file_info = $scm->getFileInfo($request_file, $commit);
+ $request_file_info = $scm->getPathInfo($request_file, $commit);
if (!$request_file_info or $request_file_info->type == 'tree') {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
@@ -394,7 +391,7 @@ class IDF_Views_Source
}
$info = self::getRequestedFileMimeType($request_file_info,
$commit, $scm);
- $rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
+ $rep = new Pluf_HTTP_Response($scm->getFile($request_file_info),
$info[0]);
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
return $rep;
@@ -410,7 +407,7 @@ class IDF_Views_Source
$commit = trim($match[2]);
$scm = IDF_Scm::get($request->project);
$branches = $scm->getBranches();
- if ('commit' != $scm->testHash($commit)) {
+ if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
@@ -441,7 +438,7 @@ class IDF_Views_Source
return $mime;
}
return self::getMimeTypeFromContent($file_info->file,
- $scm->getBlob($file_info, $commit));
+ $scm->getFile($file_info));
}
/**
@@ -451,7 +448,7 @@ class IDF_Views_Source
* @param string File content
* @return array Mime type found or 'application/octet-stream', basename, extension
*/
- public static function getMimeTypeFromContent($file, $filedata)
+ public static function getMimeTypeFromContent($file, &$filedata)
{
$info = pathinfo($file);
$res = array('application/octet-stream',
diff --git a/src/IDF/relations.php b/src/IDF/relations.php
index 3f3034f..c1e482f 100644
--- a/src/IDF/relations.php
+++ b/src/IDF/relations.php
@@ -40,6 +40,7 @@ $m['IDF_Review_FileComment'] = array('relate_to' => array('IDF_Review_Patch', 'P
$m['IDF_Key'] = array('relate_to' => array('Pluf_User'));
$m['IDF_Conf'] = array('relate_to' => array('IDF_Project'));
$m['IDF_Commit'] = array('relate_to' => array('IDF_Project', 'Pluf_User'));
+$m['IDF_Scm_Cache_Git'] = array('relate_to' => array('IDF_Project'));
Pluf_Signal::connect('Pluf_Template_Compiler::construct_template_tags_modifiers',
array('IDF_Middleware', 'updateTemplateTagsModifiers'));
diff --git a/src/IDF/templates/idf/source/git/help.html b/src/IDF/templates/idf/source/git/help.html
index 4f90309..de939c9 100644
--- a/src/IDF/templates/idf/source/git/help.html
+++ b/src/IDF/templates/idf/source/git/help.html
@@ -8,7 +8,7 @@ code.{/blocktrans}
git clone {if $project.private}{$project.getWriteRemoteAccessUrl()}{else}{$project.getRemoteAccessUrl()}{/if}
+git clone {if $project.private}{$project.getWriteRemoteAccessUrl($user)}{else}{$project.getRemoteAccessUrl()}{/if}
{aurl 'url', 'IDF_Views_User::myAccount'}{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}
@@ -22,7 +22,7 @@ code.{/blocktrans} git init git add . git commit -m "initial import" -git remote add origin {$project.getWriteRemoteAccessUrl()} +git remote add origin {$project.getWriteRemoteAccessUrl($url)} git push origin master diff --git a/src/IDF/templates/idf/source/git/tree.html b/src/IDF/templates/idf/source/git/tree.html index 3bcbcd3..5a4659c 100644 --- a/src/IDF/templates/idf/source/git/tree.html +++ b/src/IDF/templates/idf/source/git/tree.html @@ -47,15 +47,15 @@ {aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)} -{trans 'Download this version'} {trans 'or'} git clone {if $project.private}{$project.getWriteRemoteAccessUrl()}{else}{$project.getRemoteAccessUrl()}{/if}
+{trans 'Download this version'} {trans 'or'} git clone {if $project.private}{$project.getWriteRemoteAccessUrl($user)}{else}{$project.getRemoteAccessUrl()}{/if}
{/block} {block context}{trans 'Branches:'}
-{foreach $branches as $branch}
-{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
-{$branch}
+{foreach $branches as $branch => $path}
+{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, $branch)}
+{$branch}
{/foreach}