Changeset View
Changeset View
Standalone View
Standalone View
src/repository/api/ArcanistMercurialAPI.php
| <?php | <?php | ||||
| /** | /** | ||||
| * Interfaces with the Mercurial working copies. | * Interfaces with the Mercurial working copies. | ||||
| * | |||||
| * @group workingcopy | |||||
| */ | */ | ||||
| final class ArcanistMercurialAPI extends ArcanistRepositoryAPI { | final class ArcanistMercurialAPI extends ArcanistRepositoryAPI { | ||||
| private $branch; | private $branch; | ||||
| private $localCommitInfo; | private $localCommitInfo; | ||||
| private $rawDiffCache = array(); | private $rawDiffCache = array(); | ||||
| private $supportsRebase; | private $supportsRebase; | ||||
| private $supportsPhases; | private $supportsPhases; | ||||
| protected function buildLocalFuture(array $argv) { | protected function buildLocalFuture(array $argv) { | ||||
| // Mercurial has a "defaults" feature which basically breaks automation by | // Mercurial has a "defaults" feature which basically breaks automation by | ||||
| // allowing the user to add random flags to any command. This feature is | // allowing the user to add random flags to any command. This feature is | ||||
| // "deprecated" and "a bad idea" that you should "forget ... existed" | // "deprecated" and "a bad idea" that you should "forget ... existed" | ||||
| // according to project lead Matt Mackall: | // according to project lead Matt Mackall: | ||||
| // | // | ||||
| // http://markmail.org/message/hl3d6eprubmkkqh5 | // http://markmail.org/message/hl3d6eprubmkkqh5 | ||||
| // | // | ||||
| // There is an HGPLAIN environmental variable which enables "plain mode" | // There is an HGPLAIN environmental variable which enables "plain mode" | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | final class ArcanistMercurialAPI extends ArcanistRepositoryAPI { | ||||
| public function getHashFromFromSVNRevisionNumber($revision_id) { | public function getHashFromFromSVNRevisionNumber($revision_id) { | ||||
| $matches = array(); | $matches = array(); | ||||
| $string = hgsprintf('svnrev(%s)', $revision_id); | $string = hgsprintf('svnrev(%s)', $revision_id); | ||||
| list($stdout) = $this->execxLocal( | list($stdout) = $this->execxLocal( | ||||
| 'log -l 1 --template %s -r %s --', | 'log -l 1 --template %s -r %s --', | ||||
| '{node}', | '{node}', | ||||
| $string); | $string); | ||||
| if (!$stdout) { | if (!$stdout) { | ||||
| throw new ArcanistUsageException("Cannot find the HG equivalent " | throw new ArcanistUsageException( | ||||
| ."of {$revision_id} given."); | "Cannot find the HG equivalent of {$revision_id} given."); | ||||
| } | } | ||||
| return $stdout; | return $stdout; | ||||
| } | } | ||||
| public function getSVNRevisionNumberFromHash($hash) { | public function getSVNRevisionNumberFromHash($hash) { | ||||
| $matches = array(); | $matches = array(); | ||||
| list($stdout) = $this->execxLocal( | list($stdout) = $this->execxLocal( | ||||
| 'log -r %s --template {svnrev}', $hash); | 'log -r %s --template {svnrev}', $hash); | ||||
| if (!$stdout) { | if (!$stdout) { | ||||
| throw new ArcanistUsageException("Cannot find the SVN equivalent " | throw new ArcanistUsageException( | ||||
| ."of {$hash} given."); | "Cannot find the SVN equivalent of {$hash} given."); | ||||
| } | } | ||||
| return $stdout; | return $stdout; | ||||
| } | } | ||||
| public function getSourceControlPath() { | public function getSourceControlPath() { | ||||
| return '/'; | return '/'; | ||||
| } | } | ||||
| Show All 16 Lines | if ($symbolic_commit !== null) { | ||||
| hgsprintf('ancestor(%s,.)', $symbolic_commit)); | hgsprintf('ancestor(%s,.)', $symbolic_commit)); | ||||
| } catch (Exception $ex) { | } catch (Exception $ex) { | ||||
| // Try it as a revset instead of a commit id | // Try it as a revset instead of a commit id | ||||
| try { | try { | ||||
| $commit = $this->getCanonicalRevisionName( | $commit = $this->getCanonicalRevisionName( | ||||
| hgsprintf('ancestor(%R,.)', $symbolic_commit)); | hgsprintf('ancestor(%R,.)', $symbolic_commit)); | ||||
| } catch (Exception $ex) { | } catch (Exception $ex) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| "Commit '{$symbolic_commit}' is not a valid Mercurial commit ". | "Commit '{$symbolic_commit}' is not a valid Mercurial commit ". | ||||
| "identifier."); | "identifier."); | ||||
| } | } | ||||
| } | } | ||||
| $this->setBaseCommitExplanation('it is the greatest common ancestor of '. | $this->setBaseCommitExplanation( | ||||
| 'the working directory and the commit you specified explicitly.'); | 'it is the greatest common ancestor of the working directory '. | ||||
| 'and the commit you specified explicitly.'); | |||||
| return $commit; | return $commit; | ||||
| } | } | ||||
| if ($this->getBaseCommitArgumentRules() || | if ($this->getBaseCommitArgumentRules() || | ||||
| $this->getConfigurationManager()->getConfigFromAnySource('base')) { | $this->getConfigurationManager()->getConfigFromAnySource('base')) { | ||||
| $base = $this->resolveBaseCommit(); | $base = $this->resolveBaseCommit(); | ||||
| if (!$base) { | if (!$base) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| Show All 10 Lines | protected function buildBaseCommit($symbolic_commit) { | ||||
| if ($this->supportsPhases()) { | if ($this->supportsPhases()) { | ||||
| list($err, $stdout) = $this->execManualLocal( | list($err, $stdout) = $this->execManualLocal( | ||||
| 'log --branch %s -r %s --style default', | 'log --branch %s -r %s --style default', | ||||
| $this->getBranchName(), | $this->getBranchName(), | ||||
| 'draft()'); | 'draft()'); | ||||
| } else { | } else { | ||||
| list($err, $stdout) = $this->execManualLocal( | list($err, $stdout) = $this->execManualLocal( | ||||
| 'outgoing --branch %s --style default', | 'outgoing --branch %s --style default', | ||||
| $this->getBranchName()); | $this->getBranchName()); | ||||
| } | } | ||||
| if (!$err) { | if (!$err) { | ||||
| $logs = ArcanistMercurialParser::parseMercurialLog($stdout); | $logs = ArcanistMercurialParser::parseMercurialLog($stdout); | ||||
| } else { | } else { | ||||
| // Mercurial (in some versions?) raises an error when there's nothing | // Mercurial (in some versions?) raises an error when there's nothing | ||||
| // outgoing. | // outgoing. | ||||
| $logs = array(); | $logs = array(); | ||||
| ▲ Show 20 Lines • Show All 286 Lines • ▼ Show 20 Lines | public function getBulkCurrentFileData($paths) { | ||||
| return $this->getBulkFileDataAtRevision( | return $this->getBulkFileDataAtRevision( | ||||
| $paths, | $paths, | ||||
| $this->getWorkingCopyRevision()); | $this->getWorkingCopyRevision()); | ||||
| } | } | ||||
| private function getBulkFileDataAtRevision($paths, $revision) { | private function getBulkFileDataAtRevision($paths, $revision) { | ||||
| // Calling 'hg cat' on each file individually is slow (1 second per file | // Calling 'hg cat' on each file individually is slow (1 second per file | ||||
| // on a large repo) because mercurial has to decompress and parse the | // on a large repo) because mercurial has to decompress and parse the | ||||
| // entire manifest every time. Do it in one large batch instead. | // entire manifest every time. Do it in one large batch instead. | ||||
| // hg cat will write the file data to files in a temp directory | // hg cat will write the file data to files in a temp directory | ||||
| $tmpdir = Filesystem::createTemporaryDirectory(); | $tmpdir = Filesystem::createTemporaryDirectory(); | ||||
| // Mercurial doesn't create the directories for us :( | // Mercurial doesn't create the directories for us :( | ||||
| foreach ($paths as $path) { | foreach ($paths as $path) { | ||||
| $tmppath = $tmpdir.'/'.$path; | $tmppath = $tmpdir.'/'.$path; | ||||
| Filesystem::createDirectory(dirname($tmppath), 0755, true); | Filesystem::createDirectory(dirname($tmppath), 0755, true); | ||||
| ▲ Show 20 Lines • Show All 250 Lines • ▼ Show 20 Lines | final class ArcanistMercurialAPI extends ArcanistRepositoryAPI { | ||||
| public function getAuthor() { | public function getAuthor() { | ||||
| $full_author = $this->getMercurialConfig('ui.username'); | $full_author = $this->getMercurialConfig('ui.username'); | ||||
| list($author, $author_email) = $this->parseFullAuthor($full_author); | list($author, $author_email) = $this->parseFullAuthor($full_author); | ||||
| return $author; | return $author; | ||||
| } | } | ||||
| /** | /** | ||||
| * Parse the Mercurial author field | * Parse the Mercurial author field. | ||||
| * | * | ||||
| * Not everyone enters their email address as a part of the username | * Not everyone enters their email address as a part of the username | ||||
| * field. Try to make it work when it's obvious | * field. Try to make it work when it's obvious. | ||||
| * | * | ||||
| * @param string $full_author | * @param string $full_author | ||||
| * @return array | * @return array | ||||
| */ | */ | ||||
| protected function parseFullAuthor($full_author) { | protected function parseFullAuthor($full_author) { | ||||
| if (strpos($full_author, '@') === false) { | if (strpos($full_author, '@') === false) { | ||||
| $author = $full_author; | $author = $full_author; | ||||
| $author_email = null; | $author_email = null; | ||||
| Show All 11 Lines | $this->execxLocal( | ||||
| 'addremove -- %Ls', | 'addremove -- %Ls', | ||||
| $paths); | $paths); | ||||
| $this->reloadWorkingCopy(); | $this->reloadWorkingCopy(); | ||||
| } | } | ||||
| public function doCommit($message) { | public function doCommit($message) { | ||||
| $tmp_file = new TempFile(); | $tmp_file = new TempFile(); | ||||
| Filesystem::writeFile($tmp_file, $message); | Filesystem::writeFile($tmp_file, $message); | ||||
| $this->execxLocal( | $this->execxLocal('commit -l %s', $tmp_file); | ||||
| 'commit -l %s', | |||||
| $tmp_file); | |||||
| $this->reloadWorkingCopy(); | $this->reloadWorkingCopy(); | ||||
| } | } | ||||
| public function amendCommit($message = null) { | public function amendCommit($message = null) { | ||||
| if ($message === null) { | if ($message === null) { | ||||
| $message = $this->getCommitMessage('.'); | $message = $this->getCommitMessage('.'); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 319 Lines • Show Last 20 Lines | |||||