Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/diffusion/controller/DiffusionPushLogListController.php b/src/applications/diffusion/controller/DiffusionPushLogListController.php
index f8c6405844..f0a6414198 100644
--- a/src/applications/diffusion/controller/DiffusionPushLogListController.php
+++ b/src/applications/diffusion/controller/DiffusionPushLogListController.php
@@ -1,138 +1,144 @@
<?php
final class DiffusionPushLogListController extends DiffusionController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->queryKey = idx($data, 'queryKey');
}
public function processRequest() {
$request = $this->getRequest();
$controller = id(new PhabricatorApplicationSearchController($request))
->setQueryKey($this->queryKey)
->setSearchEngine(new PhabricatorRepositoryPushLogSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function renderResultsList(
array $logs,
PhabricatorSavedQuery $query) {
$viewer = $this->getRequest()->getUser();
$this->loadHandles(mpull($logs, 'getPusherPHID'));
// Figure out which repositories are editable. We only let you see remote
// IPs if you have edit capability on a repository.
$editable_repos = array();
if ($logs) {
$editable_repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withPHIDs(mpull($logs, 'getRepositoryPHID'))
->execute();
$editable_repos = mpull($editable_repos, null, 'getPHID');
}
$rows = array();
foreach ($logs as $log) {
// Reveal this if it's valid and the user can edit the repository.
$remote_addr = '-';
if (isset($editable_repos[$log->getRepositoryPHID()])) {
$remote_long = $log->getRemoteAddress();
if ($remote_long) {
$remote_addr = long2ip($remote_long);
}
}
$callsign = $log->getRepository()->getCallsign();
$rows[] = array(
phutil_tag(
'a',
array(
'href' => $this->getApplicationURI($callsign.'/'),
),
$callsign),
$this->getHandle($log->getPusherPHID())->renderLink(),
$remote_addr,
$log->getRemoteProtocol(),
$log->getRefType(),
$log->getRefName(),
phutil_tag(
'a',
array(
'href' => '/r'.$callsign.$log->getRefOld(),
),
$log->getRefOldShort()),
phutil_tag(
'a',
array(
'href' => '/r'.$callsign.$log->getRefNew(),
),
$log->getRefNewShort()),
+
+ // TODO: Make these human-readable.
+ $log->getChangeFlags(),
+ $log->getRejectCode(),
phabricator_datetime($log->getEpoch(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Repository'),
pht('Pusher'),
pht('From'),
pht('Via'),
pht('Type'),
pht('Name'),
pht('Old'),
pht('New'),
+ pht('Flags'),
+ pht('Code'),
pht('Date'),
))
->setColumnClasses(
array(
'',
'',
'',
'',
'',
'wide',
'n',
'n',
'date',
));
$box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($table);
return $box;
}
public function buildSideNavView($for_app = false) {
$viewer = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new PhabricatorRepositoryPushLogSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
}
diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
index d57f4480bb..7ce6297d57 100644
--- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
+++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
@@ -1,372 +1,460 @@
<?php
/**
- * @task git Git Hooks
- * @task hg Mercurial Hooks
- * @task svn Subversion Hooks
+ * @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
*/
final class DiffusionCommitHookEngine extends Phobject {
const ENV_USER = 'PHABRICATOR_USER';
const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS';
const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL';
+ const EMPTY_HASH = '0000000000000000000000000000000000000000';
+
private $viewer;
private $repository;
private $stdin;
private $subversionTransaction;
private $subversionRepository;
private $remoteAddress;
private $remoteProtocol;
private $transactionKey;
+
+/* -( Config )------------------------------------------------------------- */
+
+
public function setRemoteProtocol($remote_protocol) {
$this->remoteProtocol = $remote_protocol;
return $this;
}
public function getRemoteProtocol() {
return $this->remoteProtocol;
}
public function setRemoteAddress($remote_address) {
$this->remoteAddress = $remote_address;
return $this;
}
public function getRemoteAddress() {
return $this->remoteAddress;
}
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;
}
private function getTransactionKey() {
if (!$this->transactionKey) {
$entropy = Filesystem::readRandomBytes(64);
$this->transactionKey = PhabricatorHash::digestForIndex($entropy);
}
return $this->transactionKey;
}
public function setSubversionTransactionInfo($transaction, $repository) {
$this->subversionTransaction = $transaction;
$this->subversionRepository = $repository;
return $this;
}
public function setStdin($stdin) {
$this->stdin = $stdin;
return $this;
}
public function getStdin() {
return $this->stdin;
}
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;
}
+
+/* -( Hook Execution )----------------------------------------------------- */
+
+
public function execute() {
+ $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.
+ foreach ($all_updates as $update) {
+ $update->setRejectCode(
+ PhabricatorRepositoryPushLog::REJECT_DANGEROUS);
+ }
+ throw $ex;
+ }
+
+ // TODO: Fire ref herald rules.
+
+ $content_updates = $this->findContentUpdates($ref_updates);
+ $all_updates = array_merge($all_updates, $content_updates);
+
+ // TODO: Fire content Herald rules.
+ // TODO: Fire external hooks.
+
+ // If we make it this far, we're accepting these changes. Mark all the
+ // logs as accepted.
+ foreach ($all_updates as $update) {
+ $update->setRejectCode(PhabricatorRepositoryPushLog::REJECT_ACCEPT);
+ }
+ } 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.
+ foreach ($all_updates as $update) {
+ $update->save();
+ }
+
+ if ($caught) {
+ throw $caught;
+ }
+
+ return 0;
+ }
+
+ private function findRefUpdates() {
$type = $this->getRepository()->getVersionControlSystem();
switch ($type) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
- $err = $this->executeGitHook();
- break;
- case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
- $err = $this->executeSubversionHook();
- break;
+ return $this->findGitRefUpdates();
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
- $err = $this->executeMercurialHook();
- break;
+ return $this->findMercurialRefUpdates();
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ return $this->findSubversionRefUpdates();
default:
throw new Exception(pht('Unsupported repository type "%s"!', $type));
}
-
- return $err;
}
- private function newPushLog() {
- return PhabricatorRepositoryPushLog::initializeNewLog($this->getViewer())
- ->setRepositoryPHID($this->getRepository()->getPHID())
- ->setEpoch(time())
- ->setRemoteAddress($this->getRemoteAddressForLog())
- ->setRemoteProtocol($this->getRemoteProtocol())
- ->setTransactionKey($this->getTransactionKey())
- ->setRejectCode(PhabricatorRepositoryPushLog::REJECT_ACCEPT)
- ->setRejectDetails(null);
- }
-
-
- /**
- * @task git
- */
- private function executeGitHook() {
- $updates = $this->parseGitUpdates($this->getStdin());
+ private function rejectDangerousChanges(array $ref_updates) {
+ assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
- $this->rejectGitDangerousChanges($updates);
+ $repository = $this->getRepository();
+ if ($repository->shouldAllowDangerousChanges()) {
+ return;
+ }
- // TODO: Do cheap checks: non-ff commits, mutating refs without access,
- // creating or deleting things you can't touch. We can do all non-content
- // checks here.
+ $flag_dangerous = PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
- $updates = $this->findGitNewCommits($updates);
+ foreach ($ref_updates as $ref_update) {
+ if (!$ref_update->hasChangeFlags($flag_dangerous)) {
+ // This is not a dangerous change.
+ continue;
+ }
- // TODO: Now, do content checks.
+ // We either have a branch deletion or a non fast-forward branch update.
+ // Format a message and reject the push.
- // TODO: Generalize this; just getting some data in the database for now.
+ $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());
- $logs = array();
- foreach ($updates as $update) {
- $log = $this->newPushLog()
- ->setRefType($update['type'])
- ->setRefNameHash(PhabricatorHash::digestForIndex($update['ref']))
- ->setRefNameRaw($update['ref'])
- ->setRefNameEncoding(phutil_is_utf8($update['ref']) ? 'utf8' : null)
- ->setRefOld($update['old'])
- ->setRefNew($update['new'])
- ->setMergeBase(idx($update, 'merge-base'));
+ throw new DiffusionCommitHookRejectException($message);
+ }
+ }
- $flags = 0;
- if ($update['operation'] == 'create') {
- $flags = $flags | PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
- } else if ($update['operation'] == 'delete') {
- $flags = $flags | PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE;
- } else {
- // TODO: This isn't correct; these might be APPEND or REWRITE, and
- // if they're REWRITE they might be DANGEROUS. Fix this when this
- // gets generalized.
- $flags = $flags | PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
- }
+ private function findContentUpdates(array $ref_updates) {
+ assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
- $log->setChangeFlags($flags);
- $logs[] = $log;
+ $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));
}
+ }
- // Now, build logs for all the commits.
- // TODO: Generalize this, too.
- $commits = array_mergev(ipull($updates, 'commits'));
- $commits = array_unique($commits);
- foreach ($commits as $commit) {
- $log = $this->newPushLog()
- ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
- ->setRefNew($commit)
- ->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD);
- $logs[] = $log;
- }
- foreach ($logs as $log) {
- $log->save();
- }
+/* -( Git )---------------------------------------------------------------- */
- return 0;
- }
+ private function findGitRefUpdates() {
+ $ref_updates = array();
- /**
- * @task git
- */
- private function parseGitUpdates($stdin) {
- $updates = array();
+ // First, parse stdin, which lists all the ref changes. The input looks
+ // like this:
+ //
+ // <old hash> <new hash> <ref>
+ $stdin = $this->getStdin();
$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));
}
- $update = array(
- 'old' => $parts[0],
- 'old.short' => substr($parts[0], 0, 8),
- 'new' => $parts[1],
- 'new.short' => substr($parts[1], 0, 8),
- 'ref' => $parts[2],
- );
-
- if (preg_match('(^refs/heads/)', $update['ref'])) {
- $update['type'] = 'branch';
- $update['ref.short'] = substr($update['ref'], strlen('refs/heads/'));
- } else if (preg_match('(^refs/tags/)', $update['ref'])) {
- $update['type'] = 'tag';
- $update['ref.short'] = substr($update['ref'], strlen('refs/tags/'));
+
+ $ref_old = $parts[0];
+ $ref_new = $parts[1];
+ $ref_raw = $parts[2];
+
+ if (preg_match('(^refs/heads/)', $ref_raw)) {
+ $ref_type = PhabricatorRepositoryPushLog::REFTYPE_BRANCH;
+ } else if (preg_match('(^refs/tags/)', $ref_raw)) {
+ $ref_type = PhabricatorRepositoryPushLog::REFTYPE_TAG;
} else {
- $update['type'] = 'unknown';
+ $ref_type = PhabricatorRepositoryPushLog::REFTYPE_UNKNOWN;
}
- $updates[] = $update;
+ $ref_update = $this->newPushLog()
+ ->setRefType($ref_type)
+ ->setRefName($ref_raw)
+ ->setRefOld($ref_old)
+ ->setRefNew($ref_new);
+
+ $ref_updates[] = $ref_update;
}
- $updates = $this->findGitMergeBases($updates);
+ $this->findGitMergeBases($ref_updates);
+ $this->findGitChangeFlags($ref_updates);
- return $updates;
+ return $ref_updates;
}
- /**
- * @task git
- */
- private function findGitMergeBases(array $updates) {
- $empty = str_repeat('0', 40);
+ private function findGitMergeBases(array $ref_updates) {
+ assert_instances_of($ref_updates, 'PhabricatorRepositoryPushLog');
$futures = array();
- foreach ($updates as $key => $update) {
- // Updates are in the form:
- //
- // <old hash> <new hash> <ref>
- //
+ foreach ($ref_updates as $key => $ref_update) {
// 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.
+ $ref_old = $ref_update->getRefOld();
+ $ref_new = $ref_update->getRefNew();
- if ($update['old'] == $empty) {
- $updates[$key]['operation'] = 'create';
- } else if ($update['new'] == $empty) {
- $updates[$key]['operation'] = 'delete';
- } else {
- $updates[$key]['operation'] = 'update';
- $futures[$key] = $this->getRepository()->getLocalCommandFuture(
- 'merge-base %s %s',
- $update['old'],
- $update['new']);
+ if (($ref_old === self::EMPTY_HASH) ||
+ ($ref_new === self::EMPTY_HASH)) {
+ continue;
}
+
+ $futures[$key] = $this->getRepository()->getLocalCommandFuture(
+ 'merge-base %s %s',
+ $ref_old,
+ $ref_new);
}
foreach (Futures($futures)->limit(8) as $key => $future) {
// 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();
+
if ($err) {
- $updates[$key]['merge-base'] = null;
+ $merge_base = null;
} else {
- $updates[$key]['merge-base'] = rtrim($stdout, "\n");
+ $merge_base = rtrim($stdout, "\n");
}
+
+ $ref_update->setMergeBase($merge_base);
}
- return $updates;
+ return $ref_updates;
}
- private function findGitNewCommits(array $updates) {
+
+ 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;
+
+ if ($ref_old === self::EMPTY_HASH) {
+ $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(
+ "DANGEROUS CHANGE: 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.",
+ $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;
+
$futures = array();
- foreach ($updates as $key => $update) {
- if ($update['operation'] == 'delete') {
+ foreach ($ref_updates as $key => $ref_update) {
+ if ($ref_update->hasChangeFlags($flag_delete)) {
// 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',
- $update['new']);
+ $ref_update->getRefNew());
}
+ $content_updates = array();
foreach (Futures($futures)->limit(8) as $key => $future) {
list($stdout) = $future->resolvex();
+
+ 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;
+ }
+
$commits = phutil_split_lines($stdout, $retain_newlines = false);
- $updates[$key]['commits'] = $commits;
+
+ foreach ($commits as $commit) {
+ $content_updates[$commit] = $this->newPushLog()
+ ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)
+ ->setRefNew($commit)
+ ->setChangeFlags(PhabricatorRepositoryPushLog::CHANGEFLAG_ADD);
+ }
}
- return $updates;
+ return $content_updates;
}
- private function rejectGitDangerousChanges(array $updates) {
- $repository = $this->getRepository();
- if ($repository->shouldAllowDangerousChanges()) {
- return;
- }
- foreach ($updates as $update) {
- if ($update['type'] != 'branch') {
- // 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.
- continue;
- }
+/* -( Mercurial )---------------------------------------------------------- */
- if ($update['operation'] == 'create') {
- // Creating a branch is never dangerous.
- continue;
- }
- if ($update['operation'] == 'update') {
- if ($update['old'] == $update['merge-base']) {
- // This is a fast-forward update to an existing branch.
- // These are safe.
- continue;
- }
- }
+ private function findMercurialRefUpdates() {
+ // TODO: Implement.
+ return array();
+ }
- // We either have a branch deletion or a non fast-forward branch update.
- // Format a message and reject the push.
+ private function findMercurialContentUpdates(array $ref_updates) {
+ // TODO: Implement.
+ return array();
+ }
- if ($update['operation'] == 'delete') {
- $message = pht(
- "DANGEROUS CHANGE: The change you're attempting to push deletes ".
- "the branch '%s'.",
- $update['ref.short']);
- } else {
- $message = pht(
- "DANGEROUS CHANGE: 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.",
- $update['ref.short'],
- $update['old.short'],
- $update['new.short']);
- }
- $boilerplate = pht(
- "Dangerous change protection is enabled for this repository.\n".
- "Edit the repository configuration before making dangerous changes.");
+/* -( Subversion )--------------------------------------------------------- */
- $message = $message."\n".$boilerplate;
- throw new DiffusionCommitHookRejectException($message);
- }
+ private function findSubversionRefUpdates() {
+ // TODO: Implement.
+ return array();
}
- private function executeSubversionHook() {
+ private function findSubversionContentUpdates(array $ref_updates) {
+ // TODO: Implement.
+ return array();
+ }
- // TODO: Do useful things here, too.
- return 0;
- }
+/* -( Internals )---------------------------------------------------------- */
- private function executeMercurialHook() {
- // TODO: Here, too, useful things should be done.
+ private function newPushLog() {
+ // NOTE: By default, we create these with REJECT_BROKEN as the reject
+ // code. This indicates a broken hook, and covers the case where we
+ // encounter some unexpected exception and consequently reject the changes.
- return 0;
+ return PhabricatorRepositoryPushLog::initializeNewLog($this->getViewer())
+ ->attachRepository($this->getRepository())
+ ->setRepositoryPHID($this->getRepository()->getPHID())
+ ->setEpoch(time())
+ ->setRemoteAddress($this->getRemoteAddressForLog())
+ ->setRemoteProtocol($this->getRemoteProtocol())
+ ->setTransactionKey($this->getTransactionKey())
+ ->setRejectCode(PhabricatorRepositoryPushLog::REJECT_BROKEN)
+ ->setRejectDetails(null);
}
+
}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php
index 7dd442541d..b0d885bd99 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php
@@ -1,117 +1,143 @@
<?php
/**
* Records a push to a hosted repository. This allows us to store metadata
* about who pushed commits, when, and from where. We can also record the
* history of branches and tags, which is not normally persisted outside of
* the reflog.
*
* This log is written by commit hooks installed into hosted repositories.
* See @{class:DiffusionCommitHookEngine}.
*/
final class PhabricatorRepositoryPushLog
extends PhabricatorRepositoryDAO
implements PhabricatorPolicyInterface {
const REFTYPE_BRANCH = 'branch';
const REFTYPE_TAG = 'tag';
const REFTYPE_BOOKMARK = 'bookmark';
const REFTYPE_SVN = 'svn';
const REFTYPE_COMMIT = 'commit';
+ const REFTYPE_UNKNOWN = 'unknown';
const CHANGEFLAG_ADD = 1;
const CHANGEFLAG_DELETE = 2;
const CHANGEFLAG_APPEND = 4;
const CHANGEFLAG_REWRITE = 8;
const CHANGEFLAG_DANGEROUS = 16;
const REJECT_ACCEPT = 0;
const REJECT_DANGEROUS = 1;
const REJECT_HERALD = 2;
const REJECT_EXTERNAL = 3;
+ const REJECT_BROKEN = 4;
protected $repositoryPHID;
protected $epoch;
protected $pusherPHID;
protected $remoteAddress;
protected $remoteProtocol;
protected $transactionKey;
protected $refType;
protected $refNameHash;
protected $refNameRaw;
protected $refNameEncoding;
protected $refOld;
protected $refNew;
protected $mergeBase;
protected $changeFlags;
protected $rejectCode;
protected $rejectDetails;
+ private $dangerousChangeDescription = self::ATTACHABLE;
private $repository = self::ATTACHABLE;
public static function initializeNewLog(PhabricatorUser $viewer) {
return id(new PhabricatorRepositoryPushLog())
->setPusherPHID($viewer->getPHID());
}
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
) + parent::getConfiguration();
}
public function attachRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getRepository() {
return $this->assertAttached($this->repository);
}
public function getRefName() {
if ($this->getRefNameEncoding() == 'utf8') {
return $this->getRefNameRaw();
}
return phutil_utf8ize($this->getRefNameRaw());
}
+ public function setRefName($ref_raw) {
+ $encoding = phutil_is_utf8($ref_raw) ? 'utf8' : null;
+
+ $this->setRefNameRaw($ref_raw);
+ $this->setRefNameHash(PhabricatorHash::digestForIndex($ref_raw));
+ $this->setRefNameEncoding($encoding);
+
+ return $this;
+ }
+
public function getRefOldShort() {
if ($this->getRepository()->isSVN()) {
return $this->getRefOld();
}
return substr($this->getRefOld(), 0, 12);
}
public function getRefNewShort() {
if ($this->getRepository()->isSVN()) {
return $this->getRefNew();
}
return substr($this->getRefNew(), 0, 12);
}
+ public function hasChangeFlags($mask) {
+ return ($this->changeFlags & $mask);
+ }
+
+ public function attachDangerousChangeDescription($description) {
+ $this->dangerousChangeDescription = $description;
+ return $this;
+ }
+
+ public function getDangerousChangeDescription() {
+ return $this->assertAttached($this->dangerousChangeDescription);
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getRepository()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
"A repository's push logs are visible to users who can see the ".
"repository.");
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 14, 3:25 AM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
336740
Default Alt Text
(30 KB)

Event Timeline