Index: includes/Article.php
===================================================================
--- includes/Article.php	(revision 46705)
+++ includes/Article.php	(working copy)
@@ -1520,6 +1520,7 @@
 	 */
 	public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
 		global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
+		global $wgUseRCPatrol, $wgUseNPPatrol;
 
 		# Low-level sanity check
 		if( $this->mTitle->getText() == '' ) {
@@ -1597,11 +1598,17 @@
 					wfProfileOut( __METHOD__ );
 					return $status;
 				}
+				
+				# Mark as patrolled if the user can do so
+				$patrolled = $wgUseRCPatrol && $this->mTitle->userCan('autopatrol');
 
+
 				$revision = new Revision( array(
 					'page'       => $this->getId(),
 					'comment'    => $summary,
 					'minor_edit' => $isminor,
+					'bot'        => $bot,
+					'patrolled'  => $patrolled,
 					'text'       => $text,
 					'parent_id'  => $this->mLatest,
 					'user'       => $user->getId(),
@@ -1630,12 +1637,9 @@
 					$revisionId = 0;
 					$dbw->rollback();
 				} else {
-					global $wgUseRCPatrol;
 					wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, $baseRevId, $user) );
 					# Update recentchanges
 					if( !( $flags & EDIT_SUPPRESS_RC ) ) {
-						# Mark as patrolled if the user can do so
-						$patrolled = $wgUseRCPatrol && $this->mTitle->userCan('autopatrol');
 						# Add RC row to the DB
 						$rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
 							$this->mLatest, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
@@ -1695,12 +1699,17 @@
 				wfProfileOut( __METHOD__ );
 				return $status;
 			}
+			
+			# Mark as patrolled if the user can do so
+			$patrolled = ($wgUseRCPatrol || $wgUseNPPatrol) && $this->mTitle->userCan('autopatrol');
 
 			# Save the revision text...
 			$revision = new Revision( array(
 				'page'       => $newid,
 				'comment'    => $summary,
 				'minor_edit' => $isminor,
+				'bot'        => $bot,
+				'patrolled'  => $patrolled,
 				'text'       => $text,
 				'user'       => $user->getId(),
 				'user_text'  => $user->getName(),
@@ -1715,9 +1724,6 @@
 			wfRunHooks( 'NewRevisionFromEditComplete', array($this, $revision, false, $user) );
 			# Update recentchanges
 			if( !( $flags & EDIT_SUPPRESS_RC ) ) {
-				global $wgUseRCPatrol, $wgUseNPPatrol;
-				# Mark as patrolled if the user can do so
-				$patrolled = ($wgUseRCPatrol || $wgUseNPPatrol) && $this->mTitle->userCan('autopatrol');
 				# Add RC row to the DB
 				$rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
 					'', strlen($text), $revisionId, $patrolled );
Index: includes/RecentChange.php
===================================================================
--- includes/RecentChange.php	(revision 46705)
+++ includes/RecentChange.php	(working copy)
@@ -313,11 +313,26 @@
 				'rc_patrolled' => 1
 			),
 			array(
-				'rc_id' => $this->getAttribute('rc_id')
+				'rc_id' => $this->getAttribute( 'rc_id' )
 			),
 			__METHOD__
 		);
-		return $dbw->affectedRows();
+		$rows = $dbw->affectedRows();
+		$oldid = $this->getAttribute( 'rc_this_oldid' );
+		if( $oldid != 0 ) {
+			$dbw->update(
+				'revision',
+				array(
+					'rev_patrolled' => 1
+				),
+				array(
+					'rev_id' => $oldid
+				),
+				__METHOD__
+			);
+			$rows += $dbw->affectedRows();
+		}
+		return $rows;
 	}
 
 	# Makes an entry in the database corresponding to an edit
@@ -555,12 +570,12 @@
 			'rc_cur_id' => $row->page_id,
 			'rc_this_oldid'	=> $row->rev_id,
 			'rc_last_oldid'	=> isset($row->rc_last_oldid) ? $row->rc_last_oldid : 0,
-			'rc_bot'	=> 0,
+			'rc_bot'	=> $row->rev_bot ? 1 : 0,
 			'rc_moved_to_ns'	=> 0,
 			'rc_moved_to_title'	=> '',
 			'rc_ip' => '',
 			'rc_id' => $row->rc_id,
-			'rc_patrolled' => $row->rc_patrolled,
+			'rc_patrolled' => $row->rev_patrolled ? 1 : 0,
 			'rc_new' => $row->page_is_new, # obsolete
 			'rc_old_len' => $row->rc_old_len,
 			'rc_new_len' => $row->rc_new_len,
Index: includes/Revision.php
===================================================================
--- includes/Revision.php	(revision 46705)
+++ includes/Revision.php	(working copy)
@@ -267,7 +267,9 @@
 			'rev_minor_edit',
 			'rev_deleted',
 			'rev_len',
