Changeset View
Changeset View
Standalone View
Standalone View
src/applications/diffusion/engine/DiffusionCommitHookEngine.php
| Show First 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | /* -( Hook Execution )----------------------------------------------------- */ | ||||
| public function execute() { | public function execute() { | ||||
| $ref_updates = $this->findRefUpdates(); | $ref_updates = $this->findRefUpdates(); | ||||
| $all_updates = $ref_updates; | $all_updates = $ref_updates; | ||||
| $caught = null; | $caught = null; | ||||
| try { | try { | ||||
| if ($this->isPushForReview($all_updates)) { | |||||
| $diff = $this->generateDiffForReview($all_updates); | |||||
| // TODO: Enrich this diff on many dimensions; these are required | |||||
| // to save the object. | |||||
| $diff->setLintStatus(DifferentialLintStatus::LINT_NONE); | |||||
| $diff->setUnitStatus(DifferentialUnitStatus::UNIT_NONE); | |||||
| $diff->save(); | |||||
| $diff_uri = PhabricatorEnv::getProductionURI( | |||||
| '/differential/diff/'.$diff->getID().'/'); | |||||
| $console = PhutilConsole::getConsole(); | |||||
| $console->writeErr( | |||||
| "\n%s\n\n", | |||||
| pht( | |||||
| "Success! Created a new diff fom your changes:\n\n %s\n\n". | |||||
EddieGP: "fom": typo | |||||
| "Since this was a push for review, your changes will not be\n". | |||||
| "applied to the remote. This push will now be rejected.", | |||||
| $diff_uri)); | |||||
| // TODO: It would be nice to make Git think this push worked, but | |||||
| // this is just an ease-of-use issue. For now, return an error to | |||||
| // reject the changes. | |||||
| return 77; | |||||
| } | |||||
| try { | try { | ||||
| $this->rejectDangerousChanges($ref_updates); | $this->rejectDangerousChanges($ref_updates); | ||||
| } catch (DiffusionCommitHookRejectException $ex) { | } catch (DiffusionCommitHookRejectException $ex) { | ||||
| // If we're rejecting dangerous changes, flag everything that we've | // If we're rejecting dangerous changes, flag everything that we've | ||||
| // seen as rejected so it's clear that none of it was accepted. | // seen as rejected so it's clear that none of it was accepted. | ||||
| $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_DANGEROUS; | $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_DANGEROUS; | ||||
| throw $ex; | throw $ex; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | switch ($type) { | ||||
| return $this->findMercurialContentUpdates($ref_updates); | return $this->findMercurialContentUpdates($ref_updates); | ||||
| case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: | case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: | ||||
| return $this->findSubversionContentUpdates($ref_updates); | return $this->findSubversionContentUpdates($ref_updates); | ||||
| default: | default: | ||||
| throw new Exception(pht('Unsupported repository type "%s"!', $type)); | throw new Exception(pht('Unsupported repository type "%s"!', $type)); | ||||
| } | } | ||||
| } | } | ||||
| private function isPushForReview(array $ref_updates) { | |||||
| assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog'); | |||||
| $type = $this->getRepository()->getVersionControlSystem(); | |||||
| switch ($type) { | |||||
| case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: | |||||
| return $this->isGitPushForReview($ref_updates); | |||||
| case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: | |||||
| return false; | |||||
| case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: | |||||
| return false; | |||||
| default: | |||||
| throw new Exception(pht('Unsupported repository type "%s"!', $type)); | |||||
| } | |||||
| } | |||||
| private function generateDiffForReview(array $ref_updates) { | |||||
| assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog'); | |||||
| $update = head($ref_updates); | |||||
| $type = $this->getRepository()->getVersionControlSystem(); | |||||
| switch ($type) { | |||||
| case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: | |||||
| return $this->generateGitDiffForReview($update); | |||||
| default: | |||||
| throw new Exception(pht('Unsupported repository type "%s"!', $type)); | |||||
| } | |||||
| } | |||||
| /* -( Herald )------------------------------------------------------------- */ | /* -( Herald )------------------------------------------------------------- */ | ||||
| private function applyHeraldRefRules( | private function applyHeraldRefRules( | ||||
| array $ref_updates, | array $ref_updates, | ||||
| array $all_updates) { | array $all_updates) { | ||||
| $this->applyHeraldRules( | $this->applyHeraldRules( | ||||
| $ref_updates, | $ref_updates, | ||||
| ▲ Show 20 Lines • Show All 313 Lines • ▼ Show 20 Lines | foreach (Futures($futures)->limit(8) as $key => $future) { | ||||
| ->setRefNew($commit) | ->setRefNew($commit) | ||||
| ->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD); | ->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD); | ||||
| } | } | ||||
| } | } | ||||
| return $content_updates; | return $content_updates; | ||||
| } | } | ||||
| private function isGitPushForReview(array $ref_updates) { | |||||
| foreach ($ref_updates as $update) { | |||||
| $name = $update->getRefName(); | |||||
| if (preg_match('(^review(?:/|\z))', $name)) { | |||||
| if (count($ref_updates) !== 1) { | |||||
| throw new DiffusionCommitHookRejectException( | |||||
| pht( | |||||
| 'This push updates several branches, including a virtual branch '. | |||||
| 'which triggers code review (%s). When pushing changes for '. | |||||
| 'review, you must push to ONLY the virtual review branch.', | |||||
| $name)); | |||||
| } | |||||
| $update = head($ref_updates); | |||||
| $type_branch = PhabricatorRepositoryPushLog::REFTYPE_BRANCH; | |||||
| if ($update->getRefType() != $type_branch) { | |||||
| throw new DiffusionCommitHookRejectException( | |||||
| pht( | |||||
| 'This push updates a virtual ref (%s), but does not push a '. | |||||
| 'branch to it. Virtual ref names are reserved and can only be '. | |||||
| 'used for branches.', | |||||
| $name)); | |||||
| } | |||||
| $flags = $update->getChangeFlags(); | |||||
| $flag_add = PhabricatorRepositoryPushLog::CHANGEFLAG_ADD; | |||||
| $flag_delete = PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE; | |||||
| if ($flags & $flag_delete) { | |||||
| // This is deleting a virtual branch, so let it through as a normal | |||||
| // push. Most likely, this is cleaning up a mistake where a virtual | |||||
| // branch was somehow made concrete. | |||||
| return false; | |||||
| } | |||||
| if ($flags !== PhabricatorRepositoryPushLog::CHANGEFLAG_ADD) { | |||||
| throw new DiffusionCommitHookRejectException( | |||||
| pht( | |||||
| 'This push updates a virtual branch (%s). Pushes to virtual '. | |||||
| 'branches must create them, but this push does not. Usually, '. | |||||
| 'this means someone has accidentally created a real branch '. | |||||
| 'with this name. Verify the offending branch is safe to '. | |||||
| 'delete, remove it, and then try pushing again.', | |||||
| $name)); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| private function generateGitDiffForReview(PhabricatorRepositoryPushLog $ref) { | |||||
| $old = $ref->getMergeBase(); | |||||
| $new = $ref->getRefNew(); | |||||
| // TODO: Duplication with arc. | |||||
| $options = array( | |||||
| // NOTE: This is not in `arc`. | |||||
| '--binary', | |||||
| '--no-ext-diff', | |||||
| '--no-textconv', | |||||
| '--no-color', | |||||
| '--src-prefix=a/', | |||||
| '--dst-prefix=b/', | |||||
| '-U32767', | |||||
| '-M', | |||||
| '-C', | |||||
| ); | |||||
| list($diff) = $this->getRepository()->execxLocalCommand( | |||||
| 'diff %Ls %s --', | |||||
| $options, | |||||
| $old.'..'.$new); | |||||
| $changes = id(new ArcanistDiffParser())->parseDiff($diff); | |||||
| return DifferentialDiff::newFromRawChanges($changes); | |||||
20after4Unsubmitted Not Done Inline ActionsThis applies cleanly to a recent version of stable, however, there is the bug mentioned above by @liudangyi. But that one is easy enough to resolve: return DifferentialDiff::newFromRawChanges($this->getViewer(), $changes); I wonder if there is a way to maintain this as some sort of extension if the upstream is not interested in merging it? 20after4: This applies cleanly to a recent version of stable, however, there is the bug mentioned above… | |||||
| } | |||||
| /* -( Custom )------------------------------------------------------------- */ | /* -( Custom )------------------------------------------------------------- */ | ||||
| private function applyCustomHooks(array $updates) { | private function applyCustomHooks(array $updates) { | ||||
| $args = $this->getOriginalArgv(); | $args = $this->getOriginalArgv(); | ||||
| $stdin = $this->getStdin(); | $stdin = $this->getStdin(); | ||||
| $console = PhutilConsole::getConsole(); | $console = PhutilConsole::getConsole(); | ||||
| $env = array( | $env = array( | ||||
| ▲ Show 20 Lines • Show All 593 Lines • Show Last 20 Lines | |||||
"fom": typo