r80691 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r80690‎ | r80691 | r80692 >
Date:17:16, 21 January 2011
Author:happydog
Status:resolved (Comments)
Tags:
Comment:
[CodeReview] Modified CodeRepository::getDiff() so it now returns a bit more information about problems that are encountered. This means we can give more meaningful output in svnImport.php (which I have done) and probably elsewhere as well (which I have not). Previously the function returned the diff as a string, or an empty string (or maybe null) if an error occurred. Now it returns a string containing the diff on success, or an integer value indicating the type of error on failure. These values are defined by the DIFFRESULT_* constants. All code that calls this function has been updated to handle these new return values.

Note that getDiff() used to call $revision->isDiffable(), but this just returns a boolean, without indicating why it is not diffable. I therefore had to semi-duplicate this code so we can report the reason properly (i.e. whether because there are no paths, or too many paths).
Modified paths:
  • /trunk/extensions/CodeReview/CodeReview.php (modified) (history)
  • /trunk/extensions/CodeReview/api/ApiCodeDiff.php (modified) (history)
  • /trunk/extensions/CodeReview/backend/CodeRepository.php (modified) (history)
  • /trunk/extensions/CodeReview/svnImport.php (modified) (history)
  • /trunk/extensions/CodeReview/ui/CodeRevisionView.php (modified) (history)

Diff [purge]

Index: trunk/extensions/CodeReview/CodeReview.php
@@ -114,6 +114,12 @@
115115
116116 $wgGroupPermissions['steward']['repoadmin'] = true; // temp
117117
 118+// Constants returned from CodeRepository::getDiff() when no diff can be calculated.
 119+define("DIFFRESULT_BadRevision", 0);
 120+define("DIFFRESULT_NothingToCompare", 1);
 121+define("DIFFRESULT_TooManyPaths", 2);
 122+define("DIFFRESULT_NoDataReturned", 3);
 123+
118124 // If you can't directly access the remote SVN repo, you can set this
119125 // to an offsite proxy running this fun little proxy tool:
120126 // http://svn.wikimedia.org/viewvc/mediawiki/trunk/tools/codereview-proxy/
Index: trunk/extensions/CodeReview/backend/CodeRepository.php
@@ -259,29 +259,54 @@
260260 * @param int $rev Revision ID
261261 * @param $useCache 'skipcache' to avoid caching
262262 * 'cached' to *only* fetch if cached
 263+ * @return string|int The diff text on success, a DIFFRESULT_* constant on failure.
