Imported Buildbot-0.8.2 source upstream
authorDustin J. Mitchell <dustin@mozilla.com>
Fri, 29 Oct 2010 11:23:21 -0500
branchupstream
changeset 99 27c8ebbbf2b5189bd47be6fa8504610d5d8d11d3
parent 97 c2e02e5bbfdb1c7a463cb44d75b06d45070597d3
child 100 ff16a0ec34176008e2576f1ffc99a075e6638c9c
child 102 eeb2d34e29e8689d4b7df9f6e1800fc0f481a96c
push id41
push userdmitchell@mozilla.com
push dateWed, 17 Nov 2010 14:22:22 +0000
Imported Buildbot-0.8.2 source
Makefile
master/Makefile
master/NEWS
master/buildbot/buildslave.py
master/buildbot/changes/changes.py
master/buildbot/changes/gitpoller.py
master/buildbot/changes/mail.py
master/buildbot/db/connector.py
master/buildbot/db/dbspec.py
master/buildbot/db/schema/v4.py
master/buildbot/db/schema/v5.py
master/buildbot/ec2buildslave.py
master/buildbot/master.py
master/buildbot/scripts/runner.py
master/buildbot/status/builder.py
master/buildbot/status/mail.py
master/buildbot/status/persistent_queue.py
master/buildbot/status/status_push.py
master/buildbot/status/web/builder.py
master/buildbot/status/web/change_hook.py
master/buildbot/status/web/hooks/base.py
master/buildbot/status/web/hooks/github.py
master/buildbot/status/web/root.py
master/buildbot/status/web/tests.py
master/buildbot/steps/subunit.py
master/buildbot/steps/transfer.py
master/buildbot/test/fake/fakedb.py
master/buildbot/test/regressions/test_import_weird_changes.py
master/buildbot/test/unit/test_changes_mail_CVSMaildirSource.py
master/buildbot/test/unit/test_contrib_buildbot_cvs_mail.py
master/buildbot/test/unit/test_master_cleanshutdown.py
master/buildbot/test/unit/test_oldpaths.py
master/buildbot/test/unit/test_process_base.py
master/buildbot/test/unit/test_process_properties.py
master/buildbot/test/unit/test_status_builder.py
master/buildbot/test/unit/test_status_web_change_hooks_github.py
master/buildbot/util/__init__.py
master/buildbot/util/collections.py
master/docs/cfg-global.texinfo
master/docs/cfg-statustargets.texinfo
slave/NEWS
--- a/Makefile
+++ b/Makefile
@@ -6,11 +6,8 @@ docs:
 	$(MAKE) -C master/docs
 
 apidocs:
 	$(MAKE) -C apidocs
 
 pylint:
 	cd master; $(MAKE) pylint
 	cd slave; $(MAKE) pylint
-
-pyflakes:
-	cd master; $(MAKE) pyflakes
--- a/master/Makefile
+++ b/master/Makefile
@@ -1,6 +1,3 @@
 # developer utilities
 pylint:
 	pylint --rcfile=../common/pylintrc buildbot
-
-pyflakes:
-	pyflakes buildbot
--- a/master/NEWS
+++ b/master/NEWS
@@ -1,14 +1,12 @@
 Major User visible changes in Buildbot.             -*- outline -*-
    see the git log for a detailed list of changes:
    http://github.com/buildbot/buildbot/commits/master
 
-* Next Release
-
 * Buildbot 0.8.2
 
 ** Upgrading
 
 Upgrading to from the previous version will require an 'upgrade-master' run.
 However, the schema changes are backward-compatible, so if a downgrade is
 required, it will not be difficult.
 
--- a/master/buildbot/buildslave.py
+++ b/master/buildbot/buildslave.py
@@ -10,16 +10,18 @@ from twisted.application import service
 from twisted.spread import pb
 
 from buildbot.status.builder import SlaveStatus
 from buildbot.status.mail import MailNotifier
 from buildbot.interfaces import IBuildSlave, ILatentBuildSlave
 from buildbot.process.properties import Properties
 from buildbot.locks import LockAccess
 
