1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 05:50:55 +01:00

Add Mercurial repository configuration and local pull support

Summary: No actual parsing/import yet, but now you can define and pull Mercurial
repositories. I merged most of the local pull code so we can share it between
hg/git.

Test Plan:
  - Created a new Mercurial repository to track Codeigniter off Bitbucket
  - Edited / saved / etc.
  - Launched the mercurial pull daemon, it pulled the repo. Killed and
relaunched, it updated the repo.
  - Launched the git fetch deamon, it still works correctly.

Reviewers: Makinde, aran, jungejason, tuomaspelkonen

Reviewed By: Makinde

CC: aran, Makinde

Differential Revision: 793
This commit is contained in:
epriestley 2011-08-09 12:47:46 -07:00
parent 03fb1887d3
commit 4da43b31a3
12 changed files with 240 additions and 81 deletions

View file

@ -72,7 +72,7 @@ celerity_register_resource_map(array(
), ),
'aphront-form-view-css' => 'aphront-form-view-css' =>
array( array(
'uri' => '/res/c79fd668/rsrc/css/aphront/form-view.css', 'uri' => '/res/16af59d8/rsrc/css/aphront/form-view.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -1482,7 +1482,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/ac869011/typeahead.pkg.js', 'uri' => '/res/pkg/ac869011/typeahead.pkg.js',
'type' => 'js', 'type' => 'js',
), ),
70966590 => 'f6422902' =>
array( array(
'name' => 'core.pkg.css', 'name' => 'core.pkg.css',
'symbols' => 'symbols' =>
@ -1503,21 +1503,21 @@ celerity_register_resource_map(array(
13 => 'phabricator-remarkup-css', 13 => 'phabricator-remarkup-css',
14 => 'syntax-highlighting-css', 14 => 'syntax-highlighting-css',
), ),
'uri' => '/res/pkg/70966590/core.pkg.css', 'uri' => '/res/pkg/f6422902/core.pkg.css',
'type' => 'css', 'type' => 'css',
), ),
), ),
'reverse' => 'reverse' =>
array( array(
'aphront-crumbs-view-css' => '70966590', 'aphront-crumbs-view-css' => 'f6422902',
'aphront-dialog-view-css' => '70966590', 'aphront-dialog-view-css' => 'f6422902',
'aphront-form-view-css' => '70966590', 'aphront-form-view-css' => 'f6422902',
'aphront-list-filter-view-css' => '70966590', 'aphront-list-filter-view-css' => 'f6422902',
'aphront-panel-view-css' => '70966590', 'aphront-panel-view-css' => 'f6422902',
'aphront-side-nav-view-css' => '70966590', 'aphront-side-nav-view-css' => 'f6422902',
'aphront-table-view-css' => '70966590', 'aphront-table-view-css' => 'f6422902',
'aphront-tokenizer-control-css' => '70966590', 'aphront-tokenizer-control-css' => 'f6422902',
'aphront-typeahead-control-css' => '70966590', 'aphront-typeahead-control-css' => 'f6422902',
'differential-changeset-view-css' => '7bf96a66', 'differential-changeset-view-css' => '7bf96a66',
'differential-core-view-css' => '7bf96a66', 'differential-core-view-css' => '7bf96a66',
'differential-revision-add-comment-css' => '7bf96a66', 'differential-revision-add-comment-css' => '7bf96a66',
@ -1554,13 +1554,13 @@ celerity_register_resource_map(array(
'javelin-util' => '3dbf4083', 'javelin-util' => '3dbf4083',
'javelin-vector' => '3dbf4083', 'javelin-vector' => '3dbf4083',
'javelin-workflow' => '95c67dcd', 'javelin-workflow' => '95c67dcd',
'phabricator-core-buttons-css' => '70966590', 'phabricator-core-buttons-css' => 'f6422902',
'phabricator-core-css' => '70966590', 'phabricator-core-css' => 'f6422902',
'phabricator-directory-css' => '70966590', 'phabricator-directory-css' => 'f6422902',
'phabricator-keyboard-shortcut' => '95c67dcd', 'phabricator-keyboard-shortcut' => '95c67dcd',
'phabricator-keyboard-shortcut-manager' => '95c67dcd', 'phabricator-keyboard-shortcut-manager' => '95c67dcd',
'phabricator-remarkup-css' => '70966590', 'phabricator-remarkup-css' => 'f6422902',
'phabricator-standard-page-view' => '70966590', 'phabricator-standard-page-view' => 'f6422902',
'syntax-highlighting-css' => '70966590', 'syntax-highlighting-css' => 'f6422902',
), ),
)); ));

