1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 06:42:42 +01:00

Support bookmark hook operations in Mercurial

Summary: Ref T4195. Turns bookmark mutations in Mercurial into log objects.

Test Plan:
Pushed a pile of bookmarks and got logs:

{F89313}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4195

Differential Revision: https://secure.phabricator.com/D7764
This commit is contained in:
epriestley 2013-12-17 08:34:30 -08:00
parent 6f3a99eb39
commit 74251b3636
4 changed files with 129 additions and 10 deletions

View file

@ -36,9 +36,10 @@ if ($repository->isGit() || $repository->isHg()) {
pht('usage: %s should be defined!', DiffusionCommitHookEngine::ENV_USER));
}
// TODO: If this is a Mercurial repository, the hook we're responding to
// is available in $argv[2]. It's unclear if we actually need this, or if
// we can block all actions we care about with just pretxnchangegroup.
if ($repository->isHg()) {
// We respond to several different hooks in Mercurial.
$engine->setMercurialHook($argv[2]);
}
} else if ($repository->isSVN()) {
// NOTE: In Subversion, the entire environment gets wiped so we can't read

View file

@ -24,6 +24,7 @@ final class DiffusionCommitHookEngine extends Phobject {
private $remoteAddress;
private $remoteProtocol;
private $transactionKey;
private $mercurialHook;
/* -( Config )------------------------------------------------------------- */
@ -97,6 +98,15 @@ final class DiffusionCommitHookEngine extends Phobject {
return $this->viewer;
}
public function setMercurialHook($mercurial_hook) {
$this->mercurialHook = $mercurial_hook;
return $this;
}
public function getMercurialHook() {
return $this->mercurialHook;
}
/* -( Hook Execution )----------------------------------------------------- */
@ -239,7 +249,10 @@ final class DiffusionCommitHookEngine extends Phobject {
} else if (preg_match('(^refs/tags/)', $ref_raw)) {
$ref_type = PhabricatorRepositoryPushLog::REFTYPE_TAG;
} else {
$ref_type = PhabricatorRepositoryPushLog::REFTYPE_UNKNOWN;
throw new Exception(
pht(
"Unable to identify the reftype of '%s'. Rejecting push.",
$ref_raw));
}
$ref_update = $this->newPushLog()
@ -413,6 +426,20 @@ final class DiffusionCommitHookEngine extends Phobject {
private function findMercurialRefUpdates() {
$hook = $this->getMercurialHook();
switch ($hook) {
case 'pretxnchangegroup':
return $this->findMercurialChangegroupRefUpdates();
case 'prepushkey':
return $this->findMercurialPushKeyRefUpdates();
case 'pretag':
return $this->findMercurialPreTagRefUpdates();
default:
throw new Exception(pht('Unrecognized hook "%s"!', $hook));
}
}
private function findMercurialChangegroupRefUpdates() {
$hg_node = getenv('HG_NODE');
if (!$hg_node) {
throw new Exception(pht('Expected HG_NODE in environment!'));
@ -594,6 +621,87 @@ final class DiffusionCommitHookEngine extends Phobject {
return $ref_updates;
}
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 findMercurialPreTagRefUpdates() {
return array();
}
private function findMercurialContentUpdates(array $ref_updates) {
// TODO: Implement.
return array();
}
private function parseMercurialCommits($raw) {
$commits_lines = explode("\2", $raw);
$commits_lines = array_filter($commits_lines);
@ -626,11 +734,6 @@ final class DiffusionCommitHookEngine extends Phobject {
return $heads;
}
private function findMercurialContentUpdates(array $ref_updates) {
// TODO: Implement.
return array();
}
/* -( Subversion )--------------------------------------------------------- */

View file

@ -397,11 +397,27 @@ final class PhabricatorRepositoryPullEngine
$data = array();
$data[] = '[hooks]';
// This hook handles normal pushes.
$data[] = csprintf(
'pretxnchangegroup.phabricator = %s %s %s',
$bin,
$repository->getCallsign(),
'pretxnchangegroup');
// This one handles creating bookmarks.
$data[] = csprintf(
'prepushkey.phabricator = %s %s %s',
$bin,
$repository->getCallsign(),
'prepushkey');
// This one handles creating tags.
$data[] = csprintf(
'pretag.phabricator = %s %s %s',
$bin,
$repository->getCallsign(),
'pretag');
$data[] = null;
$data = implode("\n", $data);

View file

@ -18,7 +18,6 @@ final class PhabricatorRepositoryPushLog
const REFTYPE_BOOKMARK = 'bookmark';
const REFTYPE_SVN = 'svn';
const REFTYPE_COMMIT = 'commit';
const REFTYPE_UNKNOWN = 'unknown';
const CHANGEFLAG_ADD = 1;
const CHANGEFLAG_DELETE = 2;