+import sys
+
 class AbstractBuildSlave(pb.Avatar, service.MultiService):
     """This is the master-side representative for a remote buildbot slave.
     There is exactly one for each slave described in the config file (the
     c['slaves'] list). When buildbots connect in (.attach), they get a
     reference to this instance. The BotMaster object is stashed as the
     .botmaster attribute. The BotMaster is also our '.parent' Service.
 
     I represent a build slave -- a remote machine capable of
@@ -689,18 +691,17 @@ class AbstractLatentBuildSlave(AbstractB
                 self.substantiation_deferred = None
                 if self.missing_timer:
                     self.missing_timer.cancel()
                     self.missing_timer = None
                 self.stop_instance()
         return d
 
     def disconnect(self):
-        # This returns a Deferred but we don't use it
-        self._soft_disconnect() 
+        d = self._soft_disconnect()
         # this removes the slave from all builders.  It won't come back
         # without a restart (or maybe a sighup)
         self.botmaster.slaveLost(self)
 
     def stopService(self):
         res = defer.maybeDeferred(AbstractBuildSlave.stopService, self)
         if self.slave is not None:
             d = self._soft_disconnect()
--- a/master/buildbot/changes/changes.py
+++ b/master/buildbot/changes/changes.py
@@ -1,9 +1,9 @@
-import os, time
+import sys, os, time
 from cPickle import dump
 
 from zope.interface import implements
 from twisted.python import log, runtime
 from twisted.web import html
 
 from buildbot import interfaces, util
 from buildbot.process.properties import Properties
--- a/master/buildbot/changes/gitpoller.py
+++ b/master/buildbot/changes/gitpoller.py
@@ -4,16 +4,17 @@ import os
 import subprocess
 
 import select
 import errno
 
 from twisted.python import log, failure
 from twisted.internet import reactor, utils
 from twisted.internet.task import LoopingCall
+from twisted.web.client import getPage
 
 from buildbot.changes import base, changes
 
 class GitPoller(base.ChangeSource):
     """This source will poll a remote git repo for changes and submit
     them to the change master."""
     
     compare_attrs = ["repourl", "branch", "workdir",
@@ -69,17 +70,16 @@ class GitPoller(base.ChangeSource):
         self.pollinterval = pollinterval
         self.lastChange = time.time()
         self.lastPoll = time.time()
         self.gitbin = gitbin
         self.workdir = workdir
         self.usetimestamps = usetimestamps
         self.category = category
         self.project = project
-        self.changeCount = 0
         
         if self.workdir == None:
             self.workdir = tempfile.gettempdir() + '/gitpoller_work'
 
     def startService(self):
         self.loop = LoopingCall(self.poll)
         base.ChangeSource.startService(self)
         
@@ -167,17 +167,17 @@ class GitPoller(base.ChangeSource):
         return stamp
         
     def _get_commit_files(self, rev):
         args = ['log', rev, '--name-only', '--no-walk', r'--format=%n']
         fileList = self._get_git_output(args).split()
         return fileList
             
     def _get_commit_name(self, rev):
-        args = ['log', rev, '--no-walk', r'--format=%aE']
+        args = ['log', rev, '--no-walk', r'--format=%cn']
         output = self._get_git_output(args)
         
         if len(output.strip()) == 0:
             raise EnvironmentError('could not get commit name for rev %s' % rev)
 
         return output
 
     def _get_changes(self):
@@ -190,25 +190,25 @@ class GitPoller(base.ChangeSource):
         d = utils.getProcessOutput(self.gitbin, args, path=self.workdir, env={}, errortoo=1 )
 
         return d
 
     def _process_changes(self, res):
         # get the change list
         revListArgs = ['log', 'HEAD..FETCH_HEAD', r'--format=%H']
         revs = self._get_git_output(revListArgs);
-        self.changeCount = 0
+        revCount = 0
         
         # process oldest change first
         revList = revs.split()
         if revList:
             revList.reverse()
-            self.changeCount = len(revList)
+            revCount = len(revList)
             
-        log.msg('gitpoller: processing %d changes' % self.changeCount )
+        log.msg('gitpoller: processing %d changes' % revCount )
 
         for rev in revList:
             if self.usetimestamps:
                 commit_timestamp = self._get_commit_timestamp(rev)
             else:
                 commit_timestamp = None # use current time
                 
             c = changes.Change(who = self._get_commit_name(rev),
@@ -219,20 +219,18 @@ class GitPoller(base.ChangeSource):
                                branch = self.branch,
                                category = self.category,
                                project = self.project,
                                repository = self.repourl)
             self.parent.addChange(c)
             self.lastChange = self.lastPoll
             
     def _catch_up(self, res):
-        if self.changeCount == 0:
-            log.msg('gitpoller: no changes, no catch_up')
-            return self.changeCount
         log.msg('gitpoller: catching up to FETCH_HEAD')
+        
         args = ['reset', '--hard', 'FETCH_HEAD']
         d = utils.getProcessOutputAndValue(self.gitbin, args, path=self.workdir, env={})
         return d;
 
     def _changes_finished_ok(self, res):
         assert self.working
         # check for failure -- this is probably never hit but the twisted docs
         # are not clear enough to be sure. it is being kept "just in case"
--- a/master/buildbot/changes/mail.py
+++ b/master/buildbot/changes/mail.py
@@ -314,18 +314,17 @@ class BonsaiMaildirSource(MaildirSource)
             if user:
                 who = user
 
             module = items[4]
             file = items[5]
             if module and file:
                 path = "%s/%s" % (module, file)
                 files.append(path)
-            # For reference, we don't use this field
-            #sticky = items[7]
+            sticky = items[7]
             branch = items[8]
 
         # if no files changed, return nothing
         if not files:
             return None
 
         # read the comments
         comments = ""
@@ -420,35 +419,33 @@ class CVSMaildirSource(MaildirSource):
                 cvsmode = m.group(1)
                 continue
             m = filesRE.match(line)
             if m:
                 fileList = m.group(1)
                 continue
             m = modRE.match(line)
             if m:
-                # We don't actually use this
-                #module = m.group(1)
+                module = m.group(1)
                 continue
             m = pathRE.match(line)
             if m:
                 path = m.group(1)
                 continue
             m = projRE.match(line)
             if m:
                 project = m.group(1)
                 continue
             m = tagRE.match(line)
             if m:
                 branch = m.group(1)
                 continue
             m = updateRE.match(line)
             if m:
-                # We don't actually use this
-                #updateof = m.group(1)
+                updateof = m.group(1)
                 continue
             if line == "Log Message:\n":
                 break
 
         # CVS 1.11 lists files as:
         #   repo/path file,old-version,new-version file2,old-version,new-version
         # Version 1.12 lists files as:
         #   file1 old-version new-version file2 old-version new-version
@@ -574,16 +571,17 @@ class SVNCommitEmailMaildirSource(Maildi
         # timestamp out of the diffs, which would be ugly. TODO: Pulling the
         # 'Date:' header from the mail is a possibility, and
         # email.Utils.parsedate_tz may be useful. It should be configurable,
         # however, because there are a lot of broken clocks out there.
         when = util.now()
 
         files = []
         comments = ""
+        isdir = 0
         lines = list(body_line_iterator(m))
         rev = None
         while lines:
             line = lines.pop(0)
 
             # "Author: jmason"
             match = re.search(r"^Author: (\S+)", line)
             if match:
--- a/master/buildbot/db/connector.py
+++ b/master/buildbot/db/connector.py
@@ -266,19 +266,21 @@ class DBConnector(util.ComparableMixin):
             eventually(observer, category, *args)
 
     def subscribe_to(self, category, observer):
         self._subscribers[category].add(observer)
 
     def runQuery(self, *args, **kwargs):
         assert self._started
         self._pending_operation_count += 1
+        start = self._getCurrentTime()
+        #t = self._start_operation()   # why is this commented out? -warner
         d = self._pool.runQuery(*args, **kwargs)
+        #d.addBoth(self._runQuery_done, start, t)
         return d
-
     def _runQuery_done(self, res, start, t):
         self._end_operation(t)
         self._add_query_time(start)
         self._pending_operation_count -= 1
         return res
 
     def _add_query_time(self, start):
         elapsed = self._getCurrentTime() - start
--- a/master/buildbot/db/dbspec.py
+++ b/master/buildbot/db/dbspec.py
@@ -215,23 +215,21 @@ class DBSpec(object):
             raise ValueError("Unsupported dbapi %s" % driver)
 
     def _get_sqlite_dbapi_name(self):
         # see which dbapi we can use and return that name; prefer
         # pysqlite2.dbapi2 if it is available.
         sqlite_dbapi_name = None
         try:
             from pysqlite2 import dbapi2 as sqlite3
-            assert sqlite3
             sqlite_dbapi_name = "pysqlite2.dbapi2"
         except ImportError:
             # don't use built-in sqlite3 on 2.5 -- it has *bad* bugs
             if sys.version_info >= (2,6):
                 import sqlite3
-                assert sqlite3
                 sqlite_dbapi_name = "sqlite3"
             else:
                 raise
         return sqlite_dbapi_name
 
     def get_dbapi(self):
         """
         Get the dbapi module used for this connection (for things like
--- a/master/buildbot/db/schema/v4.py
+++ b/master/buildbot/db/schema/v4.py
@@ -14,70 +14,67 @@ class Upgrader(base.Upgrader):
     def makeAutoincColumn(self, name):
         if self.dbapiName == 'MySQLdb':
             return "`%s` INTEGER PRIMARY KEY AUTO_INCREMENT" % name
         elif self.dbapiName in ('sqlite3', 'pysqlite2.dbapi2'):
             return "`%s` INTEGER PRIMARY KEY AUTOINCREMENT" % name
         raise ValueError("Unsupported dbapi: %s" % self.dbapiName)
 
     def migrate_table(self, table_name, schema):
-        names = {
-            'old_name': "%s_old" % table_name,
-            'table_name': table_name,
-        }
+        old_name = "%s_old" % table_name
         cursor = self.conn.cursor()
         # If this fails, there's no cleaning up to do
         cursor.execute("""
             ALTER TABLE %(table_name)s
                 RENAME TO %(old_name)s
-        """ % names)
+        """ % locals())
 
         try:
             cursor.execute(schema)
         except:
             # Restore the original table
             cursor.execute("""
                 ALTER TABLE %(old_name)s
                     RENAME TO %(table_name)s
-            """ % names)
+            """ % locals())
             raise
 
         try:
             cursor.execute("""
                 INSERT INTO %(table_name)s
                     SELECT * FROM %(old_name)s
-            """ % names)
+            """ % locals())
             cursor.execute("""
                 DROP TABLE %(old_name)s
