aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Weiske <cweiske@cweiske.de>2012-09-21 10:33:24 +0200
committerChristian Weiske <cweiske@cweiske.de>2012-09-21 10:33:24 +0200
commit5d065586f4c16c0ec6510dba97b0d5facb859d75 (patch)
tree9ac780960aaa01e92235c72443193b585e23f3f9
parent9f4d00eb634cbf75db2143a62ce8733e901b14a8 (diff)
parent6dd1eeafade7105c0785340a43877a8d63793892 (diff)
downloadphorkie-5d065586f4c16c0ec6510dba97b0d5facb859d75.tar.gz
phorkie-5d065586f4c16c0ec6510dba97b0d5facb859d75.zip
Merge branch 'master' into milestone
-rw-r--r--ChangeLog4
-rw-r--r--data/templates/fork-remote-multiple.htm27
-rw-r--r--data/templates/fork-remote-new.htm12
-rw-r--r--data/templates/fork-remote.htm17
-rw-r--r--data/templates/new.htm3
-rw-r--r--data/templates/repo-sidebar-list.htm2
-rw-r--r--src/phorkie/ForkRemote.php114
-rw-r--r--src/phorkie/Forker.php44
-rw-r--r--src/phorkie/Renderer/Geshi.php2
-rw-r--r--src/phorkie/Repository.php7
-rw-r--r--www/.htaccess1
-rw-r--r--www/fork-remote.php45
-rw-r--r--www/fork.php17
13 files changed, 278 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index 51548c2..3212783 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2012-09-19 Christian Weiske <cweiske@bogo>
+
+ * Implement request #13: remote forking support
+
2012-09-19 Christian Weiske <cweiske@cweiske.de>
* Implement request #12: add link rel="vcs-git"
diff --git a/data/templates/fork-remote-multiple.htm b/data/templates/fork-remote-multiple.htm
new file mode 100644
index 0000000..f5a95b2
--- /dev/null
+++ b/data/templates/fork-remote-multiple.htm
@@ -0,0 +1,27 @@
+<form method="post" action="/fork-remote" enctype="multipart/form-data" class="well form-inline form-horizontal">
+ <div class="control-group">
+ <p>
+ The URL you provided contains links to several Git repositories.
+ Select one of them.
+ </p>
+ <select size="{{urlselsize}}" name="remote_url" id="sel-remote_url" style="width:100%">
+ {% for grouptitle, groupurls in urls %}
+ {% if groupurls|length == 1 %}
+ <option value="{{groupurls.0}}">{{grouptitle}}</option>
+ {% else %}
+ <optgroup label="{{grouptitle}}">
+ {% for url in groupurls %}
+ <option value="{{url}}">{{url}}</option>
+ {% endfor %}
+ </optgroup>
+ {% endif %}
+ {% endfor %}
+ </select>
+ </div>
+ <div style="text-align: right">
+ <button type="submit" class="btn btn-primary">
+ <i class="icon-share icon-white"></i> Fork
+ </button>
+ </div>
+
+</form> \ No newline at end of file
diff --git a/data/templates/fork-remote-new.htm b/data/templates/fork-remote-new.htm
new file mode 100644
index 0000000..6b48ffe
--- /dev/null
+++ b/data/templates/fork-remote-new.htm
@@ -0,0 +1,12 @@
+<form method="post" action="/fork-remote" enctype="multipart/form-data" class="well form-inline">
+ <p>
+ <strong>Copy a paste from a remote server:</strong>
+ Just paste the website or git clone URL.
+ </p>
+
+ <button type="submit" class="btn btn-primary" style="float: right">
+ <i class="icon-share icon-white"></i> Fork remote paste
+ </button>
+ <label for="remote-url">Remote paste URL</label>
+ <input type="text" name="remote_url" id="remote-url" value="{{remote_url}}" class="span5"/>
+</form>
diff --git a/data/templates/fork-remote.htm b/data/templates/fork-remote.htm
new file mode 100644
index 0000000..f651661
--- /dev/null
+++ b/data/templates/fork-remote.htm
@@ -0,0 +1,17 @@
+{% extends "base.htm" %}
+{% block title %}Fork remote paste{% endblock %}
+
+{% block content %}
+ {% include 'fork-remote-new.htm' %}
+
+ {% if error %}
+ <div class="alert alert-error">
+ {{error}}
+ </div>
+ {% endif %}
+
+ {% if urls %}
+ {% include 'fork-remote-multiple.htm' %}
+ {% endif %}
+
+{% endblock %}
diff --git a/data/templates/new.htm b/data/templates/new.htm
index 89c2fe0..8029b8c 100644
--- a/data/templates/new.htm
+++ b/data/templates/new.htm
@@ -20,6 +20,9 @@
</div>
</form>
+
+{% include 'fork-remote-new.htm' %}
+
<script type="application/javascript">
$(document).ready(function() {
initEdit();
diff --git a/data/templates/repo-sidebar-list.htm b/data/templates/repo-sidebar-list.htm
index 568656e..ed262ac 100644
--- a/data/templates/repo-sidebar-list.htm
+++ b/data/templates/repo-sidebar-list.htm
@@ -2,5 +2,5 @@
<a href="{{repo.getLink('display')}}">
{{repo.getTitle}}
</a><br/>
- &nbsp;&nbsp;&nbsp;<span title="{{repo.crdate|date('c')}}">{{dh.get(repo.crdate)}}</span>
+ &#160;&#160;&#160;<span title="{{repo.crdate|date('c')}}">{{dh.get(repo.crdate)}}</span>
</li>
diff --git a/src/phorkie/ForkRemote.php b/src/phorkie/ForkRemote.php
new file mode 100644
index 0000000..524255a
--- /dev/null
+++ b/src/phorkie/ForkRemote.php
@@ -0,0 +1,114 @@
+<?php
+namespace phorkie;
+
+class ForkRemote
+{
+ protected $url;
+
+ /**
+ * Array with keys (URL title) and values (arrays of urls)
+ * Only supported URLs are included.
+ *
+ * @var array
+ */
+ protected $arGitUrls;
+
+
+
+ public function __construct($url)
+ {
+ $this->url = $url;
+ }
+
+ public function parse()
+ {
+ $scheme = parse_url($this->url, PHP_URL_SCHEME);
+ switch ($scheme) {
+ case 'git':
+ //clearly a git url
+ $this->arGitUrls = array(array($this->url));
+ return true;
+
+ case 'ssh':
+ //FIXME: maybe loosen this when we know how to skip the
+ //"do you trust this server" question of ssh
+ $this->error = 'ssh:// URLs are not supported';
+ return false;
+
+ case 'http':
+ case 'https':
+ return $this->extractUrlsFromHtml($this->url);
+ }
+
+ $this->error = 'Unknown URLs scheme: ' . $scheme;
+ return false;
+ }
+
+ protected function extractUrlsFromHtml($url)
+ {
+ //HTML is not necessarily well-formed, and Gitorious has many problems
+ // in this regard
+ //$sx = simplexml_load_file($url);
+ libxml_use_internal_errors(true);
+ $sx = simplexml_import_dom(\DomDocument::loadHtmlFile($url));
+ $elems = $sx->xpath('//*[@rel="vcs-git"]');
+
+ $count = $anonymous = 0;
+ foreach ($elems as $elem) {
+ if (!isset($elem['href'])) {
+ continue;
+ }
+ $str = (string)$elem;
+ if (isset($elem['title'])) {
+ //<link href=".." rel="vcs-git" title="title" />
+ $title = (string)$elem['title'];
+ } else if ($str != '') {
+ //<a href=".." rel="vcs-git">title</a>
+ $title = $str;
+ } else {
+ $title = 'Unnamed repository #' . ++$anonymous;
+ }
+ $url = (string)$elem['href'];
+ if ($this->isSupported($url)) {
+ ++$count;
+ $this->arGitUrls[$title][] = $url;
+ }
+ }
+
+ return $count > 0;
+ }
+
+ /**
+ * Iterate through all git urls and return one if there is only
+ * one supported one.
+ *
+ * @return mixed Boolean false or string
+ */
+ public function getUniqueGitUrl()
+ {
+ $nFound = 0;
+ foreach ($this->arGitUrls as $title => $arUrls) {
+ foreach ($arUrls as $url) {
+ $nFound++;
+ $uniqueUrl = $url;
+ }
+ }
+
+ if ($nFound == 1) {
+ return $uniqueUrl;
+ }
+ return false;
+ }
+
+ public function getGitUrls()
+ {
+ return $this->arGitUrls;
+ }
+
+ public function isSupported($url)
+ {
+ return parse_url($url, PHP_URL_SCHEME) == 'git';
+ }
+}
+
+?>
diff --git a/src/phorkie/Forker.php b/src/phorkie/Forker.php
new file mode 100644
index 0000000..3425a72
--- /dev/null
+++ b/src/phorkie/Forker.php
@@ -0,0 +1,44 @@
+<?php
+namespace phorkie;
+
+class Forker
+{
+ public function forkLocal($repo)
+ {
+ $new = $this->fork($repo->gitDir);
+ \copy($repo->gitDir . '/description', $new->gitDir . '/description');
+ return $new;
+ }
+
+ public function forkRemote($url)
+ {
+ $new = $this->fork($url);
+ file_put_contents(
+ $new->gitDir . '/description',
+ 'Fork of ' . $url
+ );
+ return $new;
+ }
+
+
+ protected function fork($pathOrUrl)
+ {
+ $rs = new Repositories();
+ $new = $rs->createNew();
+ $vc = $new->getVc();
+ \rmdir($new->gitDir);//VersionControl_Git wants an existing dir, git clone not
+ $vc->getCommand('clone')
+ //this should be setOption, but it fails with a = between name and value
+ ->addArgument('--separate-git-dir')
+ ->addArgument($GLOBALS['phorkie']['cfg']['gitdir'] . '/' . $new->id . '.git')
+ ->addArgument($pathOrUrl)
+ ->addArgument($new->workDir)
+ ->execute();
+ foreach (\glob($new->gitDir . '/hooks/*') as $hookfile) {
+ \unlink($hookfile);
+ }
+ return $new;
+ }
+}
+
+?>
diff --git a/src/phorkie/Renderer/Geshi.php b/src/phorkie/Renderer/Geshi.php
index 2010ead..21d36c2 100644
--- a/src/phorkie/Renderer/Geshi.php
+++ b/src/phorkie/Renderer/Geshi.php
@@ -28,7 +28,7 @@ class Renderer_Geshi
}
return '<div class="code">'
- . $geshi->parse_code()
+ . str_replace('&nbsp;', '&#160;', $geshi->parse_code())
. '</div>';
}
diff --git a/src/phorkie/Repository.php b/src/phorkie/Repository.php
index 4ac5ae7..25b049c 100644
--- a/src/phorkie/Repository.php
+++ b/src/phorkie/Repository.php
@@ -310,6 +310,13 @@ class Repository
$commit->committerName = $arOutput[$current + 2];
$commit->committerEmail = $arOutput[$current + 3];
+ if (substr($arOutput[$current + 4], 0, 1) != ' ') {
+ //commit without changed lines
+ $arCommits[] = $commit;
+ $current += 4;
+ continue;
+ }
+
$arLineParts = explode(' ', trim($arOutput[$current + 4]));
$commit->filesChanged = $arLineParts[0];
$commit->linesAdded = $arLineParts[3];
diff --git a/www/.htaccess b/www/.htaccess
index 1f03fac..03bbf60 100644
--- a/www/.htaccess
+++ b/www/.htaccess
@@ -13,6 +13,7 @@ RewriteRule ^([0-9]+)/rev/(.+)$ /revision.php?id=$1&rev=$2
RewriteRule ^([0-9]+)/rev-raw/(.+)/(.+)$ /raw.php?id=$1&rev=$2&file=$3
RewriteRule ^([0-9]+)/tool/([^/]+)/(.+)$ /tool.php?id=$1&tool=$2&file=$3
+RewriteRule ^fork-remote$ /fork-remote.php
RewriteRule ^new$ /new.php
RewriteRule ^list$ /list.php
diff --git a/www/fork-remote.php b/www/fork-remote.php
new file mode 100644
index 0000000..374c8b8
--- /dev/null
+++ b/www/fork-remote.php
@@ -0,0 +1,45 @@
+<?php
+namespace phorkie;
+/**
+ * Fork a remote repository.
+ * Displays a URL selection form when multiple git urls have been found
+ */
+require_once 'www-header.php';
+
+$error = null;
+$urls = null;
+if (isset($_POST['remote_url'])) {
+ $fr = new ForkRemote($_POST['remote_url']);
+ if (false === $fr->parse()) {
+ //no url found
+ $error = 'No git:// clone URL found';
+ } else if (false !== ($gitUrl = $fr->getUniqueGitUrl())) {
+ $forker = new Forker();
+ $new = $forker->forkRemote($gitUrl);
+ redirect($new->getLink('display'));
+ } else {
+ //multiple urls found
+ $urls = $fr->getGitUrls();
+ }
+}
+
+$selsize = 0;
+if (is_array($urls)) {
+ foreach ($urls as $group) {
+ ++$selsize;
+ if (count($group) > 1) {
+ $selsize += count($group);
+ }
+ }
+}
+
+render(
+ 'fork-remote',
+ array(
+ 'remote_url' => isset($_POST['remote_url']) ? $_POST['remote_url'] : '',
+ 'error' => $error,
+ 'urls' => $urls,
+ 'urlselsize' => $selsize,
+ )
+);
+?>
diff --git a/www/fork.php b/www/fork.php
index 6c96a6a..d8a24a2 100644
--- a/www/fork.php
+++ b/www/fork.php
@@ -13,21 +13,8 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$repo = new Repository();
$repo->loadFromRequest();
-$rs = new Repositories();
-$new = $rs->createNew();
-$vc = $new->getVc();
-\rmdir($new->gitDir);//VersionControl_Git wants an existing dir, git clone not
-$vc->getCommand('clone')
- //this should be setOption, but it fails with a = between name and value
- ->addArgument('--separate-git-dir')
- ->addArgument($GLOBALS['phorkie']['cfg']['gitdir'] . '/' . $new->id . '.git')
- ->addArgument($repo->gitDir)
- ->addArgument($new->workDir)
- ->execute();
-\copy($repo->gitDir . '/description', $new->gitDir . '/description');
-foreach (\glob($new->gitDir . '/hooks/*') as $hookfile) {
- \unlink($hookfile);
-}
+$forker = new Forker();
+$new = $forker->forkLocal($repo);
//FIXME: where to put fork source link?
redirect($new->getLink('display'));