Changeset View
Changeset View
Standalone View
Standalone View
src/land/engine/ArcanistMercurialLandEngine.php
<?php | <?php | ||||
final class ArcanistMercurialLandEngine | final class ArcanistMercurialLandEngine | ||||
extends ArcanistLandEngine { | extends ArcanistLandEngine { | ||||
private $ontoBranchMarker; | private $ontoBranchMarker; | ||||
private $ontoMarkers; | private $ontoMarkers; | ||||
private $rebasedCommitMap = array(); | |||||
private $rebasedActiveCommit; | private $rebasedActiveCommit; | ||||
protected function getDefaultSymbols() { | protected function getDefaultSymbols() { | ||||
$api = $this->getRepositoryAPI(); | $api = $this->getRepositoryAPI(); | ||||
$log = $this->getLogEngine(); | $log = $this->getLogEngine(); | ||||
// TODO: In Mercurial, you normally can not create a branch and a bookmark | // TODO: In Mercurial, you normally can not create a branch and a bookmark | ||||
// with the same name. However, you can fetch a branch or bookmark from | // with the same name. However, you can fetch a branch or bookmark from | ||||
▲ Show 20 Lines • Show All 749 Lines • ▼ Show 20 Lines | foreach ($symbols as $symbol) { | ||||
} | } | ||||
} | } | ||||
return $this->confirmCommits($into_commit, $symbols, $commit_map); | return $this->confirmCommits($into_commit, $symbols, $commit_map); | ||||
} | } | ||||
protected function executeMerge(ArcanistLandCommitSet $set, $into_commit) { | protected function executeMerge(ArcanistLandCommitSet $set, $into_commit) { | ||||
$api = $this->getRepositoryAPI(); | $api = $this->getRepositoryAPI(); | ||||
$log = $this->getLogEngine(); | |||||
if ($this->getStrategy() !== 'squash') { | if ($this->getStrategy() !== 'squash') { | ||||
$log->writeError( | |||||
pht('TODO'), | |||||
pht('Merge strategy is not yet supported on Mercurial repositories.')); | |||||
throw new Exception(pht('TODO: Support merge strategies')); | throw new Exception(pht('TODO: Support merge strategies')); | ||||
} | } | ||||
// See PHI1808. When we "hg rebase ..." below, Mercurial will move | // See PHI1808. When we "hg rebase ..." below, Mercurial will move | ||||
// bookmarks which point at the old commit range to point at the rebased | // bookmarks which point at the old commit range to point at the rebased | ||||
// commit. This is somewhat surprising and we don't want this to happen: | // commit. This is somewhat surprising and we don't want this to happen: | ||||
// save the old bookmark state so we can put the bookmarks back before | // save the old bookmark state so we can put the bookmarks back before | ||||
// we continue. | // we continue. | ||||
$bookmark_refs = $api->newMarkerRefQuery() | $bookmark_refs = $api->newMarkerRefQuery() | ||||
->withMarkerTypes( | ->withMarkerTypes(array(ArcanistMarkerRef::TYPE_BOOKMARK)) | ||||
array( | |||||
ArcanistMarkerRef::TYPE_BOOKMARK, | |||||
)) | |||||
->execute(); | ->execute(); | ||||
// TODO: Add a Mercurial version check requiring 2.1.1 or newer. | // TODO: Add a Mercurial version check requiring 2.1.1 or newer. | ||||
$api->execxLocal( | $api->execxLocal( | ||||
'update --rev %s', | 'update --rev %s', | ||||
hgsprintf('%s', $into_commit)); | hgsprintf('%s', $into_commit)); | ||||
Show All 39 Lines | try { | ||||
$future = $api->execFutureLocalWithExtension( | $future = $api->execFutureLocalWithExtension( | ||||
'rebase', | 'rebase', | ||||
'rebase %Ls', | 'rebase %Ls', | ||||
$argv); | $argv); | ||||
$future->write($commit_message); | $future->write($commit_message); | ||||
$future->resolvex(); | $future->resolvex(); | ||||
} catch (CommandException $ex) { | } catch (CommandException $ex) { | ||||
$log->writeError( | |||||
pht('REBASE CONFLICT'), | |||||
pht( | |||||
'Commits for %s do not rebase cleanly onto %s. Manually rebase and '. | |||||
'resolve conflicts before landing again.', | |||||
$revision_ref->getRefDisplayName(), | |||||
$api->getDisplayHash($into_commit))); | |||||
cspeckmim: I saw this in `ArcanistGitLandEngine` and felt a little jealous
{F9714969} | |||||
// Aborting the rebase should restore the same state prior to running the | // Aborting the rebase should restore the same state prior to running the | ||||
// rebase command. | // rebase command. | ||||
$api->execManualLocalWithExtension( | $api->execManualLocalWithExtension( | ||||
'rebase', | 'rebase', | ||||
'rebase --abort'); | 'rebase --abort'); | ||||
throw $ex; | throw $ex; | ||||
} | } | ||||
Show All 17 Lines | foreach ($bookmark_refs as $bookmark_ref) { | ||||
'bookmark --force --rev %s -- %s', | 'bookmark --force --rev %s -- %s', | ||||
$bookmark_hash, | $bookmark_hash, | ||||
$bookmark_ref->getName()); | $bookmark_ref->getName()); | ||||
} | } | ||||
list($stdout) = $api->execxLocal('log --rev tip --template %s', '{node}'); | list($stdout) = $api->execxLocal('log --rev tip --template %s', '{node}'); | ||||
$new_cursor = trim($stdout); | $new_cursor = trim($stdout); | ||||
foreach ($set->getCommits() as $commit) { | |||||
$this->rebasedCommitMap[$commit->getHash()] = $new_cursor; | |||||
} | |||||
// If any of the commits that were rebased was the active commit before the | // If any of the commits that were rebased was the active commit before the | ||||
// workflow started, track the new commit so it can be used as the working | // workflow started, track the new commit so it can be used as the working | ||||
// directory after the land has succeeded. | // directory after the land has succeeded. | ||||
if (isset($obsolete_map[$this->getLocalState()->getLocalCommit()])) { | if (isset($obsolete_map[$this->getLocalState()->getLocalCommit()])) { | ||||
$this->rebasedActiveCommit = $new_cursor; | $this->rebasedActiveCommit = $new_cursor; | ||||
} | } | ||||
cspeckmimAuthorUnsubmitted Done Inline ActionsNow that the entire commit translation is being tracked I think I can remove $obsolete_map here along with $this->rebasedActiveCommit. I'll look into this real quick. cspeckmim: Now that the entire commit translation is being tracked I think I can remove `$obsolete_map`… | |||||
return $new_cursor; | return $new_cursor; | ||||
} | } | ||||
protected function pushChange($into_commit) { | protected function pushChange($into_commit) { | ||||
$api = $this->getRepositoryAPI(); | $api = $this->getRepositoryAPI(); | ||||
list($head, $body, $tail_pass, $tail_fail) = $this->newPushCommands( | list($head, $body, $tail_pass, $tail_fail) = $this->newPushCommands( | ||||
▲ Show 20 Lines • Show All 156 Lines • ▼ Show 20 Lines | protected function cascadeState(ArcanistLandCommitSet $set, $into_commit) { | ||||
// This has no effect when we're executing a merge strategy. | // This has no effect when we're executing a merge strategy. | ||||
if (!$this->isSquashStrategy()) { | if (!$this->isSquashStrategy()) { | ||||
return; | return; | ||||
} | } | ||||
$old_commit = last($set->getCommits())->getHash(); | $old_commit = last($set->getCommits())->getHash(); | ||||
$new_commit = $into_commit; | $new_commit = $into_commit; | ||||
// Rebase the child commits onto the commit which $old_commit was rebased | |||||
// into. The $into_commit will be the last set's final rebased commit but | |||||
// this set may be an intermediate set that was landed. | |||||
if (isset($this->rebasedCommitMap[$old_commit])) { | |||||
$new_commit = $this->rebasedCommitMap[$old_commit]; | |||||
} | |||||
list($output) = $api->execxLocal( | list($output) = $api->execxLocal( | ||||
'log --rev %s --template %s', | 'log --rev %s --template %s', | ||||
hgsprintf('children(%s)', $old_commit), | hgsprintf('children(%s)', $old_commit), | ||||
'{node}\n'); | '{node}\n'); | ||||
$child_hashes = phutil_split_lines($output, false); | $child_hashes = phutil_split_lines($output, false); | ||||
foreach ($child_hashes as $child_hash) { | foreach ($child_hashes as $child_hash) { | ||||
if (!strlen($child_hash)) { | if (!strlen($child_hash)) { | ||||
continue; | continue; | ||||
} | } | ||||
// TODO: If the only heads which are descendants of this child will | // If this child was rebased as part of landing then it shouldn't be | ||||
// be deleted, we can skip this rebase? | // rebased now. Doing so will result in rebase conflicts. | ||||
if (isset($this->rebasedCommitMap[$child_hash])) { | |||||
continue; | |||||
} | |||||
try { | try { | ||||
$api->execxLocalWithExtension( | $api->execxLocalWithExtension( | ||||
'rebase', | 'rebase', | ||||
'rebase --source %s --dest %s --keep --keepbranches', | 'rebase --source %s --dest %s --keep --keepbranches', | ||||
$child_hash, | $child_hash, | ||||
$new_commit); | $new_commit); | ||||
} catch (CommandException $ex) { | } catch (CommandException $ex) { | ||||
$log->writeError( | |||||
pht('REBASE CONFLICT'), | |||||
pht( | |||||
'Failed to rebase %s cleanly onto %s. Manually rebase and '. | |||||
'resolve conflicts.', | |||||
$api->getDisplayHash($child_hash), | |||||
$api->getDisplayHash($new_commit))); | |||||
// Aborting the rebase should restore the same state prior to running | // Aborting the rebase should restore the same state prior to running | ||||
// the rebase command. | // the rebase command. | ||||
$api->execManualLocalWithExtension( | $api->execManualLocalWithExtension( | ||||
'rebase', | 'rebase', | ||||
'rebase --abort'); | 'rebase --abort'); | ||||
throw $ex; | throw $ex; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 199 Lines • Show Last 20 Lines |
I saw this in ArcanistGitLandEngine and felt a little jealous