-            """ % names)
+            """ % locals())
         except:
             # Clean up the new table, and restore the original
             cursor.execute("""
                 DROP TABLE %(table_name)s
-            """ % names)
+            """ % locals())
             cursor.execute("""
                 ALTER TABLE %(old_name)s
                     RENAME TO %(table_name)s
-            """ % names)
+            """ % locals())
             raise
 
     def set_version(self):
         c = self.conn.cursor()
         c.execute("""UPDATE version set version = 4 where version = 3""")
 
     def migrate_schedulers(self):
         schedulerid_col = self.makeAutoincColumn('schedulerid')
         schema = """
             CREATE TABLE schedulers (
                 %(schedulerid_col)s, -- joins to other tables
                 `name` VARCHAR(100) NOT NULL, -- the scheduler's name according to master.cfg
                 `class_name` VARCHAR(100) NOT NULL, -- the scheduler's class
                 `state` VARCHAR(1024) NOT NULL -- JSON-encoded state dictionary
             );
-        """ % {'schedulerid_col': schedulerid_col}
+        """ % locals()
         self.migrate_table('schedulers', schema)
 
         # Fix up indices
         cursor = self.conn.cursor()
         cursor.execute("""
             CREATE UNIQUE INDEX `name_and_class` ON
                 schedulers (`name`, `class_name`)
         """)
@@ -88,17 +85,17 @@ class Upgrader(base.Upgrader):
             CREATE TABLE builds (
                 %(buildid_col)s,
                 `number` INTEGER NOT NULL, -- BuilderStatus.getBuild(number)
                 -- 'number' is scoped to both the local buildmaster and the buildername
                 `brid` INTEGER NOT NULL, -- matches buildrequests.id
                 `start_time` INTEGER NOT NULL,
                 `finish_time` INTEGER
             );
-        """ % {'buildid_col': buildid_col}
+        """ % locals()
         self.migrate_table('builds', schema)
 
     def migrate_changes(self):
         changeid_col = self.makeAutoincColumn('changeid')
         schema = """
             CREATE TABLE changes (
                 %(changeid_col)s, -- also serves as 'change number'
                 `author` VARCHAR(1024) NOT NULL,
@@ -113,17 +110,17 @@ class Upgrader(base.Upgrader):
                 -- repository specifies, along with revision and branch, the
                 -- source tree in which this change was detected.
                 `repository` TEXT NOT NULL default '',
 
                 -- project names the project this source code represents.  It is used
                 -- later to filter changes
                 `project` TEXT NOT NULL default ''
             );
-        """ % {'changeid_col': changeid_col}
+        """ % locals()
         self.migrate_table('changes', schema)
 
         # Drop changes_nextid columnt
         cursor = self.conn.cursor()
         cursor.execute("DROP TABLE changes_nextid")
 
     def migrate_buildrequests(self):
         buildrequestid_col = self.makeAutoincColumn('id')
@@ -157,53 +154,53 @@ class Upgrader(base.Upgrader):
 
                  -- results is only valid when complete==1
                 `results` SMALLINT, -- 0=SUCCESS,1=WARNINGS,etc, from status/builder.py
 
                 `submitted_at` INTEGER NOT NULL,
 
                 `complete_at` INTEGER
             );
-        """ % {'buildrequestid_col': buildrequestid_col}
+        """ % locals()
         self.migrate_table('buildrequests', schema)
 
     def migrate_buildsets(self):
         buildsetsid_col = self.makeAutoincColumn('id')
         schema = """
             CREATE TABLE buildsets (
                 %(buildsetsid_col)s,
                 `external_idstring` VARCHAR(256),
                 `reason` VARCHAR(256),
                 `sourcestampid` INTEGER NOT NULL,
                 `submitted_at` INTEGER NOT NULL,
                 `complete` SMALLINT NOT NULL default 0,
                 `complete_at` INTEGER,
                 `results` SMALLINT -- 0=SUCCESS,2=FAILURE, from status/builder.py
                  -- results is NULL until complete==1
             );
-        """ % {'buildsetsid_col': buildsetsid_col}
+        """ % locals()
         self.migrate_table("buildsets", schema)
 
     def migrate_patches(self):
         patchesid_col = self.makeAutoincColumn('id')
         schema = """
             CREATE TABLE patches (
                 %(patchesid_col)s,
                 `patchlevel` INTEGER NOT NULL,
                 `patch_base64` TEXT NOT NULL, -- encoded bytestring
                 `subdir` TEXT -- usually NULL
             );
