@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Save blame info to lint messages

Test Plan:
Applied the patch.
Looked at blame and plain blame of SVN and Git file.
Ran the lint saver.
Looked at lint messages list.
/diffusion/lint/

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Differential Revision: https://secure.phabricator.com/D5218

vrana 1091dc7a 8ec987dd

+178 -69
+3
resources/sql/patches/20130304.lintauthor.sql
··· 1 + ALTER TABLE `{$NAMESPACE}_repository`.`repository_lintmessage` 2 + ADD authorPHID varchar(64) COLLATE utf8_bin AFTER line, 3 + ADD INDEX key_author (authorPHID);
+5
scripts/repository/save_lint.php
··· 39 39 'default' => 256, 40 40 'help' => "Number of paths passed to `arc` at once.", 41 41 ), 42 + array( 43 + 'name' => 'blame', 44 + 'help' => "Assign lint errors to authors who last modified the line.", 45 + ), 42 46 )); 43 47 44 48 echo "Saving lint errors to database...\n"; ··· 48 52 ->setArc($args->getArg('arc')) 49 53 ->setSeverity($args->getArg('severity')) 50 54 ->setChunkSize($args->getArg('chunk-size')) 55 + ->setNeedsBlame($args->getArg('blame')) 51 56 ->run('.'); 52 57 53 58 echo "\nProcessed {$count} files.\n";
+85 -8
src/applications/diffusion/DiffusionLintSaveRunner.php
··· 5 5 private $severity = ArcanistLintSeverity::SEVERITY_ADVICE; 6 6 private $all = false; 7 7 private $chunkSize = 256; 8 + private $needsBlame = false; 8 9 9 10 private $svnRoot; 10 11 private $lintCommit; ··· 12 13 private $conn; 13 14 private $deletes = array(); 14 15 private $inserts = array(); 16 + private $blame = array(); 15 17 16 18 17 19 public function setArc($path) { ··· 34 36 return $this; 35 37 } 36 38 39 + public function setNeedsBlame($boolean) { 40 + $this->needsBlame = $boolean; 41 + return $this; 42 + } 43 + 37 44 38 45 public function run($dir) { 39 46 $working_copy = ArcanistWorkingCopyIdentity::newFromPath($dir); ··· 44 51 $svn_fetch = $api->getGitConfig('svn-remote.svn.fetch'); 45 52 list($this->svnRoot) = explode(':', $svn_fetch); 46 53 if ($this->svnRoot != '') { 47 - $this->svnRoot = '/' . $this->svnRoot; 54 + $this->svnRoot = '/'.$this->svnRoot; 48 55 } 49 56 } 50 57 ··· 98 105 $all_files = $api->getAllFiles(); 99 106 } 100 107 101 - $this->deletes = array(); 102 - $this->inserts = array(); 103 108 $count = 0; 104 109 105 110 $files = array(); ··· 123 128 124 129 $this->runArcLint($files); 125 130 $this->saveLintMessages(); 126 - $this->branch->setLintCommit($api->getUnderlyingWorkingCopyRevision()); 131 + 132 + $this->lintCommit = $api->getUnderlyingWorkingCopyRevision(); 133 + $this->branch->setLintCommit($this->lintCommit); 127 134 $this->branch->save(); 135 + 136 + if ($this->blame) { 137 + $this->blameAuthors(); 138 + $this->blame = array(); 139 + } 128 140 129 141 return $count; 130 142 } ··· 156 168 } 157 169 158 170 foreach ($messages as $message) { 171 + $line = idx($message, 'line', 0); 172 + 159 173 $this->inserts[] = qsprintf( 160 174 $this->conn, 161 175 '(%d, %s, %d, %s, %s, %s, %s)', 162 176 $this->branch->getID(), 163 177 $this->svnRoot.'/'.$path, 164 - idx($message, 'line', 0), 178 + $line, 165 179 idx($message, 'code', ''), 166 180 idx($message, 'severity', ''), 167 181 idx($message, 'name', ''), 168 182 idx($message, 'description', '')); 183 + 184 + if ($line && $this->needsBlame) { 185 + $this->blame[$path][$line] = true; 186 + } 169 187 } 170 188 171 189 if (count($this->deletes) >= 1024 || count($this->inserts) >= 256) { 172 - $this->saveLintMessages($this->branch); 173 - $this->deletes = array(); 174 - $this->inserts = array(); 190 + $this->saveLintMessages(); 175 191 } 176 192 } 177 193 } ··· 205 221 } 206 222 207 223 $this->conn->saveTransaction(); 224 + 225 + $this->deletes = array(); 226 + $this->inserts = array(); 227 + } 228 + 229 + 230 + private function blameAuthors() { 231 + $repository = id(new PhabricatorRepository())->load( 232 + $this->branch->getRepositoryID()); 233 + 234 + $queries = array(); 235 + $futures = array(); 236 + foreach ($this->blame as $path => $lines) { 237 + $drequest = DiffusionRequest::newFromDictionary(array( 238 + 'repository' => $repository, 239 + 'branch' => $this->branch->getName(), 240 + 'path' => $path, 241 + 'commit' => $this->lintCommit, 242 + )); 243 + $query = DiffusionFileContentQuery::newFromDiffusionRequest($drequest) 244 + ->setNeedsBlame(true); 245 + $queries[$path] = $query; 246 + $futures[$path] = $query->getFileContentFuture(); 247 + } 248 + 249 + $authors = array(); 250 + 251 + foreach (Futures($futures)->limit(8) as $path => $future) { 252 + $queries[$path]->loadFileContentFromFuture($future); 253 + list(, $rev_list, $blame_dict) = $queries[$path]->getBlameData(); 254 + foreach (array_keys($this->blame[$path]) as $line) { 255 + $commit_identifier = $rev_list[$line - 1]; 256 + $author = idx($blame_dict[$commit_identifier], 'authorPHID'); 257 + if ($author) { 258 + $authors[$author][$path][] = $line; 259 + } 260 + } 261 + } 262 + 263 + if ($authors) { 264 + $this->conn->openTransaction(); 265 + 266 + foreach ($authors as $author => $paths) { 267 + $where = array(); 268 + foreach ($paths as $path => $lines) { 269 + $where[] = qsprintf( 270 + $this->conn, 271 + '(path = %s AND line IN (%Ld))', 272 + $this->svnRoot.'/'.$path, 273 + $lines); 274 + } 275 + queryfx( 276 + $this->conn, 277 + 'UPDATE %T SET authorPHID = %s WHERE %Q', 278 + PhabricatorRepository::TABLE_LINTMESSAGE, 279 + $author, 280 + implode(' OR ', $where)); 281 + } 282 + 283 + $this->conn->saveTransaction(); 284 + } 208 285 } 209 286 210 287 }
+6 -7
src/applications/diffusion/controller/DiffusionBrowseFileController.php
··· 202 202 $rows = array(); 203 203 foreach ($text_list as $k => $line) { 204 204 $rev = $rev_list[$k]; 205 - if (isset($blame_dict[$rev]['handle'])) { 206 - $author = $blame_dict[$rev]['handle']->getName(); 207 - } else { 208 - $author = $blame_dict[$rev]['author']; 209 - } 205 + $author = $blame_dict[$rev]['author']; 210 206 $rows[] = 211 207 sprintf("%-10s %-20s %s", substr($rev, 0, 7), $author, $line); 212 208 } ··· 430 426 DiffusionFileContentQuery $file_query, 431 427 $selected) { 432 428 429 + $handles = array(); 433 430 if ($blame_dict) { 434 431 $epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch'); 435 432 $epoch_min = min($epoch_list); 436 433 $epoch_max = max($epoch_list); 437 434 $epoch_range = ($epoch_max - $epoch_min) + 1; 435 + $handles = $this->loadViewerHandles(ipull($blame_dict, 'authorPHID')); 438 436 } 439 437 440 438 $line_arr = array(); ··· 499 497 $display_line['color'] = $color; 500 498 $display_line['commit'] = $rev; 501 499 502 - if (isset($blame['handle'])) { 503 - $author_link = $blame['handle']->renderLink(); 500 + $author_phid = idx($blame, 'authorPHID'); 501 + if ($author_phid && $handles[$author_phid]) { 502 + $author_link = $handles[$author_phid]->renderLink(); 504 503 } else { 505 504 $author_link = phutil_tag( 506 505 'span',
+18 -20
src/applications/diffusion/controller/DiffusionLintController.php
··· 169 169 } 170 170 171 171 if ($owner_phids) { 172 + $or = array(); 173 + $or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids); 174 + 175 + $paths = array(); 172 176 $packages = id(new PhabricatorOwnersOwner()) 173 177 ->loadAllWhere('userPHID IN (%Ls)', $owner_phids); 174 - if (!$packages) { 175 - return array(); 178 + if ($packages) { 179 + $paths = id(new PhabricatorOwnersPath())->loadAllWhere( 180 + 'packageID IN (%Ld)', 181 + mpull($packages, 'getPackageID')); 176 182 } 177 183 178 - $paths = id(new PhabricatorOwnersPath()) 179 - ->loadAllWhere('packageID IN (%Ld)', array_keys($packages)); 180 - if (!$paths) { 181 - return array(); 182 - } 183 - 184 - $repositories = id(new PhabricatorRepository())->loadAllWhere( 185 - 'phid IN (%Ls)', 186 - array_unique(mpull($paths, 'getRepositoryPHID'))); 187 - $repositories = mpull($repositories, 'getID', 'getPHID'); 184 + if ($paths) { 185 + $repositories = id(new PhabricatorRepository())->loadAllWhere( 186 + 'phid IN (%Ls)', 187 + array_unique(mpull($paths, 'getRepositoryPHID'))); 188 + $repositories = mpull($repositories, 'getID', 'getPHID'); 188 189 189 - $branches = id(new PhabricatorRepositoryBranch())->loadAllWhere( 190 - 'repositoryID IN (%Ld)', 191 - $repositories); 192 - $branches = mgroup($branches, 'getRepositoryID'); 190 + $branches = id(new PhabricatorRepositoryBranch())->loadAllWhere( 191 + 'repositoryID IN (%Ld)', 192 + $repositories); 193 + $branches = mgroup($branches, 'getRepositoryID'); 194 + } 193 195 194 - $or = array(); 195 196 foreach ($paths as $path) { 196 197 $branch = idx($branches, $repositories[$path->getRepositoryPHID()]); 197 198 if ($branch) { ··· 206 207 $or[] = $condition; 207 208 } 208 209 } 209 - } 210 - if (!$or) { 211 - return array(); 212 210 } 213 211 $where[] = '('.implode(' OR ', $or).')'; 214 212 }
+10 -1
src/applications/diffusion/controller/DiffusionLintDetailsController.php
··· 11 11 $messages = $this->loadLintMessages($branch, $limit, $offset); 12 12 $is_dir = (substr('/'.$drequest->getPath(), -1) == '/'); 13 13 14 + $authors = $this->loadViewerHandles(ipull($messages, 'authorPHID')); 15 + 14 16 $rows = array(); 15 17 foreach ($messages as $message) { 16 18 $path = hsprintf( ··· 31 33 )), 32 34 $message['line']); 33 35 36 + $author = $message['authorPHID']; 37 + if ($author && $authors[$author]) { 38 + $author = $authors[$author]->renderLink(); 39 + } 40 + 34 41 $rows[] = array( 35 42 $path, 36 43 $line, 44 + $author, 37 45 ArcanistLintSeverity::getStringForSeverity($message['severity']), 38 46 $message['name'], 39 47 $message['description'], ··· 44 52 ->setHeaders(array( 45 53 'Path', 46 54 'Line', 55 + 'Author', 47 56 'Severity', 48 57 'Name', 49 58 'Description', 50 59 )) 51 - ->setColumnClasses(array('', 'n', '', '', '')) 60 + ->setColumnClasses(array('', 'n')) 52 61 ->setColumnVisibility(array($is_dir)); 53 62 54 63 $content = array();
+22 -19
src/applications/diffusion/query/filecontent/DiffusionFileContentQuery.php
··· 11 11 return parent::newQueryObject(__CLASS__, $request); 12 12 } 13 13 14 - final public function loadFileContent() { 15 - $this->fileContent = $this->executeQuery(); 14 + abstract public function getFileContentFuture(); 15 + abstract protected function executeQueryFromFuture(Future $future); 16 + 17 + final public function loadFileContentFromFuture(Future $future) { 18 + $this->fileContent = $this->executeQueryFromFuture($future); 16 19 17 20 $repository = $this->getRequest()->getRepository(); 18 21 $try_encoding = $repository->getDetail('encoding'); ··· 23 26 } 24 27 25 28 return $this->fileContent; 29 + } 30 + 31 + final protected function executeQuery() { 32 + return $this->loadFileContentFromFuture($this->getFileContentFuture()); 33 + } 34 + 35 + final public function loadFileContent() { 36 + return $this->executeQuery(); 26 37 } 27 38 28 39 final public function getRawData() { ··· 72 83 $commit->getEpoch(); 73 84 } 74 85 75 - $commits_data = array(); 76 86 if ($commits) { 77 87 $commits_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 78 88 'commitID IN (%Ls)', 79 89 mpull($commits, 'getID')); 80 - } 81 - 82 - $phids = array(); 83 - foreach ($commits_data as $data) { 84 - $phids[] = $data->getCommitDetail('authorPHID'); 85 - } 86 90 87 - $loader = new PhabricatorObjectHandleData(array_unique($phids)); 88 - $loader->setViewer($this->viewer); 89 - $handles = $loader->loadHandles(); 90 - 91 - foreach ($commits_data as $data) { 92 - if ($data->getCommitDetail('authorPHID')) { 93 - $commit_identifier = 94 - $commits[$data->getCommitID()]->getCommitIdentifier(); 95 - $blame_dict[$commit_identifier]['handle'] = 96 - $handles[$data->getCommitDetail('authorPHID')]; 91 + foreach ($commits_data as $data) { 92 + $author_phid = $data->getCommitDetail('authorPHID'); 93 + if (!$author_phid) { 94 + continue; 95 + } 96 + $commit = $commits[$data->getCommitID()]; 97 + $commit_identifier = $commit->getCommitIdentifier(); 98 + $blame_dict[$commit_identifier]['authorPHID'] = $author_phid; 97 99 } 98 100 } 101 + 99 102 } 100 103 101 104 return array($text_list, $rev_list, $blame_dict);
+7 -4
src/applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php
··· 2 2 3 3 final class DiffusionGitFileContentQuery extends DiffusionFileContentQuery { 4 4 5 - protected function executeQuery() { 5 + public function getFileContentFuture() { 6 6 $drequest = $this->getRequest(); 7 7 8 8 $repository = $drequest->getRepository(); ··· 10 10 $commit = $drequest->getCommit(); 11 11 12 12 if ($this->getNeedsBlame()) { 13 - list($corpus) = $repository->execxLocalCommand( 13 + return $repository->getLocalCommandFuture( 14 14 '--no-pager blame -c -l --date=short %s -- %s', 15 15 $commit, 16 16 $path); 17 17 } else { 18 - list($corpus) = $repository->execxLocalCommand( 18 + return $repository->getLocalCommandFuture( 19 19 'cat-file blob %s:%s', 20 20 $commit, 21 21 $path); 22 22 } 23 + } 24 + 25 + protected function executeQueryFromFuture(Future $future) { 26 + list($corpus) = $future->resolvex(); 23 27 24 28 $file_content = new DiffusionFileContent(); 25 29 $file_content->setCorpus($corpus); 26 30 27 31 return $file_content; 28 32 } 29 - 30 33 31 34 protected function tokenizeLine($line) { 32 35 $m = array();
+7 -3
src/applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php
··· 3 3 final class DiffusionMercurialFileContentQuery 4 4 extends DiffusionFileContentQuery { 5 5 6 - protected function executeQuery() { 6 + public function getFileContentFuture() { 7 7 $drequest = $this->getRequest(); 8 8 9 9 $repository = $drequest->getRepository(); ··· 13 13 if ($this->getNeedsBlame()) { 14 14 // NOTE: We're using "--number" instead of "--changeset" because there is 15 15 // no way to get "--changeset" to show us the full commit hashes. 16 - list($corpus) = $repository->execxLocalCommand( 16 + return $repository->getLocalCommandFuture( 17 17 'annotate --user --number --rev %s -- %s', 18 18 $commit, 19 19 $path); 20 20 } else { 21 - list($corpus) = $repository->execxLocalCommand( 21 + return $repository->getLocalCommandFuture( 22 22 'cat --rev %s -- %s', 23 23 $commit, 24 24 $path); 25 25 } 26 + } 27 + 28 + protected function executeQueryFromFuture(Future $future) { 29 + list($corpus) = $future->resolvex(); 26 30 27 31 $file_content = new DiffusionFileContent(); 28 32 $file_content->setCorpus($corpus);
+11 -7
src/applications/diffusion/query/filecontent/DiffusionSvnFileContentQuery.php
··· 2 2 3 3 final class DiffusionSvnFileContentQuery extends DiffusionFileContentQuery { 4 4 5 - protected function executeQuery() { 5 + public function getFileContentFuture() { 6 6 $drequest = $this->getRequest(); 7 7 8 8 $repository = $drequest->getRepository(); ··· 11 11 12 12 $remote_uri = $repository->getRemoteURI(); 13 13 14 + return $repository->getRemoteCommandFuture( 15 + '%C %s%s@%s', 16 + $this->getNeedsBlame() ? 'blame --force' : 'cat', 17 + $remote_uri, 18 + phutil_escape_uri($path), 19 + $commit); 20 + } 21 + 22 + protected function executeQueryFromFuture(Future $future) { 14 23 try { 15 - list($corpus) = $repository->execxRemoteCommand( 16 - '%C %s%s@%s', 17 - $this->getNeedsBlame() ? 'blame --force' : 'cat', 18 - $remote_uri, 19 - phutil_escape_uri($path), 20 - $commit); 24 + list($corpus) = $future->resolvex(); 21 25 } catch (CommandException $ex) { 22 26 $stderr = $ex->getStdErr(); 23 27 if (preg_match('/path not found$/', trim($stderr))) {
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1161 1161 'type' => 'sql', 1162 1162 'name' => $this->getPatchPath('20131302.maniphestvalue.sql'), 1163 1163 ), 1164 + '20130304.lintauthor.sql' => array( 1165 + 'type' => 'sql', 1166 + 'name' => $this->getPatchPath('20130304.lintauthor.sql'), 1167 + ), 1164 1168 ); 1165 1169 } 1166 1170