public function buildPatch() { $diff = new DifferentialDiff(); $diff->attachChangesets($this->getChangesets()); $raw_changes = $diff->buildChangesList(); $changes = array(); foreach ($raw_changes as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $viewer = $this->getViewer(); $loader = id(new PhabricatorFileBundleLoader())->setViewer($viewer); $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setLoadFileDataCallback(array($loader, 'loadFileData')); $format = $this->getFormat(); switch ($format) { case 'git': return $bundle->toGitPatch(); break; case 'unified': default: return $bundle->toUnifiedDiff(); break; } }
private function buildDependencyGraph(ArcanistBundle $bundle) { $graph = null; if ($this->getRepositoryAPI() instanceof ArcanistSubversionAPI) { return $graph; } $revision_id = $bundle->getRevisionID(); if ($revision_id) { $revisions = $this->getConduit()->callMethodSynchronous('differential.query', array('ids' => array($revision_id))); if ($revisions) { $revision = head($revisions); $rev_auxiliary = idx($revision, 'auxiliary', array()); $phids = idx($rev_auxiliary, 'phabricator:depends-on', array()); if ($phids) { $revision_phid = $revision['phid']; $graph = id(new ArcanistDifferentialDependencyGraph())->setConduit($this->getConduit())->setRepositoryAPI($this->getRepositoryAPI())->setStartPHID($revision_phid)->addNodes(array($revision_phid => $phids))->loadGraph(); } } } return $graph; }
/** * Note this code is somewhat similar to the buildPatch method in * @{class:DifferentialReviewRequestMail}. * * @return @{class:AphrontRedirectResponse} */ private function buildRawDiffResponse(DifferentialRevision $revision, array $changesets, array $vs_changesets, array $vs_map, PhabricatorRepository $repository = null) { assert_instances_of($changesets, 'DifferentialChangeset'); assert_instances_of($vs_changesets, 'DifferentialChangeset'); $viewer = $this->getRequest()->getUser(); id(new DifferentialHunkQuery())->setViewer($viewer)->withChangesets($changesets)->needAttachToChangesets(true)->execute(); $diff = new DifferentialDiff(); $diff->attachChangesets($changesets); $raw_changes = $diff->buildChangesList(); $changes = array(); foreach ($raw_changes as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $loader = id(new PhabricatorFileBundleLoader())->setViewer($viewer); $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setLoadFileDataCallback(array($loader, 'loadFileData')); $vcs = $repository ? $repository->getVersionControlSystem() : null; switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $raw_diff = $bundle->toGitPatch(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: default: $raw_diff = $bundle->toUnifiedDiff(); break; } $request_uri = $this->getRequest()->getRequestURI(); // this ends up being something like // D123.diff // or the verbose // D123.vs123.id123.whitespaceignore-all.diff // lame but nice to include these options $file_name = ltrim($request_uri->getPath(), '/') . '.'; foreach ($request_uri->getQueryParams() as $key => $value) { if ($key == 'download') { continue; } $file_name .= $key . $value . '.'; } $file_name .= 'diff'; $file = PhabricatorFile::buildFromFileDataOrHash($raw_diff, array('name' => $file_name, 'ttl' => 60 * 60 * 24, 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE)); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $file->attachToObject($revision->getPHID()); unset($unguarded); return $file->getRedirectResponse(); }
/** * Do the best we can to prevent PEBKAC and id10t issues. */ private function sanityCheck(ArcanistBundle $bundle) { $repository_api = $this->getRepositoryAPI(); // Require clean working copy $this->requireCleanWorkingCopy(); // Check to see if the bundle's project id matches the working copy // project id $bundle_project_id = $bundle->getProjectID(); $working_copy_project_id = $this->getWorkingCopy()->getProjectID(); if (empty($bundle_project_id)) { // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version = 0 // they don't come with a project id so just do nothing } else { if ($bundle_project_id != $working_copy_project_id) { if ($working_copy_project_id) { $issue = "This patch is for the '{$bundle_project_id}' project, but the " . "working copy belongs to the '{$working_copy_project_id}' project."; } else { $issue = "This patch is for the '{$bundle_project_id}' project, but the " . "working copy does not have an '.arcconfig' file to identify which " . "project it belongs to."; } $ok = phutil_console_confirm("{$issue} Still try to apply the patch?", $default_no = false); if (!$ok) { throw new ArcanistUserAbortException(); } } } // Check to see if the bundle's base revision matches the working copy // base revision if ($repository_api->supportsLocalCommits()) { $bundle_base_rev = $bundle->getBaseRevision(); if (empty($bundle_base_rev)) { // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version < 2 // they don't have a base rev so just do nothing $commit_exists = true; } else { $commit_exists = $repository_api->hasLocalCommit($bundle_base_rev); } if (!$commit_exists) { // we have a problem...! lots of work because we need to ask // differential for revision information for these base revisions // to improve our error message. $bundle_base_rev_str = null; $source_base_rev = $repository_api->getWorkingCopyRevision(); $source_base_rev_str = null; if ($repository_api instanceof ArcanistGitAPI) { $hash_type = ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT; } else { if ($repository_api instanceof ArcanistMercurialAPI) { $hash_type = ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT; } else { $hash_type = null; } } if ($hash_type) { // 2 round trips because even though we could send off one query // we wouldn't be able to tell which revisions were for which hash $hash = array($hash_type, $bundle_base_rev); $bundle_revision = $this->loadRevisionFromHash($hash); $hash = array($hash_type, $source_base_rev); $source_revision = $this->loadRevisionFromHash($hash); if ($bundle_revision) { $bundle_base_rev_str = $bundle_base_rev . ' \\ D' . $bundle_revision['id']; } if ($source_revision) { $source_base_rev_str = $source_base_rev . ' \\ D' . $source_revision['id']; } } $bundle_base_rev_str = nonempty($bundle_base_rev_str, $bundle_base_rev); $source_base_rev_str = nonempty($source_base_rev_str, $source_base_rev); $ok = phutil_console_confirm("This diff is against commit {$bundle_base_rev_str}, but the " . "commit is nowhere in the working copy. Try to apply it against " . "the current working copy state? ({$source_base_rev_str})", $default_no = false); if (!$ok) { throw new ArcanistUserAbortException(); } } } // TODO -- more sanity checks here }
private function loadBundleFromConduit(ConduitClient $conduit, $params) { $future = $conduit->callMethod('differential.getdiff', $params); $diff = $future->resolve(); $changes = array(); foreach ($diff['changes'] as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setConduit($conduit); // since the conduit method has changes, assume that these fields // could be unset $bundle->setProjectID(idx($diff, 'projectName')); $bundle->setBaseRevision(idx($diff, 'sourceControlBaseRevision')); $bundle->setRevisionID(idx($diff, 'revisionID')); $bundle->setAuthorName(idx($diff, 'authorName')); $bundle->setAuthorEmail(idx($diff, 'authorEmail')); return $bundle; }
private function loadBundleFromConduit(ConduitClient $conduit, $params) { $future = $conduit->callMethod('differential.getdiff', $params); $diff = $future->resolve(); $changes = array(); foreach ($diff['changes'] as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setConduit($conduit); $bundle->setProjectID($diff['projectName']); $bundle->setBaseRevision($diff['sourceControlBaseRevision']); $bundle->setRevisionID($diff['revisionID']); return $bundle; }
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; }
public static function newFromArcBundle($path) { $path = Filesystem::resolvePath($path); $future = new ExecFuture('tar tfO %s', $path); list($stdout, $file_list) = $future->resolvex(); $file_list = explode("\n", trim($file_list)); if (in_array('meta.json', $file_list)) { $future = new ExecFuture('tar xfO %s meta.json', $path); $meta_info = $future->resolveJSON(); $version = idx($meta_info, 'version', 0); $project_name = idx($meta_info, 'projectName'); $base_revision = idx($meta_info, 'baseRevision'); $revision_id = idx($meta_info, 'revisionID'); $encoding = idx($meta_info, 'encoding'); $author_name = idx($meta_info, 'authorName'); $author_email = idx($meta_info, 'authorEmail'); } else { // this arc bundle was probably made before we started storing meta info $version = 0; $project_name = null; $base_revision = null; $revision_id = null; $encoding = null; $author = null; } $future = new ExecFuture('tar xfO %s changes.json', $path); $changes = $future->resolveJSON(); foreach ($changes as $change_key => $change) { foreach ($change['hunks'] as $key => $hunk) { list($hunk_data) = execx('tar xfO %s hunks/%s', $path, $hunk['corpus']); $changes[$change_key]['hunks'][$key]['corpus'] = $hunk_data; } } foreach ($changes as $change_key => $change) { $changes[$change_key] = ArcanistDiffChange::newFromDictionary($change); } $obj = new ArcanistBundle(); $obj->changes = $changes; $obj->diskPath = $path; $obj->setProjectID($project_name); $obj->setBaseRevision($base_revision); $obj->setRevisionID($revision_id); $obj->setEncoding($encoding); return $obj; }
/** * Note this code is somewhat similar to the buildPatch method in * @{class:DifferentialReviewRequestMail}. * * @return @{class:AphrontRedirectResponse} */ private function buildRawDiffResponse(array $changesets, array $vs_changesets, array $vs_map, PhabricatorRepository $repository = null) { assert_instances_of($changesets, 'DifferentialChangeset'); assert_instances_of($vs_changesets, 'DifferentialChangeset'); $engine = new PhabricatorDifferenceEngine(); $generated_changesets = array(); foreach ($changesets as $changeset) { $changeset->attachHunks($changeset->loadHunks()); $right = $changeset->makeNewFile(); $choice = $changeset; $vs = idx($vs_map, $changeset->getID()); if ($vs == -1) { $left = $right; $right = $changeset->makeOldFile(); } else { if ($vs) { $choice = $vs_changeset = $vs_changesets[$vs]; $vs_changeset->attachHunks($vs_changeset->loadHunks()); $left = $vs_changeset->makeNewFile(); } else { $left = $changeset->makeOldFile(); } } $synthetic = $engine->generateChangesetFromFileContent($left, $right); if (!$synthetic->getAffectedLineCount()) { $filetype = $choice->getFileType(); if ($filetype == DifferentialChangeType::FILE_TEXT || $filetype == DifferentialChangeType::FILE_SYMLINK) { continue; } } $choice->attachHunks($synthetic->getHunks()); $generated_changesets[] = $choice; } $diff = new DifferentialDiff(); $diff->attachChangesets($generated_changesets); $diff_dict = $diff->getDiffDict(); $changes = array(); foreach ($diff_dict['changes'] as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setLoadFileDataCallback(array($this, 'loadFileByPHID')); $vcs = $repository ? $repository->getVersionControlSystem() : null; switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $raw_diff = $bundle->toGitPatch(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: default: $raw_diff = $bundle->toUnifiedDiff(); break; } $request_uri = $this->getRequest()->getRequestURI(); // this ends up being something like // D123.diff // or the verbose // D123.vs123.id123.whitespaceignore-all.diff // lame but nice to include these options $file_name = ltrim($request_uri->getPath(), '/') . '.'; foreach ($request_uri->getQueryParams() as $key => $value) { if ($key == 'download') { continue; } $file_name .= $key . $value . '.'; } $file_name .= 'diff'; $file = PhabricatorFile::buildFromFileDataOrHash($raw_diff, array('name' => $file_name)); return id(new AphrontRedirectResponse())->setURI($file->getBestURI()); }
protected function buildAttachments() { $attachments = array(); if (PhabricatorEnv::getEnvConfig('metamta.differential.attach-patches')) { $revision = $this->getRevision(); $revision_id = $revision->getID(); $diffs = $revision->loadDiffs(); $diff_number = count($diffs); $diff = array_pop($diffs); $filename = "D{$revision_id}.{$diff_number}.patch"; $diff->attachChangesets($diff->loadChangesets()); // TODO: We could batch this to improve performance. foreach ($diff->getChangesets() as $changeset) { $changeset->attachHunks($changeset->loadHunks()); } $diff_dict = $diff->getDiffDict(); $changes = array(); foreach ($diff_dict['changes'] as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $bundle = ArcanistBundle::newFromChanges($changes); $unified_diff = $bundle->toUnifiedDiff(); $attachments[] = new PhabricatorMetaMTAAttachment($unified_diff, $filename, 'text/x-patch; charset=utf-8'); } return $attachments; }
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 loadOneChangeBundle($old, $new) { $diff = $this->loadDiff($old, $new); return ArcanistBundle::newFromDiff($diff); }
private function buildPatch() { $diff = new DifferentialDiff(); $diff->attachChangesets($this->getChangesets()); // TODO: We could batch this to improve performance. foreach ($diff->getChangesets() as $changeset) { $changeset->attachHunks($changeset->loadHunks()); } $diff_dict = $diff->getDiffDict(); $changes = array(); foreach ($diff_dict['changes'] as $changedict) { $changes[] = ArcanistDiffChange::newFromDictionary($changedict); } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setLoadFileDataCallback(array($this, 'loadFileByPHID')); $format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format'); switch ($format) { case 'git': return $bundle->toGitPatch(); break; case 'unified': default: return $bundle->toUnifiedDiff(); break; } }
private function normalizeDiff($text) { $changes = id(new ArcanistDiffParser())->parseDiff($text); ksort($changes); return ArcanistBundle::newFromChanges($changes)->toGitPatch(); }