-        """ % {'patchesid_col': patchesid_col}
+        """ % locals()
         self.migrate_table("patches", schema)
 
     def migrate_sourcestamps(self):
         sourcestampsid_col = self.makeAutoincColumn('id')
         schema = """
             CREATE TABLE sourcestamps (
                 %(sourcestampsid_col)s,
                 `branch` VARCHAR(256) default NULL,
                 `revision` VARCHAR(256) default NULL,
                 `patchid` INTEGER default NULL,
                 `repository` TEXT not null default '',
                 `project` TEXT not null default ''
             );
-        """ % {'sourcestampsid_col': sourcestampsid_col}
+        """ % locals()
         self.migrate_table("sourcestamps", schema)
--- a/master/buildbot/db/schema/v5.py
+++ b/master/buildbot/db/schema/v5.py
@@ -42,13 +42,13 @@ class Upgrader(base.Upgrader):
         self.set_version()
 
     def add_index(self, table, column, length=None):
         lengthstr=""
         if length is not None and self.dbapiName == 'MySQLdb':
             lengthstr = " (%i)" % length
         q = "CREATE INDEX `%(table)s_%(column)s` ON `%(table)s` (`%(column)s`%(lengthstr)s)"
         cursor = self.conn.cursor()
-        cursor.execute(q % {'table': table, 'column': column, 'lengthstr': lengthstr})
+        cursor.execute(q % locals())
 
     def set_version(self):
         c = self.conn.cursor()
         c.execute("""UPDATE version set version = 5 where version = 4""")
--- a/master/buildbot/ec2buildslave.py
+++ b/master/buildbot/ec2buildslave.py
@@ -99,17 +99,16 @@ class EC2LatentBuildSlave(AbstractLatent
         # We currently discard the keypair data because we don't need it.
         # If we do need it in the future, we will always recreate the keypairs
         # because there is no way to
         # programmatically retrieve the private key component, unless we
         # generate it and store it on the filesystem, which is an unnecessary
         # usage requirement.
         try:
             key_pair = self.conn.get_all_key_pairs(keypair_name)[0]
-            assert key_pair
             # key_pair.delete() # would be used to recreate
         except boto.exception.EC2ResponseError, e:
             if 'InvalidKeyPair.NotFound' not in e.body:
                 if 'AuthFailure' in e.body:
                     print ('POSSIBLE CAUSES OF ERROR:\n'
                            '  Did you sign up for EC2?\n'
                            '  Did you put a credit card number in your AWS '
                            'account?\n'
@@ -118,17 +117,16 @@ class EC2LatentBuildSlave(AbstractLatent
             # make one; we would always do this, and stash the result, if we
             # needed the key (for instance, to SSH to the box).  We'd then
             # use paramiko to use the key to connect.
             self.conn.create_key_pair(keypair_name)
 
         # create security group
         try:
             group = self.conn.get_all_security_groups(security_name)[0]
-            assert group
         except boto.exception.EC2ResponseError, e:
             if 'InvalidGroup.NotFound' in e.body:
                 self.security_group = self.conn.create_security_group(
                     security_name,
                     'Authorization to access the buildbot instance.')
                 # Authorize the master as necessary
                 # TODO this is where we'd open the hole to do the reverse pb
                 # connect to the buildbot
@@ -140,17 +138,16 @@ class EC2LatentBuildSlave(AbstractLatent
                 raise
 
         # get the image
         if self.ami is not None:
             self.image = self.conn.get_image(self.ami)
         else:
             # verify we have access to at least one acceptable image
             discard = self.get_image()
-            assert discard
 
         # get the specified elastic IP, if any
         if elastic_ip is not None:
             elastic_ip = self.conn.get_all_addresses([elastic_ip])[0]
         self.elastic_ip = elastic_ip
 
     def get_image(self):
         if self.image is not None:
--- a/master/buildbot/master.py
+++ b/master/buildbot/master.py
@@ -305,21 +305,17 @@ class BotMaster(service.MultiService):
         return defer.DeferredList(dl)
 
     def shouldMergeRequests(self, builder, req1, req2):
         """Determine whether two BuildRequests should be merged for
         the given builder.
 
         """
         if self.mergeRequests is not None:
-            if callable(self.mergeRequests):
-                return self.mergeRequests(builder, req1, req2)
-            elif self.mergeRequests == False:
-                # To save typing, this allows c['mergeRequests'] = False
-                return False
+            return self.mergeRequests(builder, req1, req2)
         return req1.canBeMergedWith(req2)
 
     def getPerspective(self, mind, slavename):
         sl = self.slaves[slavename]
         if not sl:
             return None
 
         # record when this connection attempt occurred
@@ -340,17 +336,17 @@ class BotMaster(service.MultiService):
             # squabble
             old_tport = sl.slave.broker.transport
             new_tport = mind.broker.transport
             log.msg("old slave was connected from", old_tport.getPeer())
             log.msg("new slave is from", new_tport.getPeer())
 
             # ping the old slave.  If this kills it, then the new slave will connect
             # again and everyone will be happy.
-            sl.slave.callRemote("print", "master got a duplicate connection; keeping this one")
+            d = sl.slave.callRemote("print", "master got a duplicate connection; keeping this one")
 
             # now return a dummy avatar and kill the new connection in 5
             # seconds, thereby giving the ping a bit of time to kill the old
             # connection, if necessary
             def kill():
                 log.msg("killing new slave on", new_tport.getPeer())
                 new_tport.loseConnection()
             reactor.callLater(5, kill)
@@ -685,18 +681,18 @@ class BuildMaster(service.MultiService):
             if logMaxSize is not None and not \
                     isinstance(logMaxSize, int):
                 raise ValueError("logMaxSize needs to be None or int")
             logMaxTailSize = config.get('logMaxTailSize')
             if logMaxTailSize is not None and not \
                     isinstance(logMaxTailSize, int):
                 raise ValueError("logMaxTailSize needs to be None or int")
             mergeRequests = config.get('mergeRequests')
-            if mergeRequests not in (None, False) and not callable(mergeRequests):
-                raise ValueError("mergeRequests must be a callable or False")
+            if mergeRequests is not None and not callable(mergeRequests):
+                raise ValueError("mergeRequests must be a callable")
             prioritizeBuilders = config.get('prioritizeBuilders')
             if prioritizeBuilders is not None and not callable(prioritizeBuilders):
                 raise ValueError("prioritizeBuilders must be callable")
             changeHorizon = config.get("changeHorizon")
             if changeHorizon is not None and not isinstance(changeHorizon, int):
                 raise ValueError("changeHorizon needs to be an int")
 
             multiMaster = config.get("multiMaster", False)
--- a/master/buildbot/scripts/runner.py
+++ b/master/buildbot/scripts/runner.py
@@ -453,17 +453,17 @@ def upgradeMaster(config):
       })
     m.populate_if_missing(os.path.join(basedir, "master.cfg.sample"),
                           util.sibpath(__file__, "sample.cfg"),
                           overwrite=True)
     # if index.html exists, use it to override the root page tempalte
     m.move_if_present(os.path.join(basedir, "public_html/index.html"),
                       os.path.join(basedir, "templates/root.html"))
 
-    from buildbot.db import dbspec
+    from buildbot.db import connector, dbspec
     spec = dbspec.DBSpec.from_url(config["db"], basedir)
     # TODO: check that TAC file specifies the right spec
 
     # upgrade the db
     from buildbot.db.schema import manager
     sm = manager.DBSchemaManager(spec, basedir)
     sm.upgrade()
 
@@ -629,17 +629,17 @@ def stop(config, signame="TERM", wait=Fa
         time.sleep(1)
     if not quiet:
         print "never saw process go away"
 
 def restart(config):
     basedir = config['basedir']
     quiet = config['quiet']
 
-    if not isBuildmasterDir(basedir):
+    if not isBuildmasterDir(config['basedir']):
         print "not a buildmaster directory"
         sys.exit(1)
 
     from buildbot.scripts.startup import start
     try:
         stop(config, wait=True)
     except BuildbotNotRunningError:
         pass
@@ -982,17 +982,16 @@ class TryServerOptions(OptionsWithOption
         ]
     def getSynopsis(self):
         return "Usage:    buildbot tryserver [options]"
 
 
 def doTryServer(config):
     try:
         from hashlib import md5
-        assert md5
     except ImportError:
         # For Python 2.4 compatibility
         import md5
     jobdir = os.path.expanduser(config["jobdir"])
     job = sys.stdin.read()
     # now do a 'safecat'-style write to jobdir/tmp, then move atomically to
     # jobdir/new . Rather than come up with a unique name randomly, I'm just
     # going to MD5 the contents and prepend a timestamp.
--- a/master/buildbot/status/builder.py
+++ b/master/buildbot/status/builder.py
@@ -996,17 +996,21 @@ class BuildStepStatus(styles.Versioned):
         return log
 
     def addHTMLLog(self, name, html):
         assert self.started # addLog before stepStarted won't notify watchers
         logfilename = self.build.generateLogfileName(self.name, name)
         log = HTMLLogFile(self, name, logfilename, html)
         self.logs.append(log)
         for w in self.watchers:
-            w.logStarted(self.build, self, log)
+            receiver = w.logStarted(self.build, self, log)
+            # TODO: think about this: there isn't much point in letting
+            # them subscribe
+            #if receiver:
+            #    log.subscribe(receiver, True)
             w.logFinished(self.build, self, log)
 
     def logFinished(self, log):
         for w in self.watchers:
             w.logFinished(self.build, self, log)
 
     def addURL(self, name, url):
         self.urls[name] = url
--- a/master/buildbot/status/mail.py
+++ b/master/buildbot/status/mail.py
@@ -50,18 +50,16 @@ def defaultMessage(mode, name, build, re
     result = Results[results]
     ss = build.getSourceStamp()
 
     text = ""
     if mode == "all":
         text += "The Buildbot has finished a build"
     elif mode == "failing":
         text += "The Buildbot has detected a failed build"
-    elif mode == "warnings":
-        text += "The Buildbot has detected a problem in the build"
     elif mode == "passing":
         text += "The Buildbot has detected a passing build"
     elif mode == "change" and result == 'success':
         text += "The Buildbot has detected a restored build"
     else:    
         text += "The Buildbot has detected a new failure"
     if ss and ss.project:
         project = ss.project
@@ -170,17 +168,16 @@ class MailNotifier(base.StatusReceiverMu
         @param subject: a string to be used as the subject line of the message.
                         %(builder)s will be replaced with the name of the
                         builder which provoked the message.
 
         @type  mode: string (defaults to all)
         @param mode: one of:
                      - 'all': send mail about all builds, passing and failing
                      - 'failing': only send mail about builds which fail
-                     - 'warnings': send mail if builds contain warnings or fail 
                      - 'passing': only send mail about builds which succeed
                      - 'problem': only send mail about a build which failed
                      when the previous build passed
                      - 'change': only send mail about builds who change status
 
         @type  builders: list of strings
         @param builders: a list of builder names for which mail should be
                          sent. Defaults to None (send mail for all builds).
@@ -261,17 +258,17 @@ class MailNotifier(base.StatusReceiverMu
         assert isinstance(extraRecipients, (list, tuple))
         for r in extraRecipients:
             assert isinstance(r, str)
             # require full email addresses, not User names
             assert VALID_EMAIL.search(r), "%s is not a valid email" % r 
         self.extraRecipients = extraRecipients
         self.sendToInterestedUsers = sendToInterestedUsers
         self.fromaddr = fromaddr
-        assert mode in ('all', 'failing', 'problem', 'change', 'passing', 'warnings')
+        assert mode in ('all', 'failing', 'problem', 'change', 'passing')
         self.mode = mode
         self.categories = categories
         self.builders = builders
         self.addLogs = addLogs
         self.relayhost = relayhost
         self.subject = subject
         if lookup is not None:
             if type(lookup) is str:
@@ -335,18 +332,16 @@ class MailNotifier(base.StatusReceiverMu
         # here is where we actually do something.
         builder = build.getBuilder()
         if self.builders is not None and name not in self.builders:
             return # ignore this build
         if self.categories is not None and \
                builder.category not in self.categories:
             return # ignore this build
 
-        if self.mode == "warnings" and results == SUCCESS:
-            return
         if self.mode == "failing" and results != FAILURE:
             return
         if self.mode == "passing" and results != SUCCESS:
             return
         if self.mode == "problem":
             if results != FAILURE:
                 return
             prev = build.getPreviousBuild()
--- a/master/buildbot/status/persistent_queue.py
+++ b/master/buildbot/status/persistent_queue.py
@@ -1,14 +1,13 @@
 # -*- test-case-name: buildbot.test.test_persistent_queue -*-
 
 try:
     # Python 2.4+
     from collections import deque
-    assert deque
 except ImportError:
     deque = None
 import os
 import pickle
 
 from zope.interface import implements, Interface
 
 
--- a/master/buildbot/status/status_push.py
+++ b/master/buildbot/status/status_push.py
@@ -7,17 +7,16 @@ Implements the HTTP receiver."""
 import datetime
 import logging
 import os
 import urllib
 import urlparse
 
 try:
     import simplejson as json