-			'rev_parent_id'
+			'rev_parent_id',
+			'rev_bot',
+			'rev_patrolled'
 		);
 	}
 	
@@ -317,6 +319,16 @@
 				$this->mSize = null;
 			else
 				$this->mSize = intval( $row->rev_len );
+			
+			if( !isset( $row->rev_bot ) )
+				$this->mBot = 0;
+			else
+				$this->mBot = intval( $row->rev_bot );
+			
+			if( !isset( $row->rev_patrolled ) )
+				$this->mPatrolled = 0;
+			else
+				$this->mPatrolled = intval( $row->rev_patrolled );
 
 			if( isset( $row->page_latest ) ) {
 				$this->mCurrent = ( $row->rev_id == $row->page_latest );
@@ -328,7 +340,7 @@
 			}
 
 			// Lazy extraction...
-			$this->mText      = null;
+			$this->mText = null;
 			if( isset( $row->old_text ) ) {
 				$this->mTextRow = $row;
 			} else {
@@ -349,6 +361,8 @@
 			$this->mDeleted   = isset( $row['deleted']    ) ? intval( $row['deleted']    ) : 0;
 			$this->mSize      = isset( $row['len']        ) ? intval( $row['len']        ) : null;
 			$this->mParentId  = isset( $row['parent_id']  ) ? intval( $row['parent_id']  ) : null;
+			$this->mBot       = isset( $row['bot']        ) ? intval( $row['bot']        ) : 0;
+			$this->mPatrolled = isset( $row['patrolled']  ) ? intval( $row['patrolled']  ) : 0;
 
 			// Enforce spacing trimming on supplied text
 			$this->mComment   = isset( $row['comment']    ) ?  trim( strval( $row['comment'] ) ) : null;
@@ -539,9 +553,25 @@
 	}
 	
 	/**
+	 * @return bool
+	 */
+	public function isBot() {
+		return (bool)$this->mBot;
+	}
+	
+	/**
+	 * @return bool
+	 */
+	public function isPatrolled() {
+		return (bool)$this->mPatrolled;
+	}
+	
+	/**
+	 * @deprecated
 	 * @return int rcid of the unpatrolled row, zero if there isn't one
 	 */
 	public function isUnpatrolled() {
+		wfDeprecated(__METHOD__);
 		if( $this->mUnpatrolled !== NULL ) {
 			return $this->mUnpatrolled;
 		}
@@ -560,7 +590,7 @@
 	}
 
 	/**
-	 * int $field one of DELETED_* bitfield constants
+	 * @param int $field one of DELETED_* bitfield constants
 	 * @return bool
 	 */
 	public function isDeleted( $field ) {
@@ -842,7 +872,9 @@
 				'rev_deleted'    => $this->mDeleted,
 				'rev_len'	     => $this->mSize,
 				'rev_parent_id'  => is_null($this->mParentId) ?
-					$this->getPreviousRevisionId( $dbw ) : $this->mParentId
+					$this->getPreviousRevisionId( $dbw ) : $this->mParentId,
+				'rev_bot'	=> $this->mBot ? 1 : 0,
+				'rev_patrolled'	=> $this->mPatrolled ? 1 : 0
 			), __METHOD__
 		);
 
@@ -924,9 +956,10 @@
 	 * @param int      $pageId ID number of the page to read from
 	 * @param string   $summary
 	 * @param bool     $minor
+	 * @param bool     $bot
 	 * @return Revision
 	 */
