public function testDetectSlowCopiedCode() { // This tests that the detector has a reasonable runtime when a diff // contains a very large number of identical lines. See T5041. $parser = new ArcanistDiffParser(); $line = str_repeat('x', 60); $oline = '-' . $line . "\n"; $nline = '+' . $line . "\n"; $n = 1000; $oblock = str_repeat($oline, $n); $nblock = str_repeat($nline, $n); $raw_diff = <<<EODIFF diff --git a/dst b/dst new file mode 100644 index 0000000..1234567 --- /dev/null +++ b/dst @@ -0,0 +1,{$n} @@ {$nblock} diff --git a/src b/src deleted file mode 100644 index 123457..0000000 --- a/src +++ /dev/null @@ -1,{$n} +0,0 @@ {$oblock} EODIFF; $diff = DifferentialDiff::newFromRawChanges($parser->parseDiff($raw_diff)); $this->assertTrue(true); }
private function buildChangesetParser($type, $data, $file) { $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($data); $diff = DifferentialDiff::newFromRawChanges($changes); if (count($diff->getChangesets()) !== 1) { throw new Exception("Expected one changeset: {$file}"); } $changeset = head($diff->getChangesets()); $engine = new PhabricatorMarkupEngine(); $engine->setViewer(new PhabricatorUser()); $cparser = new DifferentialChangesetParser(); $cparser->setDisableCache(true); $cparser->setChangeset($changeset); $cparser->setMarkupEngine($engine); if ($type == 'one') { $cparser->setRenderer(new DifferentialChangesetOneUpTestRenderer()); } else { if ($type == 'two') { $cparser->setRenderer(new DifferentialChangesetTwoUpTestRenderer()); } else { throw new Exception("Unknown renderer type '{$type}'!"); } } return $cparser; }
protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $raw_diff = $request->getValue('diff'); $repository_phid = $request->getValue('repositoryPHID'); if ($repository_phid) { $repository = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withPHIDs(array($repository_phid))->executeOne(); if (!$repository) { throw new Exception(pht('No such repository "%s"!', $repository_phid)); } } else { $repository = null; } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($changes); $diff->setLintStatus(DifferentialLintStatus::LINT_SKIP); $diff->setUnitStatus(DifferentialUnitStatus::UNIT_SKIP); $diff->setAuthorPHID($viewer->getPHID()); $diff->setCreationMethod('web'); if ($repository) { $diff->setRepositoryPHID($repository->getPHID()); } id(new DifferentialDiffEditor())->setActor($viewer)->setContentSource(PhabricatorContentSource::newFromConduitRequest($request))->saveDiff($diff); return $this->buildDiffInfoDictionary($diff); }
public function processRequest() { $request = $this->getRequest(); if ($request->isFormPost()) { $parser = new ArcanistDiffParser(); $diff = null; try { $diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']); } catch (Exception $ex) { $diff = $request->getStr('diff'); } $changes = $parser->parseDiff($diff); $diff = DifferentialDiff::newFromRawChanges($changes); $diff->setLintStatus(DifferentialLintStatus::LINT_SKIP); $diff->setUnitStatus(DifferentialLintStatus::LINT_SKIP); $diff->setAuthorPHID($request->getUser()->getPHID()); $diff->setCreationMethod('web'); $diff->save(); return id(new AphrontRedirectResponse())->setURI('/differential/diff/' . $diff->getID() . '/'); } $form = new AphrontFormView(); $arcanist_href = PhabricatorEnv::getDoclink('article/Arcanist_User_Guide.html'); $arcanist_link = phutil_render_tag('a', array('href' => $arcanist_href, 'target' => '_blank'), 'Arcanist'); $form->setAction('/differential/diff/create/')->setEncType('multipart/form-data')->setUser($request->getUser())->appendChild('<p class="aphront-form-instructions">The best way to create a ' . "Differential diff is by using {$arcanist_link}, but you " . 'can also just paste a diff (e.g., from <tt>svn diff</tt> or ' . '<tt>git diff</tt>) into this box or upload it as a file if you ' . 'really want.</p>')->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Raw Diff')->setName('diff')->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL))->appendChild(id(new AphrontFormFileControl())->setLabel('Raw Diff from file')->setName('diff-file'))->appendChild(id(new AphrontFormSubmitControl())->setValue("Create Diff »")); $panel = new AphrontPanelView(); $panel->setHeader('Create New Diff'); $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_FORM); return $this->buildStandardPageResponse($panel, array('title' => 'Create Diff', 'tab' => 'create')); }
protected function executeQuery() { $drequest = $this->getRequest(); $repository = $drequest->getRepository(); $effective_commit = $this->getEffectiveCommit(); if (!$effective_commit) { return null; } // TODO: This side effect is kind of skethcy. $drequest->setCommit($effective_commit); $path = $drequest->getPath(); list($raw_diff) = $repository->execxLocalCommand('diff -U %d --git --change %s -- %s', 65535, $effective_commit, $path); $parser = new ArcanistDiffParser(); $try_encoding = $repository->getDetail('encoding'); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } $parser->setDetectBinaryFiles(true); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($changes); $changesets = $diff->getChangesets(); $changeset = reset($changesets); $this->renderingReference = $drequest->getBranchURIComponent($drequest->getBranch()) . $drequest->getPath() . ';' . $drequest->getCommit(); return $changeset; }
protected function executeQuery() { $drequest = $this->getRequest(); $repository = $drequest->getRepository(); $effective_commit = $this->getEffectiveCommit(); if (!$effective_commit) { return null; } // TODO: This side effect is kind of skethcy. $drequest->setCommit($effective_commit); $query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); $raw_diff = $query->loadRawDiff(); $parser = new ArcanistDiffParser(); $try_encoding = $repository->getDetail('encoding'); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } $parser->setDetectBinaryFiles(true); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($changes); $changesets = $diff->getChangesets(); $changeset = reset($changesets); $this->renderingReference = $drequest->generateURI(array('action' => 'rendering-ref')); return $changeset; }
public function testGenerateHarbormasterAutotargets() { $viewer = $this->generateNewTestUser(); $raw_diff = <<<EODIFF diff --git a/fruit b/fruit new file mode 100644 index 0000000..1c0f49d --- /dev/null +++ b/fruit @@ -0,0 +1,2 @@ +apal +banan EODIFF; $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($viewer, $changes)->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP)->setUnitStatus(DifferentialUnitStatus::UNIT_AUTO_SKIP)->attachRevision(null)->save(); $params = array('objectPHID' => $diff->getPHID(), 'targetKeys' => array(HarbormasterArcLintBuildStepImplementation::STEPKEY, HarbormasterArcUnitBuildStepImplementation::STEPKEY)); // Creation of autotargets should work from an empty state. $result = id(new ConduitCall('harbormaster.queryautotargets', $params))->setUser($viewer)->execute(); $targets = idx($result, 'targetMap'); foreach ($params['targetKeys'] as $target_key) { $this->assertTrue((bool) $result['targetMap'][$target_key]); } // Querying the same autotargets again should produce the same results, // not make new ones. $retry = id(new ConduitCall('harbormaster.queryautotargets', $params))->setUser($viewer)->execute(); $this->assertEqual($result, $retry); }
protected function executeQuery() { $drequest = $this->getRequest(); $repository = $drequest->getRepository(); if (!$drequest->getRawCommit()) { $effective_commit = $this->getEffectiveCommit(); if (!$effective_commit) { return null; } // TODO: This side effect is kind of skethcy. $drequest->setCommit($effective_commit); } else { $effective_commit = $drequest->getCommit(); } $options = array('-M', '-C', '--no-ext-diff', '--no-color', '--src-prefix=a/', '--dst-prefix=b/', '-U65535'); $options = implode(' ', $options); list($raw_diff) = execx("(cd %s && git diff {$options} %s^ %s -- %s)", $repository->getDetail('local-path'), $effective_commit, $effective_commit, $drequest->getPath()); $parser = new ArcanistDiffParser(); $parser->setDetectBinaryFiles(true); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($changes); $changesets = $diff->getChangesets(); $changeset = reset($changesets); $this->renderingReference = $drequest->getBranchURIComponent($drequest->getBranch()) . $drequest->getPath() . ';' . $drequest->getCommit(); return $changeset; }
protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $raw_diff = $request->getValue('diff'); $repository_phid = $request->getValue('repositoryPHID'); if ($repository_phid) { $repository = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withPHIDs(array($repository_phid))->executeOne(); if (!$repository) { throw new Exception(pht('No such repository "%s"!', $repository_phid)); } } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($viewer, $changes); // We're bounded by doing INSERTs for all the hunks and changesets, so // estimate the number of inserts we'll require. $size = 0; foreach ($diff->getChangesets() as $changeset) { $hunks = $changeset->getHunks(); $size += 1 + count($hunks); } $raw_limit = 10000; if ($size > $raw_limit) { throw new Exception(pht('The raw diff you have submitted is too large to parse (it affects ' . 'more than %s paths and hunks). Differential should only be used ' . 'for changes which are small enough to receive detailed human ' . 'review. See "Differential User Guide: Large Changes" in the ' . 'documentation for more information.', new PhutilNumber($raw_limit))); } $diff_data_dict = array('creationMethod' => 'web', 'authorPHID' => $viewer->getPHID(), 'repositoryPHID' => $repository_phid, 'lintStatus' => DifferentialLintStatus::LINT_SKIP, 'unitStatus' => DifferentialUnitStatus::UNIT_SKIP); $xactions = array(id(new DifferentialDiffTransaction())->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE)->setNewValue($diff_data_dict)); if ($request->getValue('viewPolicy')) { $xactions[] = id(new DifferentialDiffTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($request->getValue('viewPolicy')); } id(new DifferentialDiffEditor())->setActor($viewer)->setContentSource($request->newContentSource())->setContinueOnNoEffect(true)->setLookupRepository(false)->applyTransactions($diff, $xactions); return $this->buildDiffInfoDictionary($diff); }
private function buildChangesetParsers($type, $data, $file) { $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($data); $diff = DifferentialDiff::newFromRawChanges(PhabricatorUser::getOmnipotentUser(), $changes); $changesets = $diff->getChangesets(); $engine = new PhabricatorMarkupEngine(); $engine->setViewer(new PhabricatorUser()); $parsers = array(); foreach ($changesets as $changeset) { $cparser = new DifferentialChangesetParser(); $cparser->setUser(new PhabricatorUser()); $cparser->setDisableCache(true); $cparser->setChangeset($changeset); $cparser->setMarkupEngine($engine); if ($type == 'one') { $cparser->setRenderer(new DifferentialChangesetOneUpTestRenderer()); } else { if ($type == 'two') { $cparser->setRenderer(new DifferentialChangesetTwoUpTestRenderer()); } else { throw new Exception(pht('Unknown renderer type "%s"!', $type)); } } $parsers[] = $cparser; } return $parsers; }
private function createHunksFromFile($name) { $data = Filesystem::readFile(dirname(__FILE__) . '/data/' . $name); $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($data); if (count($changes) !== 1) { throw new Exception("Expected 1 changeset for '{$name}'!"); } $diff = DifferentialDiff::newFromRawChanges($changes); return head($diff->getChangesets())->getHunks(); }
protected function execute(ConduitAPIRequest $request) { $raw_diff = $request->getValue('diff'); $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($changes); $diff->setLintStatus(DifferentialLintStatus::LINT_SKIP); $diff->setUnitStatus(DifferentialLintStatus::LINT_SKIP); $diff->setAuthorPHID($request->getUser()->getPHID()); $diff->setCreationMethod('web'); $diff->save(); return $this->buildDiffInfoDictionary($diff); }
public function run() { $working_copy = $this->getWorkingCopy(); if (!$working_copy->getProjectID()) { throw new ArcanistUsageException("You have installed a git pre-receive hook in a remote without an " . ".arcconfig."); } // Git repositories have special rules in pre-receive hooks. We need to // construct the API against the .git directory instead of the project // root or commands don't work properly. $repository_api = ArcanistGitAPI::newHookAPI($_SERVER['PWD']); $root = $working_copy->getProjectRoot(); $parser = new ArcanistDiffParser(); $mark_revisions = array(); $stdin = file_get_contents('php://stdin'); $commits = array_filter(explode("\n", $stdin)); foreach ($commits as $commit) { list($old_ref, $new_ref, $refname) = explode(' ', $commit); list($log) = execx('(cd %s && git log -n1 %s)', $repository_api->getPath(), $new_ref); $message_log = reset($parser->parseDiff($log)); $message = ArcanistDifferentialCommitMessage::newFromRawCorpus($message_log->getMetadata('message')); $revision_id = $message->getRevisionID(); if ($revision_id) { $mark_revisions[] = $revision_id; } // TODO: Do commit message junk. $info = $repository_api->getPreReceiveHookStatus($old_ref, $new_ref); $paths = ipull($info, 'mask'); $frefs = ipull($info, 'ref'); $data = array(); foreach ($paths as $path => $mask) { list($stdout) = execx('(cd %s && git cat-file blob %s)', $repository_api->getPath(), $frefs[$path]); $data[$path] = $stdout; } // TODO: Do commit content junk. $commit_name = $new_ref; if ($revision_id) { $commit_name = 'D' . $revision_id . ' (' . $commit_name . ')'; } echo "[arc pre-receive] {$commit_name} OK...\n"; } $conduit = $this->getConduit(); $futures = array(); foreach ($mark_revisions as $revision_id) { $futures[] = $conduit->callMethod('differential.close', array('revisionID' => $revision_id)); } Futures($futures)->resolveAll(); return 0; }
protected function executeQuery() { $drequest = $this->getRequest(); $repository = $drequest->getRepository(); if (!$drequest->getRawCommit()) { $effective_commit = $this->getEffectiveCommit(); if (!$effective_commit) { return null; } // TODO: This side effect is kind of skethcy. $drequest->setCommit($effective_commit); } else { $effective_commit = $drequest->getCommit(); } $options = array('-M', '-C', '--no-ext-diff', '--no-color', '--src-prefix=a/', '--dst-prefix=b/', '-U65535'); $options = implode(' ', $options); try { list($raw_diff) = $repository->execxLocalCommand('diff %C %s^ %s -- %s', $options, $effective_commit, $effective_commit, $drequest->getPath()); } catch (CommandException $ex) { // Check if this is the root commit by seeing if it has parents. list($parents) = $repository->execxLocalCommand('log --format=%s %s --', '%P', $effective_commit); if (!strlen(trim($parents))) { // No parents means we're looking at the root revision. Diff against // the empty tree hash instead, since there is no parent so "^" does // not work. See ArcanistGitAPI for more discussion. list($raw_diff) = $repository->execxLocalCommand('diff %C %s %s -- %s', $options, ArcanistGitAPI::GIT_MAGIC_ROOT_COMMIT, $effective_commit, $drequest->getPath()); } else { throw $ex; } } $parser = new ArcanistDiffParser(); $try_encoding = $repository->getDetail('encoding'); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } $parser->setDetectBinaryFiles(true); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($changes); $changesets = $diff->getChangesets(); $changeset = reset($changesets); $this->renderingReference = $drequest->getBranchURIComponent($drequest->getBranch()) . $drequest->getPath() . ';' . $drequest->getCommit(); return $changeset; }
private function loadHunk($name) { $root = dirname(__FILE__) . '/hunk/'; $data = Filesystem::readFile($root . $name); $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($data); $viewer = PhabricatorUser::getOmnipotentUser(); $diff = DifferentialDiff::newFromRawChanges($viewer, $changes); $changesets = $diff->getChangesets(); if (count($changesets) !== 1) { throw new Exception(pht('Expected exactly one changeset from "%s".', $name)); } $changeset = head($changesets); $hunks = $changeset->getHunks(); if (count($hunks) !== 1) { throw new Exception(pht('Expected exactly one hunk from "%s".', $name)); } $hunk = head($hunks); return $hunk; }
protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $raw_diff = $request->getValue('diff'); $repository_phid = $request->getValue('repositoryPHID'); if ($repository_phid) { $repository = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withPHIDs(array($repository_phid))->executeOne(); if (!$repository) { throw new Exception(pht('No such repository "%s"!', $repository_phid)); } } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($viewer, $changes); $diff_data_dict = array('creationMethod' => 'web', 'authorPHID' => $viewer->getPHID(), 'repositoryPHID' => $repository_phid, 'lintStatus' => DifferentialLintStatus::LINT_SKIP, 'unitStatus' => DifferentialUnitStatus::UNIT_SKIP); $xactions = array(id(new DifferentialTransaction())->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE)->setNewValue($diff_data_dict)); if ($request->getValue('viewPolicy')) { $xactions[] = id(new DifferentialTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($request->getValue('viewPolicy')); } id(new DifferentialDiffEditor())->setActor($viewer)->setContentSourceFromConduitRequest($request)->setContinueOnNoEffect(true)->setLookupRepository(false)->applyTransactions($diff, $xactions); return $this->buildDiffInfoDictionary($diff); }
public function processRequest() { $request = $this->getRequest(); if ($request->isFormPost()) { $parser = new ArcanistDiffParser(); $diff = $request->getStr('diff'); $changes = $parser->parseDiff($diff); $diff = DifferentialDiff::newFromRawChanges($changes); $diff->setLintStatus(DifferentialLintStatus::LINT_SKIP); $diff->setUnitStatus(DifferentialLintStatus::LINT_SKIP); $diff->setAuthorPHID($request->getUser()->getPHID()); $diff->setCreationMethod('web'); $diff->save(); return id(new AphrontRedirectResponse())->setURI('/differential/diff/' . $diff->getID() . '/'); } $form = new AphrontFormView(); $form->setAction('/differential/diff/create/')->setUser($request->getUser())->appendChild('<p class="aphront-form-instructions">The best way to create a ' . 'Differential diff is by using <strong>Arcanist</strong>, but you ' . 'can also just paste a diff (e.g., from <tt>svn diff</tt> or ' . '<tt>git diff</tt>) into this box if you really want.</p>')->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Raw Diff')->setName('diff')->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL))->appendChild(id(new AphrontFormSubmitControl())->setValue("Create Diff »")); $panel = new AphrontPanelView(); $panel->setHeader('Create New Diff'); $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_FORM); return $this->buildStandardPageResponse($panel, array('title' => 'Create Diff', 'tab' => 'create')); }
public function testGitPathSplitting() { static $tests = array('a/old.c b/new.c' => array('old.c', 'new.c'), "a/old.c b/new.c\n" => array('old.c', 'new.c'), "a/old.c b/new.c\r\n" => array('old.c', 'new.c'), 'old.c new.c' => array('old.c', 'new.c'), '1/old.c 2/new.c' => array('old.c', 'new.c'), '"a/\\"quotes1\\"" "b/\\"quotes2\\""' => array('"quotes1"', '"quotes2"'), '"a/\\"quotes and spaces1\\"" "b/\\"quotes and spaces2\\""' => array('"quotes and spaces1"', '"quotes and spaces2"'), '"a/\\342\\230\\2031" "b/\\342\\230\\2032"' => array("☃1", "☃2"), 'a/Core Data/old.c b/Core Data/new.c' => array('Core Data/old.c', 'Core Data/new.c'), 'some file with spaces.c some file with spaces.c' => array('some file with spaces.c', 'some file with spaces.c')); foreach ($tests as $input => $expect) { $result = ArcanistDiffParser::splitGitDiffPaths($input); $this->assertEqual($expect, $result, pht('Split: %s', $input)); } static $ambiguous = array('old file with spaces.c new file with spaces.c'); foreach ($ambiguous as $input) { $caught = null; try { ArcanistDiffParser::splitGitDiffPaths($input); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof Exception, pht('Ambiguous: %s', $input)); } }
private function getDefaultParser() { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $parser = new ArcanistDiffParser(); $try_encoding = $repository->getDetail('encoding'); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } $parser->setDetectBinaryFiles(true); return $parser; }
protected function newDiffParser() { $parser = new ArcanistDiffParser(); if ($this->repositoryAPI) { $parser->setRepositoryAPI($this->getRepositoryAPI()); } $parser->setWriteDiffOnFailure(true); return $parser; }
protected function executeQuery() { $drequest = $this->getRequest(); if (!$drequest->getRawCommit()) { $effective_commit = $this->getEffectiveCommit(); if (!$effective_commit) { return null; } // TODO: Sketchy side effect. $drequest->setCommit($effective_commit); } $path_change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); $path_changes = $path_change_query->loadChanges(); $path = null; foreach ($path_changes as $change) { if ($change->getPath() == $drequest->getPath()) { $path = $change; } } if (!$path) { return null; } $change_type = $path->getChangeType(); switch ($change_type) { case DifferentialChangeType::TYPE_MULTICOPY: case DifferentialChangeType::TYPE_DELETE: if ($path->getTargetPath()) { $old = array($path->getTargetPath(), $path->getTargetCommitIdentifier()); } else { $old = array($path->getPath(), $path->getCommitIdentifier() - 1); } $old_name = $path->getPath(); $new_name = ''; $new = null; break; case DifferentialChangeType::TYPE_ADD: $old = null; $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = ''; $new_name = $path->getPath(); break; case DifferentialChangeType::TYPE_MOVE_HERE: case DifferentialChangeType::TYPE_COPY_HERE: $old = array($path->getTargetPath(), $path->getTargetCommitIdentifier()); $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = $path->getTargetPath(); $new_name = $path->getPath(); break; case DifferentialChangeType::TYPE_MOVE_AWAY: $old = array($path->getPath(), $path->getCommitIdentifier() - 1); $old_name = $path->getPath(); $new_name = null; $new = null; break; default: $old = array($path->getPath(), $path->getCommitIdentifier() - 1); $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = $path->getPath(); $new_name = $path->getPath(); break; } $futures = array('old' => $this->buildContentFuture($old), 'new' => $this->buildContentFuture($new)); $futures = array_filter($futures); foreach (Futures($futures) as $key => $future) { list($stdout) = $future->resolvex(); $futures[$key] = $stdout; } $old_data = idx($futures, 'old', ''); $new_data = idx($futures, 'new', ''); $engine = new PhabricatorDifferenceEngine(); $engine->setOldName($old_name); $engine->setNewName($new_name); $raw_diff = $engine->generateRawDiffFromFileContent($old_data, $new_data); $parser = new ArcanistDiffParser(); $parser->setDetectBinaryFiles(true); $arcanist_changes = DiffusionPathChange::convertToArcanistChanges($path_changes); $parser->setChanges($arcanist_changes); $parser->forcePath($path->getPath()); $changes = $parser->parseDiff($raw_diff); $change = $changes[$path->getPath()]; $diff = DifferentialDiff::newFromRawChanges(array($change)); $changesets = $diff->getChangesets(); $changeset = reset($changesets); $this->renderingReference = $drequest->getPath() . ';' . $drequest->getCommit(); return $changeset; }
public function run() { $source = $this->getSource(); switch ($source) { case self::SOURCE_LOCAL: $repository_api = $this->getRepositoryAPI(); $parser = new ArcanistDiffParser(); $parser->setRepositoryAPI($repository_api); if ($repository_api instanceof ArcanistGitAPI) { $this->parseBaseCommitArgument($this->getArgument('paths')); $diff = $repository_api->getFullGitDiff($repository_api->getBaseCommit(), $repository_api->getHeadCommit()); $changes = $parser->parseDiff($diff); $authors = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($this->getUserPHID()))); $author_dict = reset($authors); list($email) = $repository_api->execxLocal('config user.email'); $author = sprintf('%s <%s>', $author_dict['realName'], $email); } else { if ($repository_api instanceof ArcanistMercurialAPI) { $this->parseBaseCommitArgument($this->getArgument('paths')); $diff = $repository_api->getFullMercurialDiff(); $changes = $parser->parseDiff($diff); $authors = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($this->getUserPHID()))); list($author) = $repository_api->execxLocal('showconfig ui.username'); } else { // TODO: paths support $paths = $repository_api->getWorkingCopyStatus(); $changes = $parser->parseSubversionDiff($repository_api, $paths); $author = $this->getUserName(); } } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setProjectID($this->getWorkingCopy()->getProjectID()); $bundle->setBaseRevision($repository_api->getSourceControlBaseRevision()); // NOTE: we can't get a revision ID for SOURCE_LOCAL $parser = new PhutilEmailAddress($author); $bundle->setAuthorName($parser->getDisplayName()); $bundle->setAuthorEmail($parser->getAddress()); break; case self::SOURCE_REVISION: $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $this->getSourceID()); break; case self::SOURCE_DIFF: $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $this->getSourceID()); break; } $try_encoding = nonempty($this->getArgument('encoding'), null); if (!$try_encoding) { try { $project_info = $this->getConduit()->callMethodSynchronous('arcanist.projectinfo', array('name' => $bundle->getProjectID())); $try_encoding = $project_info['encoding']; } catch (ConduitClientException $e) { $try_encoding = null; } } if ($try_encoding) { $bundle->setEncoding($try_encoding); } $format = $this->getFormat(); switch ($format) { case self::FORMAT_GIT: echo $bundle->toGitPatch(); break; case self::FORMAT_UNIFIED: echo $bundle->toUnifiedDiff(); break; case self::FORMAT_BUNDLE: $path = $this->getArgument('arcbundle'); echo "Writing bundle to '{$path}'...\n"; $bundle->writeToDisk($path); echo "done.\n"; break; } return 0; }
protected function newDiffParser() { $parser = new ArcanistDiffParser(); $parser->setWriteDiffOnFailure(true); return $parser; }
public function getAllLocalChanges() { $diff = $this->getFullMercurialDiff(); if (!strlen(trim($diff))) { return array(); } $parser = new ArcanistDiffParser(); return $parser->parseDiff($diff); }
public function run() { $source = $this->getSource(); switch ($source) { case self::SOURCE_LOCAL: $repository_api = $this->getRepositoryAPI(); $parser = new ArcanistDiffParser(); if ($repository_api instanceof ArcanistGitAPI) { $repository_api->parseRelativeLocalCommit($this->getArgument('paths')); $diff = $repository_api->getFullGitDiff(); $changes = $parser->parseDiff($diff); } else { // TODO: paths support $paths = $repository_api->getWorkingCopyStatus(); $changes = $parser->parseSubversionDiff($repository_api, $paths); } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setProjectID($this->getWorkingCopy()->getProjectID()); $bundle->setBaseRevision($repository_api->getSourceControlBaseRevision()); // note we can't get a revision ID for SOURCE_LOCAL break; case self::SOURCE_REVISION: $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $this->getSourceID()); break; case self::SOURCE_DIFF: $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $this->getSourceID()); break; } $try_encoding = nonempty($this->getArgument('encoding'), null); if (!$try_encoding) { try { $try_encoding = $this->getRepositoryEncoding(); } catch (ConduitClientException $e) { $try_encoding = null; } } if ($try_encoding) { $bundle->setEncoding($try_encoding); } $format = $this->getFormat(); switch ($format) { case self::FORMAT_GIT: echo $bundle->toGitPatch(); break; case self::FORMAT_UNIFIED: echo $bundle->toUnifiedDiff(); break; case self::FORMAT_BUNDLE: $path = $this->getArgument('arcbundle'); echo "Writing bundle to '{$path}'...\n"; $bundle->writeToDisk($path); echo "done.\n"; break; } return 0; }
private function parseDiff($diff_file) { $contents = Filesystem::readFile($diff_file); $file = basename($diff_file); $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($contents); switch ($file) { case 'colorized.hggitdiff': $this->assertEqual(1, count($changes)); break; case 'basic-missing-both-newlines-plus.udiff': case 'basic-missing-both-newlines.udiff': case 'basic-missing-new-newline-plus.udiff': case 'basic-missing-new-newline.udiff': case 'basic-missing-old-newline-plus.udiff': case 'basic-missing-old-newline.udiff': $expect_old = strpos($file, '-old-') || strpos($file, '-both-'); $expect_new = strpos($file, '-new-') || strpos($file, '-both-'); $expect_two = strpos($file, '-plus'); $this->assertEqual(count($changes), $expect_two ? 2 : 1); $change = reset($changes); $this->assertEqual(true, $change !== null); $hunks = $change->getHunks(); $this->assertEqual(1, count($hunks)); $hunk = reset($hunks); $this->assertEqual((bool) $expect_old, $hunk->getIsMissingOldNewline()); $this->assertEqual((bool) $expect_new, $hunk->getIsMissingNewNewline()); break; case 'basic-binary.udiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'basic-multi-hunk.udiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $hunks = $change->getHunks(); $this->assertEqual(4, count($hunks)); $this->assertEqual('right', $change->getCurrentPath()); $this->assertEqual('left', $change->getOldPath()); break; case 'basic-multi-hunk-content.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $hunks = $change->getHunks(); $this->assertEqual(2, count($hunks)); $there_is_a_literal_trailing_space_here = ' '; $corpus_0 = <<<EOCORPUS asdfasdf +% quack % -% %% %% %%% EOCORPUS; $corpus_1 = <<<EOCORPUS %%%%% %%%%% {$there_is_a_literal_trailing_space_here} -! +! quack EOCORPUS; $this->assertEqual($corpus_0, $hunks[0]->getCorpus()); $this->assertEqual($corpus_1, $hunks[1]->getCorpus()); break; case 'svn-ignore-whitespace-only.svndiff': $this->assertEqual(2, count($changes)); $hunks = reset($changes)->getHunks(); $this->assertEqual(0, count($hunks)); break; case 'svn-property-add.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $hunks = reset($changes)->getHunks(); $this->assertEqual(1, count($hunks)); $this->assertEqual(array('duck' => 'quack'), $change->getNewProperties()); break; case 'svn-property-modify.svndiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(array('svn:ignore' => '*.phpz'), $change->getOldProperties()); $this->assertEqual(array('svn:ignore' => '*.php'), $change->getNewProperties()); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(array('svn:special' => '*'), $change->getOldProperties()); $this->assertEqual(array('svn:special' => 'moo'), $change->getNewProperties()); break; case 'svn-property-delete.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual($change->getOldProperties(), array('svn:special' => '*')); $this->assertEqual(array(), $change->getNewProperties()); break; case 'svn-property-merged.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual($change->getOldProperties(), array()); $this->assertEqual($change->getNewProperties(), array()); break; case 'svn-property-merge.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual($change->getOldProperties(), array()); $this->assertEqual($change->getNewProperties(), array('svn:mergeinfo' => <<<EOTEXT Merged /tfb/branches/internmove/www/html/js/help/UIFaq.js:r83462-126155 Merged /tfb/branches/ads-create-v3/www/html/js/help/UIFaq.js:r140558-142418 EOTEXT )); break; case 'svn-binary-add.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(array('svn:mime-type' => 'application/octet-stream'), $change->getNewProperties()); break; case 'svn-binary-diff.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); $this->assertEqual(count($change->getHunks()), 0); break; case 'git-delete-file.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_DELETE, $change->getType()); $this->assertEqual('scripts/intern/test/testfile2', $change->getCurrentPath()); $this->assertEqual(1, count($change->getHunks())); break; case 'git-binary-change.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); $this->assertEqual(0, count($change->getHunks())); break; case 'git-filemode-change.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(1, count($change->getHunks())); $this->assertEqual(array('unix:filemode' => '100644'), $change->getOldProperties()); $this->assertEqual(array('unix:filemode' => '100755'), $change->getNewProperties()); break; case 'git-filemode-change-only.gitdiff': $this->assertEqual(count($changes), 2); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual(array('unix:filemode' => '100644'), $change->getOldProperties()); $this->assertEqual(array('unix:filemode' => '100755'), $change->getNewProperties()); break; case 'svn-empty-file.svndiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); break; case 'git-ignore-whitespace-only.gitdiff': $this->assertEqual(count($changes), 2); $change = array_shift($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual($change->getOldPath(), 'scripts/intern/test/testfile2'); $this->assertEqual($change->getCurrentPath(), 'scripts/intern/test/testfile2'); $change = array_shift($changes); $this->assertEqual(count($change->getHunks()), 1); $this->assertEqual($change->getOldPath(), 'scripts/intern/test/testfile3'); $this->assertEqual($change->getCurrentPath(), 'scripts/intern/test/testfile3'); break; case 'git-move.gitdiff': case 'git-move-edit.gitdiff': case 'git-move-plus.gitdiff': $extra_changeset = (bool) strpos($file, '-plus'); $has_hunk = (bool) strpos($file, '-edit'); $this->assertEqual($extra_changeset ? 3 : 2, count($changes)); $change = array_shift($changes); $this->assertEqual($has_hunk ? 1 : 0, count($change->getHunks())); $this->assertEqual($change->getType(), ArcanistDiffChangeType::TYPE_MOVE_HERE); $target = $change; $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(ArcanistDiffChangeType::TYPE_MOVE_AWAY, $change->getType()); $this->assertEqual($change->getCurrentPath(), $target->getOldPath()); $this->assertEqual(true, in_array($target->getCurrentPath(), $change->getAwayPaths())); break; case 'git-merge-header.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_MESSAGE, $change->getType()); $this->assertEqual('501f6d519703458471dbea6284ec5f49d1408598', $change->getCommitHash()); break; case 'git-new-file.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_ADD, $change->getType()); break; case 'git-copy.gitdiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(ArcanistDiffChangeType::TYPE_COPY_HERE, $change->getType()); $this->assertEqual('flib/intern/widgets/ui/UIWidgetRSSBox.php', $change->getCurrentPath()); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(ArcanistDiffChangeType::TYPE_COPY_AWAY, $change->getType()); $this->assertEqual('lib/display/intern/ui/widget/UIWidgetRSSBox.php', $change->getCurrentPath()); break; case 'git-copy-plus.gitdiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(3, count($change->getHunks())); $this->assertEqual(ArcanistDiffChangeType::TYPE_COPY_HERE, $change->getType()); $this->assertEqual('flib/intern/widgets/ui/UIWidgetGraphConnect.php', $change->getCurrentPath()); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(ArcanistDiffChangeType::TYPE_COPY_AWAY, $change->getType()); $this->assertEqual('lib/display/intern/ui/widget/UIWidgetLunchtime.php', $change->getCurrentPath()); break; case 'svn-property-multiline.svndiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual(array('svn:ignore' => 'tags'), $change->getOldProperties()); $this->assertEqual(array('svn:ignore' => "tags\nasdf\nlol\nwhat"), $change->getNewProperties()); break; case 'git-empty-files.gitdiff': $this->assertEqual(2, count($changes)); while ($change = array_shift($changes)) { $this->assertEqual(0, count($change->getHunks())); } break; case 'git-mnemonicprefix.gitdiff': // Check parsing of diffs created with `diff.mnemonicprefix` // configuration option set to `true`. $this->assertEqual(1, count($changes)); $this->assertEqual(1, count(reset($changes)->getHunks())); break; case 'git-commit.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_MESSAGE, $change->getType()); $this->assertEqual('76e2f1339c298c748aa0b52030799ed202a6537b', $change->getCommitHash()); $this->assertEqual(<<<EOTEXT Deprecating UIActionButton (Part 1) Summary: Replaces calls to UIActionButton with <ui:button>. I tested most of these calls, but there were some that I didn't know how to reach, so if you are one of the owners of this code, please test your feature in my sandbox: www.ngao.devrs013.facebook.com @brosenthal, I removed some logic that was setting a disabled state on a UIActionButton, which is actually a no-op. Reviewed By: brosenthal Other Commenters: sparker, egiovanola Test Plan: www.ngao.devrs013.facebook.com Explicitly tested: * ads creation flow (add keyword) * ads manager (conversion tracking) * help center (create a discussion) * new user wizard (next step button) Revert: OK DiffCamp Revision: 94064 git-svn-id: svn+ssh://tubbs/svnroot/tfb/trunk/www@223593 2c7ba8d8 EOTEXT , $change->getMetadata('message')); break; case 'git-binary.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'git-odd-filename.gitdiff': $this->assertEqual(2, count($changes)); $change = reset($changes); $this->assertEqual('old/' . "∆" . '.jpg', $change->getOldPath()); $this->assertEqual('new/' . "∆" . '.jpg', $change->getCurrentPath()); break; case 'hg-binary-change.hgdiff': case 'hg-solo-binary-change.hgdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_ADD, $change->getType()); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'hg-binary-delete.hgdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_DELETE, $change->getType()); $this->assertEqual(ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'git-replace-symlink.gitdiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual(ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); break; case 'svn-1.7-property-added.svndiff': $this->assertEqual(1, count($changes)); $change = head($changes); $new_properties = $change->getNewProperties(); $this->assertEqual(2, count($new_properties)); $this->assertEqual('*', idx($new_properties, 'svn:executable')); $this->assertEqual('text/html', idx($new_properties, 'svn:mime-type')); break; default: throw new Exception("No test block for diff file {$diff_file}."); break; } }
public function loadWorkingCopyDifferentialRevisions(ConduitClient $conduit, array $query) { $messages = $this->getGitCommitLog(); if (!strlen($messages)) { return array(); } $parser = new ArcanistDiffParser(); $messages = $parser->parseDiff($messages); // First, try to find revisions by explicit revision IDs in commit messages. $reason_map = array(); $revision_ids = array(); foreach ($messages as $message) { $object = ArcanistDifferentialCommitMessage::newFromRawCorpus($message->getMetadata('message')); if ($object->getRevisionID()) { $revision_ids[] = $object->getRevisionID(); $reason_map[$object->getRevisionID()] = $message->getCommitHash(); } } if ($revision_ids) { $results = $conduit->callMethodSynchronous('differential.query', $query + array('ids' => $revision_ids)); foreach ($results as $key => $result) { $hash = substr($reason_map[$result['id']], 0, 16); $results[$key]['why'] = "Commit message for '{$hash}' has explicit 'Differential Revision'."; } return $results; } // If we didn't succeed, try to find revisions by hash. $hashes = array(); foreach ($this->getLocalCommitInformation() as $commit) { $hashes[] = array('gtcm', $commit['commit']); $hashes[] = array('gttr', $commit['tree']); } $results = $conduit->callMethodSynchronous('differential.query', $query + array('commitHashes' => $hashes)); foreach ($results as $key => $result) { $results[$key]['why'] = 'A git commit or tree hash in the commit range is already attached ' . 'to the Differential revision.'; } return $results; }
public static function newFromDiff($data) { $obj = new ArcanistBundle(); $parser = new ArcanistDiffParser(); $obj->changes = $parser->parseDiff($data); return $obj; }
public function loadChangesetsForCommit($identifier) { $byte_limit = HeraldCommitAdapter::getEnormousByteLimit(); $time_limit = HeraldCommitAdapter::getEnormousTimeLimit(); $vcs = $this->getRepository()->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: // For git and hg, we can use normal commands. $drequest = DiffusionRequest::newFromDictionary(array('repository' => $this->getRepository(), 'user' => $this->getViewer(), 'commit' => $identifier)); $raw_diff = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest)->setTimeout($time_limit)->setByteLimit($byte_limit)->setLinesOfContext(0)->loadRawDiff(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: // TODO: This diff has 3 lines of context, which produces slightly // incorrect "added file content" and "removed file content" results. // This may also choke on binaries, but "svnlook diff" does not support // the "--diff-cmd" flag. // For subversion, we need to use `svnlook`. $future = new ExecFuture('svnlook diff -t %s %s', $this->subversionTransaction, $this->subversionRepository); $future->setTimeout($time_limit); $future->setStdoutSizeLimit($byte_limit); $future->setStderrSizeLimit($byte_limit); list($raw_diff) = $future->resolvex(); break; default: throw new Exception(pht("Unknown VCS '%s!'", $vcs)); } if (strlen($raw_diff) >= $byte_limit) { throw new Exception(pht('The raw text of this change is enormous (larger than %d ' . 'bytes). Herald can not process it.', $byte_limit)); } if (!strlen($raw_diff)) { // If the commit is actually empty, just return no changesets. return array(); } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newEphemeralFromRawChanges($changes); return $diff->getChangesets(); }
private function loadCommitDiff() { $drequest = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $this->repository, 'commit' => $this->commit->getCommitIdentifier())); $byte_limit = self::getEnormousByteLimit(); $raw = DiffusionQuery::callConduitWithDiffusionRequest(PhabricatorUser::getOmnipotentUser(), $drequest, 'diffusion.rawdiffquery', array('commit' => $this->commit->getCommitIdentifier(), 'timeout' => self::getEnormousTimeLimit(), 'byteLimit' => $byte_limit, 'linesOfContext' => 0)); if (strlen($raw) >= $byte_limit) { throw new Exception(pht('The raw text of this change is enormous (larger than %d bytes). ' . 'Herald can not process it.', $byte_limit)); } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw); $diff = DifferentialDiff::newEphemeralFromRawChanges($changes); return $diff; }