-    assert json
 except ImportError:
     import json
 
 from buildbot.status.base import StatusReceiverMultiService
 from buildbot.status.persistent_queue import DiskQueue, IndexedQueue, \
         MemoryQueue, PersistentQueue
 from buildbot.status.web.status_json import FilterOut
 from twisted.internet import defer, reactor
--- a/master/buildbot/status/web/builder.py
+++ b/master/buildbot/status/web/builder.py
@@ -6,16 +6,17 @@ import re, urllib, time
 from twisted.python import log
 from buildbot import interfaces
 from buildbot.status.web.base import HtmlResource, BuildLineMixin, \
     path_to_build, path_to_slave, path_to_builder, path_to_change, \
     path_to_root, getAndCheckProperties, ICurrentBox, build_get_class, \
     map_branches, path_to_authfail
 from buildbot.sourcestamp import SourceStamp
 
+from buildbot.status.builder import BuildRequestStatus
 from buildbot.status.web.build import BuildsResource, StatusResourceBuild
 from buildbot import util
 
 # /builders/$builder
 class StatusResourceBuilder(HtmlResource, BuildLineMixin):
     addSlash = True
 
     def __init__(self, builder_status):
--- a/master/buildbot/status/web/change_hook.py
+++ b/master/buildbot/status/web/change_hook.py
@@ -1,17 +1,23 @@
 # code inspired/copied from contrib/github_buildbot
 #  and inspired from code from the Chromium project
 # otherwise, Andrew Melo <andrew.melo@gmail.com> wrote the rest
 
 # but "the rest" is pretty minimal
 from twisted.web import resource