View file

@ -575,6 +575,8 @@ phutil_register_library_map(array(
'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification', 'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification',
'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive', 'PhabricatorRepositoryGitHubPostReceiveController' => 'applications/repository/controller/github-post-receive',
'PhabricatorRepositoryListController' => 'applications/repository/controller/list', 'PhabricatorRepositoryListController' => 'applications/repository/controller/list',
'PhabricatorRepositoryMercurialPullDaemon' => 'applications/repository/daemon/mercurialpull',
'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/pulllocal',
'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut', 'PhabricatorRepositoryShortcut' => 'applications/repository/storage/shortcut',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/svn',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn', 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
@ -1166,10 +1168,12 @@ phutil_register_library_map(array(
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon', 'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker', 'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryDaemon', 'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryGitHubPostReceiveController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController', 'PhabricatorRepositoryListController' => 'PhabricatorRepositoryController',
'PhabricatorRepositoryMercurialPullDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorRepositoryDaemon',
'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryShortcut' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker', 'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon', 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',

View file

@ -18,15 +18,23 @@
final class PhabricatorRepositoryType { final class PhabricatorRepositoryType {
const REPOSITORY_TYPE_GIT = 'git'; const REPOSITORY_TYPE_GIT = 'git';
const REPOSITORY_TYPE_SVN = 'svn'; const REPOSITORY_TYPE_SVN = 'svn';
const REPOSITORY_TYPE_MERCURIAL = 'hg';
public static function getAllRepositoryTypes() {
static $map = array(
self::REPOSITORY_TYPE_GIT => 'Git',
self::REPOSITORY_TYPE_SVN => 'Subversion',
// TODO: Stabilize and remove caveat.
self::REPOSITORY_TYPE_MERCURIAL => 'Mercurial (LIMITED SUPPORT!)',
);
return $map;
}
public static function getNameForRepositoryType($type) { public static function getNameForRepositoryType($type) {
static $map = array( $map = self::getAllRepositoryTypes();
self::REPOSITORY_TYPE_GIT => 'Git',
self::REPOSITORY_TYPE_SVN => 'Subversion',
);
return idx($map, $type, 'Unknown'); return idx($map, $type, 'Unknown');
} }

View file

@ -30,10 +30,7 @@ class PhabricatorRepositoryCreateController
$repository = new PhabricatorRepository(); $repository = new PhabricatorRepository();
$type_map = array( $type_map = PhabricatorRepositoryType::getAllRepositoryTypes();
'git' => 'Git',
'svn' => 'Subversion',
);
$errors = array(); $errors = array();
if ($request->isFormPost()) { if ($request->isFormPost()) {

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'aphront/response/redirect'); phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/controller/base'); phutil_require_module('phabricator', 'applications/repository/controller/base');
phutil_require_module('phabricator', 'applications/repository/storage/repository'); phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phabricator', 'view/form/base'); phutil_require_module('phabricator', 'view/form/base');

View file

@ -204,6 +204,7 @@ class PhabricatorRepositoryEditController
$is_git = false; $is_git = false;
$is_svn = false; $is_svn = false;
$is_mercurial = false;
$e_ssh_key = null; $e_ssh_key = null;
$e_ssh_keyfile = null; $e_ssh_keyfile = null;
@ -215,22 +216,29 @@ class PhabricatorRepositoryEditController
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$is_svn = true; $is_svn = true;
break; break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$is_mercurial = true;
break;
default: default:
throw new Exception("Unsupported VCS!"); throw new Exception("Unsupported VCS!");
} }
$has_branches = ($is_git || $is_mercurial);
$has_local = ($is_git || $is_mercurial);
$has_http_support = $is_svn;
if ($request->isFormPost()) { if ($request->isFormPost()) {
$tracking = ($request->getStr('tracking') == 'enabled' ? true : false); $tracking = ($request->getStr('tracking') == 'enabled' ? true : false);
$repository->setDetail('tracking-enabled', $tracking); $repository->setDetail('tracking-enabled', $tracking);
$repository->setDetail('remote-uri', $request->getStr('uri')); $repository->setDetail('remote-uri', $request->getStr('uri'));
if ($is_git) { if ($has_local) {
$repository->setDetail('local-path', $request->getStr('path')); $repository->setDetail('local-path', $request->getStr('path'));
} }
$repository->setDetail( $repository->setDetail(
'pull-frequency', 'pull-frequency',
max(1, $request->getInt('frequency'))); max(1, $request->getInt('frequency')));
if ($is_git) { if ($has_branches) {
$repository->setDetail( $repository->setDetail(
'default-branch', 'default-branch',
$request->getStr('default-branch')); $request->getStr('default-branch'));
@ -290,7 +298,7 @@ class PhabricatorRepositoryEditController
$e_uri = null; $e_uri = null;
} }
if ($is_git) { if ($has_local) {
if (!$repository->getDetail('local-path')) { if (!$repository->getDetail('local-path')) {
$e_path = 'Required'; $e_path = 'Required';
$errors[] = "Local path is required."; $errors[] = "Local path is required.";
@ -319,6 +327,13 @@ class PhabricatorRepositoryEditController
$error_view->appendChild( $error_view->appendChild(
'Tracking changes were saved. You may need to restart the daemon '. 'Tracking changes were saved. You may need to restart the daemon '.
'before changes will take effect.'); 'before changes will take effect.');
} else if (!$repository->getDetail('tracking-enabled')) {
$error_view = new AphrontErrorView();
$error_view->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$error_view->setTitle('Repository Not Tracked');
$error_view->appendChild(
'Tracking is currently "Disabled" for this repository, so it will '.
'not be imported into Phabricator. You can enable it below.');
} }
switch ($repository->getVersionControlSystem()) { switch ($repository->getVersionControlSystem()) {
@ -375,12 +390,28 @@ class PhabricatorRepositoryEditController
'<h1>Remote URI</h1>'. '<h1>Remote URI</h1>'.
'<div class="aphront-form-inset">'); '<div class="aphront-form-inset">');
$uri_label = 'Repository URI'; $clone_command = null;
$fetch_command = null;
if ($is_git) { if ($is_git) {
$instructions = $clone_command = 'git clone';
'Enter the URI to clone this repository from. It should look like '. $fetch_command = 'git fetch';
'<tt>git@github.com:example/example.git</tt> or '. } else if ($is_mercurial) {
'<tt>ssh://user@host.com/git/example.git</tt>'; $clone_command = 'hg clone';
$fetch_command = 'hg pull';
}
$uri_label = 'Repository URI';
if ($has_local) {
if ($is_git) {
$instructions =
'Enter the URI to clone this repository from. It should look like '.
'<tt>git@github.com:example/example.git</tt>, <tt> '.
'<tt>ssh://user@host.com/git/example.git</tt>';
} else if ($is_mercurial) {
$instructions =
'Enter the URI to clone this repository from. It should look '.
'something like <tt>ssh://user@host.com/hg/example</tt>';
}
$form->appendChild( $form->appendChild(
'<p class="aphront-form-instructions">'.$instructions.'</p>'); '<p class="aphront-form-instructions">'.$instructions.'</p>');
} else if ($is_svn) { } else if ($is_svn) {
@ -436,9 +467,7 @@ class PhabricatorRepositoryEditController
'...specify a path on disk where the daemon should '. '...specify a path on disk where the daemon should '.
'look for a private key.')); 'look for a private key.'));
$supports_http = $is_svn; if ($has_http_support) {
if ($supports_http) {
$form $form
->appendChild( ->appendChild(
'<div class="aphront-form-instructions">'. '<div class="aphront-form-instructions">'.
@ -479,13 +508,13 @@ class PhabricatorRepositoryEditController
'<h1>Importing Repository Information</h1>'. '<h1>Importing Repository Information</h1>'.
'<div class="aphront-form-inset">'); '<div class="aphront-form-inset">');
if ($is_git) { if ($has_local) {
$form->appendChild( $form->appendChild(
'<p class="aphront-form-instructions">Select a path on local disk '. '<p class="aphront-form-instructions">Select a path on local disk '.
'which the daemons should <tt>git clone</tt> the repository into. '. 'which the daemons should <tt>'.$clone_command.'</tt> the repository '.
'This must be readable and writable by the daemons, and readable by '. 'into. This must be readable and writable by the daemons, and '.
'the webserver. The daemons will <tt>git fetch</tt> and keep this '. 'readable by the webserver. The daemons will <tt>'.$fetch_command.
'repository up to date.</p>'); '</tt> and keep this repository up to date.</p>');
$form->appendChild( $form->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setName('path') ->setName('path')
@ -523,7 +552,15 @@ class PhabricatorRepositoryEditController
'<h1>Application Configuration</h1>'. '<h1>Application Configuration</h1>'.
'<div class="aphront-form-inset">'); '<div class="aphront-form-inset">');
if ($is_git) { if ($has_branches) {
$default_branch_name = null;
if ($is_mercurial) {
$default_branch_name = 'default';
} else if ($is_git) {
$default_branch_name = 'origin/master';
}
$form $form
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
@ -532,7 +569,7 @@ class PhabricatorRepositoryEditController
->setValue( ->setValue(
$repository->getDetail( $repository->getDetail(
'default-branch', 'default-branch',
'origin/master')) $default_branch_name))
->setCaption( ->setCaption(
'Default <strong>remote</strong> branch to show in Diffusion.')); 'Default <strong>remote</strong> branch to show in Diffusion.'));
} }

View file

@ -16,40 +16,19 @@
* limitations under the License. * limitations under the License.
*/ */
class PhabricatorRepositoryGitFetchDaemon final class PhabricatorRepositoryGitFetchDaemon
extends PhabricatorRepositoryDaemon { extends PhabricatorRepositoryPullLocalDaemon {
public function run() { protected function getSupportedRepositoryType() {
$repository = $this->loadRepository(); return PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
}
if ($repository->getVersionControlSystem() != 'git') { protected function executeCreate($remote_uri, $local_path) {
throw new Exception("Not a git repository!"); execx('git clone %s %s', $remote_uri, rtrim($local_path, '/'));
} }
$tracked = $repository->getDetail('tracking-enabled'); protected function executeUpdate($remote_uri, $local_path) {
if (!$tracked) { execx('(cd %s && git fetch --all)', $local_path);
throw new Exception("Tracking is not enabled for this repository.");
}
$local_path = $repository->getDetail('local-path');
$remote_uri = $repository->getDetail('remote-uri');
if (!$local_path) {
throw new Exception("No local path is available for this repository.");
}
while (true) {
if (!Filesystem::pathExists($local_path)) {
if (!$remote_uri) {
throw new Exception("No remote URI is available.");
}
execx('mkdir -p %s', dirname($local_path));
execx('git clone %s %s', $remote_uri, rtrim($local_path, '/'));
} else {
execx('(cd %s && git fetch --all)', $local_path);
}
$this->sleep($repository->getDetail('pull-frequency', 15));
}
} }
} }

View file

@ -6,9 +6,9 @@
phutil_require_module('phabricator', 'applications/repository/daemon/base'); phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/daemon/pulllocal');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'future/exec'); phutil_require_module('phutil', 'future/exec');

View file

@ -0,0 +1,34 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorRepositoryMercurialPullDaemon
extends PhabricatorRepositoryPullLocalDaemon {
protected function getSupportedRepositoryType() {
return PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
}
protected function executeCreate($remote_uri, $local_path) {
execx('hg clone %s %s', $remote_uri, rtrim($local_path, '/'));
}
protected function executeUpdate($remote_uri, $local_path) {
execx('(cd %s && hg pull -u)', $local_path);
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/daemon/pulllocal');
phutil_require_module('phutil', 'future/exec');
phutil_require_source('PhabricatorRepositoryMercurialPullDaemon.php');

View file

@ -0,0 +1,68 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class PhabricatorRepositoryPullLocalDaemon
extends PhabricatorRepositoryDaemon {
abstract protected function getSupportedRepositoryType();
abstract protected function executeCreate($remote_uri, $local_path);
abstract protected function executeUpdate($remote_uri, $local_path);
final public function run() {
$repository = $this->loadRepository();
$expected_type = $this->getSupportedRepositoryType();
$repo_type = $repository->getVersionControlSystem();
if ($repo_type != $expected_type) {
$repo_type_name = PhabricatorRepositoryType::getNameForRepositoryType(
$repo_type);
$expected_type_name = PhabricatorRepositoryType::getNameForRepositoryType(
$expected_type);
$repo_name = $repository->getName().' ('.$repository->getCallsign().')';
throw new Exception(
"This daemon pulls '{$expected_type_name}' repositories, but the ".
"repository '{$repo_name}' is a '{$repo_type_name}' repository.");
}
$tracked = $repository->getDetail('tracking-enabled');
if (!$tracked) {
throw new Exception("Tracking is not enabled for this repository.");
}
$local_path = $repository->getDetail('local-path');
$remote_uri = $repository->getDetail('remote-uri');
if (!$local_path) {
throw new Exception("No local path is available for this repository.");
}
while (true) {
if (!Filesystem::pathExists($local_path)) {
if (!$remote_uri) {
throw new Exception("No remote URI is available.");
}
execx('mkdir -p %s', dirname($local_path));
$this->executeCreate($remote_uri, $local_path);
} else {
$this->executeUpdate($remote_uri, $local_path);
}
$this->sleep($repository->getDetail('pull-frequency', 15));
}
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/daemon/base');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'future/exec');
phutil_require_source('PhabricatorRepositoryPullLocalDaemon.php');