263264 */
264265 public function getDiff( $rev, $useCache = '' ) {
265 - global $wgMemc;
 266+ global $wgMemc, $wgCodeReviewMaxDiffPaths;
266267 wfProfileIn( __METHOD__ );
267268
 269+ $data = null;
 270+
268271 $rev1 = $rev - 1;
269272 $rev2 = $rev;
270273
 274+ // Check that a valid revision was specified.
271275 $revision = $this->getRevision( $rev );
272 - if ( $revision == null || !$revision->isDiffable() ) {
 276+ if ( $revision == null ) {
 277+ $data = DIFFRESULT_BadRevision;
 278+ }
 279+ // Check that there is at least one, and at most $wgCodeReviewMaxDiffPaths
 280+ // paths changed in this revision.
 281+ else {
 282+ $paths = $revision->getModifiedPaths();
 283+ if ( !$paths->numRows() ) {
 284+ $data = DIFFRESULT_NothingToCompare;
 285+ }
 286+ elseif ( $wgCodeReviewMaxDiffPaths > 0 && $paths->numRows() > $wgCodeReviewMaxDiffPaths ) {
 287+ $data = DIFFRESULT_TooManyPaths;
 288+ }
 289+ }
 290+
 291+ // If an error has occurred, return it.
 292+ if ( $data !== null ) {
273293 wfProfileOut( __METHOD__ );
274 - return false;
 294+ return $data;
275295 }
276296
277 - # Try memcached...
 297+ // Set up the cache key, which will be used both to check if already in the
 298+ // cache, and to write the final result to the cache.
278299 $key = wfMemcKey( 'svn', md5( $this->path ), 'diff', $rev1, $rev2 );
 300+
 301+ // If not set to explicitly skip the cache, get the current diff from memcached
 302+ // directly.
279303 if ( $useCache === 'skipcache' ) {
280304 $data = null;
281305 } else {
282306 $data = $wgMemc->get( $key );
283307 }
284308
285 - # Try the database...
 309+ // If the diff hasn't already been retrieved from the cache, see if we can get
 310+ // it from the DB.
286311 if ( !$data && $useCache != 'skipcache' ) {
287312 $dbr = wfGetDB( DB_SLAVE );
288313 $row = $dbr->selectRow( 'code_rev',
@@ -292,7 +317,7 @@
293318 if ( $row ) {
294319 $flags = explode( ',', $row->cr_flags );
295320 $data = $row->cr_diff;
296 - // If the text was fetched without an error, convert it
 321+ // If the text was fetched without an error, convert it
297322 if ( $data !== false && in_array( 'gzip', $flags ) ) {
298323 # Deal with optional compression of archived pages.
299324 # This can be done periodically via maintenance/compressOld.php, and
@@ -302,22 +327,36 @@
303328 }
304329 }
305330
306 - # Generate the diff as needed...
 331+ // If the data was not already in the cache or in the DB, we need to retrieve
 332+ // it from SVN.
307333 if ( !$data && $useCache !== 'cached' ) {
308334 $svn = SubversionAdaptor::newFromRepo( $this->path );
309335 $data = $svn->getDiff( '', $rev1, $rev2 );
 336+
 337+ // If $data is blank, report the error that no data was returned.
 338+ // TODO: Currently we can't tell the difference between an SVN/connection
 339+ // failure and an empty diff. See if we can remedy this!
 340+ if ($data == "") {
 341+ $data = DIFFRESULT_NoDataReturned;
 342+ }
 343+ // Otherwise, store the resulting diff to both the temporary cache and
 344+ // permanent DB storage.
 345+ else {
310346 // Store to cache
311 - $wgMemc->set( $key, $data, 3600 * 24 * 3 );
 347+ $wgMemc->set( $key, $data, 3600 * 24 * 3 );
 348+
312349 // Permanent DB storage
313 - $storedData = $data;
314 - $flags = Revision::compressRevisionText( $storedData );
315 - $dbw = wfGetDB( DB_MASTER );
316 - $dbw->update( 'code_rev',
317 - array( 'cr_diff' => $storedData, 'cr_flags' => $flags ),
318 - array( 'cr_repo_id' => $this->id, 'cr_id' => $rev ),
319 - __METHOD__
320 - );
 350+ $storedData = $data;
 351+ $flags = Revision::compressRevisionText( $storedData );
 352+ $dbw = wfGetDB( DB_MASTER );
 353+ $dbw->update( 'code_rev',
 354+ array( 'cr_diff' => $storedData, 'cr_flags' => $flags ),
 355+ array( 'cr_repo_id' => $this->id, 'cr_id' => $rev ),
 356+ __METHOD__
 357+ );
 358+ }
321359 }
 360+
322361 wfProfileOut( __METHOD__ );
323362 return $data;
324363 }
Index: trunk/extensions/CodeReview/svnImport.php
@@ -50,7 +50,7 @@
5151 * @param $start Int Revision to begin the import from (Default: null, means last stored revision);
5252 */
5353 private function importRepo( $repoName, $start = null, $cacheSize = 0 ) {
54 - global $wgCodeReviewImportBatchSize;
 54+ global $wgCodeReviewImportBatchSize, $wgCodeReviewMaxDiffPaths;
5555
5656 $repo = CodeRepository::newFromName( $repoName );
5757
@@ -136,6 +136,14 @@
137137 $options['LIMIT'] = $cacheSize;
138138 }
139139
 140+ // Get all rows for this repository that don't already have a diff filled in.
 141+ // This is LIMITed according to the $cacheSize setting, above, so only the
 142+ // rows that we plan to pre-cache are returned.
 143+ // TODO: This was optimised in order to skip rows that already have a diff,
 144+ // which is mostly what is required, but there may be situations where
 145+ // you want to re-calculate diffs (e.g. if $wgCodeReviewMaxDiffPaths
 146+ // changes). If these situations arise we will either want to revert
 147+ // this behaviour, or add a --force flag or something.
140148 $res = $dbw->select( 'code_rev', 'cr_id',
141149 array( 'cr_repo_id' => $repo->getId(), 'cr_diff IS NULL OR cr_diff = ""' ),
142150 __METHOD__,
@@ -143,8 +151,33 @@
144152 );
145153 foreach ( $res as $row ) {
146154 $repo->getRevision( $row->cr_id );
147 - $repo->getDiff( $row->cr_id ); // trigger caching
148 - $this->output( "Diff r{$row->cr_id} done\n" );
 155+ $diff = $repo->getDiff( $row->cr_id ); // trigger caching
 156+ $msg = "Diff r{$row->cr_id} ";
 157+ if (is_integer($diff)) {
 158+ $msg .= "Skipped: ";
 159+ switch ($diff) {
 160+ case DIFFRESULT_BadRevision:
 161+ $msg .= "Bad revision";
 162+ break;
 163+ case DIFFRESULT_NothingToCompare:
 164+ $msg .= "Nothing to compare";
 165+ break;
 166+ case DIFFRESULT_TooManyPaths:
 167+ $msg .= "Too many paths (\$wgCodeReviewMaxDiffPaths = "
 168+ . $wgCodeReviewMaxDiffPaths . ")";
 169+ break;
 170+ case DIFFRESULT_NoDataReturned:
 171+ $msg .= "No data returned - no diff data, or connection lost.";
 172+ break;
 173+ default:
 174+ $msg .= "Unknown reason!";
 175+ break;
 176+ }
 177+ }
 178+ else {
 179+ $msg .= "done";
 180+ }
 181+ $this->output( $msg . "\n" );
149182 }
150183 }
151184 else {
Index: trunk/extensions/CodeReview/api/ApiCodeDiff.php
@@ -40,7 +40,7 @@
4141
4242 $diff = $repo->getDiff( $params['rev'] );
4343
44 - if ( strval( $diff ) === '' ) {
 44+ if ( !is_string( $diff ) ) {
4545 // FIXME: Are we sure we don't want to throw an error here?
4646 $html = 'Failed to load diff.';
4747 } elseif ( strlen( $diff ) > $wgCodeReviewMaxDiffSize ) {
Index: trunk/extensions/CodeReview/ui/CodeRevisionView.php
@@ -365,7 +365,7 @@
366366 $cache = '';
367367 }
368368 $diff = $this->mRepo->getDiff( $this->mRev->getId(), $cache );
369 - if ( !$diff && $deferDiffs ) {
 369+ if ( is_integer($diff) && $deferDiffs ) {
370370 // We'll try loading it by AJAX...
371371 return $this->stubDiffLoader();
372372 } elseif ( strlen( $diff ) > $wgCodeReviewMaxDiffSize ) {

Follow-up revisions

RevisionCommit summaryAuthorDate
r80720Fixup style and whitespace from r80691reedy22:56, 21 January 2011
r80794Fix for CodeReview on-demand AJAX diff loading; regression caused by refactor...brion02:56, 23 January 2011
r80822[CodeReview] Fixed accidental null value being returned from CodeRepository::...happydog17:17, 23 January 2011
r82479Move constants to be class constants...reedy20:07, 19 February 2011

Comments

#Comment by Reedy (talk | contribs)   17:43, 21 January 2011

It would look like a lot of your code/comments is wrongly indented, and some things not faollowing the code style guidelines...

#Comment by HappyDog (talk | contribs)   18:43, 21 January 2011

Specifics please - I've tried to follow the guidelines as much as I can.

Note that there's no guidelines about comment structure, so I've used our house-style (which is fairly common, I think) of having comments at one indentation-level higher than the code they refer to. We find this makes it a lot easier to follow the flow of the code (assuming it to be well commented, of course). If the MW house style is different, it's not documented in the guidelines anywhere.

#Comment by Reedy (talk | contribs)   19:11, 21 January 2011

The comments are indented wrongly

+		// If $data is blank, report the error that no data was returned.
+		// TODO: Currently we can't tell the difference between an SVN/connection
+		//		 failure and an empty diff.  See if we can remedy this!
+			if ($data == "") {
+				$data = DIFFRESULT_NoDataReturned;
+			}
+		// Otherwise, store the resulting diff to both the temporary cache and
+		// permanent DB storage.
+			else {

Need tabbing in one more

+				}
+				else {
+					$msg .= "done";

should be

+				} else {
+					$msg .= "done";
#Comment by HappyDog (talk | contribs)   22:35, 21 January 2011

> The comments are indented wrongly.

There's no mention of comment style at Coding conventions, except to include a space after the open-comment symbol.

> } else {

The brace style for else statements isn't mentioned on that page either. I now notice that one of the examples has an else block in this format, but that could just be co-incidence. I did a cursory check on the trunk, and whilst your suggested style is much more common, both styles are used.

#Comment by Reedy (talk | contribs)   22:41, 21 January 2011

http://www.mediawiki.org/wiki/Manual:Coding_conventions#General_style

"MediaWiki's indenting style is similar to the so-called "One True Brace Style". Braces are placed on the same line as the start of the function, conditional, loop, etc."

http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS


That answers the brace query. The MOS says it is like this, and the reason the code isn't, is we haven't mass updated it


For comments, look at all other code, they go at the same indenting level, otherwise it just makes the code harder to read and identify

#Comment by Hashar (talk | contribs)   23:08, 21 January 2011

This is a great idea happydog. Thanks for the code!

#Comment by Reedy (talk | contribs)   23:21, 21 January 2011

You also should really be using class constants...

#Comment by Brion VIBBER (talk | contribs)   02:27, 23 January 2011

This seems to have broken on-demand AJAX diff loading.

CodeRepository::getDiff() returns null when we're loading in 'cached' mode and don't have anything saved in cache already. Null does not match is_integer(), so the AJAX loading code gets skipped over.

#Comment by Brion VIBBER (talk | contribs)   02:56, 23 January 2011

I did a quick regression fix in r80794, but it wouldn't hurt to double-check things.

#Comment by HappyDog (talk | contribs)   16:56, 23 January 2011

Can we not just add another constant, e.g. DIFFRESULT_NothingInCache, and return that?

#Comment by HappyDog (talk | contribs)   17:19, 23 January 2011

Done - r80822.

#Comment by Reedy (talk | contribs)   11:49, 16 February 2011

Constants still need moving to class constants

#Comment by HappyDog (talk | contribs)   12:40, 23 March 2011

Done by Reedy in r82479.

Status & tagging log