+from buildbot.status.builder import FAILURE
 import re
+from buildbot import util, interfaces
+import traceback
+import sys
+from buildbot.process.properties import Properties
+from buildbot.changes.changes import Change
 from twisted.python.reflect import namedModule
-from twisted.python.log import msg
+from twisted.python.log import msg,err
 from buildbot.util import json
 
 class ChangeHookResource(resource.Resource):
      # this is a cheap sort of template thingy
     contentType = "text/html; charset=utf-8"
     children    = {}
     def __init__(self, dialects={}):
         """
--- a/master/buildbot/status/web/hooks/base.py
+++ b/master/buildbot/status/web/hooks/base.py
@@ -1,15 +1,25 @@
 # code inspired/copied from contrib/github_buildbot
 #  and inspired from code from the Chromium project
 # otherwise, Andrew Melo <andrew.melo@gmail.com> wrote the rest
 
 # but "the rest" is pretty minimal
+from twisted.web import resource
+from buildbot.status.builder import FAILURE
+import re
+from buildbot import util, interfaces
+import logging
+import traceback
+import sys
+from buildbot.process.properties import Properties
 from buildbot.changes.changes import Change
+from twisted.python.reflect import namedModule
 from buildbot.util import json
+from twisted.python.log import msg,err
     
 def getChanges(request, options=None):
         """
         Consumes a naive build notification (the default for now)
         basically, set POST variables to match commit object parameters:
         revision, revlink, comments, branch, who, files, links
         
         files, links and properties will be de-json'd, the rest are interpreted as strings
--- a/master/buildbot/status/web/hooks/github.py
+++ b/master/buildbot/status/web/hooks/github.py
@@ -4,28 +4,38 @@ github_buildbot.py is based on git_build
 
 github_buildbot.py will determine the repository information from the JSON 
 HTTP POST it receives from github.com and build the appropriate repository.
 If your github repository is private, you must add a ssh key to the github
 repository for the user who initiated the build on the buildslave.
 
 """
 
+import tempfile
 import logging
 import re
 import sys
 import traceback
+from twisted.web import server, resource
+from twisted.internet import reactor
+from twisted.spread import pb
+from twisted.cred import credentials
+from optparse import OptionParser
 from buildbot.changes.changes import Change
 import datetime
+import time
 from twisted.python import log
 import calendar
+import time
+import calendar
+import datetime
+import re
 
 try:
     import json
-    assert json
 except ImportError:
     import simplejson as json
 
 # python is silly about how it handles timezones
 class fixedOffset(datetime.tzinfo):
     """
     fixed offset timezone
     """
@@ -71,18 +81,17 @@ def getChanges(request, options = None):
             request
                 the http request object
         """
         try:
             payload = json.loads(request.args['payload'][0])
             user = payload['repository']['owner']['name']
             repo = payload['repository']['name']
             repo_url = payload['repository']['url']
-            # This field is unused:
-            #private = payload['repository']['private']
+            private = payload['repository']['private']
             changes = process_change(payload, user, repo, repo_url)
             log.msg("Received %s changes from github" % len(changes))
             return changes
         except Exception:
             logging.error("Encountered an exception:")
             for msg in traceback.format_exception(*sys.exc_info()):
                 logging.error(msg.strip())
 
--- a/master/buildbot/status/web/root.py
+++ b/master/buildbot/status/web/root.py
@@ -1,11 +1,13 @@
 from twisted.web.util import redirectTo
+from twisted.python import log
+from twisted.internet import reactor
 
-from buildbot.status.web.base import HtmlResource, path_to_authfail
+from buildbot.status.web.base import HtmlResource, path_to_root, path_to_authfail
 from buildbot.util.eventual import eventually
 
 class RootPage(HtmlResource):
     title = "Buildbot"
 
     def content(self, request, cxt):
         status = self.getStatus(request)
 
--- a/master/buildbot/status/web/tests.py
+++ b/master/buildbot/status/web/tests.py
@@ -1,13 +1,16 @@
 
 import urllib
 from buildbot.status.web.base import HtmlResource, path_to_builder, \
      path_to_build, css_classes
+from buildbot.status.web.logs import LogsResource
 from buildbot.status.builder import Results
+from buildbot import util
+from time import ctime
 
 # /builders/$builder/builds/$buildnum/steps/$stepname
 class StatusResourceBuildTest(HtmlResource):
     title = "Test Result"
     addSlash = True
 
     def __init__(self, build_status, test_result):
         HtmlResource.__init__(self)
--- a/master/buildbot/steps/subunit.py
+++ b/master/buildbot/steps/subunit.py
@@ -1,11 +1,11 @@
 
 from buildbot.steps.shell import ShellCommand
-from buildbot.status.builder import SUCCESS, FAILURE
+from buildbot.status.builder import SUCCESS, FAILURE, WARNINGS, SKIPPED
 
 class SubunitShellCommand(ShellCommand):
     """A ShellCommand that sniffs subunit output.
     """
 
     def __init__(self, *args, **kwargs):
         ShellCommand.__init__(self, *args, **kwargs)
         # importing here gets around an import loop
--- a/master/buildbot/steps/transfer.py
+++ b/master/buildbot/steps/transfer.py
@@ -1,14 +1,13 @@
 # -*- test-case-name: buildbot.test.test_transfer -*-
 
 import os.path, tarfile, tempfile
 try:
     from cStringIO import StringIO
-    assert StringIO
 except ImportError:
     from StringIO import StringIO
 from twisted.internet import reactor
 from twisted.spread import pb
 from twisted.python import log
 from buildbot.process.buildstep import RemoteCommand, BuildStep
 from buildbot.process.buildstep import SUCCESS, FAILURE, SKIPPED
 from buildbot.interfaces import BuildSlaveTooOldError
