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 $parentCommits;
private $isHeadCommit; private $isHeadCommit;
private $isImplicitCommit; private $isImplicitCommit;
private $relatedRevisionRefs = array();
private $directSymbols = array(); private $directSymbols = array();
private $indirectSymbols = array(); private $indirectSymbols = array();
@ -156,5 +157,14 @@ final class ArcanistLandCommit
return null; 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,96 +835,76 @@ abstract class ArcanistLandEngine extends Phobject {
foreach ($commit_map as $commit) { foreach ($commit_map as $commit) {
$hash = $commit->getHash(); $hash = $commit->getHash();
$state_ref = $state_refs[$hash]; $state_ref = $state_refs[$hash];
$revision_refs = $state_ref->getRevisionRefs(); $revision_refs = $state_ref->getRevisionRefs();
$commit->setRelatedRevisionRefs($revision_refs);
// 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;
}
}
}
}
if (count($revision_refs) === 1) {
$revision_ref = head($revision_refs);
$commit->setExplicitRevisionRef($revision_ref);
continue;
}
if (!$revision_refs) {
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.
$symbols = $commit->getIndirectSymbols();
$raw_symbols = mpull($symbols, 'getSymbol');
$symbol_list = implode(', ', $raw_symbols);
$display_hash = $this->getDisplayHash($hash);
// TODO: Include "use 'arc look --type commit abc' to figure out why"
// once that works?
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:',
$display_hash,
implode(', ', $raw_symbols),
phutil_count($revision_refs)));
foreach ($revision_refs as $revision_ref) {
echo tsprintf(
'%s',
$revision_ref->newDisplayRef());
}
echo tsprintf("\n");
throw new PhutilArgumentUsageException(
pht(
'Revision for commit "%s" is ambiguous. Use "--revision" to force '.
'selection of a particular revision.',
$display_hash));
} }
if ($force_ref) { // For commits which have exactly one related revision, select it now.
$phid_map = array();
foreach ($commit_map as $commit) { foreach ($commit_map as $commit) {
$explicit_ref = $commit->getExplicitRevisionRef(); $revision_refs = $commit->getRelatedRevisionRefs();
if ($explicit_ref) {
$revision_phid = $explicit_ref->getPHID(); if (count($revision_refs) !== 1) {
$phid_map[$revision_phid] = $revision_phid; continue;
}
} }
$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;
}
$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(); $force_phid = $force_ref->getPHID();
// If none of the commits have a revision, forcing the revision is $confirm_force = array();
// reasonable and we don't need to confirm it. foreach ($commit_map as $key => $commit) {
$revision_ref = $commit->getExplicitRevisionRef();
// If some of the commits have a revision, but it's the same as the if (!$revision_ref) {
// revision we're forcing, forcing the revision is also reasonable. continue;
}
// Discard the revision we're trying to force, then check if there's if ($revision_ref->getPHID() === $force_phid) {
// anything left. If some of the commits have a different revision, continue;
// make sure the user is really doing what they expect. }
unset($phid_map[$force_phid]); $confirm_force[] = $commit;
}
if ($confirm_force) {
if ($phid_map) {
// TODO: Make this more clear. // TODO: Make this more clear.
// TODO: Show all the commits.
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
@ -933,10 +913,73 @@ abstract class ArcanistLandEngine extends Phobject {
'ALL these commits wiht a different unrelated revision???')); 'ALL these commits wiht a different unrelated revision???'));
} }
foreach ($commit_map as $commit) { foreach ($confirm_force as $commit) {
$commit->setExplicitRevisionRef($force_ref); $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:',
$display_hash,
implode(', ', $raw_symbols),
phutil_count($revision_refs)));
foreach ($revision_refs as $revision_ref) {
echo tsprintf(
'%s',
$revision_ref->newDisplayRef());
}
echo tsprintf("\n");
throw new PhutilArgumentUsageException(
pht(
'Revision for commit "%s" is ambiguous. Use "--revision" to force '.
'selection of a particular revision.',
$display_hash));
}
}
// 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) { final protected function getDisplayHash($hash) {