in src/workflow/ArcanistLandWorkflow.php [726:962]
private function findRevision() {
$repository_api = $this->getRepositoryAPI();
$this->parseBaseCommitArgument(array($this->ontoRemoteBranch));
$revision_id = $this->getArgument('revision');
if ($revision_id) {
$revision_id = $this->normalizeRevisionID($revision_id);
$revisions = $this->getConduit()->callMethodSynchronous(
'differential.query',
array(
'ids' => array($revision_id),
));
if (!$revisions) {
throw new ArcanistUsageException(pht(
"No such revision '%s'!",
"D{$revision_id}"));
}
} else {
$revisions = $repository_api->loadWorkingCopyDifferentialRevisions(
$this->getConduit(),
array());
}
if (!count($revisions)) {
throw new ArcanistUsageException(pht(
"arc can not identify which revision exists on %s '%s'. Update the ".
"revision with recent changes to synchronize the %s name and hashes, ".
"or use '%s' to amend the commit message at HEAD, or use ".
"'%s' to select a revision explicitly.",
$this->branchType,
$this->branch,
$this->branchType,
'arc amend',
'--revision <id>'));
} else if (count($revisions) > 1) {
switch ($this->branchType) {
case self::REFTYPE_BOOKMARK:
$message = pht(
"There are multiple revisions on feature bookmark '%s' which are ".
"not present on '%s':\n\n".
"%s\n".
'Separate these revisions onto different bookmarks, or use '.
'--revision <id> to use the commit message from <id> '.
'and land them all.',
$this->branch,
$this->onto,
$this->renderRevisionList($revisions));
break;
case self::REFTYPE_BRANCH:
default:
$message = pht(
"There are multiple revisions on feature branch '%s' which are ".
"not present on '%s':\n\n".
"%s\n".
'If you want all these diffs to be landed to Submit Queue atomically, '.
"use arc stack.\n Alternatively, you can ".
'separate these revisions onto different branches, or use '.
'--revision <id> to use the commit message from <id> '.
'and land them all.',
$this->branch,
$this->onto,
$this->renderRevisionList($revisions));
break;
}
throw new ArcanistUsageException($message);
}
$this->revision = head($revisions);
$rev_status = $this->revision['status'];
$rev_id = $this->revision['id'];
$rev_title = $this->revision['title'];
$rev_auxiliary = idx($this->revision, 'auxiliary', array());
$full_name = pht('D%d: %s', $rev_id, $rev_title);
if ($this->revision['authorPHID'] != $this->getUserPHID()) {
$other_author = $this->getConduit()->callMethodSynchronous(
'user.query',
array(
'phids' => array($this->revision['authorPHID']),
));
$other_author = ipull($other_author, 'userName', 'phid');
$other_author = $other_author[$this->revision['authorPHID']];
$ok = phutil_console_confirm(pht(
"This %s has revision '%s' but you are not the author. Land this ".
"revision by %s?",
$this->branchType,
$full_name,
$other_author));
if (!$ok) {
throw new ArcanistUserAbortException();
}
}
// UBER CODE
$uber_prevent_unaccepted_changes = $this->getConfigFromAnySource(
'uber.land.prevent-unaccepted-changes',
false);
if ($uber_prevent_unaccepted_changes && $rev_status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
throw new ArcanistUsageException(
pht("Revision '%s' has not been accepted.", "D{$rev_id}: {$rev_title}"));
}
// UBER CODE END
$state_warning = null;
$state_header = null;
if ($rev_status == ArcanistDifferentialRevisionStatus::CHANGES_PLANNED) {
$state_header = pht('REVISION HAS CHANGES PLANNED');
$state_warning = pht(
'The revision you are landing ("%s") is currently in the "%s" state, '.
'indicating that you expect to revise it before moving forward.'.
"\n\n".
'Normally, you should resubmit it for review and wait until it is '.
'"%s" by reviewers before you continue.'.
"\n\n".
'To resubmit the revision for review, either: update the revision '.
'with revised changes; or use "Request Review" from the web interface.',
$full_name,
pht('Changes Planned'),
pht('Accepted'));
} else if ($rev_status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
$state_header = pht('REVISION HAS NOT BEEN ACCEPTED');
$state_warning = pht(
'The revision you are landing ("%s") has not been "%s" by reviewers.',
$full_name,
pht('Accepted'));
}
// UBER CODE
// Check if all paths were reviewed by reviewers listed on METADATA files.
// If this check throws an exception - silently pass.
$this->uberMetadataReviewersCheck($rev_id);
// UBER CODE END
if ($state_warning !== null) {
$prompt = pht('Land revision in the wrong state?');
id(new PhutilConsoleBlock())
->addParagraph(tsprintf('<bg:yellow>** %s **</bg>', $state_header))
->addParagraph(tsprintf('%B', $state_warning))
->draw();
$ok = phutil_console_confirm($prompt);
if (!$ok) {
throw new ArcanistUserAbortException();
}
}
$uber_review_check_enabled = $this->getConfigFromAnySource(
'uber.land.review-check',
false);
if ($uber_review_check_enabled) {
if (!$repository_api instanceof ArcanistGitAPI) {
throw new ArcanistUsageException(pht(
"'%s' is only supported for GIT repositories.",
'uber.land.review-check'));
}
$local_diff = $this->normalizeDiff(
$repository_api->getFullGitDiff(
$repository_api->getBaseCommit(),
$repository_api->getHeadCommit()));
$reviewed_diff = $this->normalizeDiff(
$this->getConduit()->callMethodSynchronous(
'differential.getrawdiff',
array('diffID' => head($this->revision['diffs']))));
if ($local_diff !== $reviewed_diff) {
$ok = phutil_console_confirm(pht(
"Your working copy changes do not match diff submitted for review. ".
"Continue anyway?"));
if (!$ok) {
throw new ArcanistUserAbortException();
}
}
}
if ($rev_auxiliary) {
$phids = idx($rev_auxiliary, 'phabricator:depends-on', array());
if ($phids) {
$dep_on_revs = $this->getConduit()->callMethodSynchronous(
'differential.query',
array(
'phids' => $phids,
'status' => 'status-open',
));
$open_dep_revs = array();
foreach ($dep_on_revs as $dep_on_rev) {
$dep_on_rev_id = $dep_on_rev['id'];
$dep_on_rev_title = $dep_on_rev['title'];
$dep_on_rev_status = $dep_on_rev['status'];
$open_dep_revs[$dep_on_rev_id] = $dep_on_rev_title;
}
if (!empty($open_dep_revs)) {
$open_revs = array();
foreach ($open_dep_revs as $id => $title) {
$open_revs[] = ' - D'.$id.': '.$title;
}
$open_revs = implode("\n", $open_revs);
echo pht(
"Revision '%s' depends on open revisions:\n\n%s",
"D{$rev_id}: {$rev_title}",
$open_revs);
$ok = phutil_console_confirm(pht('Continue anyway?'));
if (!$ok) {
throw new ArcanistUserAbortException();
}
}
}
}
$message = $this->getConduit()->callMethodSynchronous(
'differential.getcommitmessage',
array(
'revision_id' => $rev_id,
));
$this->messageFile = new TempFile();
Filesystem::writeFile($this->messageFile, $message);
echo pht(
"Landing revision '%s'...",
"D{$rev_id}: {$rev_title}")."\n";
$diff_phid = idx($this->revision, 'activeDiffPHID');
if ($diff_phid) {
$this->checkForBuildables($diff_phid);
}
}