--- a/master/buildbot/test/fake/fakedb.py
+++ b/master/buildbot/test/fake/fakedb.py
@@ -1,15 +1,14 @@
 import sys, time
 
 from twisted.internet import defer
 
 try:
     from pysqlite2 import dbapi2 as sqlite3
-    assert sqlite3
 except ImportError:
     # don't use built-in sqlite3 on 2.5 -- it has *bad* bugs
     if sys.version_info >= (2,6):
         import sqlite3
     else:
         raise
 
 def get_sqlite_memory_connection():
--- a/master/buildbot/test/regressions/test_import_weird_changes.py
+++ b/master/buildbot/test/regressions/test_import_weird_changes.py
@@ -1,15 +1,15 @@
 import os
 import shutil
 import cPickle
 
 from twisted.trial import unittest
 
-from buildbot.changes.changes import Change
+from buildbot.changes.changes import Change, OldChangeMaster
 
 from buildbot.db.schema import manager
 from buildbot.db.dbspec import DBSpec
 from buildbot.db.connector import DBConnector
 
 class TestWeirdChanges(unittest.TestCase):
     def setUp(self):
         self.basedir = "WeirdChanges"
--- a/master/buildbot/test/unit/test_changes_mail_CVSMaildirSource.py
+++ b/master/buildbot/test/unit/test_changes_mail_CVSMaildirSource.py
@@ -1,13 +1,17 @@
+from mock import Mock, patch_object
+from buildbot.interfaces import ParameterError
 from twisted.trial import unittest
 
 from email import message_from_string
