1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-21 22:32:41 +01:00

Improve the logic for identifying ambiguous commits and applying "--revision" to them

Summary: Ref T13546. This is mostly minor cleanup that improves behavior under "--revision".

Test Plan: Ran `arc land --into-empty` and `arc land --into-empty --revision 123` with ambiguous revisions in history to hit both the force and non-force outcomes.

Maniphest Tasks: T13546

Differential Revision: https://secure.phabricator.com/D21325
This commit is contained in:
epriestley 2020-06-07 08:04:22 -07:00
parent 8a53b5a451
commit 709c9cb6fb
2 changed files with 132 additions and 79 deletions

View file

@ -12,6 +12,7 @@ final class ArcanistLandCommit
private $parentCommits;
private $isHeadCommit;
private $isImplicitCommit;
private $relatedRevisionRefs = array();
private $directSymbols = array();
private $indirectSymbols = array();
@ -156,5 +157,14 @@ final class ArcanistLandCommit
return null;
}
public function setRelatedRevisionRefs(array $refs) {
assert_instances_of($refs, 'ArcanistRevisionRef');
$this->relatedRevisionRefs = $refs;
return $this;
}
public function getRelatedRevisionRefs() {
return $this->relatedRevisionRefs;
}
}

View file

@ -835,51 +835,129 @@ abstract class ArcanistLandEngine extends Phobject {
foreach ($commit_map as $commit) {
$hash = $commit->getHash();
$state_ref = $state_refs[$hash];
$revision_refs = $state_ref->getRevisionRefs();
// If we have several possible revisions but one of them matches the
// "--revision" argument, just select it. This is relatively safe and
// reasonable and doesn't need a warning.
if ($force_ref) {
if (count($revision_refs) > 1) {
foreach ($revision_refs as $revision_ref) {
if ($revision_ref->getPHID() === $force_ref->getPHID()) {
$revision_refs = array($revision_ref);
break;
}
}
}
$commit->setRelatedRevisionRefs($revision_refs);
}
// For commits which have exactly one related revision, select it now.
foreach ($commit_map as $commit) {
$revision_refs = $commit->getRelatedRevisionRefs();
if (count($revision_refs) !== 1) {
continue;
}
if (count($revision_refs) === 1) {
$revision_ref = head($revision_refs);
$commit->setExplicitRevisionRef($revision_ref);
}
// If we have a "--revision", select that revision for any commits with
// no known related revisions.
// Also select that revision for any commits which have several possible
// revisions including that revision. This is relatively safe and
// reasonable and doesn't require a warning.
if ($force_ref) {
$force_phid = $force_ref->getPHID();
foreach ($commit_map as $commit) {
if ($commit->getExplicitRevisionRef()) {
continue;
}
if (!$revision_refs) {
$revision_refs = $commit->getRelatedRevisionRefs();
if ($revision_refs) {
$revision_refs = mpull($revision_refs, null, 'getPHID');
if (!isset($revision_refs[$force_phid])) {
continue;
}
}
$commit->setExplicitRevisionRef($force_ref);
}
}
// If we have a "--revision", identify any commits which it is not yet
// selected for. These are commits which are not associated with the
// identified revision but are associated with one or more other revisions.
if ($force_ref) {
$force_phid = $force_ref->getPHID();
$confirm_force = array();
foreach ($commit_map as $key => $commit) {
$revision_ref = $commit->getExplicitRevisionRef();
if (!$revision_ref) {
continue;
}
// TODO: If we have several refs but all but one are abandoned or closed
// or authored by someone else, we could guess what you mean.
if ($revision_ref->getPHID() === $force_phid) {
continue;
}
$confirm_force[] = $commit;
}
if ($confirm_force) {
// TODO: Make this more clear.
// TODO: Show all the commits.
throw new PhutilArgumentUsageException(
pht(
'TODO: You are forcing a revision, but commits are associated '.
'with some other revision. Are you REALLY sure you want to land '.
'ALL these commits wiht a different unrelated revision???'));
}
foreach ($confirm_force as $commit) {
$commit->setExplicitRevisionRef($force_ref);
}
}
// Finally, raise an error if we're left with ambiguous revisions. This
// happens when we have no "--revision" and some commits in the range
// that are associated with more than one revision.
$ambiguous = array();
foreach ($commit_map as $commit) {
if ($commit->getExplicitRevisionRef()) {
continue;
}
if (!$commit->getRelatedRevisionRefs()) {
continue;
}
$ambiguous[] = $commit;
}
if ($ambiguous) {
foreach ($ambiguous as $commit) {
$symbols = $commit->getIndirectSymbols();
$raw_symbols = mpull($symbols, 'getSymbol');
$symbol_list = implode(', ', $raw_symbols);
$display_hash = $this->getDisplayHash($hash);
$revision_refs = $commit->getRelatedRevisionRefs();
// TODO: Include "use 'arc look --type commit abc' to figure out why"
// once that works?
// TODO: We could print all the ambiguous commits.
// TODO: Suggest "--pick" as a remedy once it exists?
echo tsprintf(
"\n%!\n%W\n\n",
pht('AMBIGUOUS REVISION'),
pht(
'The revision associated with commit "%s" (an ancestor of: %s) '.
'is ambiguous. These %s revision(s) are associated with the commit:',
'is ambiguous. These %s revision(s) are associated with the '.
'commit:',
$display_hash,
implode(', ', $raw_symbols),
phutil_count($revision_refs)));
@ -898,45 +976,10 @@ abstract class ArcanistLandEngine extends Phobject {
'selection of a particular revision.',
$display_hash));
}
if ($force_ref) {
$phid_map = array();
foreach ($commit_map as $commit) {
$explicit_ref = $commit->getExplicitRevisionRef();
if ($explicit_ref) {
$revision_phid = $explicit_ref->getPHID();
$phid_map[$revision_phid] = $revision_phid;
}
}
$force_phid = $force_ref->getPHID();
// If none of the commits have a revision, forcing the revision is
// reasonable and we don't need to confirm it.
// If some of the commits have a revision, but it's the same as the
// revision we're forcing, forcing the revision is also reasonable.
// Discard the revision we're trying to force, then check if there's
// anything left. If some of the commits have a different revision,
// make sure the user is really doing what they expect.
unset($phid_map[$force_phid]);
if ($phid_map) {
// TODO: Make this more clear.
throw new PhutilArgumentUsageException(
pht(
'TODO: You are forcing a revision, but commits are associated '.
'with some other revision. Are you REALLY sure you want to land '.
'ALL these commits wiht a different unrelated revision???'));
}
foreach ($commit_map as $commit) {
$commit->setExplicitRevisionRef($force_ref);
}
}
// NOTE: We may exit this method with commits that are still unassociated.
// These will be handled later by the "implicit commits" mechanism.
}
final protected function getDisplayHash($hash) {