Skip to content

Commit 1091dc7

Browse files
author
vrana
committed
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
1 parent 8ec987d commit 1091dc7

11 files changed

+178
-69
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ALTER TABLE `{$NAMESPACE}_repository`.`repository_lintmessage`
2+
ADD authorPHID varchar(64) COLLATE utf8_bin AFTER line,
3+
ADD INDEX key_author (authorPHID);

scripts/repository/save_lint.php

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
'default' => 256,
4040
'help' => "Number of paths passed to `arc` at once.",
4141
),
42+
array(
43+
'name' => 'blame',
44+
'help' => "Assign lint errors to authors who last modified the line.",
45+
),
4246
));
4347

4448
echo "Saving lint errors to database...\n";
@@ -48,6 +52,7 @@
4852
->setArc($args->getArg('arc'))
4953
->setSeverity($args->getArg('severity'))
5054
->setChunkSize($args->getArg('chunk-size'))
55+
->setNeedsBlame($args->getArg('blame'))
5156
->run('.');
5257

5358
echo "\nProcessed {$count} files.\n";

src/applications/diffusion/DiffusionLintSaveRunner.php

+85-8
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ final class DiffusionLintSaveRunner {
55
private $severity = ArcanistLintSeverity::SEVERITY_ADVICE;
66
private $all = false;
77
private $chunkSize = 256;
8+
private $needsBlame = false;
89

910
private $svnRoot;
1011
private $lintCommit;
1112
private $branch;
1213
private $conn;
1314
private $deletes = array();
1415
private $inserts = array();
16+
private $blame = array();
1517

1618

1719
public function setArc($path) {
@@ -34,6 +36,11 @@ public function setChunkSize($number) {
3436
return $this;
3537
}
3638

39+
public function setNeedsBlame($boolean) {
40+
$this->needsBlame = $boolean;
41+
return $this;
42+
}
43+
3744

3845
public function run($dir) {
3946
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($dir);
@@ -44,7 +51,7 @@ public function run($dir) {
4451
$svn_fetch = $api->getGitConfig('svn-remote.svn.fetch');
4552
list($this->svnRoot) = explode(':', $svn_fetch);
4653
if ($this->svnRoot != '') {
47-
$this->svnRoot = '/' . $this->svnRoot;
54+
$this->svnRoot = '/'.$this->svnRoot;
4855
}
4956
}
5057

@@ -98,8 +105,6 @@ public function run($dir) {
98105
$all_files = $api->getAllFiles();
99106
}
100107

101-
$this->deletes = array();
102-
$this->inserts = array();
103108
$count = 0;
104109

105110
$files = array();
@@ -123,9 +128,16 @@ public function run($dir) {
123128

124129
$this->runArcLint($files);
125130
$this->saveLintMessages();
126-
$this->branch->setLintCommit($api->getUnderlyingWorkingCopyRevision());
131+
132+
$this->lintCommit = $api->getUnderlyingWorkingCopyRevision();
133+
$this->branch->setLintCommit($this->lintCommit);
127134
$this->branch->save();
128135

136+
if ($this->blame) {
137+
$this->blameAuthors();
138+
$this->blame = array();
139+
}
140+
129141
return $count;
130142
}
131143

@@ -156,22 +168,26 @@ private function runArcLint(array $files) {
156168
}
157169

158170
foreach ($messages as $message) {
171+
$line = idx($message, 'line', 0);
172+
159173
$this->inserts[] = qsprintf(
160174
$this->conn,
161175
'(%d, %s, %d, %s, %s, %s, %s)',
162176
$this->branch->getID(),
163177
$this->svnRoot.'/'.$path,
164-
idx($message, 'line', 0),
178+
$line,
165179
idx($message, 'code', ''),
166180
idx($message, 'severity', ''),
167181
idx($message, 'name', ''),
168182
idx($message, 'description', ''));
183+
184+
if ($line && $this->needsBlame) {
185+
$this->blame[$path][$line] = true;
186+
}
169187
}
170188

171189
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();
175191
}
176192
}
177193
}
@@ -205,6 +221,67 @@ private function saveLintMessages() {
205221
}
206222

207223
$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+
}
208285
}
209286

210287
}

src/applications/diffusion/controller/DiffusionBrowseFileController.php

