Page MenuHomePhabricator
Paste P2110

PhabricatorRepositoryRewindManagementWorkflow.php
ActivePublic

Authored by epriestley on Nov 1 2018, 12:45 PM.
<?php
final class PhabricatorRepositoryRewindManagementWorkflow
extends PhabricatorRepositoryManagementWorkflow {
protected function didConstruct() {
$this
->setName('rewind')
->setExamples('**rewind** __repository__ ...')
->setSynopsis(pht('Rewind refs in __repository__ based on push logs.'))
->setArguments(
array(
array(
'name' => 'repos',
'wildcard' => true,
),
array(
'name' => 'to',
'param' => 'epoch',
),
));
}
public function execute(PhutilArgumentParser $args) {
$repos = $this->loadLocalRepositories($args, 'repos');
if (!$repos) {
throw new PhutilArgumentUsageException(
pht('Specify one or more repositories to rewind.'));
}
$to = $args->getArg('to');
$to = $this->parseTimeArgument($to);
if (!$to) {
$to = PhabricatorTime::getNow();
}
foreach ($repos as $repo) {
$this->rewindRepository($repo, $to);
}
}
private function rewindRepository(
PhabricatorRepository $repository,
$epoch) {
echo tsprintf(
"%s\n",
pht(
'Loading repository state ("%s")...',
$repository->getDisplayName()));
$log = new PhabricatorRepositoryPushLog();
$conn = $log->establishConnection('w');
$state = queryfx_all(
$conn,
'SELECT id, refType, refNameRaw, refNew, epoch
FROM %T
WHERE repositoryPHID = %s AND epoch <= %d
GROUP BY refType, refNameRaw
ORDER BY id DESC',
$log->getTableName(),
$repository->getPHID(),
$epoch);
$map = array();
$n = 0;
foreach ($state as $row) {
if ($row['refNew'] == '0000000000000000000000000000000000000000') {
continue;
}
$map[$row['refType']][$row['refNameRaw']] = array(
'id' => $row['id'],
'type' => $row['refType'],
'raw' => $row['refNameRaw'],
'ref' => $row['refNew'],
'epoch' => $row['epoch'],
);
$n++;
}
echo tsprintf(
"%s\n",
pht('Done (found %s refs).', new PhutilNumber($n)));
echo tsprintf(
"%s\n",
pht(
'Loading tags...'));
$raw_refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->execute();
$raw_tags = array();
foreach ($raw_refs as $raw_ref) {
if ($raw_ref->getRefType() != 'tag') {
continue;
}
$raw_tags[$raw_ref->getShortName()] = $raw_ref->getCommitIdentifier();
}
echo tsprintf(
"%s\n",
pht('Done (found %s tags).', phutil_count($raw_tags)));
foreach ($map as $type => $refs) {
foreach ($refs as $name => $ref) {
$hash = $ref['ref'];
list($err, $merge_base) = $repository->execLocalCommand(
'cat-file -t %s',
$hash);
$exists = !$err;
$is_branch = ($type === 'branch');
$is_tag = ($type === 'tag');
if (!$exists) {
echo tsprintf(
"<bg:yellow> %s </bg> %s\n",
pht('MISSING'),
pht(
'Ref "%s" of type "%s" points at missing commit "%s".',
$name,
$type,
$hash));
continue;
}
$info = pht(
'Rewind #%d @ %s',
$ref['id'],
date('Y-m-d g:i:s A', $ref['epoch']));
if ($is_branch) {
list($stdout) = $repository->execxLocalCommand(
'branch --contains %s -- %s',
$hash,
$name);
$stdout = trim($stdout);
if (strlen($stdout)) {
echo tsprintf(
"<bg:green> %s </bg> %s\n",
pht('AHEAD'),
pht(
'Branch "%s" is already at or ahead of "%s".',
$name,
$hash));
continue;
}
$suggest[] = csprintf(
'git branch -- %s %s # %s',
$name,
$hash,
$info);
}
if ($is_tag) {
if (idx($raw_tags, $name) === $hash) {
echo tsprintf(
"<bg:green> %s </bg> %s\n",
pht('TAG'),
pht(
'Tag "%s" already points at "%s".',
$name,
$hash));
continue;
}
$suggest[] = csprintf(
'git tag -m %s -- %s %s # %s',
pht('<Rewind #%d>', $ref['id']),
$name,
$hash,
$info);
}
}
}
foreach ($raw_refs as $raw_ref) {
$type = $raw_ref->getRefType();
$name = $raw_ref->getShortName();
if (isset($map[$type][$name])) {
continue;
}
switch ($type) {
case 'tag':
$suggest[] = csprintf(
'git tag -d %s --',
$name);
break;
case 'branch':
$suggest[] = csprintf(
'git branch -D %s --',
$name);
break;
}
}
if ($suggest) {
echo tsprintf(
"<bg:cyan>** %s **</bg>\n\n%B\n\n",
pht('SUGGESTED ADJUSTMENTS'),
implode("\n", $suggest));
}
echo tsprintf(
"%s\n",
pht('Done.'));
return 0;
}
}