From d700e7f22d6db173c37aaa4c87bcd692644a870b Mon Sep 17 00:00:00 2001 From: Asher Baker Date: Fri, 8 Nov 2013 11:37:57 -0800 Subject: [PATCH] Add support for landing to hosted Mercurial repos. Summary: I've kept this as close as possible to the Git version for ease of review and later refactoring of them both together. At minimum, the functions to get the working dir should probably be cleaned up one day. Test Plan: Landed a revision. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley CC: Korvin, epriestley, aran Differential Revision: https://secure.phabricator.com/D7534 --- src/__phutil_library_map__.php | 2 + .../DifferentialGetWorkingCopy.php | 28 ++++ .../DifferentialRevisionLandController.php | 2 +- .../landing/DifferentialLandingStrategy.php | 16 ++- .../DifferentialLandingToHostedMercurial.php | 120 ++++++++++++++++++ 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/applications/differential/landing/DifferentialLandingToHostedMercurial.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index aea1adb006..115c47ff68 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -388,6 +388,7 @@ phutil_register_library_map(array( 'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php', 'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php', 'DifferentialLandingToHostedGit' => 'applications/differential/landing/DifferentialLandingToHostedGit.php', + 'DifferentialLandingToHostedMercurial' => 'applications/differential/landing/DifferentialLandingToHostedMercurial.php', 'DifferentialLinesFieldSpecification' => 'applications/differential/field/specification/DifferentialLinesFieldSpecification.php', 'DifferentialLintFieldSpecification' => 'applications/differential/field/specification/DifferentialLintFieldSpecification.php', 'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php', @@ -2647,6 +2648,7 @@ phutil_register_library_map(array( 'DifferentialJIRAIssuesFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener', 'DifferentialLandingToHostedGit' => 'DifferentialLandingStrategy', + 'DifferentialLandingToHostedMercurial' => 'DifferentialLandingStrategy', 'DifferentialLinesFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialLintFieldSpecification' => 'DifferentialFieldSpecification', 'DifferentialLocalCommitsView' => 'AphrontView', diff --git a/src/applications/differential/DifferentialGetWorkingCopy.php b/src/applications/differential/DifferentialGetWorkingCopy.php index 01641261d2..6118aebe10 100644 --- a/src/applications/differential/DifferentialGetWorkingCopy.php +++ b/src/applications/differential/DifferentialGetWorkingCopy.php @@ -36,4 +36,32 @@ final class DifferentialGetWorkingCopy { return $workspace; } + /** + * Creates and/or cleans a workspace for the requested repo. + * + * return ArcanistMercurialAPI + */ + public static function getCleanMercurialWorkspace( + PhabricatorRepository $repo) { + + $origin_path = $repo->getLocalPath(); + + $path = rtrim($origin_path, '/'); + $path = $path . '__workspace'; + + if (!Filesystem::pathExists($path)) { + $repo->execxLocalCommand( + 'clone -- file://%s %s', + $origin_path, + $path); + } + + $workspace = new ArcanistMercurialAPI($path); + $workspace->execxLocal('pull'); + $workspace->execxLocal('update --clean default'); + $workspace->reloadWorkingCopy(); + + return $workspace; + } + } diff --git a/src/applications/differential/controller/DifferentialRevisionLandController.php b/src/applications/differential/controller/DifferentialRevisionLandController.php index 003c4900ac..fad8b48e07 100644 --- a/src/applications/differential/controller/DifferentialRevisionLandController.php +++ b/src/applications/differential/controller/DifferentialRevisionLandController.php @@ -68,7 +68,7 @@ final class DifferentialRevisionLandController extends DifferentialController { $prompt = hsprintf('%s

%s', pht( 'This will squash and rebase revision %s, and push it to '. - 'origin/master.', + 'the default / master branch.', $revision_id), pht('It is an experimental feature and may not work.')); diff --git a/src/applications/differential/landing/DifferentialLandingStrategy.php b/src/applications/differential/landing/DifferentialLandingStrategy.php index 46a0f74333..0c7efad5f0 100644 --- a/src/applications/differential/landing/DifferentialLandingStrategy.php +++ b/src/applications/differential/landing/DifferentialLandingStrategy.php @@ -35,7 +35,21 @@ abstract class DifferentialLandingStrategy { try { return DifferentialGetWorkingCopy::getCleanGitWorkspace($repository); } catch (Exception $e) { - throw new PhutilProxyException ( + throw new PhutilProxyException( + 'Failed to allocate a workspace', + $e); + } + } + + /** + * might break if repository is not Mercurial. + */ + protected function getMercurialWorkspace(PhabricatorRepository $repository) { + try { + return DifferentialGetWorkingCopy::getCleanMercurialWorkspace( + $repository); + } catch (Exception $e) { + throw new PhutilProxyException( 'Failed to allocate a workspace', $e); } diff --git a/src/applications/differential/landing/DifferentialLandingToHostedMercurial.php b/src/applications/differential/landing/DifferentialLandingToHostedMercurial.php new file mode 100644 index 0000000000..fd533aaf8b --- /dev/null +++ b/src/applications/differential/landing/DifferentialLandingToHostedMercurial.php @@ -0,0 +1,120 @@ +getUser(); + + $workspace = $this->getMercurialWorkspace($repository); + + try { + $this->commitRevisionToWorkspace( + $revision, + $workspace, + $viewer); + } catch (Exception $e) { + throw new PhutilProxyException( + 'Failed to commit patch', + $e); + } + + try { + $this->pushWorkspaceRepository( + $repository, + $workspace, + $viewer); + } catch (Exception $e) { + throw new PhutilProxyException( + 'Failed to push changes upstream', + $e); + } + } + + public function commitRevisionToWorkspace( + DifferentialRevision $revision, + ArcanistRepositoryAPI $workspace, + PhabricatorUser $user) { + + $diff_id = $revision->loadActiveDiff()->getID(); + + $call = new ConduitCall( + 'differential.getrawdiff', + array( + 'diffID' => $diff_id, + )); + + $call->setUser($user); + $raw_diff = $call->execute(); + + $future = $workspace->execFutureLocal('patch --no-commit -'); + $future->write($raw_diff); + $future->resolvex(); + + $workspace->reloadWorkingCopy(); + + $call = new ConduitCall( + 'differential.getcommitmessage', + array( + 'revision_id' => $revision->getID(), + )); + + $call->setUser($user); + $message = $call->execute(); + + $author = id(new PhabricatorUser())->loadOneWhere( + 'phid = %s', + $revision->getAuthorPHID()); + + $author_string = sprintf( + '%s <%s>', + $author->getRealName(), + $author->loadPrimaryEmailAddress()); + $author_date = $revision->getDateCreated(); + + $workspace->execxLocal( + 'commit --date=%s --user=%s '. + '--message=%s', + $author_date.' 0', + $author_string, + $message); + } + + + public function pushWorkspaceRepository( + PhabricatorRepository $repository, + ArcanistRepositoryAPI $workspace, + PhabricatorUser $user) { + + $workspace->execxLocal("push -b default"); + } + + public function createMenuItems( + PhabricatorUser $viewer, + DifferentialRevision $revision, + PhabricatorRepository $repository) { + + $vcs = $repository->getVersionControlSystem(); + if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL) { + return; + } + + if (!$repository->isHosted()) { + return; + } + + $can_push = PhabricatorPolicyFilter::hasCapability( + $viewer, + $repository, + DiffusionCapabilityPush::CAPABILITY); + + return $this->createActionView( + $revision, + pht('Land to Hosted Repository'), + !$can_push); + } +}