+6-7
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,7 @@ private function buildCorpus($selected,
202202
$rows = array();
203203
foreach ($text_list as $k => $line) {
204204
$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'];
210206
$rows[] =
211207
sprintf("%-10s %-20s %s", substr($rev, 0, 7), $author, $line);
212208
}
@@ -430,11 +426,13 @@ private function buildDisplayRows(
430426
DiffusionFileContentQuery $file_query,
431427
$selected) {
432428

429+
$handles = array();
433430
if ($blame_dict) {
434431
$epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
435432
$epoch_min = min($epoch_list);
436433
$epoch_max = max($epoch_list);
437434
$epoch_range = ($epoch_max - $epoch_min) + 1;
435+
$handles = $this->loadViewerHandles(ipull($blame_dict, 'authorPHID'));
438436
}
439437

440438
$line_arr = array();
@@ -499,8 +497,9 @@ private function buildDisplayRows(
499497
$display_line['color'] = $color;
500498
$display_line['commit'] = $rev;
501499

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();
504503
} else {
505504
$author_link = phutil_tag(
506505
'span',

src/applications/diffusion/controller/DiffusionLintController.php

+18-20
Original file line numberDiff line numberDiff line change
@@ -169,29 +169,30 @@ private function loadLintCodes(array $owner_phids) {
169169
}
170170

171171
if ($owner_phids) {
172+
$or = array();
173+
$or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids);
174+
175+
$paths = array();
172176
$packages = id(new PhabricatorOwnersOwner())
173177
->loadAllWhere('userPHID IN (%Ls)', $owner_phids);
174-
if (!$packages) {
175-
return array();
176-
}
177-
178-
$paths = id(new PhabricatorOwnersPath())
179-
->loadAllWhere('packageID IN (%Ld)', array_keys($packages));
180-
if (!$paths) {
181-
return array();
178+
if ($packages) {
179+
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
180+
'packageID IN (%Ld)',
181+
mpull($packages, 'getPackageID'));
182182
}
183183

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');
188189

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+
}
193195

194-
$or = array();
195196
foreach ($paths as $path) {
196197
$branch = idx($branches, $repositories[$path->getRepositoryPHID()]);
197198
if ($branch) {
@@ -207,9 +208,6 @@ private function loadLintCodes(array $owner_phids) {
207208
}
208209
}
209210
}
210-
if (!$or) {
211-
return array();
212-
}
213211
$where[] = '('.implode(' OR ', $or).')';
214212
}
215213

src/applications/diffusion/controller/DiffusionLintDetailsController.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public function processRequest() {
1111
$messages = $this->loadLintMessages($branch, $limit, $offset);
1212
$is_dir = (substr('/'.$drequest->getPath(), -1) == '/');
1313

14+
$authors = $this->loadViewerHandles(ipull($messages, 'authorPHID'));
15+
1416
$rows = array();
1517
foreach ($messages as $message) {
1618
$path = hsprintf(
@@ -31,9 +33,15 @@ public function processRequest() {
3133
)),
3234
$message['line']);
3335

36+
$author = $message['authorPHID'];
37+
if ($author && $authors[$author]) {
38+
$author = $authors[$author]->renderLink();
39+
}
40+
3441
$rows[] = array(
3542
$path,
3643
$line,
44+
$author,
3745
ArcanistLintSeverity::getStringForSeverity($message['severity']),
3846
$message['name'],
3947
$message['description'],
@@ -44,11 +52,12 @@ public function processRequest() {
4452
->setHeaders(array(
4553
'Path',
4654
'Line',
55+
'Author',
4756
'Severity',
4857
'Name',
4958
'Description',
5059
))
51-
->setColumnClasses(array('', 'n', '', '', ''))
60+
->setColumnClasses(array('', 'n'))
5261
->setColumnVisibility(array($is_dir));
5362

5463
$content = array();

src/applications/diffusion/query/filecontent/DiffusionFileContentQuery.php

+22-19
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ final public static function newFromDiffusionRequest(
1111
return parent::newQueryObject(__CLASS__, $request);
1212
}
1313

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);
1619

1720
$repository = $this->getRequest()->getRepository();
1821
$try_encoding = $repository->getDetail('encoding');
@@ -25,6 +28,14 @@ final public function loadFileContent() {
2528
return $this->fileContent;
2629
}
2730

31+
final protected function executeQuery() {
32+
return $this->loadFileContentFromFuture($this->getFileContentFuture());
33+
}
34+
35+
final public function loadFileContent() {
36+
return $this->executeQuery();
37+
}
38+
2839
final public function getRawData() {
2940
return $this->fileContent->getCorpus();
3041
}
@@ -72,30 +83,22 @@ final public function getBlameData() {
7283
$commit->getEpoch();
7384
}
7485

75-
$commits_data = array();
7686
if ($commits) {
7787
$commits_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
7888
'commitID IN (%Ls)',
7989
mpull($commits, 'getID'));
80-
}
81-
82-
$phids = array();
83-
foreach ($commits_data as $data) {
84-
$phids[] = $data->getCommitDetail('authorPHID');
85-
}
86-
87-
$loader = new PhabricatorObjectHandleData(array_unique($phids));
88-
$loader->setViewer($this->viewer);
89-
$handles = $loader->loadHandles();
9090

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;
9799
}
98100
}
101+
99102
}
100103

101104
return array($text_list, $rev_list, $blame_dict);

0 commit comments

Comments
 (0)