-from email.Utils import parsedate_tz, mktime_tz
-from buildbot.changes.mail import CVSMaildirSource
+from email.Utils import parseaddr, parsedate_tz, mktime_tz
+import datetime
+from buildbot.status.builder import SUCCESS, FAILURE
+from buildbot.changes.mail import MaildirSource, CVSMaildirSource
 
 #
 # Sample message from CVS version 1.11
 #
 
 cvs1_11_msg = """From: Andy Howell <andy@example.com>
 To: buildbot@example.com
 Subject: cvs module MyModuleName
@@ -109,30 +113,28 @@ class TestCVSMaildirSource(unittest.Test
         self.assert_(len(propList) == 0 )
 
     def test_CVSMaildirSource_create_change_from_cvs1_12_with_no_path(self):
         msg = cvs1_12_msg.replace('Path: base/module/src', '')
         m = message_from_string(msg)
         src = CVSMaildirSource('/dev/null')
         try:
             change = src.parse( m )
-            assert change
         except ValueError:
             pass
         else:
             self.fail('Expect ValueError.')
 
     def test_CVSMaildirSource_create_change_with_bad_cvsmode(self):
         # Branch is indicated afer 'Tag:' in modified file list
         msg = cvs1_11_msg.replace('Cvsmode: 1.11', 'Cvsmode: 9.99')
         m = message_from_string(msg)
         src = CVSMaildirSource('/dev/null')
         try:
             change = src.parse( m )
-            assert change
         except ValueError:
             pass
         else:
             self.fail('Expected ValueError')
 
     def test_CVSMaildirSource_create_change_with_branch(self):
         # Branch is indicated afer 'Tag:' in modified file list
         msg = cvs1_11_msg.replace('        GNUmakefile',
--- a/master/buildbot/test/unit/test_contrib_buildbot_cvs_mail.py
+++ b/master/buildbot/test/unit/test_contrib_buildbot_cvs_mail.py
@@ -1,9 +1,10 @@
 import sys
+import datetime
 import re
 import subprocess
 import os
 
 from twisted.trial import unittest
 
 test = '''
 Update of /cvsroot/test
--- a/master/buildbot/test/unit/test_master_cleanshutdown.py
+++ b/master/buildbot/test/unit/test_master_cleanshutdown.py
@@ -1,10 +1,10 @@
 # Test clean shutdown functionality of the master
-from mock import Mock
+from mock import Mock, patch, patch_object
 from twisted.trial import unittest
 from twisted.internet import defer
 from buildbot.master import BotMaster
 
 class TestCleanShutdown(unittest.TestCase):
     def setUp(self):
         self.master = BotMaster()
         self.master.reactor = Mock()
@@ -120,17 +120,16 @@ class TestCleanShutdown(unittest.TestCas
         build.waitUntilFinished.return_value = d_finished
 
         self.master.builders = Mock()
         self.master.builders.values.return_value = [builder]
 
         self.assertEquals(self.master._get_processors(), [builder.run])
 
         d_shutdown = self.master.cleanShutdown()
-        assert d_shutdown
 
         # Trigger the loop to get things going
         self.master.loop.trigger()
 
         # First we wait for it to quiet down again
         d = self.master.loop.when_quiet()
 
         # Next we check that we haven't stopped yet, since there's a running
--- a/master/buildbot/test/unit/test_oldpaths.py
+++ b/master/buildbot/test/unit/test_oldpaths.py
@@ -3,47 +3,37 @@ from twisted.trial import unittest
 
 class OldImportPaths(unittest.TestCase):
     """
     Test that old, deprecated import paths still work.
     """
 
     def test_scheduler_Scheduler(self):
         from buildbot.scheduler import Scheduler
-        assert Scheduler
 
     def test_scheduler_AnyBranchScheduler(self):
         from buildbot.scheduler import AnyBranchScheduler
-        assert AnyBranchScheduler
 
     def test_scheduler_Dependent(self):
         from buildbot.scheduler import Dependent
-        assert Dependent
 
     def test_scheduler_Periodic(self):
         from buildbot.scheduler import Periodic
-        assert Periodic
 
     def test_scheduler_Nightly(self):
         from buildbot.scheduler import Nightly
-        assert Nightly
 
     def test_scheduler_Triggerable(self):
         from buildbot.scheduler import Triggerable
-        assert Triggerable
 
     def test_scheduler_Try_Jobdir(self):
         from buildbot.scheduler import Try_Jobdir
-        assert Try_Jobdir
 
     def test_scheduler_Try_Userpass(self):
         from buildbot.scheduler import Try_Userpass
-        assert Try_Userpass
 
     def test_changes_changes_ChangeMaster(self):
         # this must exist to open old changes pickles
         from buildbot.changes.changes import ChangeMaster
-        assert ChangeMaster
 
     def test_changes_changes_Change(self):
         # this must exist to open old changes pickles
         from buildbot.changes.changes import Change
-        assert Change
--- a/master/buildbot/test/unit/test_process_base.py
+++ b/master/buildbot/test/unit/test_process_base.py
@@ -1,16 +1,17 @@
 from twisted.trial import unittest
 from twisted.internet import defer
 
 from buildbot.process.base import Build
 from buildbot.process.properties import Properties
+from buildbot.buildrequest import BuildRequest
 from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS, RETRY, EXCEPTION
-from buildbot.locks import SlaveLock
-from buildbot.process.buildstep import LoggingBuildStep
+from buildbot.locks import SlaveLock, RealSlaveLock
+from buildbot.process.buildstep import BuildStep, LoggingBuildStep
 
 from mock import Mock
 
 class FakeChange:
     properties = Properties()
     who = "me"
 
 class FakeSource:
@@ -167,16 +168,17 @@ class TestBuild(unittest.TestCase):
 
         b = Build([r])
         b.setBuilder(Mock())
         b.builder.botmaster = FakeMaster()
         slavebuilder = Mock()
         status = Mock()
 
         l = SlaveLock('lock')
+        claimCount = [0]
         lock_access = l.access('counting')
         l.access = lambda mode: lock_access
         real_lock = b.builder.botmaster.getLockByID(l).getLock(slavebuilder)
         b.setLocks([l])
 
         step = Mock()
         step.return_value = step
         step.startStep.return_value = SUCCESS
@@ -203,16 +205,17 @@ class TestBuild(unittest.TestCase):
 
         b = Build([r])
         b.setBuilder(Mock())
         b.builder.botmaster = FakeMaster()
         slavebuilder = Mock()
         status = Mock()
 
         l = SlaveLock('lock')
+        claimCount = [0]
         lock_access = l.access('counting')
         l.access = lambda mode: lock_access
         real_lock = b.builder.botmaster.getLockByID(l).getLock(slavebuilder)
 
         step = LoggingBuildStep(locks=[lock_access])
         def factory(*args):
             return step
         b.setStepFactories([(factory, {})])
--- a/master/buildbot/test/unit/test_process_properties.py
+++ b/master/buildbot/test/unit/test_process_properties.py
@@ -1,9 +1,10 @@
 from twisted.trial import unittest
+from twisted.internet import defer
 
 from buildbot.process.properties import PropertyMap, Properties, WithProperties
 
 class FakeProperties(object):
     def __init__(self, **kwargs):
         self.dict = kwargs
     def __getitem__(self, k):
         return self.dict[k]
--- a/master/buildbot/test/unit/test_status_builder.py
+++ b/master/buildbot/test/unit/test_status_builder.py
@@ -1,12 +1,13 @@
 import os
 
 from buildbot.status import builder
 #from buildbot.util import json
+from mock import Mock
 
 from twisted.trial import unittest
 
 class TestBuildStepStatus(unittest.TestCase):
     def setupBuilder(self, buildername, category=None):
         b = builder.BuilderStatus(buildername=buildername, category=category)
         # Ackwardly, Status sets this member variable.
         b.basedir = os.path.abspath(self.mktemp())
--- a/master/buildbot/test/unit/test_status_web_change_hooks_github.py
+++ b/master/buildbot/test/unit/test_status_web_change_hooks_github.py
@@ -1,13 +1,14 @@
 import buildbot.status.web.change_hook as change_hook
 from buildbot.test.util.web import MockRequest
 from buildbot.util import json
 
 from twisted.trial import unittest
+from mock import Mock
 
 # Sample GITHUB commit payload from http://help.github.com/post-receive-hooks/
 # Added "modfied" and "removed", and change email
 
 gitJsonPayload = """
 {
   "before": "5aef35982fb2d34e9d9d4502f6ede1072793222d",
   "repository": {
--- a/master/buildbot/util/__init__.py
+++ b/master/buildbot/util/__init__.py
@@ -125,17 +125,16 @@ def none_or_str(x):
         return str(x)
     return x
 
 # place a working json module at 'buildbot.util.json'.  Code is from
 # Paul Wise <pabs@debian.org>:
 #   http://lists.debian.org/debian-python/2010/02/msg00016.html
 try:
     import json # python 2.6
-    assert json
 except ImportError:
     import simplejson as json # python 2.4 to 2.5
 try:
     _tmp = json.loads
 except AttributeError:
     import warnings
     import sys
     warnings.warn("Use simplejson, not the old json module.")
--- a/master/buildbot/util/collections.py
+++ b/master/buildbot/util/collections.py
@@ -1,11 +1,10 @@
 try:
     from collections import defaultdict
-    assert defaultdict
 except ImportError:
     # collections.defaultdict only appeared in py2.5, but buildbot supports 2.4
     class defaultdict(dict):
         def __init__(self, default_factory=None, *args, **kwargs):
             self._default_factory = default_factory
             dict.__init__(self, *args, **kwargs)
         def __getitem__(self, key):
             if key not in self and self._default_factory:
--- a/master/docs/cfg-global.texinfo
+++ b/master/docs/cfg-global.texinfo
@@ -270,19 +270,16 @@ developers forcing builds for different 
 @example
 def mergeRequests(builder, req1, req2):
     if req1.source.canBeMergedWith(req2.source) and  req1.reason == req2.reason:
        return True
     return False
 c['mergeRequests'] = mergeRequests
 @end example
 
-If desired, request merging can be disabled globally by setting
-@code{c['mergeRequests'] = False} instead of a callable function.
-
 @node Prioritizing Builders
 @subsection Prioritizing Builders
 
 @bcindex c['prioritizeBuilders']
 
 By default, buildbot will attempt to start builds on builders in order from the
 builder with the oldest pending request to the newest. This behaviour can be
 customized with the @code{c['prioritizeBuilders']} configuration key.
--- a/master/docs/cfg-statustargets.texinfo
+++ b/master/docs/cfg-statustargets.texinfo
@@ -880,18 +880,16 @@ provoked the message.
 (string). Default to 'all'. One of:
 @table @code
 @item all
 Send mail about all builds, both passing and failing
 @item change
 Only send mail about builds which change status
 @item failing
 Only send mail about builds which fail
-@item warning
-Only send mail about builds which fail or generate warnings
 @item passing
 Only send mail about builds which succeed
 @item problem
 Only send mail about a build which failed when the previous build has passed.
 If your builds usually pass, then this will only send mail when a problem
 occurs.
 @end table
 
--- a/slave/NEWS
+++ b/slave/NEWS
@@ -1,14 +1,12 @@
 Major User visible changes in Buildslave.             -*- outline -*-
    see the git log for a detailed list of changes:
    http://github.com/buildbot/buildbot/commits/master
 
-* Next Release
-
 * Buildbot-Slave 0.8.2
 
 ** Log Rotation
 
 The default 'create-slave' output now rotates ten twistd.log files, each of
 about 10MiB.  This is a change from older versions, which would rotate an
 unbounded number of 1MiB files.