public function processRequest() { $request = $this->getRequest(); $repository_phid = $request->getStr('repositoryPHID'); $repository = id(new PhabricatorRepository())->loadOneWhere('phid = %s', $repository_phid); if (!$repository) { return new Aphront400Response(); } $query_path = $request->getStr('q'); if (preg_match('@/$@', $query_path)) { $query_dir = $query_path; } else { $query_dir = dirname($query_path) . '/'; } $query_dir = ltrim($query_dir, '/'); $drequest = DiffusionRequest::newFromDictionary(array('repository' => $repository, 'path' => $query_dir)); $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); $paths = $browse_query->loadPaths(); $output = array(); foreach ($paths as $path) { $full_path = $query_dir . $path->getPath(); if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) { $full_path .= '/'; } $output[] = array('/' . $full_path, null, substr(md5($full_path), 0, 7)); } return id(new AphrontAjaxResponse())->setContent($output); }
public function willProcessRequest(array $data) { // This controller doesn't use blob/path stuff, just pass the dictionary // in directly instead of using the AphrontRequest parsing mechanism. $drequest = DiffusionRequest::newFromDictionary($data); $this->diffusionRequest = $drequest; }
protected function processDiffusionRequest(AphrontRequest $request) { $repository_phid = $request->getStr('repositoryPHID'); $repository = id(new PhabricatorRepositoryQuery())->setViewer($request->getUser())->withPHIDs(array($repository_phid))->executeOne(); if (!$repository) { return new Aphront400Response(); } $query_path = $request->getStr('q'); if (preg_match('@/$@', $query_path)) { $query_dir = $query_path; } else { $query_dir = dirname($query_path) . '/'; } $query_dir = ltrim($query_dir, '/'); $drequest = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'repository' => $repository, 'path' => $query_dir)); $this->setDiffusionRequest($drequest); $browse_results = DiffusionBrowseResultSet::newFromConduit($this->callConduitWithDiffusionRequest('diffusion.browsequery', array('path' => $drequest->getPath(), 'commit' => $drequest->getCommit()))); $paths = $browse_results->getPaths(); $output = array(); foreach ($paths as $path) { $full_path = $query_dir . $path->getPath(); if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) { $full_path .= '/'; } $output[] = array('/' . $full_path, null, substr(md5($full_path), 0, 7)); } return id(new AphrontAjaxResponse())->setContent($output); }
private static function loadDiffusionChangesForCommit($commit) { $repository = id(new PhabricatorRepository())->load($commit->getRepositoryID()); $data = array('user' => PhabricatorUser::getOmnipotentUser(), 'initFromConduit' => false, 'repository' => $repository, 'commit' => $commit->getCommitIdentifier()); $drequest = DiffusionRequest::newFromDictionary($data); $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); return $change_query->loadChanges(); }
public function getURI() { if ($this->isExternal) { return $this->externalURI; } $request = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $this->getRepository())); return $request->generateURI(array('action' => 'browse', 'path' => $this->getPath(), 'line' => $this->getLineNumber())); }
protected final function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $viewer = PhabricatorUser::getOmnipotentUser(); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.querycommits', array('repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $this->parseCommitWithRef($repository, $commit, $ref); }
public function fromPartial($partial_string) { $this->objectPHID = null; // Look for diffs $matches = array(); if (preg_match('/^D([1-9]\\d*)$/', $partial_string, $matches)) { $diff_id = $matches[1]; // TOOD: (T603) This is all slated for annihilation. $diff_rev = id(new DifferentialRevision())->load($diff_id); if (!$diff_rev) { throw new ReleephCommitFinderException("{$partial_string} does not refer to an existing diff."); } $commit_phids = $diff_rev->loadCommitPHIDs(); if (!$commit_phids) { throw new ReleephCommitFinderException("{$partial_string} has no commits associated with it yet."); } $this->objectPHID = $diff_rev->getPHID(); $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('phid IN (%Ls) ORDER BY epoch ASC', $commit_phids); return head($commits); } // Look for a raw commit number, or r<callsign><commit-number>. $repository = $this->releephProject->getRepository(); $dr_data = null; $matches = array(); if (preg_match('/^r(?P<callsign>[A-Z]+)(?P<commit>\\w+)$/', $partial_string, $matches)) { $callsign = $matches['callsign']; if ($callsign != $repository->getCallsign()) { throw new ReleephCommitFinderException(sprintf('%s is in a different repository to this Releeph project (%s).', $partial_string, $repository->getCallsign())); } else { $dr_data = $matches; } } else { $dr_data = array('callsign' => $repository->getCallsign(), 'commit' => $partial_string); } try { $dr_data['user'] = $this->getUser(); $dr = DiffusionRequest::newFromDictionary($dr_data); } catch (Exception $ex) { $message = "No commit matches {$partial_string}: " . $ex->getMessage(); throw new ReleephCommitFinderException($message); } $phabricator_repository_commit = $dr->loadCommit(); if (!$phabricator_repository_commit) { throw new ReleephCommitFinderException("The commit {$partial_string} doesn't exist in this repository."); } // When requesting a single commit, if it has an associated review we // imply the review was requested instead. This is always correct for now // and consistent with the older behavior, although it might not be the // right rule in the future. $phids = PhabricatorEdgeQuery::loadDestinationPHIDs($phabricator_repository_commit->getPHID(), PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV); if ($phids) { $this->objectPHID = head($phids); } return $phabricator_repository_commit; }
public function fromPartial($partial_string) { $this->objectPHID = null; // Look for diffs $matches = array(); if (preg_match('/^D([1-9]\\d*)$/', $partial_string, $matches)) { $diff_id = $matches[1]; $diff_rev = id(new DifferentialRevisionQuery())->setViewer($this->getUser())->withIDs(array($diff_id))->needCommitPHIDs(true)->executeOne(); if (!$diff_rev) { throw new ReleephCommitFinderException(pht('%s does not refer to an existing diff.', $partial_string)); } $commit_phids = $diff_rev->getCommitPHIDs(); if (!$commit_phids) { throw new ReleephCommitFinderException(pht('%s has no commits associated with it yet.', $partial_string)); } $this->objectPHID = $diff_rev->getPHID(); $commits = id(new DiffusionCommitQuery())->setViewer($this->getUser())->withPHIDs($commit_phids)->execute(); $commits = msort($commits, 'getEpoch'); return head($commits); } // Look for a raw commit number, or r<callsign><commit-number>. $repository = $this->releephProject->getRepository(); $dr_data = null; $matches = array(); if (preg_match('/^r(?P<callsign>[A-Z]+)(?P<commit>\\w+)$/', $partial_string, $matches)) { $callsign = $matches['callsign']; if ($callsign != $repository->getCallsign()) { throw new ReleephCommitFinderException(pht('%s is in a different repository to this Releeph project (%s).', $partial_string, $repository->getCallsign())); } else { $dr_data = $matches; } } else { $dr_data = array('callsign' => $repository->getCallsign(), 'commit' => $partial_string); } try { $dr_data['user'] = $this->getUser(); $dr = DiffusionRequest::newFromDictionary($dr_data); } catch (Exception $ex) { $message = pht('No commit matches %s: %s', $partial_string, $ex->getMessage()); throw new ReleephCommitFinderException($message); } $phabricator_repository_commit = $dr->loadCommit(); if (!$phabricator_repository_commit) { throw new ReleephCommitFinderException(pht("The commit %s doesn't exist in this repository.", $partial_string)); } // When requesting a single commit, if it has an associated review we // imply the review was requested instead. This is always correct for now // and consistent with the older behavior, although it might not be the // right rule in the future. $phids = PhabricatorEdgeQuery::loadDestinationPHIDs($phabricator_repository_commit->getPHID(), DiffusionCommitHasRevisionEdgeType::EDGECONST); if ($phids) { $this->objectPHID = head($phids); } return $phabricator_repository_commit; }
protected function execute(ConduitAPIRequest $request) { $drequest = DiffusionRequest::newFromDictionary(array('callsign' => $request->getValue('callsign'), 'path' => $request->getValue('path'))); $history = DiffusionHistoryQuery::newFromDiffusionRequest($drequest)->setLimit(self::RESULT_LIMIT)->needDirectChanges(true)->needChildChanges(true)->loadHistory(); $raw_commit_identifiers = mpull($history, 'getCommitIdentifier'); $result = array(); foreach ($raw_commit_identifiers as $id) { $result[] = 'r' . $request->getValue('callsign') . $id; } return $result; }
public function getURI() { if (!$this->repository) { // This symbol is in the index, but we don't know which Repository it's // part of. Usually this means the Arcanist Project hasn't been linked // to a Repository. We can't generate a URI, so just fail. return null; } $request = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $this->getRepository())); return $request->generateURI(array('action' => 'browse', 'path' => $this->getPath(), 'line' => $this->getLineNumber())); }
private function loadContext(array $options) { $request = $this->getRequest(); $viewer = $this->getViewer(); $identifier = $this->getRepositoryIdentifierFromRequest($request); $params = $options + array('repository' => $identifier, 'user' => $viewer, 'blob' => $this->getDiffusionBlobFromRequest($request), 'commit' => $request->getURIData('commit'), 'path' => $request->getURIData('path'), 'line' => $request->getURIData('line'), 'branch' => $request->getURIData('branch'), 'lint' => $request->getStr('lint')); $drequest = DiffusionRequest::newFromDictionary($params); if (!$drequest) { return new Aphront404Response(); } $this->diffusionRequest = $drequest; return null; }
protected function execute(ConduitAPIRequest $request) { $drequest = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'callsign' => $request->getValue('callsign'), 'path' => $request->getValue('path'), 'branch' => $request->getValue('branch'))); $limit = nonempty($request->getValue('limit'), self::DEFAULT_LIMIT); $history_result = DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $drequest, 'diffusion.historyquery', array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => $limit, 'needDirectChanges' => true, 'needChildChanges' => true)); $history = DiffusionPathChange::newFromConduit($history_result['pathChanges']); $raw_commit_identifiers = mpull($history, 'getCommitIdentifier'); $result = array(); foreach ($raw_commit_identifiers as $id) { $result[] = 'r' . $request->getValue('callsign') . $id; } return $result; }
protected final function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { if (!$this->shouldSkipImportStep()) { $viewer = PhabricatorUser::getOmnipotentUser(); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($viewer, DiffusionRequest::newFromDictionary(array('repository' => $repository, 'user' => $viewer)), 'diffusion.querycommits', array('repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $this->updateCommitData($ref); } if ($this->shouldQueueFollowupTasks()) { $this->queueTask($this->getFollowupTaskClass(), array('commitID' => $commit->getID()), array('priority' => PhabricatorWorker::PRIORITY_DEFAULT)); } }
public static function loadAffectedPaths(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, PhabricatorUser $user) { $drequest = DiffusionRequest::newFromDictionary(array('user' => $user, 'repository' => $repository, 'commit' => $commit->getCommitIdentifier())); $path_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); $paths = $path_query->loadChanges(); $result = array(); foreach ($paths as $path) { $basic_path = '/' . $path->getPath(); if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) { $basic_path = rtrim($basic_path, '/') . '/'; } $result[] = $basic_path; } return $result; }
public function renderResultsList(array $branches, PhabricatorSavedQuery $saved) { assert_instances_of($branches, 'ReleephBranch'); $viewer = $this->getRequest()->getUser(); $products = mpull($branches, 'getProduct'); $repo_phids = mpull($products, 'getRepositoryPHID'); $repos = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withPHIDs($repo_phids)->execute(); $repos = mpull($repos, null, 'getPHID'); $phids = mpull($branches, 'getCreatedByUserPHID'); $this->loadHandles($phids); $requests = array(); if ($branches) { $requests = id(new ReleephRequestQuery())->setViewer($viewer)->withBranchIDs(mpull($branches, 'getID'))->withStatus(ReleephRequestQuery::STATUS_OPEN)->execute(); $requests = mgroup($requests, 'getBranchID'); } $list = id(new PHUIObjectItemListView())->setUser($viewer); foreach ($branches as $branch) { $diffusion_href = null; $repo = idx($repos, $branch->getProduct()->getRepositoryPHID()); if ($repo) { $drequest = DiffusionRequest::newFromDictionary(array('user' => $viewer, 'repository' => $repo)); $diffusion_href = $drequest->generateURI(array('action' => 'branch', 'branch' => $branch->getName())); } $branch_link = $branch->getName(); if ($diffusion_href) { $branch_link = phutil_tag('a', array('href' => $diffusion_href), $branch_link); } $item = id(new PHUIObjectItemView())->setHeader($branch->getDisplayName())->setHref($this->getApplicationURI('branch/' . $branch->getID() . '/'))->addAttribute($branch_link); if (!$branch->getIsActive()) { $item->setDisabled(true); } $commit = $branch->getCutPointCommit(); if ($commit) { $item->addIcon('none', phabricator_datetime($commit->getEpoch(), $viewer)); } $open_count = count(idx($requests, $branch->getID(), array())); if ($open_count) { $item->setBarColor('orange'); $item->addIcon('fa-code-fork', pht('%d Open Pull Request(s)', new PhutilNumber($open_count))); } $list->addItem($item); } return $list; }
private function loadRawPatchText(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $drequest = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $repository, 'commit' => $commit->getCommitIdentifier())); $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); $raw_query->setLinesOfContext(3); $time_key = 'metamta.diffusion.time-limit'; $byte_key = 'metamta.diffusion.byte-limit'; $time_limit = PhabricatorEnv::getEnvConfig($time_key); $byte_limit = PhabricatorEnv::getEnvConfig($byte_key); if ($time_limit) { $raw_query->setTimeout($time_limit); } $raw_diff = $raw_query->loadRawDiff(); $size = strlen($raw_diff); if ($byte_limit && $size > $byte_limit) { $pretty_size = phutil_format_bytes($size); $pretty_limit = phutil_format_bytes($byte_limit); throw new Exception(pht('Patch size of %s exceeds configured byte size limit (%s) of %s.', $pretty_size, $byte_key, $pretty_limit)); } return $raw_diff; }
public function execute(PhutilArgumentParser $args) { $commits = $this->loadCommits($args, 'commits'); if (!$commits) { throw new PhutilArgumentUsageException(pht('Specify one or more commits to resolve users for.')); } $console = PhutilConsole::getConsole(); foreach ($commits as $commit) { $repo = $commit->getRepository(); $name = $repo->formatCommitName($commit->getCommitIdentifier()); $console->writeOut("%s\n", pht('Examining commit %s...', $name)); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest($this->getViewer(), DiffusionRequest::newFromDictionary(array('repository' => $repo, 'user' => $this->getViewer())), 'diffusion.querycommits', array('repositoryPHID' => $repo->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true)); if (empty($refs_raw['data'])) { throw new Exception(pht('Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $author = $ref->getAuthor(); $console->writeOut("%s\n", pht('Raw author string: %s', coalesce($author, 'null'))); if ($author !== null) { $handle = $this->resolveUser($commit, $author); if ($handle) { $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } $committer = $ref->getCommitter(); $console->writeOut("%s\n", pht('Raw committer string: %s', coalesce($committer, 'null'))); if ($committer !== null) { $handle = $this->resolveUser($commit, $committer); if ($handle) { $console->writeOut("%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut("%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } } return 0; }
public function processRequest() { $request = $this->getRequest(); $repository_phid = $request->getStr('repositoryPHID'); $repository = id(new PhabricatorRepository())->loadOneWhere('phid = %s', $repository_phid); if (!$repository) { return new Aphront400Response(); } $path = $request->getStr('path'); $path = ltrim($path, '/'); $drequest = DiffusionRequest::newFromDictionary(array('repository' => $repository, 'path' => $path)); $browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest); $browse_query->needValidityOnly(true); $valid = $browse_query->loadPaths(); if (!$valid) { switch ($browse_query->getReasonForEmptyResultSet()) { case DiffusionBrowseQuery::REASON_IS_FILE: $valid = true; break; case DiffusionBrowseQuery::REASON_IS_EMPTY: $valid = true; break; } } $output = array('valid' => (bool) $valid); if (!$valid) { $branch = $drequest->getBranch(); if ($branch) { $message = 'Not found in ' . $branch; } else { $message = 'Not found at HEAD'; } } else { $message = 'OK'; } $output['message'] = $message; return id(new AphrontAjaxResponse())->setContent($output); }
public function processRequest() { $request = $this->getRequest(); $repository_phid = $request->getStr('repositoryPHID'); $repository = id(new PhabricatorRepositoryQuery())->setViewer($request->getUser())->withPHIDs(array($repository_phid))->executeOne(); if (!$repository) { return new Aphront400Response(); } $path = $request->getStr('path'); $path = ltrim($path, '/'); $drequest = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'repository' => $repository, 'path' => $path)); $this->setDiffusionRequest($drequest); $browse_results = DiffusionBrowseResultSet::newFromConduit($this->callConduitWithDiffusionRequest('diffusion.browsequery', array('path' => $drequest->getPath(), 'commit' => $drequest->getCommit(), 'needValidityOnly' => true))); $valid = $browse_results->isValidResults(); if (!$valid) { switch ($browse_results->getReasonForEmptyResultSet()) { case DiffusionBrowseResultSet::REASON_IS_FILE: $valid = true; break; case DiffusionBrowseResultSet::REASON_IS_EMPTY: $valid = true; break; } } $output = array('valid' => (bool) $valid); if (!$valid) { $branch = $drequest->getBranch(); if ($branch) { $message = pht('Not found in %s', $branch); } else { $message = pht('Not found at HEAD'); } } else { $message = pht('OK'); } $output['message'] = $message; return id(new AphrontAjaxResponse())->setContent($output); }
/** * This method is final because most queries will need to construct a * @{class:DiffusionRequest} and use it. Consolidating this codepath and * enforcing @{method:getDiffusionRequest} works when we need it is good. * * @{method:getResult} should be overridden by subclasses as necessary, e.g. * there is a common operation across all version control systems that * should occur after @{method:getResult}, like formatting a timestamp. */ protected final function execute(ConduitAPIRequest $request) { $drequest = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'callsign' => $request->getValue('callsign'), 'branch' => $request->getValue('branch'), 'path' => $request->getValue('path'), 'commit' => $request->getValue('commit'))); // Figure out whether we're going to handle this request on this device, // or proxy it to another node in the cluster. // If this is a cluster request and we need to proxy, we'll explode here // to prevent infinite recursion. $is_cluster_request = $request->getIsClusterRequest(); $repository = $drequest->getRepository(); $client = $repository->newConduitClient($request->getUser(), $is_cluster_request); if ($client) { // We're proxying, so just make an intracluster call. return $client->callMethodSynchronous($this->getAPIMethodName(), $request->getAllParameters()); } else { // We pass this flag on to prevent proxying of any other Conduit calls // which we need to make in order to respond to this one. Although we // could safely proxy them, we take a big performance hit in the common // case, and doing more proxying wouldn't exercise any additional code so // we wouldn't gain a testability/predictability benefit. $drequest->setIsClusterRequest($is_cluster_request); $this->setDiffusionRequest($drequest); return $this->getResult($request); } }
private function loadContext(array $options) { $request = $this->getRequest(); $viewer = $this->getViewer(); $identifier = $this->getRepositoryIdentifierFromRequest($request); $params = $options + array('repository' => $identifier, 'user' => $viewer, 'blob' => $this->getDiffusionBlobFromRequest($request), 'commit' => $request->getURIData('commit'), 'path' => $request->getURIData('path'), 'line' => $request->getURIData('line'), 'branch' => $request->getURIData('branch'), 'lint' => $request->getStr('lint')); $drequest = DiffusionRequest::newFromDictionary($params); if (!$drequest) { return new Aphront404Response(); } // If the client is making a request like "/diffusion/1/...", but the // repository has a different canonical path like "/diffusion/XYZ/...", // redirect them to the canonical path. $request_path = $request->getPath(); $repository = $drequest->getRepository(); $canonical_path = $repository->getCanonicalPath($request_path); if ($canonical_path !== null) { if ($canonical_path != $request_path) { return id(new AphrontRedirectResponse())->setURI($canonical_path); } } $this->diffusionRequest = $drequest; return null; }
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(); }
protected function processDiffusionRequest(AphrontRequest $request) { $user = $request->getUser(); // This controller doesn't use blob/path stuff, just pass the dictionary // in directly instead of using the AphrontRequest parsing mechanism. $data = $request->getURIMap(); $data['user'] = $user; $drequest = DiffusionRequest::newFromDictionary($data); $this->diffusionRequest = $drequest; if ($request->getStr('diff')) { return $this->buildRawDiffResponse($drequest); } $repository = $drequest->getRepository(); $callsign = $repository->getCallsign(); $content = array(); $commit = id(new DiffusionCommitQuery())->setViewer($request->getUser())->withRepository($repository)->withIdentifiers(array($drequest->getCommit()))->needCommitData(true)->needAuditRequests(true)->executeOne(); $crumbs = $this->buildCrumbs(array('commit' => true)); if (!$commit) { $exists = $this->callConduitWithDiffusionRequest('diffusion.existsquery', array('commit' => $drequest->getCommit())); if (!$exists) { return new Aphront404Response(); } $error = id(new PHUIInfoView())->setTitle(pht('Commit Still Parsing'))->appendChild(pht('Failed to load the commit because the commit has not been ' . 'parsed yet.')); return $this->buildApplicationPage(array($crumbs, $error), array('title' => pht('Commit Still Parsing'))); } $audit_requests = $commit->getAudits(); $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user); $commit_data = $commit->getCommitData(); $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub'); if ($is_foreign) { $subpath = $commit_data->getCommitDetail('svn-subpath'); $error_panel = new PHUIInfoView(); $error_panel->setTitle(pht('Commit Not Tracked')); $error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING); $error_panel->appendChild(pht("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('%s'), so no " . "information is available.", $subpath)); $content[] = $error_panel; } else { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $engine->setConfig('viewer', $user); require_celerity_resource('phabricator-remarkup-css'); $parents = $this->callConduitWithDiffusionRequest('diffusion.commitparentsquery', array('commit' => $drequest->getCommit())); if ($parents) { $parents = id(new DiffusionCommitQuery())->setViewer($user)->withRepository($repository)->withIdentifiers($parents)->execute(); } $headsup_view = id(new PHUIHeaderView())->setHeader(nonempty($commit->getSummary(), pht('Commit Detail'))); $headsup_actions = $this->renderHeadsupActionList($commit, $repository); $commit_properties = $this->loadCommitProperties($commit, $commit_data, $parents, $audit_requests); $property_list = id(new PHUIPropertyListView())->setHasKeyboardShortcuts(true)->setUser($user)->setObject($commit); foreach ($commit_properties as $key => $value) { $property_list->addProperty($key, $value); } $message = $commit_data->getCommitMessage(); $revision = $commit->getCommitIdentifier(); $message = $this->linkBugtraq($message); $message = $engine->markupText($message); $property_list->invokeWillRenderEvent(); $property_list->setActionList($headsup_actions); $detail_list = new PHUIPropertyListView(); $detail_list->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $detail_list->addTextContent(phutil_tag('div', array('class' => 'diffusion-commit-message phabricator-remarkup'), $message)); $headsup_view->setTall(true); $object_box = id(new PHUIObjectBoxView())->setHeader($headsup_view)->addPropertyList($property_list)->addPropertyList($detail_list); $content[] = $object_box; } $content[] = $this->buildComments($commit); $hard_limit = 1000; if ($commit->isImported()) { $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); $change_query->setLimit($hard_limit + 1); $changes = $change_query->loadChanges(); } else { $changes = array(); } $was_limited = count($changes) > $hard_limit; if ($was_limited) { $changes = array_slice($changes, 0, $hard_limit); } $content[] = $this->buildMergesTable($commit); $highlighted_audits = $commit->getAuthorityAudits($user, $this->auditAuthorityPHIDs); $count = count($changes); $bad_commit = null; if ($count == 0) { $bad_commit = queryfx_one(id(new PhabricatorRepository())->establishConnection('r'), 'SELECT * FROM %T WHERE fullCommitName = %s', PhabricatorRepository::TABLE_BADCOMMIT, 'r' . $callsign . $commit->getCommitIdentifier()); } $show_changesets = false; if ($bad_commit) { $content[] = $this->renderStatusMessage(pht('Bad Commit'), $bad_commit['description']); } else { if ($is_foreign) { // Don't render anything else. } else { if (!$commit->isImported()) { $content[] = $this->renderStatusMessage(pht('Still Importing...'), pht('This commit is still importing. Changes will be visible once ' . 'the import finishes.')); } else { if (!count($changes)) { $content[] = $this->renderStatusMessage(pht('Empty Commit'), pht('This commit is empty and does not affect any paths.')); } else { if ($was_limited) { $content[] = $this->renderStatusMessage(pht('Enormous Commit'), pht('This commit is enormous, and affects more than %d files. ' . 'Changes are not shown.', $hard_limit)); } else { $show_changesets = true; // The user has clicked "Show All Changes", and we should show all the // changes inline even if there are more than the soft limit. $show_all_details = $request->getBool('show_all'); $change_panel = new PHUIObjectBoxView(); $header = new PHUIHeaderView(); $header->setHeader(pht('Changes (%s)', new PhutilNumber($count))); $change_panel->setID('toc'); if ($count > self::CHANGES_LIMIT && !$show_all_details) { $icon = id(new PHUIIconView())->setIconFont('fa-files-o'); $button = id(new PHUIButtonView())->setText(pht('Show All Changes'))->setHref('?show_all=true')->setTag('a')->setIcon($icon); $warning_view = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setTitle(pht('Very Large Commit'))->appendChild(pht('This commit is very large. Load each file individually.')); $change_panel->setInfoView($warning_view); $header->addActionLink($button); } $changesets = DiffusionPathChange::convertToDifferentialChangesets($user, $changes); // TODO: This table and panel shouldn't really be separate, but we need // to clean up the "Load All Files" interaction first. $change_table = $this->buildTableOfContents($changesets); $change_panel->setTable($change_table); $change_panel->setHeader($header); $content[] = $change_panel; $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $vcs_supports_directory_changes = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $vcs_supports_directory_changes = false; break; default: throw new Exception(pht('Unknown VCS.')); } $references = array(); foreach ($changesets as $key => $changeset) { $file_type = $changeset->getFileType(); if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { if (!$vcs_supports_directory_changes) { unset($changesets[$key]); continue; } } $references[$key] = $drequest->generateURI(array('action' => 'rendering-ref', 'path' => $changeset->getFilename())); } // TODO: Some parts of the views still rely on properties of the // DifferentialChangeset. Make the objects ephemeral to make sure we don't // accidentally save them, and then set their ID to the appropriate ID for // this application (the path IDs). $path_ids = array_flip(mpull($changes, 'getPath')); foreach ($changesets as $changeset) { $changeset->makeEphemeral(); $changeset->setID($path_ids[$changeset->getFilename()]); } if ($count <= self::CHANGES_LIMIT || $show_all_details) { $visible_changesets = $changesets; } else { $visible_changesets = array(); $inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments($user, $commit->getPHID()); $path_ids = mpull($inlines, null, 'getPathID'); foreach ($changesets as $key => $changeset) { if (array_key_exists($changeset->getID(), $path_ids)) { $visible_changesets[$key] = $changeset; } } } $change_list_title = DiffusionView::nameCommit($repository, $commit->getCommitIdentifier()); $change_list = new DifferentialChangesetListView(); $change_list->setTitle($change_list_title); $change_list->setChangesets($changesets); $change_list->setVisibleChangesets($visible_changesets); $change_list->setRenderingReferences($references); $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/'); $change_list->setRepository($repository); $change_list->setUser($user); // TODO: Try to setBranch() to something reasonable here? $change_list->setStandaloneURI('/diffusion/' . $callsign . '/diff/'); $change_list->setRawFileURIs(null, '/diffusion/' . $callsign . '/diff/?view=r'); $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/'); $content[] = $change_list->render(); } } } } } $content[] = $this->renderAddCommentPanel($commit, $audit_requests); $commit_id = 'r' . $callsign . $commit->getCommitIdentifier(); $short_name = DiffusionView::nameCommit($repository, $commit->getCommitIdentifier()); $prefs = $user->loadPreferences(); $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE; $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED; $show_filetree = $prefs->getPreference($pref_filetree); $collapsed = $prefs->getPreference($pref_collapse); if ($show_changesets && $show_filetree) { $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())->setTitle($short_name)->setBaseURI(new PhutilURI('/' . $commit_id))->build($changesets)->setCrumbs($crumbs)->setCollapsed((bool) $collapsed)->appendChild($content); $content = $nav; } else { $content = array($crumbs, $content); } return $this->buildApplicationPage($content, array('title' => $commit_id, 'pageObjects' => array($commit->getPHID()))); }
public function willProcessRequest(array $data) { $this->diffusionRequest = DiffusionRequest::newFromDictionary($data); }
private function isCommitOnBranch(PhabricatorRepository $repo, PhabricatorRepositoryCommit $commit, ReleephBranch $releeph_branch) { switch ($repo->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: list($output) = $repo->execxLocalCommand('branch --all --no-color --contains %s', $commit->getCommitIdentifier()); $remote_prefix = 'remotes/origin/'; $branches = array(); foreach (array_filter(explode("\n", $output)) as $line) { $tokens = explode(' ', $line); $ref = last($tokens); if (strncmp($ref, $remote_prefix, strlen($remote_prefix)) === 0) { $branch = substr($ref, strlen($remote_prefix)); $branches[$branch] = $branch; } } return idx($branches, $releeph_branch->getName()); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(DiffusionRequest::newFromDictionary(array('user' => $this->getUser(), 'repository' => $repo, 'commit' => $commit->getCommitIdentifier()))); $path_changes = $change_query->loadChanges(); $commit_paths = mpull($path_changes, 'getPath'); $branch_path = $releeph_branch->getName(); $in_branch = array(); $ex_branch = array(); foreach ($commit_paths as $path) { if (strncmp($path, $branch_path, strlen($branch_path)) === 0) { $in_branch[] = $path; } else { $ex_branch[] = $path; } } if ($in_branch && $ex_branch) { $error = pht('CONFUSION: commit %s in %s contains %d path change(s) that were ' . 'part of a Releeph branch, but also has %d path change(s) not ' . 'part of a Releeph branch!', $commit->getCommitIdentifier(), $repo->getCallsign(), count($in_branch), count($ex_branch)); phlog($error); } return !empty($in_branch); break; } }
private function loadChangedByCommit(DifferentialDiff $diff) { $repository = $this->repository; $vs_changesets = array(); $vs_diff = id(new DifferentialDiff())->loadOneWhere('revisionID = %d AND creationMethod != %s ORDER BY id DESC LIMIT 1', $diff->getRevisionID(), 'commit'); foreach ($vs_diff->loadChangesets() as $changeset) { $path = $changeset->getAbsoluteRepositoryPath($repository, $vs_diff); $path = ltrim($path, '/'); $vs_changesets[$path] = $changeset; } $changesets = array(); foreach ($diff->getChangesets() as $changeset) { $path = $changeset->getAbsoluteRepositoryPath($repository, $diff); $path = ltrim($path, '/'); $changesets[$path] = $changeset; } if (array_fill_keys(array_keys($changesets), true) != array_fill_keys(array_keys($vs_changesets), true)) { return $vs_diff; } $hunks = id(new DifferentialHunk())->loadAllWhere('changesetID IN (%Ld)', mpull($vs_changesets, 'getID')); $hunks = mgroup($hunks, 'getChangesetID'); foreach ($vs_changesets as $changeset) { $changeset->attachHunks(idx($hunks, $changeset->getID(), array())); } $file_phids = array(); foreach ($vs_changesets as $changeset) { $metadata = $changeset->getMetadata(); $file_phid = idx($metadata, 'new:binary-phid'); if ($file_phid) { $file_phids[$file_phid] = $file_phid; } } $files = array(); if ($file_phids) { $files = id(new PhabricatorFile())->loadAllWhere('phid IN (%Ls)', $file_phids); $files = mpull($files, null, 'getPHID'); } foreach ($changesets as $path => $changeset) { $vs_changeset = $vs_changesets[$path]; $file_phid = idx($vs_changeset->getMetadata(), 'new:binary-phid'); if ($file_phid) { if (!isset($files[$file_phid])) { return $vs_diff; } $drequest = DiffusionRequest::newFromDictionary(array('repository' => $this->repository, 'commit' => $this->commit->getCommitIdentifier(), 'path' => $path)); $corpus = DiffusionFileContentQuery::newFromDiffusionRequest($drequest)->loadFileContent()->getCorpus(); if ($files[$file_phid]->loadFileData() != $corpus) { return $vs_diff; } } else { $context = implode("\n", $changeset->makeChangesWithContext()); $vs_context = implode("\n", $vs_changeset->makeChangesWithContext()); // We couldn't just compare $context and $vs_context because following // diffs will be considered different: // // -(empty line) // -echo 'test'; // (empty line) // // (empty line) // -echo "test"; // -(empty line) $hunk = id(new DifferentialHunk())->setChanges($context); $vs_hunk = id(new DifferentialHunk())->setChanges($vs_context); if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() || $hunk->makeNewFile() != $vs_hunk->makeNewFile()) { return $vs_diff; } } } return null; }
protected function getResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $path_dicts = $request->getValue('paths', array()); $paths = array(); foreach ($path_dicts as $dict) { $paths[] = DiffusionRepositoryPath::newFromDictionary($dict); } $best = -1; $readme = ''; $best_render_type = 'plain'; foreach ($paths as $result_path) { $file_type = $result_path->getFileType(); if ($file_type != ArcanistDiffChangeType::FILE_NORMAL && $file_type != ArcanistDiffChangeType::FILE_TEXT) { // Skip directories, etc. continue; } $path = strtolower($result_path->getPath()); if ($path === 'readme') { $path .= '.remarkup'; } if (strncmp($path, 'readme.', 7) !== 0) { continue; } $priority = 0; switch (substr($path, 7)) { case 'remarkup': $priority = 100; $render_type = 'remarkup'; break; case 'rainbow': $priority = 90; $render_type = 'rainbow'; break; case 'md': $priority = 50; $render_type = 'remarkup'; break; case 'txt': $priority = 10; $render_type = 'plain'; break; default: $priority = 0; $render_type = 'plain'; break; } if ($priority > $best) { $best = $priority; $readme = $result_path; $best_render_type = $render_type; } } if (!$readme) { return ''; } $readme_request = DiffusionRequest::newFromDictionary(array('user' => $request->getUser(), 'repository' => $drequest->getRepository(), 'commit' => $drequest->getStableCommit(), 'path' => $readme->getFullPath())); $file_content = DiffusionFileContent::newFromConduit(DiffusionQuery::callConduitWithDiffusionRequest($request->getUser(), $readme_request, 'diffusion.filecontentquery', array('commit' => $drequest->getStableCommit(), 'path' => $readme->getFullPath(), 'needsBlame' => false))); $readme_content = $file_content->getCorpus(); switch ($best_render_type) { case 'plain': $readme_content = phutil_escape_html_newlines($readme_content); $class = null; break; case 'rainbow': $highlighter = new PhutilRainbowSyntaxHighlighter(); $readme_content = $highlighter->getHighlightFuture($readme_content)->resolve(); $readme_content = phutil_escape_html_newlines($readme_content); require_celerity_resource('syntax-highlighting-css'); $class = 'remarkup-code'; break; case 'remarkup': // TODO: This is sketchy, but make sure we hit the markup cache. $markup_object = id(new PhabricatorMarkupOneOff())->setEngineRuleset('diffusion-readme')->setContent($readme_content); $markup_field = 'default'; $readme_content = id(new PhabricatorMarkupEngine())->setViewer($request->getUser())->addObject($markup_object, $markup_field)->process()->getOutput($markup_object, $markup_field); $engine = $markup_object->newMarkupEngine($markup_field); $toc = PhutilRemarkupHeaderBlockRule::renderTableOfContents($engine); if ($toc) { $toc = phutil_tag_div('phabricator-remarkup-toc', array(phutil_tag_div('phabricator-remarkup-toc-header', pht('Table of Contents')), $toc)); $readme_content = array($toc, $readme_content); } $class = 'phabricator-remarkup'; break; } $readme_content = phutil_tag('div', array('class' => $class), $readme_content); return $readme_content; }
public function getDiffusionBrowseURIForPath(PhabricatorUser $user, $path, $line = null, $branch = null) { $drequest = DiffusionRequest::newFromDictionary(array('user' => $user, 'repository' => $this, 'path' => $path, 'branch' => $branch)); return $drequest->generateURI(array('action' => 'browse', 'line' => $line)); }
private function blameAuthors() { $repository = id(new PhabricatorRepositoryQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withIDs(array($this->branch->getRepositoryID()))->executeOne(); $queries = array(); $futures = array(); foreach ($this->blame as $path => $lines) { $drequest = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $repository, 'branch' => $this->branch->getName(), 'path' => $path, 'commit' => $this->lintCommit)); // TODO: Restore blame information / generally fix this workflow. $query = DiffusionFileContentQuery::newFromDiffusionRequest($drequest); $queries[$path] = $query; $futures[$path] = $query->getFileContentFuture(); } $authors = array(); $futures = id(new FutureIterator($futures))->limit(8); foreach ($futures as $path => $future) { $queries[$path]->loadFileContentFromFuture($future); list(, $rev_list, $blame_dict) = $queries[$path]->getBlameData(); foreach (array_keys($this->blame[$path]) as $line) { $commit_identifier = $rev_list[$line - 1]; $author = idx($blame_dict[$commit_identifier], 'authorPHID'); if ($author) { $authors[$author][$path][] = $line; } } } if ($authors) { $this->conn->openTransaction(); foreach ($authors as $author => $paths) { $where = array(); foreach ($paths as $path => $lines) { $where[] = qsprintf($this->conn, '(path = %s AND line IN (%Ld))', $this->svnRoot . '/' . $path, $lines); } queryfx($this->conn, 'UPDATE %T SET authorPHID = %s WHERE %Q', PhabricatorRepository::TABLE_LINTMESSAGE, $author, implode(' OR ', $where)); } $this->conn->saveTransaction(); } }
public function getDiffusionBrowseURIForPath($path) { $drequest = DiffusionRequest::newFromDictionary(array('repository' => $this, 'path' => $path)); return $drequest->generateURI(array('action' => 'browse')); }