2013-12-02 15:45:36 -08:00
|
|
|
<?php
|
|
|
|
|
|
2013-12-05 11:56:14 -08:00
|
|
|
/**
|
2013-12-17 08:32:33 -08:00
|
|
|
* @task config Configuring the Hook Engine
|
|
|
|
|
* @task hook Hook Execution
|
|
|
|
|
* @task git Git Hooks
|
|
|
|
|
* @task hg Mercurial Hooks
|
|
|
|
|
* @task svn Subversion Hooks
|
|
|
|
|
* @task internal Internals
|
2013-12-05 11:56:14 -08:00
|
|
|
*/
|
2013-12-02 15:45:36 -08:00
|
|
|
final class DiffusionCommitHookEngine extends Phobject {
|
|
|
|
|
|
2013-12-05 11:59:22 -08:00
|
|
|
const ENV_USER = 'PHABRICATOR_USER';
|
|
|
|
|
const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS';
|
|
|
|
|
const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL';
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
const EMPTY_HASH = '0000000000000000000000000000000000000000';
|
|
|
|
|
|
2013-12-02 15:45:36 -08:00
|
|
|
private $viewer;
|
|
|
|
|
private $repository;
|
|
|
|
|
private $stdin;
|
2014-01-03 12:26:10 -08:00
|
|
|
private $originalArgv;
|
2013-12-02 15:45:55 -08:00
|
|
|
private $subversionTransaction;
|
|
|
|
|
private $subversionRepository;
|
2013-12-05 11:59:22 -08:00
|
|
|
private $remoteAddress;
|
|
|
|
|
private $remoteProtocol;
|
2013-12-05 11:59:41 -08:00
|
|
|
private $transactionKey;
|
2013-12-17 08:34:30 -08:00
|
|
|
private $mercurialHook;
|
2013-12-18 14:19:18 -08:00
|
|
|
private $mercurialCommits = array();
|
2013-12-26 10:40:16 -08:00
|
|
|
private $gitCommits = array();
|
2013-12-05 11:59:22 -08:00
|
|
|
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
private $heraldViewerProjects;
|
2014-03-25 19:43:26 -07:00
|
|
|
private $rejectCode = PhabricatorRepositoryPushLog::REJECT_BROKEN;
|
|
|
|
|
private $rejectDetails;
|
2014-03-26 10:44:06 -07:00
|
|
|
private $emailPHIDs = array();
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
/* -( Config )------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
2013-12-05 11:59:22 -08:00
|
|
|
public function setRemoteProtocol($remote_protocol) {
|
|
|
|
|
$this->remoteProtocol = $remote_protocol;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getRemoteProtocol() {
|
|
|
|
|
return $this->remoteProtocol;
|
|
|
|
|
}
|
2013-12-02 15:45:55 -08:00
|
|
|
|
2013-12-05 11:59:22 -08:00
|
|
|
public function setRemoteAddress($remote_address) {
|
|
|
|
|
$this->remoteAddress = $remote_address;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getRemoteAddress() {
|
|
|
|
|
return $this->remoteAddress;
|
|
|
|
|
}
|
2013-12-02 15:45:55 -08:00
|
|
|
|
2013-12-05 11:59:41 -08:00
|
|
|
private function getRemoteAddressForLog() {
|
|
|
|
|
// If whatever we have here isn't a valid IPv4 address, just store `null`.
|
|
|
|
|
// Older versions of PHP return `-1` on failure instead of `false`.
|
|
|
|
|
$remote_address = $this->getRemoteAddress();
|
|
|
|
|
$remote_address = max(0, ip2long($remote_address));
|
|
|
|
|
$remote_address = nonempty($remote_address, null);
|
|
|
|
|
return $remote_address;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-02 15:45:55 -08:00
|
|
|
public function setSubversionTransactionInfo($transaction, $repository) {
|
|
|
|
|
$this->subversionTransaction = $transaction;
|
|
|
|
|
$this->subversionRepository = $repository;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
2013-12-02 15:45:36 -08:00
|
|
|
|
|
|
|
|
public function setStdin($stdin) {
|
|
|
|
|
$this->stdin = $stdin;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getStdin() {
|
|
|
|
|
return $this->stdin;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-03 12:26:10 -08:00
|
|
|
public function setOriginalArgv(array $original_argv) {
|
|
|
|
|
$this->originalArgv = $original_argv;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getOriginalArgv() {
|
|
|
|
|
return $this->originalArgv;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-02 15:45:36 -08:00
|
|
|
public function setRepository(PhabricatorRepository $repository) {
|
|
|
|
|
$this->repository = $repository;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getRepository() {
|
|
|
|
|
return $this->repository;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setViewer(PhabricatorUser $viewer) {
|
|
|
|
|
$this->viewer = $viewer;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getViewer() {
|
|
|
|
|
return $this->viewer;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:34:30 -08:00
|
|
|
public function setMercurialHook($mercurial_hook) {
|
|
|
|
|
$this->mercurialHook = $mercurial_hook;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getMercurialHook() {
|
|
|
|
|
return $this->mercurialHook;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
/* -( Hook Execution )----------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
2013-12-02 15:45:36 -08:00
|
|
|
public function execute() {
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_updates = $this->findRefUpdates();
|
|
|
|
|
$all_updates = $ref_updates;
|
|
|
|
|
|
|
|
|
|
$caught = null;
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$this->rejectDangerousChanges($ref_updates);
|
|
|
|
|
} catch (DiffusionCommitHookRejectException $ex) {
|
|
|
|
|
// If we're rejecting dangerous changes, flag everything that we've
|
|
|
|
|
// seen as rejected so it's clear that none of it was accepted.
|
2014-03-25 19:43:26 -07:00
|
|
|
$this->rejectCode = PhabricatorRepositoryPushLog::REJECT_DANGEROUS;
|
2013-12-17 08:32:33 -08:00
|
|
|
throw $ex;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-18 14:18:45 -08:00
|
|
|
$this->applyHeraldRefRules($ref_updates, $all_updates);
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
$content_updates = $this->findContentUpdates($ref_updates);
|
|
|
|
|
$all_updates = array_merge($all_updates, $content_updates);
|
|
|
|
|
|
2013-12-18 14:18:45 -08:00
|
|
|
$this->applyHeraldContentRules($content_updates, $all_updates);
|
|
|
|
|
|
2014-01-03 12:26:10 -08:00
|
|
|
// Run custom scripts in `hook.d/` directories.
|
|
|
|
|
$this->applyCustomHooks($all_updates);
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
// If we make it this far, we're accepting these changes. Mark all the
|
|
|
|
|
// logs as accepted.
|
2014-03-25 19:43:26 -07:00
|
|
|
$this->rejectCode = PhabricatorRepositoryPushLog::REJECT_ACCEPT;
|
2013-12-17 08:32:33 -08:00
|
|
|
} catch (Exception $ex) {
|
|
|
|
|
// We'll throw this again in a minute, but we want to save all the logs
|
|
|
|
|
// first.
|
|
|
|
|
$caught = $ex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save all the logs no matter what the outcome was.
|
2014-03-25 19:43:26 -07:00
|
|
|
$event = $this->newPushEvent();
|
|
|
|
|
|
|
|
|
|
$event->setRejectCode($this->rejectCode);
|
|
|
|
|
$event->setRejectDetails($this->rejectDetails);
|
|
|
|
|
|
|
|
|
|
$event->openTransaction();
|
|
|
|
|
$event->save();
|
|
|
|
|
foreach ($all_updates as $update) {
|
|
|
|
|
$update->setPushEventPHID($event->getPHID());
|
|
|
|
|
$update->save();
|
|
|
|
|
}
|
|
|
|
|
$event->saveTransaction();
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
if ($caught) {
|
|
|
|
|
throw $caught;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 10:44:06 -07:00
|
|
|
if ($this->emailPHIDs) {
|
|
|
|
|
// If Herald rules triggered email to users, queue a worker to send the
|
|
|
|
|
// mail. We do this out-of-process so that we block pushes as briefly
|
|
|
|
|
// as possible.
|
|
|
|
|
|
|
|
|
|
// (We do need to pull some commit info here because the commit objects
|
|
|
|
|
// may not exist yet when this worker runs, which could be immediately.)
|
|
|
|
|
|
|
|
|
|
PhabricatorWorker::scheduleTask(
|
|
|
|
|
'PhabricatorRepositoryPushMailWorker',
|
|
|
|
|
array(
|
|
|
|
|
'eventPHID' => $event->getPHID(),
|
|
|
|
|
'emailPHIDs' => array_values($this->emailPHIDs),
|
|
|
|
|
'info' => $this->loadCommitInfoForWorker($all_updates),
|
2014-07-12 03:02:06 +10:00
|
|
|
),
|
|
|
|
|
PhabricatorWorker::PRIORITY_ALERTS);
|
2014-03-26 10:44:06 -07:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function findRefUpdates() {
|
2013-12-02 15:45:36 -08:00
|
|
|
$type = $this->getRepository()->getVersionControlSystem();
|
|
|
|
|
switch ($type) {
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
2013-12-17 08:32:33 -08:00
|
|
|
return $this->findGitRefUpdates();
|
2013-12-02 15:46:03 -08:00
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
2013-12-17 08:32:33 -08:00
|
|
|
return $this->findMercurialRefUpdates();
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
|
|
|
return $this->findSubversionRefUpdates();
|
2013-12-02 15:45:36 -08:00
|
|
|
default:
|
|
|
|
|
throw new Exception(pht('Unsupported repository type "%s"!', $type));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function rejectDangerousChanges(array $ref_updates) {
|
|
|
|
|
assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
|
2013-12-02 15:45:36 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$repository = $this->getRepository();
|
|
|
|
|
if ($repository->shouldAllowDangerousChanges()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-12-03 10:28:39 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$flag_dangerous = PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
|
2013-12-02 15:45:36 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
foreach ($ref_updates as $ref_update) {
|
|
|
|
|
if (!$ref_update->hasChangeFlags($flag_dangerous)) {
|
|
|
|
|
// This is not a dangerous change.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2013-12-02 15:45:55 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
// We either have a branch deletion or a non fast-forward branch update.
|
|
|
|
|
// Format a message and reject the push.
|
2013-12-02 15:45:55 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$message = pht(
|
|
|
|
|
"DANGEROUS CHANGE: %s\n".
|
|
|
|
|
"Dangerous change protection is enabled for this repository.\n".
|
|
|
|
|
"Edit the repository configuration before making dangerous changes.",
|
|
|
|
|
$ref_update->getDangerousChangeDescription());
|
2013-12-05 11:59:22 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
throw new DiffusionCommitHookRejectException($message);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-05 11:56:14 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function findContentUpdates(array $ref_updates) {
|
|
|
|
|
assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
|
2013-12-05 11:56:14 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$type = $this->getRepository()->getVersionControlSystem();
|
|
|
|
|
switch ($type) {
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
|
|
|
return $this->findGitContentUpdates($ref_updates);
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
|
|
|
|
return $this->findMercurialContentUpdates($ref_updates);
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
|
|
|
return $this->findSubversionContentUpdates($ref_updates);
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception(pht('Unsupported repository type "%s"!', $type));
|
2013-12-05 11:56:14 -08:00
|
|
|
}
|
2013-12-17 08:32:33 -08:00
|
|
|
}
|
2013-12-05 11:56:14 -08:00
|
|
|
|
2013-12-05 11:59:41 -08:00
|
|
|
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
/* -( Herald )------------------------------------------------------------- */
|
|
|
|
|
|
2013-12-18 14:18:45 -08:00
|
|
|
private function applyHeraldRefRules(
|
|
|
|
|
array $ref_updates,
|
|
|
|
|
array $all_updates) {
|
|
|
|
|
$this->applyHeraldRules(
|
|
|
|
|
$ref_updates,
|
|
|
|
|
new HeraldPreCommitRefAdapter(),
|
|
|
|
|
$all_updates);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function applyHeraldContentRules(
|
|
|
|
|
array $content_updates,
|
|
|
|
|
array $all_updates) {
|
|
|
|
|
$this->applyHeraldRules(
|
|
|
|
|
$content_updates,
|
|
|
|
|
new HeraldPreCommitContentAdapter(),
|
|
|
|
|
$all_updates);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function applyHeraldRules(
|
|
|
|
|
array $updates,
|
|
|
|
|
HeraldAdapter $adapter_template,
|
|
|
|
|
array $all_updates) {
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
|
2013-12-18 14:18:45 -08:00
|
|
|
if (!$updates) {
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-18 14:18:45 -08:00
|
|
|
$adapter_template->setHookEngine($this);
|
|
|
|
|
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
$engine = new HeraldEngine();
|
|
|
|
|
$rules = null;
|
|
|
|
|
$blocking_effect = null;
|
2013-12-20 12:39:40 -08:00
|
|
|
$blocked_update = null;
|
2013-12-18 14:18:45 -08:00
|
|
|
foreach ($updates as $update) {
|
|
|
|
|
$adapter = id(clone $adapter_template)
|
|
|
|
|
->setPushLog($update);
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
|
|
|
|
|
if ($rules === null) {
|
|
|
|
|
$rules = $engine->loadRulesForAdapter($adapter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$effects = $engine->applyRules($rules, $adapter);
|
|
|
|
|
$engine->applyEffects($effects, $adapter, $rules);
|
|
|
|
|
$xscript = $engine->getTranscript();
|
|
|
|
|
|
2014-03-26 10:44:06 -07:00
|
|
|
// Store any PHIDs we want to send email to for later.
|
|
|
|
|
foreach ($adapter->getEmailPHIDs() as $email_phid) {
|
|
|
|
|
$this->emailPHIDs[$email_phid] = $email_phid;
|
|
|
|
|
}
|
|
|
|
|
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
if ($blocking_effect === null) {
|
|
|
|
|
foreach ($effects as $effect) {
|
|
|
|
|
if ($effect->getAction() == HeraldAdapter::ACTION_BLOCK) {
|
|
|
|
|
$blocking_effect = $effect;
|
2013-12-20 12:39:40 -08:00
|
|
|
$blocked_update = $update;
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($blocking_effect) {
|
2014-03-25 19:43:26 -07:00
|
|
|
$this->rejectCode = PhabricatorRepositoryPushLog::REJECT_HERALD;
|
|
|
|
|
$this->rejectDetails = $blocking_effect->getRulePHID();
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
|
|
|
|
|
$message = $blocking_effect->getTarget();
|
|
|
|
|
if (!strlen($message)) {
|
|
|
|
|
$message = pht('(None.)');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rules = mpull($rules, null, 'getID');
|
|
|
|
|
$rule = idx($rules, $effect->getRuleID());
|
|
|
|
|
if ($rule && strlen($rule->getName())) {
|
|
|
|
|
$rule_name = $rule->getName();
|
|
|
|
|
} else {
|
|
|
|
|
$rule_name = pht('Unnamed Herald Rule');
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-20 12:39:40 -08:00
|
|
|
$blocked_ref_name = coalesce(
|
|
|
|
|
$blocked_update->getRefName(),
|
|
|
|
|
$blocked_update->getRefNewShort());
|
|
|
|
|
$blocked_name = $blocked_update->getRefType().'/'.$blocked_ref_name;
|
|
|
|
|
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
throw new DiffusionCommitHookRejectException(
|
|
|
|
|
pht(
|
2013-12-20 12:39:40 -08:00
|
|
|
"This push was rejected by Herald push rule %s.\n".
|
|
|
|
|
"Change: %s\n".
|
|
|
|
|
" Rule: %s\n".
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
"Reason: %s",
|
|
|
|
|
'H'.$blocking_effect->getRuleID(),
|
2013-12-20 12:39:40 -08:00
|
|
|
$blocked_name,
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
$rule_name,
|
|
|
|
|
$message));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function loadViewerProjectPHIDsForHerald() {
|
|
|
|
|
// This just caches the viewer's projects so we don't need to load them
|
|
|
|
|
// over and over again when applying Herald rules.
|
|
|
|
|
if ($this->heraldViewerProjects === null) {
|
|
|
|
|
$this->heraldViewerProjects = id(new PhabricatorProjectQuery())
|
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
|
->withMemberPHIDs(array($this->getViewer()->getPHID()))
|
|
|
|
|
->execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mpull($this->heraldViewerProjects, 'getPHID');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
/* -( Git )---------------------------------------------------------------- */
|
2013-12-05 11:56:14 -08:00
|
|
|
|
2013-12-02 15:45:55 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function findGitRefUpdates() {
|
|
|
|
|
$ref_updates = array();
|
2013-12-02 15:46:03 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
// First, parse stdin, which lists all the ref changes. The input looks
|
|
|
|
|
// like this:
|
|
|
|
|
//
|
|
|
|
|
// <old hash> <new hash> <ref>
|
2013-12-02 15:45:36 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$stdin = $this->getStdin();
|
2013-12-02 15:45:36 -08:00
|
|
|
$lines = phutil_split_lines($stdin, $retain_endings = false);
|
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
|
$parts = explode(' ', $line, 3);
|
|
|
|
|
if (count($parts) != 3) {
|
|
|
|
|
throw new Exception(pht('Expected "old new ref", got "%s".', $line));
|
|
|
|
|
}
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
$ref_old = $parts[0];
|
|
|
|
|
$ref_new = $parts[1];
|
|
|
|
|
$ref_raw = $parts[2];
|
|
|
|
|
|
|
|
|
|
if (preg_match('(^refs/heads/)', $ref_raw)) {
|
|
|
|
|
$ref_type = PhabricatorRepositoryPushLog::REFTYPE_BRANCH;
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
$ref_raw = substr($ref_raw, strlen('refs/heads/'));
|
2013-12-17 08:32:33 -08:00
|
|
|
} else if (preg_match('(^refs/tags/)', $ref_raw)) {
|
|
|
|
|
$ref_type = PhabricatorRepositoryPushLog::REFTYPE_TAG;
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
$ref_raw = substr($ref_raw, strlen('refs/tags/'));
|
2013-12-03 10:27:45 -08:00
|
|
|
} else {
|
2013-12-17 08:34:30 -08:00
|
|
|
throw new Exception(
|
|
|
|
|
pht(
|
|
|
|
|
"Unable to identify the reftype of '%s'. Rejecting push.",
|
|
|
|
|
$ref_raw));
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_update = $this->newPushLog()
|
|
|
|
|
->setRefType($ref_type)
|
|
|
|
|
->setRefName($ref_raw)
|
|
|
|
|
->setRefOld($ref_old)
|
|
|
|
|
->setRefNew($ref_new);
|
|
|
|
|
|
|
|
|
|
$ref_updates[] = $ref_update;
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$this->findGitMergeBases($ref_updates);
|
|
|
|
|
$this->findGitChangeFlags($ref_updates);
|
2013-12-03 10:27:45 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
return $ref_updates;
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function findGitMergeBases(array $ref_updates) {
|
|
|
|
|
assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
|
2013-12-03 10:27:45 -08:00
|
|
|
|
|
|
|
|
$futures = array();
|
2013-12-17 08:32:33 -08:00
|
|
|
foreach ($ref_updates as $key => $ref_update) {
|
2013-12-03 10:27:45 -08:00
|
|
|
// If the old hash is "00000...", the ref is being created (either a new
|
|
|
|
|
// branch, or a new tag). If the new hash is "00000...", the ref is being
|
|
|
|
|
// deleted. If both are nonempty, the ref is being updated. For updates,
|
|
|
|
|
// we'll figure out the `merge-base` of the old and new objects here. This
|
|
|
|
|
// lets us reject non-FF changes cheaply; later, we'll figure out exactly
|
|
|
|
|
// which commits are new.
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_old = $ref_update->getRefOld();
|
|
|
|
|
$ref_new = $ref_update->getRefNew();
|
2013-12-03 10:27:45 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
if (($ref_old === self::EMPTY_HASH) ||
|
|
|
|
|
($ref_new === self::EMPTY_HASH)) {
|
|
|
|
|
continue;
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
$futures[$key] = $this->getRepository()->getLocalCommandFuture(
|
|
|
|
|
'merge-base %s %s',
|
|
|
|
|
$ref_old,
|
|
|
|
|
$ref_new);
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (Futures($futures)->limit(8) as $key => $future) {
|
2013-12-11 14:46:46 -08:00
|
|
|
|
|
|
|
|
// If 'old' and 'new' have no common ancestors (for example, a force push
|
|
|
|
|
// which completely rewrites a ref), `git merge-base` will exit with
|
|
|
|
|
// an error and no output. It would be nice to find a positive test
|
|
|
|
|
// for this instead, but I couldn't immediately come up with one. See
|
|
|
|
|
// T4224. Assume this means there are no ancestors.
|
|
|
|
|
|
|
|
|
|
list($err, $stdout) = $future->resolve();
|
2013-12-17 08:32:33 -08:00
|
|
|
|
2013-12-11 14:46:46 -08:00
|
|
|
if ($err) {
|
2013-12-17 08:32:33 -08:00
|
|
|
$merge_base = null;
|
2013-12-11 14:46:46 -08:00
|
|
|
} else {
|
2013-12-17 08:32:33 -08:00
|
|
|
$merge_base = rtrim($stdout, "\n");
|
2013-12-11 14:46:46 -08:00
|
|
|
}
|
2013-12-17 08:32:33 -08:00
|
|
|
|
2013-12-26 10:40:16 -08:00
|
|
|
$ref_update = $ref_updates[$key];
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_update->setMergeBase($merge_base);
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
return $ref_updates;
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
private function findGitChangeFlags(array $ref_updates) {
|
|
|
|
|
assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
|
|
|
|
|
|
|
|
|
|
foreach ($ref_updates as $key => $ref_update) {
|
|
|
|
|
$ref_old = $ref_update->getRefOld();
|
|
|
|
|
$ref_new = $ref_update->getRefNew();
|
|
|
|
|
$ref_type = $ref_update->getRefType();
|
|
|
|
|
|
|
|
|
|
$ref_flags = 0;
|
|
|
|
|
$dangerous = null;
|
|
|
|
|
|
2014-07-10 10:17:17 -07:00
|
|
|
if (($ref_old === self::EMPTY_HASH) && ($ref_new === self::EMPTY_HASH)) {
|
|
|
|
|
// This happens if you try to delete a tag or branch which does not
|
|
|
|
|
// exist by pushing directly to the ref. Git will warn about it but
|
|
|
|
|
// allow it. Just call it a delete, without flagging it as dangerous.
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
|
|
|
|
|
} else if ($ref_old === self::EMPTY_HASH) {
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
|
|
|
|
|
} else if ($ref_new === self::EMPTY_HASH) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
|
|
|
|
|
if ($ref_type == PhabricatorRepositoryPushLog::REFTYPE_BRANCH) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
|
|
|
|
|
$dangerous = pht(
|
|
|
|
|
"The change you're attempting to push deletes the branch '%s'.",
|
|
|
|
|
$ref_update->getRefName());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$merge_base = $ref_update->getMergeBase();
|
|
|
|
|
if ($merge_base == $ref_old) {
|
|
|
|
|
// This is a fast-forward update to an existing branch.
|
|
|
|
|
// These are safe.
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
|
|
|
|
|
} else {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_REWRITE;
|
|
|
|
|
|
|
|
|
|
// For now, we don't consider deleting or moving tags to be a
|
|
|
|
|
// "dangerous" update. It's way harder to get wrong and should be easy
|
|
|
|
|
// to recover from once we have better logging. Only add the dangerous
|
|
|
|
|
// flag if this ref is a branch.
|
|
|
|
|
|
|
|
|
|
if ($ref_type == PhabricatorRepositoryPushLog::REFTYPE_BRANCH) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
|
|
|
|
|
|
|
|
|
|
$dangerous = pht(
|
2013-12-17 08:34:15 -08:00
|
|
|
"The change you're attempting to push updates the branch '%s' ".
|
|
|
|
|
"from '%s' to '%s', but this is not a fast-forward. Pushes ".
|
|
|
|
|
"which rewrite published branch history are dangerous.",
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_update->getRefName(),
|
|
|
|
|
$ref_update->getRefOldShort(),
|
|
|
|
|
$ref_update->getRefNewShort());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ref_update->setChangeFlags($ref_flags);
|
|
|
|
|
if ($dangerous !== null) {
|
|
|
|
|
$ref_update->attachDangerousChangeDescription($dangerous);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $ref_updates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function findGitContentUpdates(array $ref_updates) {
|
|
|
|
|
$flag_delete = PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
|
|
|
|
|
|
2013-12-03 10:27:45 -08:00
|
|
|
$futures = array();
|
2013-12-17 08:32:33 -08:00
|
|
|
foreach ($ref_updates as $key => $ref_update) {
|
|
|
|
|
if ($ref_update->hasChangeFlags($flag_delete)) {
|
2013-12-03 10:27:45 -08:00
|
|
|
// Deleting a branch or tag can never create any new commits.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: This piece of magic finds all new commits, by walking backward
|
|
|
|
|
// from the new value to the value of *any* existing ref in the
|
|
|
|
|
// repository. Particularly, this will cover the cases of a new branch, a
|
|
|
|
|
// completely moved tag, etc.
|
|
|
|
|
$futures[$key] = $this->getRepository()->getLocalCommandFuture(
|
|
|
|
|
'log --format=%s %s --not --all',
|
|
|
|
|
'%H',
|
2013-12-17 08:32:33 -08:00
|
|
|
$ref_update->getRefNew());
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
$content_updates = array();
|
2013-12-03 10:27:45 -08:00
|
|
|
foreach (Futures($futures)->limit(8) as $key => $future) {
|
|
|
|
|
list($stdout) = $future->resolvex();
|
2013-12-17 08:32:33 -08:00
|
|
|
|
|
|
|
|
if (!strlen(trim($stdout))) {
|
|
|
|
|
// This change doesn't have any new commits. One common case of this
|
|
|
|
|
// is creating a new tag which points at an existing commit.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 10:27:45 -08:00
|
|
|
$commits = phutil_split_lines($stdout, $retain_newlines = false);
|
2013-12-17 08:32:33 -08:00
|
|
|
|
2013-12-26 10:40:16 -08:00
|
|
|
// If we're looking at a branch, mark all of the new commits as on that
|
|
|
|
|
// branch. It's only possible for these commits to be on updated branches,
|
|
|
|
|
// since any other branch heads are necessarily behind them.
|
|
|
|
|
$branch_name = null;
|
|
|
|
|
$ref_update = $ref_updates[$key];
|
|
|
|
|
$type_branch = PhabricatorRepositoryPushLog::REFTYPE_BRANCH;
|
|
|
|
|
if ($ref_update->getRefType() == $type_branch) {
|
|
|
|
|
$branch_name = $ref_update->getRefName();
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
foreach ($commits as $commit) {
|
2013-12-26 10:40:16 -08:00
|
|
|
if ($branch_name) {
|
|
|
|
|
$this->gitCommits[$commit][] = $branch_name;
|
|
|
|
|
}
|
2013-12-17 08:32:33 -08:00
|
|
|
$content_updates[$commit] = $this->newPushLog()
|
|
|
|
|
->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
|
|
|
|
|
->setRefNew($commit)
|
|
|
|
|
->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD);
|
|
|
|
|
}
|
2013-12-02 15:45:36 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
return $content_updates;
|
2013-12-02 15:45:36 -08:00
|
|
|
}
|
|
|
|
|
|
2014-01-03 12:26:10 -08:00
|
|
|
/* -( Custom )------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
private function applyCustomHooks(array $updates) {
|
|
|
|
|
$args = $this->getOriginalArgv();
|
|
|
|
|
$stdin = $this->getStdin();
|
|
|
|
|
$console = PhutilConsole::getConsole();
|
|
|
|
|
|
|
|
|
|
$env = array(
|
|
|
|
|
'PHABRICATOR_REPOSITORY' => $this->getRepository()->getCallsign(),
|
|
|
|
|
self::ENV_USER => $this->getViewer()->getUsername(),
|
|
|
|
|
self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(),
|
|
|
|
|
self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$directories = $this->getRepository()->getHookDirectories();
|
|
|
|
|
foreach ($directories as $directory) {
|
|
|
|
|
$hooks = $this->getExecutablesInDirectory($directory);
|
|
|
|
|
sort($hooks);
|
|
|
|
|
foreach ($hooks as $hook) {
|
|
|
|
|
// NOTE: We're explicitly running the hooks in sequential order to
|
|
|
|
|
// make this more predictable.
|
|
|
|
|
$future = id(new ExecFuture('%s %Ls', $hook, $args))
|
|
|
|
|
->setEnv($env, $wipe_process_env = false)
|
|
|
|
|
->write($stdin);
|
|
|
|
|
|
|
|
|
|
list($err, $stdout, $stderr) = $future->resolve();
|
|
|
|
|
if (!$err) {
|
|
|
|
|
// This hook ran OK, but echo its output in case there was something
|
|
|
|
|
// informative.
|
2014-06-09 11:36:49 -07:00
|
|
|
$console->writeOut('%s', $stdout);
|
|
|
|
|
$console->writeErr('%s', $stderr);
|
2014-01-03 12:26:10 -08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-25 19:43:26 -07:00
|
|
|
$this->rejectCode = PhabricatorRepositoryPushLog::REJECT_EXTERNAL;
|
|
|
|
|
$this->rejectDetails = basename($hook);
|
2014-01-03 12:26:10 -08:00
|
|
|
|
|
|
|
|
throw new DiffusionCommitHookRejectException(
|
|
|
|
|
pht(
|
|
|
|
|
"This push was rejected by custom hook script '%s':\n\n%s%s",
|
|
|
|
|
basename($hook),
|
|
|
|
|
$stdout,
|
|
|
|
|
$stderr));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getExecutablesInDirectory($directory) {
|
|
|
|
|
$executables = array();
|
|
|
|
|
|
|
|
|
|
if (!Filesystem::pathExists($directory)) {
|
|
|
|
|
return $executables;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (Filesystem::listDirectory($directory) as $path) {
|
|
|
|
|
$full_path = $directory.DIRECTORY_SEPARATOR.$path;
|
2014-05-05 10:54:53 -07:00
|
|
|
if (!is_executable($full_path)) {
|
|
|
|
|
// Don't include non-executable files.
|
|
|
|
|
continue;
|
2014-01-03 12:26:10 -08:00
|
|
|
}
|
2014-05-05 10:54:53 -07:00
|
|
|
|
|
|
|
|
if (basename($full_path) == 'README') {
|
|
|
|
|
// Don't include README, even if it is marked as executable. It almost
|
|
|
|
|
// certainly got caught in the crossfire of a sweeping `chmod`, since
|
|
|
|
|
// users do this with some frequency.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$executables[] = $full_path;
|
2014-01-03 12:26:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $executables;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 10:28:39 -08:00
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
/* -( Mercurial )---------------------------------------------------------- */
|
2013-12-03 10:28:39 -08:00
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function findMercurialRefUpdates() {
|
2013-12-17 08:34:30 -08:00
|
|
|
$hook = $this->getMercurialHook();
|
|
|
|
|
switch ($hook) {
|
|
|
|
|
case 'pretxnchangegroup':
|
|
|
|
|
return $this->findMercurialChangegroupRefUpdates();
|
|
|
|
|
case 'prepushkey':
|
|
|
|
|
return $this->findMercurialPushKeyRefUpdates();
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception(pht('Unrecognized hook "%s"!', $hook));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function findMercurialChangegroupRefUpdates() {
|
2013-12-17 08:34:15 -08:00
|
|
|
$hg_node = getenv('HG_NODE');
|
|
|
|
|
if (!$hg_node) {
|
|
|
|
|
throw new Exception(pht('Expected HG_NODE in environment!'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: We need to make sure this is passed to subprocesses, or they won't
|
|
|
|
|
// be able to see new commits. Mercurial uses this as a marker to determine
|
|
|
|
|
// whether the pending changes are visible or not.
|
|
|
|
|
$_ENV['HG_PENDING'] = getenv('HG_PENDING');
|
|
|
|
|
$repository = $this->getRepository();
|
|
|
|
|
|
|
|
|
|
$futures = array();
|
|
|
|
|
|
|
|
|
|
foreach (array('old', 'new') as $key) {
|
|
|
|
|
$futures[$key] = $repository->getLocalCommandFuture(
|
|
|
|
|
'heads --template %s',
|
Never use "{branches}" in Mercurial
Summary:
Fixes T5304. Mercurial features a "{branches}" template keyword, documented as:
```
branches List of strings. The name of the branch on which the
changeset was committed. Will be empty if the branch name
was default.
```
At some time long in the past, I misinterpreted this to mean "list of branches where the branch head is a descendant of the commit". It is more like "list of zero or one elements, possibly containing the name of the branch the commit was originally made to, if that branch was not 'default'".
In fact, it seems like this is because a //very// long time in the past, Mercurial worked roughly like I expected:
> Ages ago (2005), we had a very different and ultimately unworkable
> approach to named branches that worked vaguely like .hgtags and allowed
> multiple branch names per revision.
http://marc.info/?l=mercurial-devel&m=129883069414855
This appears to be deprecated in modern Mercurial (it's not in the modern web documentation) although I can't find a commit about it so maybe that's just a documentation issue.
In any case, `{branches}` seems to never be useful: `{branch}` provides the same information without the awkward "default-if-empty" case.
Switch from `{branches}` to either `{branch}` (where that's good enough, notably in the hook engine) or `(descendants(%s) and head())`, which is equivalent to `--contains` in Git.
This fixes pushing to branches with spaces in their names, and makes the "Branches" / "Contains" queries moderately more consistent.
Test Plan:
- Pushed to a Mercurial branch with a space in it.
- Viewed list of branches in a Mercurial repository.
- Viewed containing branches of a Mercurial commit in Diffusion.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5304
Differential Revision: https://secure.phabricator.com/D9453
2014-06-20 11:48:31 -07:00
|
|
|
'{node}\1{branch}\2');
|
2013-12-17 08:34:15 -08:00
|
|
|
}
|
|
|
|
|
// Wipe HG_PENDING out of the old environment so we see the pre-commit
|
|
|
|
|
// state of the repository.
|
|
|
|
|
$futures['old']->updateEnv('HG_PENDING', null);
|
|
|
|
|
|
|
|
|
|
$futures['commits'] = $repository->getLocalCommandFuture(
|
2014-06-03 17:08:13 -07:00
|
|
|
'log --rev %s --template %s',
|
|
|
|
|
hgsprintf('%s:%s', $hg_node, 'tip'),
|
Never use "{branches}" in Mercurial
Summary:
Fixes T5304. Mercurial features a "{branches}" template keyword, documented as:
```
branches List of strings. The name of the branch on which the
changeset was committed. Will be empty if the branch name
was default.
```
At some time long in the past, I misinterpreted this to mean "list of branches where the branch head is a descendant of the commit". It is more like "list of zero or one elements, possibly containing the name of the branch the commit was originally made to, if that branch was not 'default'".
In fact, it seems like this is because a //very// long time in the past, Mercurial worked roughly like I expected:
> Ages ago (2005), we had a very different and ultimately unworkable
> approach to named branches that worked vaguely like .hgtags and allowed
> multiple branch names per revision.
http://marc.info/?l=mercurial-devel&m=129883069414855
This appears to be deprecated in modern Mercurial (it's not in the modern web documentation) although I can't find a commit about it so maybe that's just a documentation issue.
In any case, `{branches}` seems to never be useful: `{branch}` provides the same information without the awkward "default-if-empty" case.
Switch from `{branches}` to either `{branch}` (where that's good enough, notably in the hook engine) or `(descendants(%s) and head())`, which is equivalent to `--contains` in Git.
This fixes pushing to branches with spaces in their names, and makes the "Branches" / "Contains" queries moderately more consistent.
Test Plan:
- Pushed to a Mercurial branch with a space in it.
- Viewed list of branches in a Mercurial repository.
- Viewed containing branches of a Mercurial commit in Diffusion.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5304
Differential Revision: https://secure.phabricator.com/D9453
2014-06-20 11:48:31 -07:00
|
|
|
'{node}\1{branch}\2');
|
2013-12-17 08:34:15 -08:00
|
|
|
|
|
|
|
|
// Resolve all of the futures now. We don't need the 'commits' future yet,
|
|
|
|
|
// but it simplifies the logic to just get it out of the way.
|
|
|
|
|
foreach (Futures($futures) as $future) {
|
2013-12-23 10:43:45 -08:00
|
|
|
$future->resolve();
|
2013-12-17 08:34:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list($commit_raw) = $futures['commits']->resolvex();
|
|
|
|
|
$commit_map = $this->parseMercurialCommits($commit_raw);
|
2013-12-18 14:19:18 -08:00
|
|
|
$this->mercurialCommits = $commit_map;
|
2013-12-17 08:34:15 -08:00
|
|
|
|
2013-12-23 10:43:45 -08:00
|
|
|
// NOTE: `hg heads` exits with an error code and no output if the repository
|
|
|
|
|
// has no heads. Most commonly this happens on a new repository. We know
|
|
|
|
|
// we can run `hg` successfully since the `hg log` above didn't error, so
|
|
|
|
|
// just ignore the error code.
|
|
|
|
|
|
|
|
|
|
list($err, $old_raw) = $futures['old']->resolve();
|
2013-12-17 08:34:15 -08:00
|
|
|
$old_refs = $this->parseMercurialHeads($old_raw);
|
|
|
|
|
|
2013-12-23 10:43:45 -08:00
|
|
|
list($err, $new_raw) = $futures['new']->resolve();
|
2013-12-17 08:34:15 -08:00
|
|
|
$new_refs = $this->parseMercurialHeads($new_raw);
|
|
|
|
|
|
|
|
|
|
$all_refs = array_keys($old_refs + $new_refs);
|
|
|
|
|
|
|
|
|
|
$ref_updates = array();
|
|
|
|
|
foreach ($all_refs as $ref) {
|
|
|
|
|
$old_heads = idx($old_refs, $ref, array());
|
|
|
|
|
$new_heads = idx($new_refs, $ref, array());
|
|
|
|
|
|
|
|
|
|
sort($old_heads);
|
|
|
|
|
sort($new_heads);
|
|
|
|
|
|
2014-05-14 16:59:15 -07:00
|
|
|
if (!$old_heads && !$new_heads) {
|
|
|
|
|
// This should never be possible, as it makes no sense. Explode.
|
|
|
|
|
throw new Exception(
|
|
|
|
|
pht(
|
|
|
|
|
'Mercurial repository has no new or old heads for branch "%s" '.
|
|
|
|
|
'after push. This makes no sense; rejecting change.',
|
|
|
|
|
$ref));
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:34:15 -08:00
|
|
|
if ($old_heads === $new_heads) {
|
|
|
|
|
// No changes to this branch, so skip it.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$stray_heads = array();
|
2014-05-14 16:59:15 -07:00
|
|
|
|
|
|
|
|
if ($old_heads && !$new_heads) {
|
|
|
|
|
// This is a branch deletion with "--close-branch".
|
|
|
|
|
$head_map = array();
|
|
|
|
|
foreach ($old_heads as $old_head) {
|
|
|
|
|
$head_map[$old_head] = array(self::EMPTY_HASH);
|
|
|
|
|
}
|
|
|
|
|
} else if (count($old_heads) > 1) {
|
2013-12-17 08:34:15 -08:00
|
|
|
// HORRIBLE: In Mercurial, branches can have multiple heads. If the
|
|
|
|
|
// old branch had multiple heads, we need to figure out which new
|
|
|
|
|
// heads descend from which old heads, so we can tell whether you're
|
|
|
|
|
// actively creating new heads (dangerous) or just working in a
|
|
|
|
|
// repository that's already full of garbage (strongly discouraged but
|
|
|
|
|
// not as inherently dangerous). These cases should be very uncommon.
|
|
|
|
|
|
2014-06-03 17:07:49 -07:00
|
|
|
// NOTE: We're only looking for heads on the same branch. The old
|
|
|
|
|
// tip of the branch may be the branchpoint for other branches, but that
|
|
|
|
|
// is OK.
|
|
|
|
|
|
2013-12-17 08:34:15 -08:00
|
|
|
$dfutures = array();
|
|
|
|
|
foreach ($old_heads as $old_head) {
|
|
|
|
|
$dfutures[$old_head] = $repository->getLocalCommandFuture(
|
2014-06-03 17:07:49 -07:00
|
|
|
'log --branch %s --rev %s --template %s',
|
|
|
|
|
$ref,
|
2013-12-17 08:34:15 -08:00
|
|
|
hgsprintf('(descendants(%s) and head())', $old_head),
|
|
|
|
|
'{node}\1');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$head_map = array();
|
|
|
|
|
foreach (Futures($dfutures) as $future_head => $dfuture) {
|
|
|
|
|
list($stdout) = $dfuture->resolvex();
|
2014-05-14 16:59:15 -07:00
|
|
|
$descendant_heads = array_filter(explode("\1", $stdout));
|
|
|
|
|
if ($descendant_heads) {
|
|
|
|
|
// This old head has at least one descendant in the push.
|
|
|
|
|
$head_map[$future_head] = $descendant_heads;
|
|
|
|
|
} else {
|
|
|
|
|
// This old head has no descendants, so it is being deleted.
|
|
|
|
|
$head_map[$future_head] = array(self::EMPTY_HASH);
|
|
|
|
|
}
|
2013-12-17 08:34:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now, find all the new stray heads this push creates, if any. These
|
|
|
|
|
// are new heads which do not descend from the old heads.
|
|
|
|
|
$seen = array_fuse(array_mergev($head_map));
|
|
|
|
|
foreach ($new_heads as $new_head) {
|
2014-05-14 16:59:15 -07:00
|
|
|
if ($new_head === self::EMPTY_HASH) {
|
|
|
|
|
// If a branch head is being deleted, don't insert it as an add.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2013-12-17 08:34:15 -08:00
|
|
|
if (empty($seen[$new_head])) {
|
|
|
|
|
$head_map[self::EMPTY_HASH][] = $new_head;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if ($old_heads) {
|
|
|
|
|
$head_map[head($old_heads)] = $new_heads;
|
|
|
|
|
} else {
|
|
|
|
|
$head_map[self::EMPTY_HASH] = $new_heads;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($head_map as $old_head => $child_heads) {
|
|
|
|
|
foreach ($child_heads as $new_head) {
|
|
|
|
|
if ($new_head === $old_head) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ref_flags = 0;
|
|
|
|
|
$dangerous = null;
|
|
|
|
|
if ($old_head == self::EMPTY_HASH) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
|
|
|
|
|
} else {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-14 16:59:15 -07:00
|
|
|
|
|
|
|
|
$deletes_existing_head = ($new_head == self::EMPTY_HASH);
|
2013-12-17 08:34:15 -08:00
|
|
|
$splits_existing_head = (count($child_heads) > 1);
|
|
|
|
|
$creates_duplicate_head = ($old_head == self::EMPTY_HASH) &&
|
|
|
|
|
(count($head_map) > 1);
|
|
|
|
|
|
|
|
|
|
if ($splits_existing_head || $creates_duplicate_head) {
|
|
|
|
|
$readable_child_heads = array();
|
|
|
|
|
foreach ($child_heads as $child_head) {
|
|
|
|
|
$readable_child_heads[] = substr($child_head, 0, 12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
|
|
|
|
|
|
|
|
|
|
if ($splits_existing_head) {
|
|
|
|
|
// We're splitting an existing head into two or more heads.
|
|
|
|
|
// This is dangerous, and a super bad idea. Note that we're only
|
|
|
|
|
// raising this if you're actively splitting a branch head. If a
|
|
|
|
|
// head split in the past, we don't consider appends to it
|
|
|
|
|
// to be dangerous.
|
|
|
|
|
$dangerous = pht(
|
|
|
|
|
"The change you're attempting to push splits the head of ".
|
|
|
|
|
"branch '%s' into multiple heads: %s. This is inadvisable ".
|
|
|
|
|
"and dangerous.",
|
|
|
|
|
$ref,
|
|
|
|
|
implode(', ', $readable_child_heads));
|
|
|
|
|
} else {
|
|
|
|
|
// We're adding a second (or more) head to a branch. The new
|
|
|
|
|
// head is not a descendant of any old head.
|
|
|
|
|
$dangerous = pht(
|
|
|
|
|
"The change you're attempting to push creates new, divergent ".
|
|
|
|
|
"heads for the branch '%s': %s. This is inadvisable and ".
|
|
|
|
|
"dangerous.",
|
|
|
|
|
$ref,
|
|
|
|
|
implode(', ', $readable_child_heads));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-14 16:59:15 -07:00
|
|
|
if ($deletes_existing_head) {
|
|
|
|
|
// TODO: Somewhere in here we should be setting CHANGEFLAG_REWRITE
|
|
|
|
|
// if we are also creating at least one other head to replace
|
|
|
|
|
// this one.
|
|
|
|
|
|
|
|
|
|
// NOTE: In Git, this is a dangerous change, but it is not dangerous
|
|
|
|
|
// in Mercurial. Mercurial branches are version controlled, and
|
|
|
|
|
// Mercurial does not prompt you for any special flags when pushing
|
|
|
|
|
// a `--close-branch` commit by default.
|
|
|
|
|
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:34:15 -08:00
|
|
|
$ref_update = $this->newPushLog()
|
|
|
|
|
->setRefType(PhabricatorRepositoryPushLog::REFTYPE_BRANCH)
|
|
|
|
|
->setRefName($ref)
|
|
|
|
|
->setRefOld($old_head)
|
|
|
|
|
->setRefNew($new_head)
|
|
|
|
|
->setChangeFlags($ref_flags);
|
|
|
|
|
|
|
|
|
|
if ($dangerous !== null) {
|
|
|
|
|
$ref_update->attachDangerousChangeDescription($dangerous);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ref_updates[] = $ref_update;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $ref_updates;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:34:30 -08:00
|
|
|
private function findMercurialPushKeyRefUpdates() {
|
|
|
|
|
$key_namespace = getenv('HG_NAMESPACE');
|
|
|
|
|
|
|
|
|
|
if ($key_namespace === 'phases') {
|
|
|
|
|
// Mercurial changes commit phases as part of normal push operations. We
|
|
|
|
|
// just ignore these, as they don't seem to represent anything
|
|
|
|
|
// interesting.
|
|
|
|
|
return array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$key_name = getenv('HG_KEY');
|
|
|
|
|
|
|
|
|
|
$key_old = getenv('HG_OLD');
|
|
|
|
|
if (!strlen($key_old)) {
|
|
|
|
|
$key_old = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$key_new = getenv('HG_NEW');
|
|
|
|
|
if (!strlen($key_new)) {
|
|
|
|
|
$key_new = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($key_namespace !== 'bookmarks') {
|
|
|
|
|
throw new Exception(
|
|
|
|
|
pht(
|
|
|
|
|
"Unknown Mercurial key namespace '%s', with key '%s' (%s -> %s). ".
|
|
|
|
|
"Rejecting push.",
|
|
|
|
|
$key_namespace,
|
|
|
|
|
$key_name,
|
|
|
|
|
coalesce($key_old, pht('null')),
|
|
|
|
|
coalesce($key_new, pht('null'))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($key_old === $key_new) {
|
|
|
|
|
// We get a callback when the bookmark doesn't change. Just ignore this,
|
|
|
|
|
// as it's a no-op.
|
|
|
|
|
return array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ref_flags = 0;
|
|
|
|
|
$merge_base = null;
|
|
|
|
|
if ($key_old === null) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
|
|
|
|
|
} else if ($key_new === null) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
|
|
|
|
|
} else {
|
|
|
|
|
list($merge_base_raw) = $this->getRepository()->execxLocalCommand(
|
|
|
|
|
'log --template %s --rev %s',
|
|
|
|
|
'{node}',
|
|
|
|
|
hgsprintf('ancestor(%s, %s)', $key_old, $key_new));
|
|
|
|
|
|
|
|
|
|
if (strlen(trim($merge_base_raw))) {
|
|
|
|
|
$merge_base = trim($merge_base_raw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($merge_base && ($merge_base === $key_old)) {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
|
|
|
|
|
} else {
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_REWRITE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ref_update = $this->newPushLog()
|
|
|
|
|
->setRefType(PhabricatorRepositoryPushLog::REFTYPE_BOOKMARK)
|
|
|
|
|
->setRefName($key_name)
|
|
|
|
|
->setRefOld(coalesce($key_old, self::EMPTY_HASH))
|
|
|
|
|
->setRefNew(coalesce($key_new, self::EMPTY_HASH))
|
|
|
|
|
->setChangeFlags($ref_flags);
|
|
|
|
|
|
|
|
|
|
return array($ref_update);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function findMercurialContentUpdates(array $ref_updates) {
|
2013-12-18 14:19:18 -08:00
|
|
|
$content_updates = array();
|
|
|
|
|
|
|
|
|
|
foreach ($this->mercurialCommits as $commit => $branches) {
|
|
|
|
|
$content_updates[$commit] = $this->newPushLog()
|
|
|
|
|
->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
|
|
|
|
|
->setRefNew($commit)
|
|
|
|
|
->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $content_updates;
|
2013-12-17 08:34:30 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:34:15 -08:00
|
|
|
private function parseMercurialCommits($raw) {
|
|
|
|
|
$commits_lines = explode("\2", $raw);
|
|
|
|
|
$commits_lines = array_filter($commits_lines);
|
|
|
|
|
$commit_map = array();
|
|
|
|
|
foreach ($commits_lines as $commit_line) {
|
Never use "{branches}" in Mercurial
Summary:
Fixes T5304. Mercurial features a "{branches}" template keyword, documented as:
```
branches List of strings. The name of the branch on which the
changeset was committed. Will be empty if the branch name
was default.
```
At some time long in the past, I misinterpreted this to mean "list of branches where the branch head is a descendant of the commit". It is more like "list of zero or one elements, possibly containing the name of the branch the commit was originally made to, if that branch was not 'default'".
In fact, it seems like this is because a //very// long time in the past, Mercurial worked roughly like I expected:
> Ages ago (2005), we had a very different and ultimately unworkable
> approach to named branches that worked vaguely like .hgtags and allowed
> multiple branch names per revision.
http://marc.info/?l=mercurial-devel&m=129883069414855
This appears to be deprecated in modern Mercurial (it's not in the modern web documentation) although I can't find a commit about it so maybe that's just a documentation issue.
In any case, `{branches}` seems to never be useful: `{branch}` provides the same information without the awkward "default-if-empty" case.
Switch from `{branches}` to either `{branch}` (where that's good enough, notably in the hook engine) or `(descendants(%s) and head())`, which is equivalent to `--contains` in Git.
This fixes pushing to branches with spaces in their names, and makes the "Branches" / "Contains" queries moderately more consistent.
Test Plan:
- Pushed to a Mercurial branch with a space in it.
- Viewed list of branches in a Mercurial repository.
- Viewed containing branches of a Mercurial commit in Diffusion.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5304
Differential Revision: https://secure.phabricator.com/D9453
2014-06-20 11:48:31 -07:00
|
|
|
list($node, $branch) = explode("\1", $commit_line);
|
|
|
|
|
$commit_map[$node] = array($branch);
|
2013-12-17 08:34:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $commit_map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function parseMercurialHeads($raw) {
|
|
|
|
|
$heads_map = $this->parseMercurialCommits($raw);
|
|
|
|
|
|
|
|
|
|
$heads = array();
|
|
|
|
|
foreach ($heads_map as $commit => $branches) {
|
|
|
|
|
foreach ($branches as $branch) {
|
|
|
|
|
$heads[$branch][] = $commit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $heads;
|
2013-12-17 08:32:33 -08:00
|
|
|
}
|
2013-12-03 10:28:39 -08:00
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
/* -( Subversion )--------------------------------------------------------- */
|
2013-12-03 10:28:39 -08:00
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function findSubversionRefUpdates() {
|
2013-12-17 11:11:52 -08:00
|
|
|
// Subversion doesn't have any kind of mutable ref metadata.
|
2013-12-17 08:32:33 -08:00
|
|
|
return array();
|
2013-12-03 10:28:39 -08:00
|
|
|
}
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function findSubversionContentUpdates(array $ref_updates) {
|
2013-12-17 11:11:52 -08:00
|
|
|
list($youngest) = execx(
|
|
|
|
|
'svnlook youngest %s',
|
|
|
|
|
$this->subversionRepository);
|
|
|
|
|
$ref_new = (int)$youngest + 1;
|
|
|
|
|
|
|
|
|
|
$ref_flags = 0;
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
|
|
|
|
|
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
|
|
|
|
|
|
|
|
|
|
$ref_content = $this->newPushLog()
|
|
|
|
|
->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
|
|
|
|
|
->setRefNew($ref_new)
|
|
|
|
|
->setChangeFlags($ref_flags);
|
|
|
|
|
|
|
|
|
|
return array($ref_content);
|
2013-12-17 08:32:33 -08:00
|
|
|
}
|
2013-12-03 10:27:45 -08:00
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
/* -( Internals )---------------------------------------------------------- */
|
2013-12-03 10:27:45 -08:00
|
|
|
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
private function newPushLog() {
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
// NOTE: We generate PHIDs up front so the Herald transcripts can pick them
|
|
|
|
|
// up.
|
|
|
|
|
$phid = id(new PhabricatorRepositoryPushLog())->generatePHID();
|
|
|
|
|
|
2013-12-17 08:32:33 -08:00
|
|
|
return PhabricatorRepositoryPushLog::initializeNewLog($this->getViewer())
|
Add Herald support for blocking ref changes
Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`.
Test Plan:
This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail:
Here's a hook-based reject message:
>>> orbital ~/repos/POEMS $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 274 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: +---------------------------------------------------------------+
remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * |
remote: +---------------------------------------------------------------+
remote: \
remote: \ ^ /^
remote: \ / \ // \
remote: \ |\___/| / \// .\
remote: \ /V V \__ / // | \ \ *----*
remote: / / \/_/ // | \ \ \ |
remote: @___@` \/_ // | \ \ \/\ \
remote: 0/0/| \/_ // | \ \ \ \
remote: 0/0/0/0/| \/// | \ \ | |
remote: 0/0/0/0/0/_|_ / ( // | \ _\ | /
remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / /
remote: ,-} _ *-.|.-~-. .~ ~
remote: \ \__/ `/\ / ~-. _ .-~ /
remote: \____(Oo) *. } { /
remote: ( (--) .----~-.\ \-` .~
remote: //__\\ \ DENIED! ///.----..< \ _ -~
remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~
remote:
remote:
remote: This commit was rejected by Herald pre-commit rule H24.
remote: Rule: No Branches Called Blarp
remote: Reason: "blarp" is a bad branch name
remote:
To ssh://dweller@localhost/diffusion/POEMS/
! [remote rejected] blarp -> blarp (pre-receive hook declined)
error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/'
Here's a transcript, showing that all the field values populate sensibly:
{F90453}
Here's a rule:
{F90454}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4195
Differential Revision: https://secure.phabricator.com/D7782
2013-12-17 15:23:55 -08:00
|
|
|
->setPHID($phid)
|
2013-12-17 08:32:33 -08:00
|
|
|
->setRepositoryPHID($this->getRepository()->getPHID())
|
2014-03-26 10:44:06 -07:00
|
|
|
->attachRepository($this->getRepository())
|
2014-03-25 19:43:26 -07:00
|
|
|
->setEpoch(time());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function newPushEvent() {
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
return PhabricatorRepositoryPushEvent::initializeNewEvent($viewer)
|
|
|
|
|
->setRepositoryPHID($this->getRepository()->getPHID())
|
2013-12-17 08:32:33 -08:00
|
|
|
->setRemoteAddress($this->getRemoteAddressForLog())
|
|
|
|
|
->setRemoteProtocol($this->getRemoteProtocol())
|
2014-03-25 19:43:26 -07:00
|
|
|
->setEpoch(time());
|
2013-12-03 10:27:45 -08:00
|
|
|
}
|
2013-12-17 08:32:33 -08:00
|
|
|
|
2013-12-18 14:18:58 -08:00
|
|
|
public function loadChangesetsForCommit($identifier) {
|
2014-01-06 12:12:30 -08:00
|
|
|
$byte_limit = HeraldCommitAdapter::getEnormousByteLimit();
|
|
|
|
|
$time_limit = HeraldCommitAdapter::getEnormousTimeLimit();
|
|
|
|
|
|
2013-12-18 14:18:58 -08:00
|
|
|
$vcs = $this->getRepository()->getVersionControlSystem();
|
|
|
|
|
switch ($vcs) {
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
|
|
|
|
// For git and hg, we can use normal commands.
|
|
|
|
|
$drequest = DiffusionRequest::newFromDictionary(
|
|
|
|
|
array(
|
|
|
|
|
'repository' => $this->getRepository(),
|
|
|
|
|
'user' => $this->getViewer(),
|
|
|
|
|
'commit' => $identifier,
|
|
|
|
|
));
|
2014-01-06 12:12:30 -08:00
|
|
|
|
2013-12-18 14:18:58 -08:00
|
|
|
$raw_diff = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest)
|
2014-01-06 12:12:30 -08:00
|
|
|
->setTimeout($time_limit)
|
|
|
|
|
->setByteLimit($byte_limit)
|
2013-12-18 14:18:58 -08:00
|
|
|
->setLinesOfContext(0)
|
|
|
|
|
->loadRawDiff();
|
|
|
|
|
break;
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
|
|
|
// TODO: This diff has 3 lines of context, which produces slightly
|
|
|
|
|
// incorrect "added file content" and "removed file content" results.
|
|
|
|
|
// This may also choke on binaries, but "svnlook diff" does not support
|
|
|
|
|
// the "--diff-cmd" flag.
|
|
|
|
|
|
|
|
|
|
// For subversion, we need to use `svnlook`.
|
2014-01-06 12:12:30 -08:00
|
|
|
$future = new ExecFuture(
|
2013-12-18 14:18:58 -08:00
|
|
|
'svnlook diff -t %s %s',
|
|
|
|
|
$this->subversionTransaction,
|
|
|
|
|
$this->subversionRepository);
|
2014-01-06 12:12:30 -08:00
|
|
|
|
|
|
|
|
$future->setTimeout($time_limit);
|
|
|
|
|
$future->setStdoutSizeLimit($byte_limit);
|
|
|
|
|
$future->setStderrSizeLimit($byte_limit);
|
|
|
|
|
|
|
|
|
|
list($raw_diff) = $future->resolvex();
|
2013-12-18 14:18:58 -08:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception(pht("Unknown VCS '%s!'", $vcs));
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-06 12:12:30 -08:00
|
|
|
if (strlen($raw_diff) >= $byte_limit) {
|
|
|
|
|
throw new Exception(
|
|
|
|
|
pht(
|
|
|
|
|
'The raw text of this change is enormous (larger than %d '.
|
|
|
|
|
'bytes). Herald can not process it.',
|
|
|
|
|
$byte_limit));
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-14 16:59:15 -07:00
|
|
|
if (!strlen($raw_diff)) {
|
|
|
|
|
// If the commit is actually empty, just return no changesets.
|
|
|
|
|
return array();
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-18 14:18:58 -08:00
|
|
|
$parser = new ArcanistDiffParser();
|
|
|
|
|
$changes = $parser->parseDiff($raw_diff);
|
2014-11-19 12:16:07 -08:00
|
|
|
$diff = DifferentialDiff::newFromRawChanges(
|
|
|
|
|
$this->getViewer(),
|
|
|
|
|
$changes);
|
2013-12-18 14:18:58 -08:00
|
|
|
return $diff->getChangesets();
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-19 06:55:57 -08:00
|
|
|
public function loadCommitRefForCommit($identifier) {
|
|
|
|
|
$repository = $this->getRepository();
|
|
|
|
|
$vcs = $repository->getVersionControlSystem();
|
|
|
|
|
switch ($vcs) {
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
2013-12-19 11:05:06 -08:00
|
|
|
return id(new DiffusionLowLevelCommitQuery())
|
2013-12-19 06:55:57 -08:00
|
|
|
->setRepository($repository)
|
|
|
|
|
->withIdentifier($identifier)
|
|
|
|
|
->execute();
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
|
|
|
// For subversion, we need to use `svnlook`.
|
|
|
|
|
list($message) = execx(
|
|
|
|
|
'svnlook log -t %s %s',
|
|
|
|
|
$this->subversionTransaction,
|
|
|
|
|
$this->subversionRepository);
|
|
|
|
|
|
|
|
|
|
return id(new DiffusionCommitRef())
|
|
|
|
|
->setMessage($message);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception(pht("Unknown VCS '%s!'", $vcs));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-26 10:40:16 -08:00
|
|
|
public function loadBranches($identifier) {
|
|
|
|
|
$repository = $this->getRepository();
|
|
|
|
|
$vcs = $repository->getVersionControlSystem();
|
|
|
|
|
switch ($vcs) {
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
|
|
|
return idx($this->gitCommits, $identifier, array());
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
Never use "{branches}" in Mercurial
Summary:
Fixes T5304. Mercurial features a "{branches}" template keyword, documented as:
```
branches List of strings. The name of the branch on which the
changeset was committed. Will be empty if the branch name
was default.
```
At some time long in the past, I misinterpreted this to mean "list of branches where the branch head is a descendant of the commit". It is more like "list of zero or one elements, possibly containing the name of the branch the commit was originally made to, if that branch was not 'default'".
In fact, it seems like this is because a //very// long time in the past, Mercurial worked roughly like I expected:
> Ages ago (2005), we had a very different and ultimately unworkable
> approach to named branches that worked vaguely like .hgtags and allowed
> multiple branch names per revision.
http://marc.info/?l=mercurial-devel&m=129883069414855
This appears to be deprecated in modern Mercurial (it's not in the modern web documentation) although I can't find a commit about it so maybe that's just a documentation issue.
In any case, `{branches}` seems to never be useful: `{branch}` provides the same information without the awkward "default-if-empty" case.
Switch from `{branches}` to either `{branch}` (where that's good enough, notably in the hook engine) or `(descendants(%s) and head())`, which is equivalent to `--contains` in Git.
This fixes pushing to branches with spaces in their names, and makes the "Branches" / "Contains" queries moderately more consistent.
Test Plan:
- Pushed to a Mercurial branch with a space in it.
- Viewed list of branches in a Mercurial repository.
- Viewed containing branches of a Mercurial commit in Diffusion.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5304
Differential Revision: https://secure.phabricator.com/D9453
2014-06-20 11:48:31 -07:00
|
|
|
// NOTE: This will be "the branch the commit was made to", not
|
|
|
|
|
// "a list of all branch heads which descend from the commit".
|
|
|
|
|
// This is consistent with Mercurial, but possibly confusing.
|
2013-12-26 10:40:16 -08:00
|
|
|
return idx($this->mercurialCommits, $identifier, array());
|
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
|
|
|
// Subversion doesn't have branches.
|
|
|
|
|
return array();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 10:44:06 -07:00
|
|
|
private function loadCommitInfoForWorker(array $all_updates) {
|
|
|
|
|
$type_commit = PhabricatorRepositoryPushLog::REFTYPE_COMMIT;
|
|
|
|
|
|
|
|
|
|
$map = array();
|
|
|
|
|
foreach ($all_updates as $update) {
|
|
|
|
|
if ($update->getRefType() != $type_commit) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$map[$update->getRefNew()] = array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($map as $identifier => $info) {
|
|
|
|
|
$ref = $this->loadCommitRefForCommit($identifier);
|
|
|
|
|
$map[$identifier] += array(
|
|
|
|
|
'summary' => $ref->getSummary(),
|
|
|
|
|
'branches' => $this->loadBranches($identifier),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $map;
|
|
|
|
|
}
|
2013-12-19 06:55:57 -08:00
|
|
|
|
2013-12-02 15:45:36 -08:00
|
|
|
}
|