-	public static function newNullRevision( $dbw, $pageId, $summary, $minor ) {
+	public static function newNullRevision( $dbw, $pageId, $summary, $minor, $bot = false ) {
 		wfProfileIn( __METHOD__ );
 
 		$current = $dbw->selectRow(
@@ -943,6 +976,8 @@
 				'page'       => $pageId,
 				'comment'    => $summary,
 				'minor_edit' => $minor,
+				'bot'        => $bot,
+				'patrolled'  => 1, # null revisions are always patrolled
 				'text_id'    => $current->rev_text_id,
 				'parent_id'  => $current->page_latest,
 				'len'        => $current->rev_len
Index: maintenance/populateRevBotPatrolled.inc
===================================================================
--- maintenance/populateRevBotPatrolled.inc	(revision 0)
+++ maintenance/populateRevBotPatrolled.inc	(revision 0)
@@ -0,0 +1,53 @@
+<?php
+
+define( 'BATCH_SIZE', 200 );
+
+function populate_rev_bot_patrolled( $db ) {
+	echo "Populating rev_bot and rev_patrolled columns\n";
+	$start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ );
+	$end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ );
+	if( is_null( $start ) || is_null( $end ) ){
+		echo "...revision table seems to be empty.\n";
+		$db->insert( 'updatelog',
+			array( 'ul_key' => 'populate rev_bot and rev_patrolled' ),
+			__FUNCTION__,
+			'IGNORE' );
+		return;
+	}
+	# Do remaining chunk
+	$end += BATCH_SIZE - 1;
+	$blockStart = $start;
+	$blockEnd = $start + BATCH_SIZE - 1;
+	$count = 0;
+	$changed = 0;
+	while( $blockEnd <= $end ) {
+		echo "...doing rc_this_oldid from $blockStart to $blockEnd\n";
+		$cond = "rc_this_oldid BETWEEN $blockStart AND $blockEnd";
+		$res = $db->select( 'recentchanges', 
+			array( 'rc_this_oldid', 'rc_bot', 'rc_patrolled' ), 
+			$cond, __FUNCTION__ );
+		foreach( $res as $row ) {
+			$db->update( 'revision',
+				array(	'rev_bot' => $row->rc_bot,
+					'rev_patrolled' => $row->rc_patrolled ),
+				array(	'rev_id' => $row->rc_this_oldid ),
+				__FUNCTION__ );
+			$count++;
+		}
+		$blockStart += BATCH_SIZE - 1;
+		$blockEnd += BATCH_SIZE - 1;
+		wfWaitForSlaves( 5 );
+	}
+	$logged = $db->insert( 'updatelog',
+		array( 'ul_key' => 'populate rev_bot and rev_patrolled' ),
+		__FUNCTION__,
+		'IGNORE' );
+	if( $logged ) {
+		echo "rev_bot and rev_patrolled population complete ... {$count} rows\n";
+		return true;
+	} else {
+		echo "Could not insert rev_bot and rev_patrolled population row.\n";
+		return false;
+	}
+}
+
Index: maintenance/populateRevBotPatrolled.php
===================================================================
--- maintenance/populateRevBotPatrolled.php	(revision 0)
+++ maintenance/populateRevBotPatrolled.php	(revision 0)
@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * Fills the rev_bot and rev_patrolled fields from the recentchanges table
+ */
+
+require_once 'commandLine.inc';
+require_once 'populateRevBotPatrolled.inc';
+	
+$db =& wfGetDB( DB_MASTER );
+if ( !$db->tableExists( 'revision' ) ) {
+	echo "revision table does not exist\n";
+	exit( 1 );
+}
+
+populate_rev_bot_patrolled( $db );
Index: maintenance/tables.sql
===================================================================
--- maintenance/tables.sql	(revision 46705)
+++ maintenance/tables.sql	(working copy)
@@ -278,7 +278,15 @@
 
   -- Key to revision.rev_id
   -- This field is used to add support for a tree structure (The Adjacency List Model)
-  rev_parent_id int unsigned default NULL
+  rev_parent_id int unsigned default NULL,
+  
+  -- Records whether the edit was marked with the bot flag.
+  -- These edits are hidden from Special:Recentchanges by default
+  rev_bot tinyint unsigned NOT NULL default 0,
+  
+  -- Records whether the edit was marked as patrolled. If patrolling is enabled,
+  -- non-patrolled edits will have a warning flag on Special:Recentchanges
+  rev_patrolled tinyint unsigned NOT NULL default 0
 
 ) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
 -- In case tables are created as MyISAM, use row hints for MySQL <5.0 to avoid 4GB limit
Index: maintenance/updaters.inc
===================================================================
--- maintenance/updaters.inc	(revision 46705)
+++ maintenance/updaters.inc	(working copy)
@@ -153,6 +153,9 @@
 		array( 'add_table', 'change_tag',			   'patch-change_tag.sql' ),
 		array( 'add_table', 'tag_summary',			   'patch-change_tag.sql' ),
 		array( 'add_table', 'valid_tag',			   'patch-change_tag.sql' ),
+		array( 'add_field', 'revision',    'rev_bot',             'patch-rev_bot.sql' ),
+		array( 'add_field', 'revision',    'rev_patrolled',       'patch-rev_patrolled.sql' ),
+		array( 'do_populate_rev_bot_patrolled' ),
 	),
 
 	'sqlite' => array(
@@ -1235,6 +1238,17 @@
 	populate_rev_parent_id( $wgDatabase );
 }
 
+function do_populate_rev_bot_patrolled() {
+	if( update_row_exists( 'populate rev_bot and rev_patrolled' ) ) {
+		echo "...rev_bot and rev_patrolled columns already populated.\n";
+		return;
+	}
+	require_once( 'populateRevBotPatrolled.inc' );
+	
+	global $wgDatabase;
+	populate_rev_bot_patrolled( $wgDatabase );
+}
+
 function update_password_format() {
 	if ( update_row_exists( 'password format' ) ) {
 		echo "...password hash format already changed\n";
