Merge "Remove support for refs/publish/"
diff --git a/BUILD b/BUILD
index 5ddf8b1..f0b1954 100644
--- a/BUILD
+++ b/BUILD
@@ -23,7 +23,6 @@
     cmd = ("cat bazel-out/volatile-status.txt bazel-out/stable-status.txt | " +
            "grep STABLE_BUILD_GERRIT_LABEL | cut -d ' ' -f 2 > $@"),
     stamp = 1,
-    visibility = ["//visibility:public"],
 )
 
 genrule(
@@ -31,7 +30,6 @@
     srcs = ["//Documentation:licenses.txt"],
     outs = ["LICENSES.txt"],
     cmd = "cp $< $@",
-    visibility = ["//visibility:public"],
 )
 
 pkg_war(
diff --git a/Documentation/BUILD b/Documentation/BUILD
index b322fef..8a7a313 100644
--- a/Documentation/BUILD
+++ b/Documentation/BUILD
@@ -40,7 +40,6 @@
         ":prettify_files",
         "//:LICENSES.txt",
     ],
-    visibility = ["//visibility:public"],
 )
 
 license_map(
@@ -50,7 +49,6 @@
         "//polygerrit-ui/app:polygerrit_ui",
         "//java/com/google/gerrit/pgm",
     ],
-    visibility = ["//visibility:public"],
 )
 
 license_map(
@@ -58,7 +56,6 @@
     targets = [
         "//polygerrit-ui/app:polygerrit_ui",
     ],
-    visibility = ["//visibility:public"],
 )
 
 DOC_DIR = "Documentation"
@@ -86,7 +83,6 @@
     srcs = SRCS,
     attributes = documentation_attributes(),
     backend = "html5",
-    visibility = ["//visibility:public"],
 )
 
 genasciidoc_zip(
@@ -95,7 +91,6 @@
     attributes = documentation_attributes(),
     backend = "html5",
     directory = DOC_DIR,
-    visibility = ["//visibility:public"],
 )
 
 genasciidoc_zip(
@@ -105,5 +100,4 @@
     backend = "html5",
     directory = DOC_DIR,
     searchbox = False,
-    visibility = ["//visibility:public"],
 )
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 516cf13..1d1f17e 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -1204,8 +1204,7 @@
 [[capability_accessDatabase]]
 === Access Database
 
-Allow users to access the database using the `gsql` command, and view code
-review metadata refs in repositories.
+Allow users to view code review metadata refs in repositories.
 
 
 [[capability_administrateServer]]
diff --git a/Documentation/cmd-gsql.txt b/Documentation/cmd-gsql.txt
deleted file mode 100644
index 7f2aaf7..0000000
--- a/Documentation/cmd-gsql.txt
+++ /dev/null
@@ -1,64 +0,0 @@
-= gerrit gsql
-
-== NAME
-gerrit gsql - Administrative interface to active database.
-
-== SYNOPSIS
-[verse]
---
-_ssh_ -p <port> <host> _gerrit gsql_
-  [--format {PRETTY | JSON | JSON_SINGLE}]
-  [-c QUERY]
---
-
-== DESCRIPTION
-Provides interactive query support directly against the underlying
-SQL database used by the host Gerrit server.  All SQL statements
-are supported, including SELECT, UPDATE, INSERT, DELETE and ALTER.
-
-== OPTIONS
---format::
-	Set the format records are output in.  In PRETTY (the
-	default) records are displayed in a tabular output suitable
-	for reading by a human on a sufficiently wide terminal.
-	In JSON mode records are output as JSON objects using the
-	column names as the property names, one object per line.
-	In JSON_SINGLE mode the whole result set is output as a
-	single JSON object.
-
--c::
-	Execute the single query statement supplied, and then exit.
-
-== ACCESS
-Caller must have been granted the
-link:access-control.html#capability_accessDatabase[Access Database]
-global capability.
-
-== SCRIPTING
-Intended for interactive use only, unless format is JSON, or
-JSON_SINGLE.
-
-== EXAMPLES
-To manually correct a user's SSH user name:
-
-----
-$ ssh -p 29418 review.example.com gerrit gsql
-Welcome to Gerrit Code Review v2.0.25
-(PostgreSQL 8.3.8)
-
-Type '\h' for help.  Type '\r' to clear the buffer.
-
-gerrit> update accounts set ssh_user_name = 'alice' where account_id=1;
-UPDATE 1; 1 ms
-gerrit> \q
-Bye
-
-$ ssh -p 29418 review.example.com gerrit flush-caches --cache sshkeys --cache accounts
-----
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 25099fa..e999218 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -130,9 +130,6 @@
 link:cmd-gc.html[gerrit gc]::
 	Run the Git garbage collection.
 
-link:cmd-gsql.html[gerrit gsql]::
-	Administrative interface to active database.
-
 link:cmd-index-activate.html[gerrit index activate]::
 	Activate the latest index version available.
 
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index fc9d405..037731d 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -693,7 +693,9 @@
 allows to limit the memory used by H2 and thus prevent out-of-memory
 caused by the H2 database using too much memory.
 +
-See <<database.h2.cacheSize,database.h2.cacheSize>> for a detailed discussion.
+Technically the H2 cache size is configured using the CACHE_SIZE parameter in
+the H2 JDBC connection URL, as described
+link:http://www.h2database.com/html/features.html#cache_settings[here]
 +
 Default is unset, using up to half of the available memory.
 +
@@ -1677,215 +1679,6 @@
 +
 Default is 1 hour.
 
-[[database]]
-=== Section database
-
-The database section configures ReviewDb, where Gerrit stores its metadata
-records about account groups and change reviews. Starting from 2.15, accounts
-are always stored in NoteDb and, optionally, changes too. See the
-link:note-db.html[NoteDb documentation] for more information.
-
-Note that user file reviewed flags are stored in a separate database. See the
-<<accountPatchReviewDb,accountPatchReviewDb>> section for more information.
-
-----
-[database]
-  type = POSTGRESQL
-  hostname = localhost
-  database = reviewdb
-  username = gerrit
-  password = s3kr3t
-----
-
-[[database.type]]database.type::
-+
-Type of database server to connect to.  If set this value will be
-used to automatically create correct database.driver and database.url
-values to open the connection.
-+
-* `DB2`
-+
-Connect to a DB2 database server.
-+
-* `DERBY`
-+
-Connect to an Apache Derby database server.
-+
-* `H2`
-+
-Connect to a local embedded H2 database.
-+
-* `JDBC`
-+
-Connect using a JDBC driver class name and URL.
-+
-* `MAXDB`
-+
-Connect to an SAP MaxDB database server.
-+
-* `MYSQL`
-+
-Connect to a MySQL database server.
-+
-* `MARIADB`
-+
-Connect to a MariaDB database server.
-+
-* `ORACLE`
-+
-Connect to an Oracle database server.
-+
-* `POSTGRESQL`
-+
-Connect to a PostgreSQL database server.
-
-+
-If not specified, database.driver and database.url are used as-is,
-and if they are also not specified, defaults to H2.
-
-[[database.hostname]]database.hostname::
-+
-Hostname of the database server.  Defaults to 'localhost'.
-
-[[database.port]]database.port::
-+
-Port number of the database server.  Defaults to the default port
-of the server named by database.type.
-
-[[database.database]]database.database::
-+
-For POSTGRESQL or MYSQL, the name of the database on the server.
-+
-For H2, this is the path to the database, and if not absolute is
-relative to `'$site_path'`.
-
-[[database.username]]database.username::
-+
-Username to connect to the database server as.
-
-[[database.password]]database.password::
-+
-Password to authenticate to the database server with.
-
-[[database.driver]]database.driver::
-+
-Name of the JDBC driver class to connect to the database with.
-Setting this usually isn't necessary as it can be derived from
-database.type or database.url for any supported database.
-
-[[database.url]]database.url::
-+
-'jdbc:' URL for the database.  Setting this variable usually
-isn't necessary as it can be constructed from the all of the
-above properties.
-
-[[database.connectionPool]]database.connectionPool::
-+
-If true, use connection pooling for database connections. Otherwise, a
-new database connection is opened for each request.
-+
-Default is false for MySQL, and true for other database backends.
-
-[[database.poolLimit]]database.poolLimit::
-+
-Maximum number of open database connections.  If the server needs
-more than this number, request processing threads will wait up
-to <<database.poolMaxWait, poolMaxWait>> seconds for a
-connection to be released before they abort with an exception.
-This limit must be several units higher than the total number of
-httpd and sshd threads as some request processing code paths may
-need multiple connections.
-+
-Default is <<sshd.threads, sshd.threads>>
- + <<httpd.maxThreads, httpd.maxThreads>> + 2.
-+
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
-
-[[database.poolMinIdle]]database.poolMinIdle::
-+
-Minimum number of connections to keep idle in the pool.
-Default is 4.
-+
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
-
-[[database.poolMaxIdle]]database.poolMaxIdle::
-+
-Maximum number of connections to keep idle in the pool.  If there
-are more idle connections, connections will be closed instead of
-being returned back to the pool.
-Default is min(<<database.poolLimit, database.poolLimit>>, 16).
-+
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
-
-[[database.poolMaxWait]]database.poolMaxWait::
-+
-Maximum amount of time a request processing thread will wait to
-acquire a database connection from the pool.  If no connection is
-released within this time period, the processing thread will abort
-its current operations and return an error to the client.
-Values should use common unit suffixes to express their setting:
-+
-* ms, milliseconds
-* s, sec, second, seconds
-* m, min, minute, minutes
-* h, hr, hour, hours
-
-+
---
-If a unit suffix is not specified, `milliseconds` is assumed.
-
-Default is `30 seconds`.
-
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
---
-
-[[database.dataSourceInterceptorClass]]database.dataSourceInterceptorClass::
-
-Class that implements DataSourceInterceptor interface to monitor SQL activity.
-This class must have default constructor and be available on Gerrit's bootstrap
-classpath, e. g. in `$gerrit_site/lib` directory. Example implementation of
-SQL monitoring can be found in javamelody-plugin.
-
-[[database.h2]]database.h2::
-+
-The settings in this section are used for the reviewdb if the
-<<database.type,database.type>> is H2.
-+
-Additionally gerrit uses H2 for storing reviewed flags on changes.
-
-[[database.h2.cacheSize]]database.h2.cacheSize::
-+
-The size of the H2 internal database cache, in bytes. The H2 internal cache for
-persistent H2-backed caches is controlled by
-<<cache.h2CacheSize,cache.h2CacheSize>>.
-+
-H2 uses memory to cache its database content. The parameter `cacheSize`
-allows to limit the memory used by H2 and thus prevent out-of-memory
-caused by the H2 database using too much memory.
-+
-Technically the H2 cache size is configured using the CACHE_SIZE parameter in
-the H2 JDBC connection URL, as described
-link:http://www.h2database.com/html/features.html#cache_settings[here]
-+
-Default is unset, using up to half of the available memory.
-+
-H2 will persist this value in the database, so to unset explicitly specify 0.
-+
-Common unit suffixes of 'k', 'm', or 'g' are supported.
-
-[[database.h2.autoServer]]database.h2.autoServer::
-+
-If `true` enable the automatic mixed mode
-(see link:http://www.h2database.com/html/features.html#auto_mixed_mode[Automatic Mixed Mode]).
-This enables concurrent access to the embedded H2 database from command line
-utils (e.g. MigrateToNoteDb).
-+
-Default is `false`.
-
 [[download]]
 === Section download
 
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index 35f44c0..bda0b67 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -124,16 +124,6 @@
 link:https://gerrit.googlesource.com/plugins/reviewnotes/+doc/master/src/main/resources/Documentation/about.md[
 Documentation]
 
-[[review-strategy]]
-=== review-strategy
-
-This plugin allows users to configure different review strategies.
-
-link:https://gerrit-review.googlesource.com/admin/repos/plugins/review-strategy[
-Project] |
-link:https://gerrit.googlesource.com/plugins/review-strategy/+doc/master/src/main/resources/Documentation/about.md[
-Documentation]
-
 [[singleusergroup]]
 === singleusergroup
 
@@ -566,6 +556,16 @@
 link:https://gerrit.googlesource.com/plugins/reparent/+doc/master/src/main/resources/Documentation/config.md[
 Configuration]
 
+[[review-strategy]]
+=== review-strategy
+
+This plugin allows users to configure different review strategies.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/review-strategy[
+Project] |
+link:https://gerrit.googlesource.com/plugins/review-strategy/+doc/master/src/main/resources/Documentation/about.md[
+Documentation]
+
 [[reviewers]]
 === reviewers
 
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
deleted file mode 100644
index 2153751..0000000
--- a/Documentation/database-setup.txt
+++ /dev/null
@@ -1,280 +0,0 @@
-[[createdb]]
-== Database Setup
-
-During the init phase of Gerrit you will need to specify which database to use.
-
-[[createdb_h2]]
-=== H2
-
-If you choose H2, Gerrit will automatically set up the embedded H2 database as
-backend so no set up or configuration is necessary.
-
-Using the embedded H2 database is the easiest way to get a Gerrit
-site up and running, making it ideal for proof of concepts or small team
-servers.  On the flip side, H2 is not the recommended option for large
-corporate installations. This is because there is no easy way to interact
-with the database while Gerrit is offline, it's not easy to backup the data,
-and it's not possible to set up H2 in a load balanced/hotswap configuration.
-
-If this option interests you, you might want to consider
-link:linux-quickstart.html[the quick guide].
-
-[[createdb_derby]]
-=== Apache Derby
-
-If Derby is selected, Gerrit will automatically set up the embedded Derby
-database as backend so no set up or configuration is necessary.
-
-Currently only support for embedded mode is added. There are two other
-deployment options for Apache Derby that can be added later:
-
-* link:http://db.apache.org/derby/papers/DerbyTut/ns_intro.html#Network+Server+Options[
-Derby Network Server (standalone mode)]
-
-* link:http://db.apache.org/derby/papers/DerbyTut/ns_intro.html#Embedded+Server[
-Embedded Server (hybrid mode)]
-
-[[createdb_postgres]]
-=== PostgreSQL
-
-This option is more complicated than the H2 option but is recommended
-for larger installations. It's the database backend with the largest userbase
-in the Gerrit community.
-
-Create a user for the web application within PostgreSQL, assign it a
-password, create a database to store the metadata, and grant the user
-full rights on the newly created database:
-
-----
-  $ createuser --username=postgres -RDIElPS gerrit
-  $ createdb --username=postgres -E UTF-8 -O gerrit reviewdb
-----
-
-Visit PostgreSQL's link:http://www.postgresql.org/docs/9.1/interactive/index.html[documentation] for further information regarding
-using PostgreSQL.
-
-[[createdb_mysql]]
-=== MySQL
-
-Requirements: MySQL version 5.1 or later.
-
-This option is also more complicated than the H2 option. Just as with
-PostgreSQL it's also recommended for larger installations.
-
-Create a user for the web application within the database, assign it a
-password, create a database, and give the newly created user full
-rights on it:
-
-----
-  mysql
-
-  CREATE USER 'gerrit'@'localhost' IDENTIFIED BY 'secret';
-  CREATE DATABASE reviewdb DEFAULT CHARACTER SET 'utf8';
-  GRANT ALL ON reviewdb.* TO 'gerrit'@'localhost';
-  FLUSH PRIVILEGES;
-----
-
-Visit MySQL's link:http://dev.mysql.com/doc/[documentation] for further
-information regarding using MySQL.
-
-[[createdb_mariadb]]
-=== MariaDB
-
-Requirements: MariaDB version 5.5 or later.
-
-Refer to MySQL section above how to create MariaDB database.
-
-Visit MariaDB's link:https://mariadb.com/kb/en/mariadb/[documentation] for further
-information regarding using MariaDB.
-
-[[createdb_oracle]]
-=== Oracle
-
-PostgreSQL or H2 is the recommended database for Gerrit Code Review.
-Oracle is supported for environments where running on an existing Oracle
-installation simplifies administrative overheads, such as database backups.
-
-Create a user for the web application within sqlplus, assign it a
-password, and grant the user full rights on the newly created database:
-
-----
-  SQL> create user gerrit identified by secret_password default tablespace users;
-  SQL> grant connect, resources to gerrit;
-----
-
-JDBC driver ojdbc6.jar must be obtained from your Oracle distribution. Gerrit
-initialization process tries to copy it from a known location:
-
-----
-/u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
-----
-
-If this file can not be located at this place, then the alternative location
-can be provided.
-
-Instance name is the Oracle SID. Sample database section in
-$site_path/etc/gerrit.config:
-
-----
-[database]
-        type = oracle
-        instance = xe
-        hostname = localhost
-        username = gerrit
-        port = 1521
-----
-
-Sample database section in $site_path/etc/secure.config:
-
-----
-[database]
-        password = secret_password
-----
-
-[[createdb_maxdb]]
-=== SAP MaxDB
-
-SAP MaxDB is a supported database for running Gerrit Code Review. However it is
-recommended only for environments where you intend to run Gerrit on an existing
-MaxDB installation to reduce administrative overhead.
-
-In the MaxDB studio or using the SQLCLI command line interface create a user
-'gerrit' with the user class 'RESOURCE' and a password <secret password>. This
-will also create an associated schema on the database.
-
-To run Gerrit on MaxDB, you need to obtain the MaxDB JDBC driver. It can be
-found in your MaxDB installation at the following location:
-
-- on Windows 64bit at "C:\Program Files\sdb\MaxDB\runtime\jar\sapdbc.jar"
-- on Linux at "/opt/sdb/MaxDB/runtime/jar/sapdbc.jar"
-
-It needs to be stored in the 'lib' folder of the review site.
-
-In the following sample database section it is assumed that the database name is
-'reviewdb' and the database is installed on localhost:
-
-In $site_path/etc/gerrit.config:
-
-----
-[database]
-        type = maxdb
-        database = reviewdb
-        hostname = localhost
-        username = gerrit
-
-----
-
-In $site_path/etc/secure.config:
-
-----
-[database]
-        password = <secret password>
-----
-
-Visit SAP MaxDB's link:http://maxdb.sap.com/documentation/[documentation] for further
-information regarding using SAP MaxDB.
-
-[[createdb_db2]]
-=== DB2
-
-IBM DB2 is a supported database for running Gerrit Code Review. However it is
-recommended only for environments where you intend to run Gerrit on an existing
-DB2 installation to reduce administrative overhead.
-
-Create a system wide user for the Gerrit application, and grant the user
-full rights on the newly created database:
-
-----
-  db2 => create database gerrit
-  db2 => connect to gerrit
-  db2 => grant connect,accessctrl,dataaccess,dbadm,secadm on database to gerrit;
-----
-
-JDBC driver db2jcc4.jar and db2jcc_license_cu.jar must be obtained
-from your DB2 distribution. Gerrit initialization process tries to copy
-it from a known location:
-
-----
-/opt/ibm/db2/V10.5/java/db2jcc4.jar
-/opt/ibm/db2/V10.5/java/db2jcc_license_cu.jar
-----
-
-If these files cannot be located at this place, then an alternative location
-can be provided during init step execution.
-
-Sample database section in $site_path/etc/gerrit.config:
-
-----
-[database]
-        type = db2
-        database = gerrit
-        hostname = localhost
-        username = gerrit
-        port = 50001
-----
-
-Sample database section in $site_path/etc/secure.config:
-
-----
-[database]
-        password = secret_password
-----
-
-[[createdb_hana]]
-=== SAP HANA
-
-SAP HANA is a supported database for running Gerrit Code Review. However it is
-recommended only for environments where you intend to run Gerrit on an existing
-HANA installation to reduce administrative overhead.
-
-In the HANA studio or the SAP HANA Web-based Development Workbench create a user
-'GERRIT2' with the role 'RESTRICTED_USER_JDBC_ACCESS' and a password
-<secret password>. This will also create an associated schema on the database.
-As this user would be required to change the password upon first login you might
-want to to disable the password lifetime check by executing
-'ALTER USER GERRIT2 DISABLE PASSWORD LIFETIME'.
-
-To run Gerrit on HANA, you need to obtain the HANA JDBC driver. It can be found
-as described
-link:http://help.sap.com/saphelp_hanaplatform/helpdata/en/ff/15928cf5594d78b841fbbe649f04b4/frameset.htm[here].
-It needs to be stored in the 'lib' folder of the review site.
-
-In the following sample database section it is assumed that HANA is running on
-the host 'hana.host' and listening on port '4242' where a schema/user GERRIT2
-was created:
-
-In $site_path/etc/gerrit.config:
-
-----
-[database]
-        type = hana
-        hostname = hana.host
-        port = 4242
-        username = GERRIT2
-
-----
-
-In order to configure a specific database in a multi-database environment (MDC)
-the database name has to be specified additionally:
-
-In $site_path/etc/gerrit.config:
-
-----
-[database]
-        type = hana
-        hostname = hana.host
-        database = tdb1
-        port = 4242
-        username = GERRIT2
-
-----
-
-In $site_path/etc/secure.config:
-
-----
-[database]
-        password = <secret password>
-----
-
-Visit SAP HANA's link:http://help.sap.com/hana_appliance/[documentation] for
-further information regarding using SAP HANA.
diff --git a/Documentation/dev-design.txt b/Documentation/dev-design.txt
index 2d6e24c..d2fd958 100644
--- a/Documentation/dev-design.txt
+++ b/Documentation/dev-design.txt
@@ -65,6 +65,9 @@
 changing the implementation from Python on Google App Engine, to Java
 on a J2EE servlet container and an SQL database.
 
+Since Gerrit 3.x link:note-db.html[NoteDb] replaced the SQL database
+and all metadata is now stored in Git.
+
 * link:http://video.google.com/videoplay?docid=-8502904076440714866[Mondrian Code Review On The Web]
 * link:https://github.com/rietveld-codereview/rietveld[Rietveld - Code Review for Subversion]
 * link:http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst;hb=HEAD[Gitosis README]
@@ -83,9 +86,7 @@
 
 Each Git commit created on the client desktop system is converted
 into a unique change record which can be reviewed independently.
-Change records are stored in a database: PostgreSQL, MySQL, or the
-built-in H2, where they can be queried to present customized user
-dashboards, enumerating any pending changes.
+Change records are stored in NoteDb.
 
 A summary of each newly uploaded change is automatically emailed
 to reviewers, so they receive a direct hyperlink to review the
@@ -167,7 +168,6 @@
 online and operating in order to authenticate that user.
 
 * link:http://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html[Git Repository Format]
-* link:http://www.postgresql.org/about/[About PostgreSQL]
 * link:http://openid.net/developers/specs/[OpenID Specifications]
 
 *1  Although an effort is underway to eliminate the use of the
@@ -363,7 +363,7 @@
 project's mailing list archives.
 
 The user's name and email address is stored unencrypted in the
-Gerrit metadata store, typically a PostgreSQL database.
+link:config-accounts.html#all-users[All-Users] repository.
 
 The snail-mail mailing address, country, and phone and fax numbers
 are gathered to help project leads contact the user should there
@@ -633,12 +633,6 @@
 
 === Backups
 
-PostgreSQL and MySQL can be configured to replicate their data to
-other systems, where they are applied to a warm-standby backup in
-real time.  Gerrit instances which care about redundancy will setup
-this feature of PostgreSQL or MySQL to ensure the warm-standby is
-reasonably current should the master go offline.
-
 Using the standard replication plugin, Gerrit can be configured
 to replicate changes made to the local Git repositories over any
 standard Git transports. After the plugin is installed, remote
diff --git a/Documentation/dev-inspector.txt b/Documentation/dev-inspector.txt
index 2134f2f..b1559ca 100644
--- a/Documentation/dev-inspector.txt
+++ b/Documentation/dev-inspector.txt
@@ -54,8 +54,6 @@
 ----
 "Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
 "m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
-"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
-"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
 
 Welcome to the Gerrit Inspector
 Enter help() to see the above again, EOF to quit and stop Gerrit
@@ -109,71 +107,11 @@
 'registerNatives', 'toString', 'wait']
 ----
 
-Startup script provides some convenient variables to access some global Gerrit components,
-for example a connection to the review database is kept open:
-
-----
->>> ds
-org.apache.commons.dbcp.BasicDataSource@61db2215
->>> ds.driverClassName
-u'org.postgresql.Driver'
->>> ds.dataSource
-org.apache.commons.dbcp.PoolingDataSource@23226fe1
->>> ds.dataSource.connection
-jdbc:postgresql://localhost/reviewdb, UserName=rv, PostgreSQL Native Driver
-----
-
-It is also possible to interact with the ORM layer:
-
-----
->>> db = schk.schema.open()
->>> db
-com.google.gerrit.reviewdb.server.ReviewDb_Schema_GwtOrm$$28@24cbbdf3
->>> db.getDialect()
-com.google.gwtorm.schema.sql.DialectPostgreSQL@4de07d3e
->>> for x in db.patchSets().iterateAllEntities():
-...     print x
-...
-[PatchSet 1,1]
-[PatchSet 2,1]
-[PatchSet 3,1]
-[PatchSet 4,1]
-[PatchSet 5,1]
-[PatchSet 6,1]
-[PatchSet 7,1]
-[PatchSet 8,1]
-[PatchSet 6,2]
->>> for x in db.patchComments().iterateAllEntities():
-...     print x
-com.google.gerrit.reviewdb.client.PatchLineComment@5381298a
-com.google.gerrit.reviewdb.client.PatchLineComment@44ce4dda
-com.google.gerrit.reviewdb.client.PatchLineComment@44594680
->>> dir(com.google.gerrit.reviewdb.client.PatchLineComment)
-['Key', 'STATUS_DRAFT', 'STATUS_PUBLISHED', 'Status', '__class__',
-'__copy__', '__deepcopy__', '__delattr__', '__doc__', '__eq__',
-'__getattribute__', '__hash__', '__init__', '__ne__', '__new__',
-'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
-'__unicode__', 'author', 'class', 'clone', 'equals', 'finalize',
-'getAuthor', 'getClass', 'getKey', 'getLine', 'getMessage',
-'getParentUuid', 'getSide', 'getStatus', 'getWrittenOn', 'hashCode',
-'key', 'line', 'lineNbr', 'message', 'notify', 'notifyAll',
-'parentUuid', 'registerNatives', 'setMessage', 'setSide', 'setStatus',
-'side', 'status', 'toString', 'updated', 'wait', 'writtenOn']
->>> for x in db.patchComments().iterateAllEntities():
-...     print x.status, x.line, x.message
-...
-P 2 I like it!
-P 2 more
-P 1 better
-----
-
 A built-in *help()* function provides values of global variables
 defined in the interpreter:
 
 ----
 >>> help()
-"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
-"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
 "m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
 "Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
 "d" is "com.google.gerrit.pgm.Daemon@28a3f689"
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index 92d080b..433e0c1 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -187,27 +187,6 @@
 CAUTION: When using the Inspector, be careful not to modify the internal state
 of the system.
 
-=== Querying the database
-
-The embedded H2 database can be queried and updated from the command line. If
-the daemon is not running, run:
-
-----
-  $(bazel info output_base)/external/local_jdk/bin/java \
-     -jar bazel-bin/gerrit.war gsql -d ../gerrit_testsite -s
-----
-
-NOTE: To learn why using `java -jar` isn't sufficient, see
-<<special_bazel_java_version,this explanation>>.
-
-Alternatively, if the daemon is running and the database is in use, use an
-administrator user account to connect over SSH:
-
-----
-  ssh -p 29418 user@localhost gerrit gsql
-----
-
-
 == Switching between branches
 
 When using `git checkout` without `--recurse-submodules` to switch between
diff --git a/Documentation/install-j2ee.txt b/Documentation/install-j2ee.txt
index 91d73cc..48751b7 100644
--- a/Documentation/install-j2ee.txt
+++ b/Documentation/install-j2ee.txt
@@ -14,9 +14,8 @@
 
 == Installation
 
-* Complete the link:install.html#createdb[database setup] and
-  link:install.html#init[site initialization] tasks described
-  in the standard installation documentation.
+* Complete the link:install.html#init[site initialization] task
+  described in the standard installation documentation.
 
 * Stop the embedded daemon that was automatically started by 'init':
 +
@@ -24,13 +23,6 @@
   review_site/bin/gerrit.sh stop
 ----
 
-* Configure JNDI DataSource 'jdbc/ReviewDb'.
-+
-This DataSource must point to the database you created above.
-Don't forget to ensure your JNDI configuration can load the
-necessary JDBC drivers.  You may wish to ensure connection pooling
-is configured and enabled within the DataSource.
-
 * Deploy the 'gerrit.war' file to your application server.
 +
 The deployment process differs between servers, but typically this
@@ -70,20 +62,8 @@
   java -jar webapps/gerrit.war cat extra/jetty7/gerrit.xml >contexts/gerrit.xml
 ----
 
-Install the required additional libraries by copying them into the
-`'$JETTY_HOME'/lib/ext` directory:
-
-----
-  cp ../review_db/lib/* lib/ext/
-  java -jar webapps/gerrit.war cat lib/commons-dbcp-1.2.2.jar >lib/ext/commons-dbcp-1.2.2.jar
-  java -jar webapps/gerrit.war cat lib/commons-pool-1.5.4.jar >lib/ext/commons-pool-1.5.4.jar
-  java -jar webapps/gerrit.war cat lib/h2-1.2.128.jar >lib/ext/h2-1.2.128.jar
-  java -jar webapps/gerrit.war cat lib/postgresql-8.4-701.jdbc4.jar >lib/ext/postgresql-8.4-701.jdbc4.jar
-----
-
 Edit `'$JETTY_HOME'/contexts/gerrit.xml` to correctly configure
-the database and outgoing SMTP connections, especially the user
-and password fields.
+outgoing SMTP connections.
 
 If OpenID authentication (or certain enterprise single-sign-on
 solutions) is being used, you may need to increase the
diff --git a/Documentation/install.txt b/Documentation/install.txt
index dbca368..be55417 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -9,10 +9,6 @@
 +
 Gerrit is not yet compatible with Java 9 or newer at this time.
 
-By default, Gerrit uses link:note-db.html[NoteDB] as the storage backend. (If
-desired, you can _optionally_ use an external database such as MySQL or
-PostgreSQL.)
-
 [[cryptography]]
 == Configure Java for Strong Cryptography
 
@@ -60,8 +56,6 @@
 If you would prefer to build Gerrit directly from source, review
 the notes under link:dev-readme.html[developer setup].
 
-include::database-setup.txt[]
-
 [[init]]
 == Initialize the Site
 
@@ -217,8 +211,7 @@
         --StartPath=C:\MY\GERRIT\SITE ^
         --StartMode=jvm --StopMode=jvm ^
         --StartClass=com.google.gerrit.launcher.GerritLauncher --StartMethod=daemonStart ^
-        --StopClass=com.google.gerrit.launcher.GerritLauncher --StopMethod=daemonStop ^
-        ++DependsOn=postgresql-x64-9.4
+        --StopClass=com.google.gerrit.launcher.GerritLauncher --StopMethod=daemonStop
 ====
 
 [[customize]]
@@ -255,8 +248,6 @@
 
 == External Documentation Links
 
-* http://www.postgresql.org/docs/[PostgreSQL Documentation]
-* http://dev.mysql.com/doc/[MySQL Documentation]
 * http://www.kernel.org/pub/software/scm/git/docs/git-daemon.html[git-daemon]
 
 
diff --git a/Documentation/pgm-gsql.txt b/Documentation/pgm-gsql.txt
deleted file mode 100644
index 4986522..0000000
--- a/Documentation/pgm-gsql.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-= gsql
-
-== NAME
-gsql - Administrative interface to idle database
-
-== SYNOPSIS
-[verse]
---
-_java_ -jar gerrit.war _gsql_
-  -d <SITE_PATH>
---
-
-== DESCRIPTION
-Interactive query support against the configured SQL database.
-All SQL statements are supported, including SELECT, UPDATE, INSERT,
-DELETE and ALTER.
-
-This command is primarily intended to access a local H2 database
-which is not currently open by a Gerrit daemon.  To access an open
-database use link:cmd-gsql.html[gerrit gsql] over SSH.
-
-== OPTIONS
-
--d::
---site-path::
-	Location of the gerrit.config file, and all other per-site
-	configuration data, supporting libraries and log files.
-
-== CONTEXT
-This command can only be run on a server which has direct
-connectivity to the metadata database, and local access to the
-managed Git repositories.
-
-== EXAMPLES
-To manually correct a user's SSH user name:
-
-----
-	$ java -jar gerrit.war gsql
-	Welcome to Gerrit Code Review v2.0.25
-	(PostgreSQL 8.3.8)
-
-	Type '\h' for help.  Type '\r' to clear the buffer.
-
-	gerrit> update accounts set ssh_user_name = 'alice' where account_id=1;
-	UPDATE 1; 1 ms
-	gerrit> \q
-	Bye
-----
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/pgm-index.txt b/Documentation/pgm-index.txt
index d61cc0b..dde0231 100644
--- a/Documentation/pgm-index.txt
+++ b/Documentation/pgm-index.txt
@@ -15,9 +15,6 @@
 link:pgm-daemon.html[daemon]::
 	Gerrit HTTP, SSH network server.
 
-link:pgm-gsql.html[gsql]::
-	Administrative interface to idle database.
-
 link:pgm-prolog-shell.html[prolog-shell]::
 	Simple interactive Prolog interpreter.
 
diff --git a/WORKSPACE b/WORKSPACE
index 58bde27..9e51fcd 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -568,18 +568,18 @@
     sha1 = "18d4d07010c24405129a6dbb0e92057f8779fb9d",
 )
 
-AUTO_VALUE_VERSION = "1.6.2"
+AUTO_VALUE_VERSION = "1.6.3"
 
 maven_jar(
     name = "auto-value",
     artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
-    sha1 = "e7eae562942315a983eea3e191b72d755c153620",
+    sha1 = "8edb6675b9c09ffdcc19937428e7ef1e3d066e12",
 )
 
 maven_jar(
     name = "auto-value-annotations",
     artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
-    sha1 = "ed193d86e0af90cc2342aedbe73c5d86b03fa09b",
+    sha1 = "b88c1bb7f149f6d2cc03898359283e57b08f39cc",
 )
 
 # Transitive dependency of commons-compress
@@ -995,12 +995,6 @@
 )
 
 maven_jar(
-    name = "postgresql",
-    artifact = "org.postgresql:postgresql:42.2.5",
-    sha1 = "951b7eda125f3137538a94e2cbdcf744088ad4c2",
-)
-
-maven_jar(
     name = "commons-io",
     artifact = "commons-io:commons-io:2.2",
     sha1 = "83b5b8a7ba1c08f9e8c8ff2373724e33d3c1e22a",
@@ -1022,8 +1016,8 @@
 # and httpasyncclient as necessary.
 maven_jar(
     name = "elasticsearch-rest-client",
-    artifact = "org.elasticsearch.client:elasticsearch-rest-client:6.5.1",
-    sha1 = "d87892e24ef361b9fff5435246b0f0b8f4561fe8",
+    artifact = "org.elasticsearch.client:elasticsearch-rest-client:6.5.2",
+    sha1 = "6ad0dd15affe56be7dfe638fe317bc5f7456b730",
 )
 
 JACKSON_VERSION = "2.9.7"
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 362803f..eae3e6b 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -49,7 +49,6 @@
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.PermissionRule.Action;
 import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.api.changes.RelatedChangeAndCommitInfo;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
@@ -428,10 +427,10 @@
 
     db = reviewDbProvider.open();
 
-    // All groups which were added during the server start (e.g. in ReviewDbSchemaCreator) aren't
+    // All groups which were added during the server start (e.g. in SchemaCreatorImpl) aren't
     // contained in the instance of the group index which is available here and in tests. There are
     // two reasons:
-    // 1) No group index is available in ReviewDbSchemaCreator when using an in-memory database.
+    // 1) No group index is available in SchemaCreatorImpl when using an in-memory database.
     // (This could be fixed by using the IndexManagerOnInit in InMemoryDatabase similar as BaseInit
     // uses it.)
     // 2) During the on-init part of the server start, we use another instance of the index than
@@ -1177,10 +1176,6 @@
     }
   }
 
-  protected RevCommit getHead(Repository repo) throws Exception {
-    return getHead(repo, "HEAD");
-  }
-
   @Nullable
   protected RevCommit getRemoteHead(Project.NameKey project, String branch) throws Exception {
     try (Repository repo = repoManager.openRepository(project)) {
@@ -1650,13 +1645,4 @@
     comments.sort(Comparator.comparing(c -> c.id));
     return comments;
   }
-
-  protected List<RelatedChangeAndCommitInfo> getRelated(PatchSet.Id ps) throws Exception {
-    return getRelated(ps.getParentKey(), ps.get());
-  }
-
-  protected List<RelatedChangeAndCommitInfo> getRelated(Change.Id changeId, int ps)
-      throws Exception {
-    return gApi.changes().id(changeId.get()).revision(ps).related().changes;
-  }
 }
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 96a9053..4788ab7 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -40,15 +40,11 @@
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
 import com.google.gerrit.server.ssh.NoSshModule;
-import com.google.gerrit.server.util.ManualRequestContext;
-import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.SocketUtil;
 import com.google.gerrit.server.util.SystemLog;
 import com.google.gerrit.testing.FakeEmailSender;
 import com.google.gerrit.testing.FakeGroupAuditService;
-import com.google.gerrit.testing.InMemoryDatabase;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.NoteDbChecker;
 import com.google.gerrit.testing.NoteDbMode;
 import com.google.gerrit.testing.SshMode;
 import com.google.inject.AbstractModule;
@@ -315,7 +311,7 @@
       if (!desc.memory()) {
         init(desc, baseConfig, site);
       }
-      return start(desc, baseConfig, site, testSysModule, null, null);
+      return start(desc, baseConfig, site, testSysModule, null);
     } catch (Exception e) {
       throw e;
     }
@@ -333,8 +329,6 @@
    * @param testSysModule optional additional module to add to the system injector.
    * @param inMemoryRepoManager {@link InMemoryRepositoryManager} that should be used if the site is
    *     started in memory
-   * @param inMemoryDatabaseInstance {@link com.google.gerrit.testing.InMemoryDatabase.Instance}
-   *     that should be used if the site is started in memory
    * @param additionalArgs additional command-line arguments for the daemon program; only allowed if
    *     the test is not in-memory.
    * @return started server.
@@ -346,7 +340,6 @@
       Path site,
       @Nullable Module testSysModule,
       @Nullable InMemoryRepositoryManager inMemoryRepoManager,
-      @Nullable InMemoryDatabase.Instance inMemoryDatabaseInstance,
       String... additionalArgs)
       throws Exception {
     checkArgument(site != null, "site is required (even for in-memory server");
@@ -371,8 +364,7 @@
 
     if (desc.memory()) {
       checkArgument(additionalArgs.length == 0, "cannot pass args to in-memory server");
-      return startInMemory(
-          desc, site, baseConfig, daemon, inMemoryRepoManager, inMemoryDatabaseInstance);
+      return startInMemory(desc, site, baseConfig, daemon, inMemoryRepoManager);
     }
     return startOnDisk(desc, site, daemon, serverStarted, additionalArgs);
   }
@@ -382,8 +374,7 @@
       Path site,
       Config baseConfig,
       Daemon daemon,
-      @Nullable InMemoryRepositoryManager inMemoryRepoManager,
-      @Nullable InMemoryDatabase.Instance inMemoryDatabaseInstance)
+      @Nullable InMemoryRepositoryManager inMemoryRepoManager)
       throws Exception {
     Config cfg = desc.buildConfig(baseConfig);
     mergeTestConfig(cfg);
@@ -398,8 +389,7 @@
     daemon.setLuceneModule(LuceneIndexModule.singleVersionAllLatest(0, isSlave(baseConfig)));
     daemon.setDatabaseForTesting(
         ImmutableList.<Module>of(
-            new InMemoryTestingDatabaseModule(
-                cfg, site, inMemoryRepoManager, inMemoryDatabaseInstance),
+            new InMemoryTestingDatabaseModule(cfg, site, inMemoryRepoManager),
             new AbstractModule() {
               @Override
               protected void configure() {
@@ -597,21 +587,9 @@
       inMemoryRepoManager = server.testInjector.getInstance(InMemoryRepositoryManager.class);
     }
 
-    InMemoryDatabase.Instance dbInstance = null;
-    if (hasBinding(server.testInjector, InMemoryDatabase.class)) {
-      InMemoryDatabase inMemoryDatabase = server.testInjector.getInstance(InMemoryDatabase.class);
-      dbInstance = inMemoryDatabase.getDbInstance();
-      dbInstance.setKeepOpen(true);
-    }
-    try {
-      server.close();
-      server.daemon.stop();
-      return start(server.desc, cfg, site, null, inMemoryRepoManager, dbInstance);
-    } finally {
-      if (dbInstance != null) {
-        dbInstance.setKeepOpen(false);
-      }
-    }
+    server.close();
+    server.daemon.stop();
+    return start(server.desc, cfg, site, null, inMemoryRepoManager);
   }
 
   private static boolean hasBinding(Injector injector, Class<?> clazz) {
@@ -620,40 +598,19 @@
 
   @Override
   public void close() throws Exception {
-    try {
-      checkNoteDbState();
-    } finally {
-      daemon.getLifecycleManager().stop();
-      if (daemonService != null) {
-        System.out.println("Gerrit Server Shutdown");
-        daemonService.shutdownNow();
-        daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
-      }
-      RepositoryCache.clear();
+    daemon.getLifecycleManager().stop();
+    if (daemonService != null) {
+      System.out.println("Gerrit Server Shutdown");
+      daemonService.shutdownNow();
+      daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
     }
+    RepositoryCache.clear();
   }
 
   public Path getSitePath() {
     return sitePath;
   }
 
-  private void checkNoteDbState() throws Exception {
-    NoteDbMode mode = NoteDbMode.get();
-    if (mode != NoteDbMode.CHECK && mode != NoteDbMode.PRIMARY) {
-      return;
-    }
-    NoteDbChecker checker = testInjector.getInstance(NoteDbChecker.class);
-    OneOffRequestContext oneOffRequestContext =
-        testInjector.getInstance(OneOffRequestContext.class);
-    try (ManualRequestContext ctx = oneOffRequestContext.open()) {
-      if (mode == NoteDbMode.CHECK) {
-        checker.rebuildAndCheckAllChanges();
-      } else if (mode == NoteDbMode.PRIMARY) {
-        checker.assertNoReviewDbChanges(desc.testDescription());
-      }
-    }
-  }
-
   @Override
   public String toString() {
     return MoreObjects.toStringHelper(this).addValue(desc).toString();
diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 0bbf9f1..e15d162 100644
--- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -28,16 +28,11 @@
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.config.TrackingFootersProvider;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
 import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.schema.DataSourceType;
 import com.google.gerrit.server.schema.NotesMigrationSchemaFactory;
 import com.google.gerrit.server.schema.ReviewDbFactory;
 import com.google.gerrit.server.schema.ReviewDbSchemaModule;
-import com.google.gerrit.server.schema.ReviewDbSchemaVersion;
 import com.google.gerrit.testing.InMemoryDatabase;
-import com.google.gerrit.testing.InMemoryH2Type;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.OrmRuntimeException;
@@ -46,7 +41,6 @@
 import com.google.inject.Key;
 import com.google.inject.ProvisionException;
 import com.google.inject.TypeLiteral;
-import com.google.inject.util.Providers;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -56,17 +50,12 @@
   private final Config cfg;
   private final Path sitePath;
   @Nullable private final InMemoryRepositoryManager repoManager;
-  @Nullable private final InMemoryDatabase.Instance inMemoryDatabaseInstance;
 
   InMemoryTestingDatabaseModule(
-      Config cfg,
-      Path sitePath,
-      @Nullable InMemoryRepositoryManager repoManager,
-      @Nullable InMemoryDatabase.Instance inMemoryDatabaseInstance) {
+      Config cfg, Path sitePath, @Nullable InMemoryRepositoryManager repoManager) {
     this.cfg = cfg;
     this.sitePath = sitePath;
     this.repoManager = repoManager;
-    this.inMemoryDatabaseInstance = inMemoryDatabaseInstance;
     makeSiteDirs(sitePath);
   }
 
@@ -85,16 +74,13 @@
     }
 
     bind(MetricMaker.class).to(DisabledMetricMaker.class);
-    bind(DataSourceType.class).to(InMemoryH2Type.class);
 
     install(new NotesMigration.Module());
     TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
         new TypeLiteral<SchemaFactory<ReviewDb>>() {};
     bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
-    bind(InMemoryDatabase.Instance.class).toProvider(Providers.of(inMemoryDatabaseInstance));
     bind(Key.get(schemaFactory, ReviewDbFactory.class)).to(InMemoryDatabase.class);
     bind(InMemoryDatabase.class).in(SINGLETON);
-    bind(ChangeBundleReader.class).to(GwtormChangeBundleReader.class);
 
     listener().to(CreateDatabase.class);
 
@@ -102,7 +88,6 @@
     bind(TrackingFooters.class).toProvider(TrackingFootersProvider.class).in(SINGLETON);
 
     install(new ReviewDbSchemaModule());
-    bind(ReviewDbSchemaVersion.class).to(ReviewDbSchemaVersion.C);
 
     install(new SshdModule());
   }
@@ -125,9 +110,7 @@
     }
 
     @Override
-    public void stop() {
-      mem.getDbInstance().drop();
-    }
+    public void stop() {}
   }
 
   private static void makeSiteDirs(Path p) {
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index 5e45df2..d0735c8 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
-import static java.util.stream.Collectors.toList;
 import static org.junit.Assert.assertEquals;
 
 import com.google.common.base.Strings;
@@ -31,7 +30,6 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -43,7 +41,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Stream;
 import org.eclipse.jgit.api.TagCommand;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.PersonIdent;
@@ -144,7 +141,6 @@
   private final ChangeNotes.Factory notesFactory;
   private final ApprovalsUtil approvalsUtil;
   private final Provider<InternalChangeQuery> queryProvider;
-  private final NotesMigration notesMigration;
   private final ReviewDb db;
   private final TestRepository<?> testRepo;
 
@@ -162,7 +158,6 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      NotesMigration notesMigration,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
       @Assisted TestRepository<?> testRepo)
@@ -171,7 +166,6 @@
         notesFactory,
         approvalsUtil,
         queryProvider,
-        notesMigration,
         db,
         i,
         testRepo,
@@ -185,7 +179,6 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      NotesMigration notesMigration,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
       @Assisted TestRepository<?> testRepo,
@@ -195,7 +188,6 @@
         notesFactory,
         approvalsUtil,
         queryProvider,
-        notesMigration,
         db,
         i,
         testRepo,
@@ -210,7 +202,6 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      NotesMigration notesMigration,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
       @Assisted TestRepository<?> testRepo,
@@ -222,7 +213,6 @@
         notesFactory,
         approvalsUtil,
         queryProvider,
-        notesMigration,
         db,
         i,
         testRepo,
@@ -237,24 +227,13 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      NotesMigration notesMigration,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
       @Assisted TestRepository<?> testRepo,
       @Assisted String subject,
       @Assisted Map<String, String> files)
       throws Exception {
-    this(
-        notesFactory,
-        approvalsUtil,
-        queryProvider,
-        notesMigration,
-        db,
-        i,
-        testRepo,
-        subject,
-        files,
-        null);
+    this(notesFactory, approvalsUtil, queryProvider, db, i, testRepo, subject, files, null);
   }
 
   @AssistedInject
@@ -262,7 +241,6 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      NotesMigration notesMigration,
       @Assisted ReviewDb db,
       @Assisted PersonIdent i,
       @Assisted TestRepository<?> testRepo,
@@ -275,7 +253,6 @@
         notesFactory,
         approvalsUtil,
         queryProvider,
-        notesMigration,
         db,
         i,
         testRepo,
@@ -288,7 +265,6 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       Provider<InternalChangeQuery> queryProvider,
-      NotesMigration notesMigration,
       ReviewDb db,
       PersonIdent i,
       TestRepository<?> testRepo,
@@ -301,7 +277,6 @@
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.queryProvider = queryProvider;
-    this.notesMigration = notesMigration;
     this.subject = subject;
     this.files = files;
     this.changeId = changeId;
@@ -436,22 +411,15 @@
       assertThat(c.getSubject()).isEqualTo(resSubj);
       assertThat(c.getStatus()).isEqualTo(expectedStatus);
       assertThat(Strings.emptyToNull(c.getTopic())).isEqualTo(expectedTopic);
-      if (notesMigration.readChanges()) {
-        assertReviewers(c, ReviewerStateInternal.REVIEWER, expectedReviewers);
-        assertReviewers(c, ReviewerStateInternal.CC, expectedCcs);
-      } else {
-        assertReviewers(
-            c,
-            ReviewerStateInternal.REVIEWER,
-            Stream.concat(expectedReviewers.stream(), expectedCcs.stream()).collect(toList()));
-      }
+      assertReviewers(c, ReviewerStateInternal.REVIEWER, expectedReviewers);
+      assertReviewers(c, ReviewerStateInternal.CC, expectedCcs);
     }
 
     private void assertReviewers(
         Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers)
         throws OrmException {
       Iterable<Account.Id> actualIds =
-          approvalsUtil.getReviewers(db, notesFactory.createChecked(db, c)).byState(state);
+          approvalsUtil.getReviewers(notesFactory.createChecked(db, c)).byState(state);
       assertThat(actualIds)
           .containsExactlyElementsIn(Sets.newHashSet(TestAccount.ids(expectedReviewers)));
     }
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index a50de1a..c03c8b6 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -209,7 +209,7 @@
   private GerritServer startImpl(@Nullable Module testSysModule, String... additionalArgs)
       throws Exception {
     return GerritServer.start(
-        serverDesc, baseConfig, sitePaths.site_path, testSysModule, null, null, additionalArgs);
+        serverDesc, baseConfig, sitePaths.site_path, testSysModule, null, additionalArgs);
   }
 
   protected static void runGerrit(String... args) throws Exception {
diff --git a/java/com/google/gerrit/common/data/GlobalCapability.java b/java/com/google/gerrit/common/data/GlobalCapability.java
index 3e11256..fbe1deb 100644
--- a/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -22,7 +22,7 @@
 
 /** Server wide capabilities. Represented as {@link Permission} objects. */
 public class GlobalCapability {
-  /** Ability to access the database (with gsql). */
+  /** Ability to view code review metadata refs in repositories. */
   public static final String ACCESS_DATABASE = "accessDatabase";
 
   /**
diff --git a/java/com/google/gerrit/httpd/init/SiteInitializer.java b/java/com/google/gerrit/httpd/init/SiteInitializer.java
index 67510cd..04a0ddd 100644
--- a/java/com/google/gerrit/httpd/init/SiteInitializer.java
+++ b/java/com/google/gerrit/httpd/init/SiteInitializer.java
@@ -47,7 +47,7 @@
       if (sitePath != null) {
         Path site = Paths.get(sitePath);
         logger.atInfo().log("Initializing site at %s", site.toRealPath().normalize());
-        new BaseInit(site, false, true, pluginsDistribution, pluginsToInstall).run();
+        new BaseInit(site, false, pluginsDistribution, pluginsToInstall).run();
         return;
       }
 
@@ -62,14 +62,7 @@
       }
       if (site != null) {
         logger.atInfo().log("Initializing site at %s", site.toRealPath().normalize());
-        new BaseInit(
-                site,
-                new ReviewDbDataSourceProvider(),
-                false,
-                false,
-                pluginsDistribution,
-                pluginsToInstall)
-            .run();
+        new BaseInit(site, false, pluginsDistribution, pluginsToInstall).run();
       }
     } catch (Exception e) {
       logger.atSevere().withCause(e).log("Site init failed");
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index b73d57e..8b113b2 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -85,9 +85,6 @@
 import com.google.gerrit.server.plugins.PluginModule;
 import com.google.gerrit.server.project.DefaultProjectNameLockManager;
 import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.DataSourceModule;
-import com.google.gerrit.server.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.DataSourceType;
 import com.google.gerrit.server.schema.DatabaseModule;
 import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
 import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
@@ -266,28 +263,6 @@
 
       Module configModule = new GerritServerConfigModule();
       modules.add(configModule);
-
-      Injector cfgInjector = Guice.createInjector(sitePathModule, configModule, secureStore);
-      Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
-      String dbType = cfg.getString("database", null, "type");
-
-      final DataSourceType dst =
-          Guice.createInjector(new DataSourceModule(), configModule, sitePathModule, secureStore)
-              .getInstance(Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
-      modules.add(
-          new LifecycleModule() {
-            @Override
-            protected void configure() {
-              bind(DataSourceType.class).toInstance(dst);
-              bind(DataSourceProvider.Context.class)
-                  .toInstance(DataSourceProvider.Context.MULTI_USER);
-              bind(Key.get(DataSource.class, Names.named("ReviewDb")))
-                  .toProvider(DataSourceProvider.class)
-                  .in(SINGLETON);
-              listener().to(DataSourceProvider.class);
-            }
-          });
-
     } else {
       modules.add(
           new LifecycleModule() {
diff --git a/java/com/google/gerrit/launcher/GerritLauncher.java b/java/com/google/gerrit/launcher/GerritLauncher.java
index 5406d52..868c1d5 100644
--- a/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -94,7 +94,6 @@
       System.err.println("  init            Initialize a Gerrit installation");
       System.err.println("  reindex         Rebuild the secondary index");
       System.err.println("  daemon          Run the Gerrit network daemons");
-      System.err.println("  gsql            Run the interactive query console");
       System.err.println("  version         Display the build version number");
       System.err.println("  passwd          Set or change password in secure.config");
 
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index e480f77..e7e8ea3 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -14,9 +14,7 @@
 
 package com.google.gerrit.pgm;
 
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
@@ -86,15 +84,12 @@
 import com.google.gerrit.server.mail.receive.MailReceiver;
 import com.google.gerrit.server.mail.send.SmtpEmailSender;
 import com.google.gerrit.server.mime.MimeUtil2Module;
-import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
-import com.google.gerrit.server.notedb.rebuild.OnlineNoteDbMigrator;
 import com.google.gerrit.server.patch.DiffExecutorModule;
 import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
 import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
 import com.google.gerrit.server.plugins.PluginModule;
 import com.google.gerrit.server.project.DefaultProjectNameLockManager;
 import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.DataSourceProvider;
 import com.google.gerrit.server.schema.InMemoryAccountPatchReviewStore;
 import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
 import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
@@ -128,7 +123,6 @@
 import javax.servlet.http.HttpServletRequest;
 import org.eclipse.jgit.lib.Config;
 import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
 
 /** Run SSH daemon portions of Gerrit. */
 public class Daemon extends SiteProgram {
@@ -177,15 +171,6 @@
   @Option(name = "--stop-only", usage = "Stop the daemon", hidden = true)
   private boolean stopOnly;
 
-  @Option(
-      name = "--migrate-to-note-db",
-      usage = "Automatically migrate changes to NoteDb",
-      handler = ExplicitBooleanOptionHandler.class)
-  private boolean migrateToNoteDb;
-
-  @Option(name = "--trial", usage = "(With --migrate-to-note-db) " + MigrateToNoteDb.TRIAL_USAGE)
-  private boolean trial;
-
   private final LifecycleManager manager = new LifecycleManager();
   private Injector dbInjector;
   private Injector cfgInjector;
@@ -289,7 +274,6 @@
       if (inspector) {
         JythonShell shell = new JythonShell();
         shell.set("m", manager);
-        shell.set("ds", dbInjector.getInstance(DataSourceProvider.class));
         shell.set("d", this);
         shell.run();
       } else {
@@ -338,7 +322,7 @@
   @VisibleForTesting
   public void start() throws IOException {
     if (dbInjector == null) {
-      dbInjector = createDbInjector(true /* enableMetrics */, MULTI_USER);
+      dbInjector = createDbInjector(true /* enableMetrics */);
     }
     cfgInjector = createCfgInjector();
     config = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
@@ -495,9 +479,6 @@
       modules.add(new AccountDeactivator.Module());
       modules.add(new ChangeCleanupRunner.Module());
     }
-    if (migrateToNoteDb()) {
-      modules.add(new OnlineNoteDbMigrator.Module(trial));
-    }
     if (testSysModule != null) {
       modules.add(testSysModule);
     }
@@ -507,18 +488,11 @@
         ModuleOverloader.override(modules, LibModuleLoader.loadModules(cfgInjector)));
   }
 
-  private boolean migrateToNoteDb() {
-    return migrateToNoteDb || NoteDbMigrator.getAutoMigrate(requireNonNull(config));
-  }
-
   private Module createIndexModule() {
     if (luceneModule != null) {
       return luceneModule;
     }
-    boolean onlineUpgrade =
-        VersionManager.getOnlineUpgrade(config)
-            // Schema upgrade is handled by OnlineNoteDbMigrator in this case.
-            && !migrateToNoteDb();
+    boolean onlineUpgrade = VersionManager.getOnlineUpgrade(config);
     switch (indexType) {
       case LUCENE:
         return onlineUpgrade
diff --git a/java/com/google/gerrit/pgm/Gsql.java b/java/com/google/gerrit/pgm/Gsql.java
deleted file mode 100644
index 004486b..0000000
--- a/java/com/google/gerrit/pgm/Gsql.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm;
-
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.pgm.util.RuntimeShutdown;
-import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.sshd.commands.QueryShell;
-import com.google.gerrit.sshd.commands.QueryShell.Factory;
-import com.google.inject.Injector;
-import java.io.IOException;
-import org.kohsuke.args4j.Option;
-
-/** Run Gerrit's SQL query tool */
-public class Gsql extends SiteProgram {
-  private final LifecycleManager manager = new LifecycleManager();
-  private Injector dbInjector;
-
-  @Option(name = "--format", usage = "Set output format")
-  private QueryShell.OutputFormat format = QueryShell.OutputFormat.PRETTY;
-
-  @Option(name = "-c", metaVar = "SQL QUERY", usage = "Query to execute")
-  private String query;
-
-  @Override
-  public int run() throws Exception {
-    mustHaveValidSite();
-
-    dbInjector = createDbInjector(SINGLE_USER);
-    manager.add(dbInjector);
-    manager.start();
-    RuntimeShutdown.add(
-        () -> {
-          try {
-            System.in.close();
-          } catch (IOException e) {
-            // Ignored
-          }
-          manager.stop();
-        });
-    final QueryShell shell = shellFactory().create(System.in, System.out);
-    shell.setOutputFormat(format);
-    if (query != null) {
-      shell.execute(query);
-    } else {
-      shell.run();
-    }
-    return 0;
-  }
-
-  private Factory shellFactory() {
-    return dbInjector
-        .createChildInjector(
-            new FactoryModule() {
-              @Override
-              protected void configure() {
-                factory(QueryShell.Factory.class);
-              }
-            })
-        .getInstance(QueryShell.Factory.class);
-  }
-}
diff --git a/java/com/google/gerrit/pgm/Init.java b/java/com/google/gerrit/pgm/Init.java
index 4ea31da..f31fb2c 100644
--- a/java/com/google/gerrit/pgm/Init.java
+++ b/java/com/google/gerrit/pgm/Init.java
@@ -95,7 +95,7 @@
   }
 
   public Init(Path sitePath) {
-    super(sitePath, true, true, new WarDistribution(), null);
+    super(sitePath, true, new WarDistribution(), null);
     batchMode = true;
     noAutoStart = true;
   }
diff --git a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
index feeaa27..14a0b5d 100644
--- a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
+++ b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.pgm;
 
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
 
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lifecycle.LifecycleManager;
@@ -54,7 +53,7 @@
 
   @Override
   public int run() throws Exception {
-    Injector dbInjector = createDbInjector(MULTI_USER);
+    Injector dbInjector = createDbInjector();
     manager.add(dbInjector, dbInjector.createChildInjector(NoteDbSchemaVersionCheck.module()));
     manager.start();
     dbInjector
diff --git a/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java b/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java
index 4ace62b..e12bde2 100644
--- a/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java
+++ b/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gerrit.server.schema.DataSourceProvider;
 import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
 import com.google.inject.Injector;
 import com.google.inject.Key;
@@ -49,7 +48,7 @@
 
   @Override
   public int run() throws Exception {
-    Injector dbInjector = createDbInjector(DataSourceProvider.Context.SINGLE_USER);
+    Injector dbInjector = createDbInjector();
     SitePaths sitePaths = new SitePaths(getSitePath());
     ThreadSettingsConfig threadSettingsConfig = dbInjector.getInstance(ThreadSettingsConfig.class);
     Config fakeCfg = new Config();
diff --git a/java/com/google/gerrit/pgm/MigrateToNoteDb.java b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
deleted file mode 100644
index 61d7ed9..0000000
--- a/java/com/google/gerrit/pgm/MigrateToNoteDb.java
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.pgm.util.BatchProgramModule;
-import com.google.gerrit.pgm.util.RuntimeShutdown;
-import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.pgm.util.ThreadLimiter;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.git.GarbageCollection;
-import com.google.gerrit.server.index.DummyIndexModule;
-import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
-import com.google.gerrit.server.notedb.rebuild.GcAllUsers;
-import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
-import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Provider;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
-
-public class MigrateToNoteDb extends SiteProgram {
-  static final String TRIAL_USAGE =
-      "Trial mode: migrate changes and turn on reading from NoteDb, but leave ReviewDb as the"
-          + " source of truth";
-
-  private static final int ISSUE_8022_THREAD_LIMIT = 4;
-
-  @Option(name = "--threads", usage = "Number of threads to use for rebuilding NoteDb")
-  private Integer threads;
-
-  @Option(
-      name = "--project",
-      usage =
-          "Only rebuild these projects, do no other migration; incompatible with --change;"
-              + " recommended for debugging only")
-  private List<String> projects = new ArrayList<>();
-
-  @Option(
-      name = "--change",
-      usage =
-          "Only rebuild these changes, do no other migration; incompatible with --project;"
-              + " recommended for debugging only")
-  private List<Integer> changes = new ArrayList<>();
-
-  @Option(
-      name = "--force",
-      usage =
-          "Force rebuilding changes where ReviewDb is still the source of truth, even if they"
-              + " were previously migrated")
-  private boolean force;
-
-  @Option(name = "--trial", usage = TRIAL_USAGE)
-  private boolean trial;
-
-  @Option(
-      name = "--sequence-gap",
-      usage =
-          "gap in change sequence numbers between last ReviewDb number and first NoteDb number;"
-              + " negative indicates using the value of noteDb.changes.initialSequenceGap (default"
-              + " 1000)")
-  private int sequenceGap;
-
-  @Option(
-      name = "--reindex",
-      usage =
-          "Reindex all changes after migration; defaults to false in trial mode, true otherwise",
-      handler = ExplicitBooleanOptionHandler.class)
-  private Boolean reindex;
-
-  private Injector dbInjector;
-  private Injector sysInjector;
-  private LifecycleManager dbManager;
-  private LifecycleManager sysManager;
-
-  @Inject private GcAllUsers gcAllUsers;
-  @Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
-
-  @Override
-  public int run() throws Exception {
-    RuntimeShutdown.add(this::stop);
-    try {
-      mustHaveValidSite();
-      dbInjector = createDbInjector(MULTI_USER);
-
-      dbManager = new LifecycleManager();
-      dbManager.add(dbInjector);
-      dbManager.start();
-
-      threads = limitThreads();
-
-      sysInjector = createSysInjector();
-      sysInjector.injectMembers(this);
-      sysManager = new LifecycleManager();
-      sysManager.add(sysInjector);
-      sysInjector
-          .getInstance(PluginGuiceEnvironment.class)
-          .setDbCfgInjector(dbInjector, dbInjector);
-      sysManager.start();
-
-      try (NoteDbMigrator migrator =
-          migratorBuilderProvider
-              .get()
-              .setThreads(threads)
-              .setProgressOut(System.err)
-              .setProjects(projects.stream().map(Project.NameKey::new).collect(toList()))
-              .setChanges(changes.stream().map(Change.Id::new).collect(toList()))
-              .setTrialMode(trial)
-              .setForceRebuild(force)
-              .setSequenceGap(sequenceGap)
-              .build()) {
-        if (!projects.isEmpty() || !changes.isEmpty()) {
-          migrator.rebuild();
-        } else {
-          migrator.migrate();
-        }
-      }
-      try (PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out, UTF_8), true)) {
-        gcAllUsers.run(w);
-      }
-    } finally {
-      stop();
-    }
-
-    boolean reindex = firstNonNull(this.reindex, !trial);
-    if (!reindex) {
-      return 0;
-    }
-    // Reindex all indices, to save the user from having to run yet another program by hand while
-    // their server is offline.
-    List<String> reindexArgs =
-        ImmutableList.of(
-            "--site-path",
-            getSitePath().toString(),
-            "--threads",
-            Integer.toString(threads),
-            "--index",
-            ChangeSchemaDefinitions.NAME);
-    System.out.println("Migration complete, reindexing changes with:");
-    System.out.println("  reindex " + reindexArgs.stream().collect(joining(" ")));
-    Reindex reindexPgm = new Reindex();
-    return reindexPgm.main(reindexArgs.stream().toArray(String[]::new));
-  }
-
-  private int limitThreads() {
-    if (threads != null) {
-      return threads;
-    }
-    int actualThreads;
-    int procs = Runtime.getRuntime().availableProcessors();
-    DataSourceType dsType = dbInjector.getInstance(DataSourceType.class);
-    if (dsType.getDriver().equals("org.h2.Driver") && procs > ISSUE_8022_THREAD_LIMIT) {
-      System.out.println(
-          "Not using more than "
-              + ISSUE_8022_THREAD_LIMIT
-              + " threads due to http://crbug.com/gerrit/8022");
-      System.out.println("Can be increased by passing --threads, but may cause errors");
-      actualThreads = ISSUE_8022_THREAD_LIMIT;
-    } else {
-      actualThreads = procs;
-    }
-    actualThreads = ThreadLimiter.limitThreads(dbInjector, actualThreads);
-    return actualThreads;
-  }
-
-  private Injector createSysInjector() {
-    return dbInjector.createChildInjector(
-        new FactoryModule() {
-          @Override
-          public void configure() {
-            install(dbInjector.getInstance(BatchProgramModule.class));
-            install(new DummyIndexModule());
-            factory(ChangeResource.Factory.class);
-            factory(GarbageCollection.Factory.class);
-          }
-        });
-  }
-
-  private void stop() {
-    try {
-      LifecycleManager m = sysManager;
-      sysManager = null;
-      if (m != null) {
-        m.stop();
-      }
-    } finally {
-      LifecycleManager m = dbManager;
-      dbManager = null;
-      if (m != null) {
-        m.stop();
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/pgm/ProtobufImport.java b/java/com/google/gerrit/pgm/ProtobufImport.java
index 0179b1d..0335cbb 100644
--- a/java/com/google/gerrit/pgm/ProtobufImport.java
+++ b/java/com/google/gerrit/pgm/ProtobufImport.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.pgm;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
 import static java.util.Objects.requireNonNull;
 
 import com.google.auto.value.AutoValue;
@@ -63,6 +62,7 @@
  * <p><strong>Warning</strong>: This method blindly upserts data into the database. It should only
  * be used to restore a protobuf-formatted backup into a new, empty site.
  */
+// TODO(dborowitz): Delete this program.
 public class ProtobufImport extends SiteProgram {
   @Option(
       name = "--file",
@@ -81,7 +81,7 @@
   public int run() throws Exception {
     mustHaveValidSite();
 
-    Injector dbInjector = createDbInjector(SINGLE_USER);
+    Injector dbInjector = createDbInjector();
     manager.add(dbInjector);
     manager.start();
     RuntimeShutdown.add(manager::stop);
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index 52d3bd3..cb129a7 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.pgm;
 
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.toSet;
 
@@ -29,7 +28,6 @@
 import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.pgm.util.BatchProgramModule;
 import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.pgm.util.ThreadLimiter;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.index.IndexModule;
@@ -81,10 +79,9 @@
   @Override
   public int run() throws Exception {
     mustHaveValidSite();
-    dbInjector = createDbInjector(MULTI_USER);
+    dbInjector = createDbInjector();
     cfgInjector = dbInjector.createChildInjector();
     globalConfig = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
-    threads = ThreadLimiter.limitThreads(dbInjector, threads);
     overrideConfig();
     LifecycleManager dbManager = new LifecycleManager();
     dbManager.add(dbInjector);
diff --git a/java/com/google/gerrit/pgm/Rulec.java b/java/com/google/gerrit/pgm/Rulec.java
index add06ef..aa72ae0 100644
--- a/java/com/google/gerrit/pgm/Rulec.java
+++ b/java/com/google/gerrit/pgm/Rulec.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.pgm;
 
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.pgm.rules.PrologCompiler;
@@ -60,7 +58,7 @@
 
   @Override
   public int run() throws Exception {
-    dbInjector = createDbInjector(SINGLE_USER);
+    dbInjector = createDbInjector();
     manager.add(dbInjector);
     manager.start();
     dbInjector
diff --git a/java/com/google/gerrit/pgm/SwitchSecureStore.java b/java/com/google/gerrit/pgm/SwitchSecureStore.java
index 3f10130..733c9d1 100644
--- a/java/com/google/gerrit/pgm/SwitchSecureStore.java
+++ b/java/com/google/gerrit/pgm/SwitchSecureStore.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.pgm;
 
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
@@ -86,7 +84,7 @@
     logger.atInfo().log(
         "Current secureStoreClass property (%s) will be replaced with %s",
         currentSecureStoreName, newSecureStore);
-    Injector dbInjector = createDbInjector(SINGLE_USER);
+    Injector dbInjector = createDbInjector();
     SecureStore currentStore = getSecureStore(currentSecureStoreName, dbInjector);
     SecureStore newStore = getSecureStore(newSecureStore, dbInjector);
 
diff --git a/java/com/google/gerrit/pgm/init/BaseInit.java b/java/com/google/gerrit/pgm/init/BaseInit.java
index 9a92408..7d9ac3f 100644
--- a/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.pgm.init;
 
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
 import static com.google.inject.Scopes.SINGLETON;
 import static com.google.inject.Stage.PRODUCTION;
 
@@ -44,13 +42,10 @@
 import com.google.gerrit.server.plugins.JarScanner;
 import com.google.gerrit.server.schema.NoteDbSchemaUpdater;
 import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.server.schema.ReviewDbSchemaUpdater;
 import com.google.gerrit.server.schema.UpdateUI;
 import com.google.gerrit.server.securestore.SecureStore;
 import com.google.gerrit.server.securestore.SecureStoreClassName;
 import com.google.gerrit.server.securestore.SecureStoreProvider;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.gwtorm.server.StatementExecutor;
@@ -60,7 +55,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Module;
-import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
 import com.google.inject.spi.Message;
 import com.google.inject.util.Providers;
@@ -76,14 +70,12 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import javax.sql.DataSource;
 
 /** Initialize a new Gerrit installation. */
 public class BaseInit extends SiteProgram {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private final boolean standalone;
-  private final boolean initDb;
   protected final PluginsDistribution pluginsDistribution;
   private final List<String> pluginsToInstall;
 
@@ -91,7 +83,6 @@
 
   protected BaseInit(PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) {
     this.standalone = true;
-    this.initDb = true;
     this.pluginsDistribution = pluginsDistribution;
     this.pluginsToInstall = pluginsToInstall;
   }
@@ -99,22 +90,10 @@
   public BaseInit(
       Path sitePath,
       boolean standalone,
-      boolean initDb,
       PluginsDistribution pluginsDistribution,
       List<String> pluginsToInstall) {
-    this(sitePath, null, standalone, initDb, pluginsDistribution, pluginsToInstall);
-  }
-
-  public BaseInit(
-      Path sitePath,
-      final Provider<DataSource> dsProvider,
-      boolean standalone,
-      boolean initDb,
-      PluginsDistribution pluginsDistribution,
-      List<String> pluginsToInstall) {
-    super(sitePath, dsProvider);
+    super(sitePath);
     this.standalone = standalone;
-    this.initDb = initDb;
     this.pluginsDistribution = pluginsDistribution;
     this.pluginsToInstall = pluginsToInstall;
   }
@@ -257,7 +236,7 @@
     }
 
     m.add(new GerritServerConfigModule());
-    m.add(new InitModule(standalone, initDb));
+    m.add(new InitModule(standalone));
     m.add(
         new AbstractModule() {
           @Override
@@ -359,7 +338,6 @@
     public final ConsoleUI ui;
     public final SitePaths site;
     public final InitFlags flags;
-    final ReviewDbSchemaUpdater reviewDbSchemaUpdater;
     final NoteDbSchemaUpdater noteDbSchemaUpdater;
     final SchemaFactory<ReviewDb> schema;
     final GitRepositoryManager repositoryManager;
@@ -369,58 +347,26 @@
         ConsoleUI ui,
         SitePaths site,
         InitFlags flags,
-        ReviewDbSchemaUpdater reviewDbSchemaUpdater,
         NoteDbSchemaUpdater noteDbSchemaUpdater,
         @ReviewDbFactory SchemaFactory<ReviewDb> schema,
         GitRepositoryManager repositoryManager) {
       this.ui = ui;
       this.site = site;
       this.flags = flags;
-      this.reviewDbSchemaUpdater = reviewDbSchemaUpdater;
       this.noteDbSchemaUpdater = noteDbSchemaUpdater;
       this.schema = schema;
       this.repositoryManager = repositoryManager;
     }
 
     void upgradeSchema() throws OrmException {
-      final List<String> pruneList = new ArrayList<>();
-      UpdateUI uiImpl = new UpdateUIImpl(ui, pruneList);
-      reviewDbSchemaUpdater.update(uiImpl);
-
-      if (!pruneList.isEmpty()) {
-        StringBuilder msg = new StringBuilder();
-        msg.append("Execute the following SQL to drop unused objects:\n");
-        msg.append("\n");
-        for (String sql : pruneList) {
-          msg.append("  ");
-          msg.append(sql);
-          msg.append(";\n");
-        }
-
-        if (ui.isBatch()) {
-          System.err.print(msg);
-          System.err.flush();
-
-        } else if (ui.yesno(true, "%s\nExecute now", msg)) {
-          try (JdbcSchema db = (JdbcSchema) unwrapDb(schema.open());
-              JdbcExecutor e = new JdbcExecutor(db)) {
-            for (String sql : pruneList) {
-              e.execute(sql);
-            }
-          }
-        }
-      }
-
-      noteDbSchemaUpdater.update(uiImpl);
+      noteDbSchemaUpdater.update(new UpdateUIImpl(ui));
     }
 
     private static class UpdateUIImpl implements UpdateUI {
       private final ConsoleUI consoleUi;
-      private final List<String> pruneList;
 
-      UpdateUIImpl(ConsoleUI consoleUi, List<String> pruneList) {
+      UpdateUIImpl(ConsoleUI consoleUi) {
         this.consoleUi = consoleUi;
-        this.pruneList = pruneList;
       }
 
       @Override
@@ -451,11 +397,8 @@
 
       @Override
       public void pruneSchema(StatementExecutor e, List<String> prune) {
-        for (String p : prune) {
-          if (!pruneList.contains(p)) {
-            pruneList.add(p);
-          }
-        }
+        // Do nothing in NoteDb.
+        // TODO(dborowitz): Remove this method in the base class.
       }
     }
   }
@@ -475,7 +418,7 @@
               bind(InitFlags.class).toInstance(init.flags);
             }
           });
-      Injector dbInjector = createDbInjector(SINGLE_USER);
+      Injector dbInjector = createDbInjector();
       switch (IndexModule.getIndexType(dbInjector)) {
         case LUCENE:
           modules.add(new LuceneIndexModuleOnInit());
diff --git a/java/com/google/gerrit/pgm/init/DB2Initializer.java b/java/com/google/gerrit/pgm/init/DB2Initializer.java
deleted file mode 100644
index 9dc1088..0000000
--- a/java/com/google/gerrit/pgm/init/DB2Initializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class DB2Initializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "50001";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, false);
-    databaseSection.string("Database name", "database", "gerrit");
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java b/java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java
deleted file mode 100644
index 2701957..0000000
--- a/java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-/** Abstraction of initializer for the database section */
-interface DatabaseConfigInitializer {
-
-  /**
-   * Performs database platform specific configuration steps and writes configuration parameters
-   * into the given database section
-   */
-  void initConfig(Section databaseSection);
-}
diff --git a/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java b/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
deleted file mode 100644
index 44f883a..0000000
--- a/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.AbstractModule;
-import com.google.inject.name.Names;
-
-public class DatabaseConfigModule extends AbstractModule {
-
-  private final SitePaths site;
-
-  public DatabaseConfigModule(SitePaths site) {
-    this.site = site;
-  }
-
-  @Override
-  protected void configure() {
-    bind(SitePaths.class).toInstance(site);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("db2"))
-        .to(DB2Initializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("derby"))
-        .to(DerbyInitializer.class);
-    bind(DatabaseConfigInitializer.class).annotatedWith(Names.named("h2")).to(H2Initializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("jdbc"))
-        .to(JDBCInitializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("mariadb"))
-        .to(MariaDbInitializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("mysql"))
-        .to(MySqlInitializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("oracle"))
-        .to(OracleInitializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("postgresql"))
-        .to(PostgreSQLInitializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("maxdb"))
-        .to(MaxDbInitializer.class);
-    bind(DatabaseConfigInitializer.class)
-        .annotatedWith(Names.named("hana"))
-        .to(HANAInitializer.class);
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/DerbyInitializer.java b/java/com/google/gerrit/pgm/init/DerbyInitializer.java
deleted file mode 100644
index 3aad0f4..0000000
--- a/java/com/google/gerrit/pgm/init/DerbyInitializer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.die;
-
-import com.google.gerrit.common.FileUtil;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.nio.file.Path;
-
-class DerbyInitializer implements DatabaseConfigInitializer {
-
-  private final SitePaths site;
-
-  @Inject
-  DerbyInitializer(SitePaths site) {
-    this.site = site;
-  }
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    String path = databaseSection.get("database");
-    Path db;
-    if (path == null) {
-      db = site.resolve("db").resolve("ReviewDB");
-      databaseSection.set("database", db.toString());
-    } else {
-      db = site.resolve(path);
-    }
-    if (db == null) {
-      throw die("database.database must be supplied for Derby");
-    }
-    db = db.getParent();
-    FileUtil.mkdirsOrDie(db, "cannot create database.database");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/H2Initializer.java b/java/com/google/gerrit/pgm/init/H2Initializer.java
deleted file mode 100644
index 63aa6ec..0000000
--- a/java/com/google/gerrit/pgm/init/H2Initializer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.die;
-
-import com.google.gerrit.common.FileUtil;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.nio.file.Path;
-
-class H2Initializer implements DatabaseConfigInitializer {
-
-  private final SitePaths site;
-
-  @Inject
-  H2Initializer(SitePaths site) {
-    this.site = site;
-  }
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    String path = databaseSection.get("database");
-    Path db;
-    if (path == null) {
-      db = site.resolve("db").resolve("ReviewDB");
-      databaseSection.set("database", db.toString());
-    } else {
-      db = site.resolve(path);
-    }
-    if (db == null) {
-      throw die("database.database must be supplied for H2");
-    }
-    db = db.getParent();
-    FileUtil.mkdirsOrDie(db, "cannot create database.database");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/HANAInitializer.java b/java/com/google/gerrit/pgm/init/HANAInitializer.java
deleted file mode 100644
index 713392d..0000000
--- a/java/com/google/gerrit/pgm/init/HANAInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class HANAInitializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "(hana default)";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, true);
-    databaseSection.string("Database name", "database", null);
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/InitDatabase.java b/java/com/google/gerrit/pgm/init/InitDatabase.java
deleted file mode 100644
index 558716c..0000000
--- a/java/com/google/gerrit/pgm/init/InitDatabase.java
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.inject.Stage.PRODUCTION;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.pgm.init.api.InitFlags;
-import com.google.gerrit.pgm.init.api.InitStep;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.inject.Binding;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
-import java.lang.annotation.Annotation;
-import java.util.List;
-import java.util.Set;
-import org.eclipse.jgit.lib.Config;
-
-/** Initialize the {@code database} configuration section. */
-@Singleton
-class InitDatabase implements InitStep {
-  private final ConsoleUI ui;
-  private final SitePaths site;
-  private final Libraries libraries;
-  private final InitFlags flags;
-  private final Section database;
-  private final Section idSection;
-  private final Section noteDbChanges;
-
-  @Inject
-  InitDatabase(
-      ConsoleUI ui,
-      SitePaths site,
-      Libraries libraries,
-      InitFlags flags,
-      Section.Factory sections) {
-    this.ui = ui;
-    this.site = site;
-    this.libraries = libraries;
-    this.flags = flags; // Don't grab any flags yet; they aren't initialized until BaseInit#run.
-    this.database = sections.get("database", null);
-    this.idSection = sections.get(GerritServerIdProvider.SECTION, null);
-    this.noteDbChanges = sections.get(SECTION_NOTE_DB, CHANGES.key());
-  }
-
-  @Override
-  public void run() {
-    initSqlDb();
-    if (flags.isNew) {
-      initNoteDb();
-    }
-  }
-
-  private void initSqlDb() {
-    ui.header("SQL Database");
-
-    Set<String> allowedValues = Sets.newTreeSet();
-    Injector i = Guice.createInjector(PRODUCTION, new DatabaseConfigModule(site));
-    List<Binding<DatabaseConfigInitializer>> dbConfigBindings =
-        i.findBindingsByType(new TypeLiteral<DatabaseConfigInitializer>() {});
-    for (Binding<DatabaseConfigInitializer> binding : dbConfigBindings) {
-      Annotation annotation = binding.getKey().getAnnotation();
-      if (annotation instanceof Named) {
-        allowedValues.add(((Named) annotation).value());
-      }
-    }
-
-    if (!Strings.isNullOrEmpty(database.get("url"))
-        && Strings.isNullOrEmpty(database.get("type"))) {
-      database.set("type", "jdbc");
-    }
-
-    String dbType = database.select("Database server type", "type", "h2", allowedValues);
-
-    DatabaseConfigInitializer dci =
-        i.getInstance(Key.get(DatabaseConfigInitializer.class, Names.named(dbType.toLowerCase())));
-
-    if (dci instanceof MySqlInitializer) {
-      libraries.mysqlDriver.downloadRequired();
-    } else if (dci instanceof MariaDbInitializer) {
-      libraries.mariadbDriver.downloadRequired();
-    } else if (dci instanceof OracleInitializer) {
-      libraries.oracleDriver.downloadRequired();
-    } else if (dci instanceof DB2Initializer) {
-      libraries.db2Driver.downloadRequired();
-    } else if (dci instanceof HANAInitializer) {
-      libraries.hanaDriver.downloadRequired();
-    }
-
-    dci.initConfig(database);
-
-    // Initialize UUID for NoteDb on first init.
-    String id = idSection.get(GerritServerIdProvider.KEY);
-    if (Strings.isNullOrEmpty(id)) {
-      idSection.set(GerritServerIdProvider.KEY, GerritServerIdProvider.generate());
-    }
-  }
-
-  private void initNoteDb() {
-    ui.header("NoteDb Database");
-    ui.message(
-        "Use NoteDb for change metadata?\n"
-            + "  See documentation:\n"
-            + "  https://gerrit-review.googlesource.com/Documentation/note-db.html\n");
-    if (!ui.yesno(true, "Enable")) {
-      return;
-    }
-
-    Config defaultConfig = new Config();
-    NotesMigrationState.FINAL.setConfigValues(defaultConfig);
-    for (String name : defaultConfig.getNames(SECTION_NOTE_DB, CHANGES.key())) {
-      noteDbChanges.set(name, defaultConfig.getString(SECTION_NOTE_DB, CHANGES.key(), name));
-    }
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/InitModule.java b/java/com/google/gerrit/pgm/init/InitModule.java
index f677ceb..75145de 100644
--- a/java/com/google/gerrit/pgm/init/InitModule.java
+++ b/java/com/google/gerrit/pgm/init/InitModule.java
@@ -26,11 +26,9 @@
 public class InitModule extends FactoryModule {
 
   private final boolean standalone;
-  private final boolean initDb;
 
-  public InitModule(boolean standalone, boolean initDb) {
+  public InitModule(boolean standalone) {
     this.standalone = standalone;
-    this.initDb = initDb;
   }
 
   @Override
@@ -43,12 +41,8 @@
 
     // Steps are executed in the order listed here.
     //
-    step().to(UpgradeFrom2_0_x.class);
-
     step().to(InitGitManager.class);
-    if (initDb) {
-      step().to(InitDatabase.class);
-    }
+    step().to(InitNoteDb.class);
     step().to(InitLogging.class);
     step().to(InitIndex.class);
     step().to(InitAuth.class);
diff --git a/java/com/google/gerrit/pgm/init/InitNoteDb.java b/java/com/google/gerrit/pgm/init/InitNoteDb.java
new file mode 100644
index 0000000..dc52868
--- /dev/null
+++ b/java/com/google/gerrit/pgm/init/InitNoteDb.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.pgm.init;
+
+import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
+import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
+
+import com.google.gerrit.pgm.init.api.InitStep;
+import com.google.gerrit.pgm.init.api.Section;
+import com.google.gerrit.server.notedb.NotesMigrationState;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
+
+/** Initialize the NoteDb in gerrit site. */
+@Singleton
+class InitNoteDb implements InitStep {
+
+  private final Section noteDbChanges;
+
+  @Inject
+  InitNoteDb(Section.Factory sections) {
+    this.noteDbChanges = sections.get(SECTION_NOTE_DB, CHANGES.key());
+  }
+
+  @Override
+  public void run() {
+    initNoteDb();
+  }
+
+  private void initNoteDb() {
+    Config defaultConfig = new Config();
+    NotesMigrationState.FINAL.setConfigValues(defaultConfig);
+    for (String name : defaultConfig.getNames(SECTION_NOTE_DB, CHANGES.key())) {
+      noteDbChanges.set(name, defaultConfig.getString(SECTION_NOTE_DB, CHANGES.key(), name));
+    }
+  }
+}
diff --git a/java/com/google/gerrit/pgm/init/JDBCInitializer.java b/java/com/google/gerrit/pgm/init/JDBCInitializer.java
deleted file mode 100644
index e3a1d66..0000000
--- a/java/com/google/gerrit/pgm/init/JDBCInitializer.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.pgm.init.api.Section;
-
-class JDBCInitializer implements DatabaseConfigInitializer {
-  @Override
-  public void initConfig(Section database) {
-    boolean hasUrl = Strings.emptyToNull(database.get("url")) != null;
-    database.string("URL", "url", null);
-    guessDriver(database);
-    database.string("Driver class name", "driver", null);
-    database.string("Database username", "username", hasUrl ? null : username());
-    database.password("username", "password");
-  }
-
-  private void guessDriver(Section database) {
-    String url = Strings.emptyToNull(database.get("url"));
-    if (url != null && Strings.isNullOrEmpty(database.get("driver"))) {
-      if (url.startsWith("jdbc:derby:")) {
-        database.set("driver", "org.apache.derby.jdbc.EmbeddedDriver");
-      } else if (url.startsWith("jdbc:h2:")) {
-        database.set("driver", "org.h2.Driver");
-      } else if (url.startsWith("jdbc:mariadb:")) {
-        database.set("driver", "org.mariadb.jdbc.Driver");
-      } else if (url.startsWith("jdbc:mysql:")) {
-        database.set("driver", "com.mysql.jdbc.Driver");
-      } else if (url.startsWith("jdbc:postgresql:")) {
-        database.set("driver", "org.postgresql.Driver");
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/MariaDbInitializer.java b/java/com/google/gerrit/pgm/init/MariaDbInitializer.java
deleted file mode 100644
index db32113..0000000
--- a/java/com/google/gerrit/pgm/init/MariaDbInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-class MariaDbInitializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "(mariadb default)";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, true);
-    databaseSection.string("Database name", "database", "reviewdb");
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/MaxDbInitializer.java b/java/com/google/gerrit/pgm/init/MaxDbInitializer.java
deleted file mode 100644
index 0f696b7..0000000
--- a/java/com/google/gerrit/pgm/init/MaxDbInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class MaxDbInitializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "(maxdb default)";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, true);
-    databaseSection.string("Database name", "database", "reviewdb");
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/MySqlInitializer.java b/java/com/google/gerrit/pgm/init/MySqlInitializer.java
deleted file mode 100644
index 037b52b..0000000
--- a/java/com/google/gerrit/pgm/init/MySqlInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-class MySqlInitializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "(mysql default)";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, true);
-    databaseSection.string("Database name", "database", "reviewdb");
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/OracleInitializer.java b/java/com/google/gerrit/pgm/init/OracleInitializer.java
deleted file mode 100644
index ffbaf34..0000000
--- a/java/com/google/gerrit/pgm/init/OracleInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class OracleInitializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "1521";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, false);
-    databaseSection.string("Instance name", "instance", "xe");
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java b/java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java
deleted file mode 100644
index 65a66de..0000000
--- a/java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-class PostgreSQLInitializer implements DatabaseConfigInitializer {
-
-  @Override
-  public void initConfig(Section databaseSection) {
-    final String defPort = "(postgresql default)";
-    databaseSection.string("Server hostname", "hostname", "localhost");
-    databaseSection.string("Server port", "port", defPort, true);
-    databaseSection.string("Database name", "database", "reviewdb");
-    databaseSection.string("Database username", "username", username());
-    databaseSection.password("username", "password");
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java b/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java
deleted file mode 100644
index 95ff8d7..0000000
--- a/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.die;
-import static com.google.gerrit.pgm.init.api.InitUtil.savePublic;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.base.Splitter;
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.pgm.init.api.InitFlags;
-import com.google.gerrit.pgm.init.api.InitStep;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.securestore.SecureStore;
-import com.google.gerrit.server.util.SocketUtil;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.InetSocketAddress;
-import java.net.URLDecoder;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Properties;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-
-/** Upgrade from a 2.0.x site to a 2.1 site. */
-@Singleton
-class UpgradeFrom2_0_x implements InitStep {
-  static final String[] etcFiles = {
-    "gerrit.config", //
-    "secure.config", //
-    "replication.config", //
-    "ssh_host_rsa_key", //
-    "ssh_host_rsa_key.pub", //
-    "ssh_host_dsa_key", //
-    "ssh_host_dsa_key.pub", //
-    "ssh_host_key", //
-    "contact_information.pub", //
-    "gitweb_config.perl", //
-    "keystore", //
-    "GerritSite.css", //
-    "GerritSiteFooter.html", //
-    "GerritSiteHeader.html", //
-  };
-
-  private final ConsoleUI ui;
-
-  private final FileBasedConfig cfg;
-  private final SecureStore sec;
-  private final Path site_path;
-  private final Path etc_dir;
-  private final Section.Factory sections;
-
-  @Inject
-  UpgradeFrom2_0_x(
-      final SitePaths site,
-      final InitFlags flags,
-      final ConsoleUI ui,
-      final Section.Factory sections) {
-    this.ui = ui;
-    this.sections = sections;
-
-    this.cfg = flags.cfg;
-    this.sec = flags.sec;
-    this.site_path = site.site_path;
-    this.etc_dir = site.etc_dir;
-  }
-
-  boolean isNeedUpgrade() {
-    for (String name : etcFiles) {
-      if (Files.exists(site_path.resolve(name))) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  @Override
-  public void run() throws IOException, ConfigInvalidException {
-    if (!isNeedUpgrade()) {
-      return;
-    }
-
-    if (!ui.yesno(true, "Upgrade '%s'", site_path.toAbsolutePath())) {
-      throw die("aborted by user");
-    }
-
-    for (String name : etcFiles) {
-      Path src = site_path.resolve(name);
-      Path dst = etc_dir.resolve(name);
-      if (Files.exists(src)) {
-        if (Files.exists(dst)) {
-          throw die("File " + src + " would overwrite " + dst);
-        }
-        try {
-          Files.move(src, dst);
-        } catch (IOException e) {
-          throw die("Cannot rename " + src + " to " + dst, e);
-        }
-      }
-    }
-
-    // We have to reload the configuration after the rename as
-    // the initial load pulled up an non-existent (and thus
-    // believed to be empty) file.
-    //
-    cfg.load();
-
-    final Properties oldprop = readGerritServerProperties();
-    if (oldprop != null) {
-      final Section database = sections.get("database", null);
-
-      String url = oldprop.getProperty("url");
-      if (url != null && !convertUrl(database, url)) {
-        database.set("type", "jdbc");
-        database.set("driver", oldprop.getProperty("driver"));
-        database.set("url", url);
-      }
-
-      String username = oldprop.getProperty("user");
-      if (username == null || username.isEmpty()) {
-        username = oldprop.getProperty("username");
-      }
-      if (username != null && !username.isEmpty()) {
-        cfg.setString("database", null, "username", username);
-      }
-
-      String password = oldprop.getProperty("password");
-      if (password != null && !password.isEmpty()) {
-        sec.set("database", null, "password", password);
-      }
-    }
-
-    String[] values;
-
-    values = cfg.getStringList("ldap", null, "password");
-    cfg.unset("ldap", null, "password");
-    sec.setList("ldap", null, "password", Arrays.asList(values));
-
-    values = cfg.getStringList("sendemail", null, "smtpPass");
-    cfg.unset("sendemail", null, "smtpPass");
-    sec.setList("sendemail", null, "smtpPass", Arrays.asList(values));
-
-    savePublic(cfg);
-  }
-
-  private boolean convertUrl(Section database, String url) throws UnsupportedEncodingException {
-    String username = null;
-    String password = null;
-
-    if (url.contains("?")) {
-      final int q = url.indexOf('?');
-      for (String pair : Splitter.on('&').split(url.substring(q + 1))) {
-        final int eq = pair.indexOf('=');
-        if (0 < eq) {
-          return false;
-        }
-
-        String n = URLDecoder.decode(pair.substring(0, eq), UTF_8.name());
-        String v = URLDecoder.decode(pair.substring(eq + 1), UTF_8.name());
-
-        if ("user".equals(n) || "username".equals(n)) {
-          username = v;
-
-        } else if ("password".equals(n)) {
-          password = v;
-
-        } else {
-          // There is a parameter setting we don't recognize, use the
-          // JDBC URL format instead to preserve the configuration.
-          //
-          return false;
-        }
-      }
-      url = url.substring(0, q);
-    }
-
-    if (url.startsWith("jdbc:h2:file:")) {
-      url = url.substring("jdbc:h2:file:".length());
-      database.set("type", "h2");
-      database.set("database", url);
-      return true;
-    }
-
-    if (url.startsWith("jdbc:postgresql://")) {
-      url = url.substring("jdbc:postgresql://".length());
-      final int sl = url.indexOf('/');
-      if (sl < 0) {
-        return false;
-      }
-
-      final InetSocketAddress addr = SocketUtil.parse(url.substring(0, sl), 0);
-      database.set("type", "postgresql");
-      sethost(database, addr);
-      database.set("database", url.substring(sl + 1));
-      setuser(database, username, password);
-      return true;
-    }
-
-    if (url.startsWith("jdbc:postgresql:")) {
-      url = url.substring("jdbc:postgresql:".length());
-      database.set("type", "postgresql");
-      database.set("hostname", "localhost");
-      database.set("database", url);
-      setuser(database, username, password);
-      return true;
-    }
-
-    if (url.startsWith("jdbc:mysql://")) {
-      url = url.substring("jdbc:mysql://".length());
-      final int sl = url.indexOf('/');
-      if (sl < 0) {
-        return false;
-      }
-
-      final InetSocketAddress addr = SocketUtil.parse(url.substring(0, sl), 0);
-      database.set("type", "mysql");
-      sethost(database, addr);
-      database.set("database", url.substring(sl + 1));
-      setuser(database, username, password);
-      return true;
-    }
-
-    return false;
-  }
-
-  private void sethost(Section database, InetSocketAddress addr) {
-    database.set("hostname", SocketUtil.hostname(addr));
-    if (0 < addr.getPort()) {
-      database.set("port", String.valueOf(addr.getPort()));
-    }
-  }
-
-  private void setuser(Section database, String username, String password) {
-    if (username != null && !username.isEmpty()) {
-      database.set("username", username);
-    }
-    if (password != null && !password.isEmpty()) {
-      sec.set("database", null, "password", password);
-    }
-  }
-
-  private Properties readGerritServerProperties() throws IOException {
-    final Properties srvprop = new Properties();
-    final String name = System.getProperty("GerritServer");
-    Path path;
-    if (name != null) {
-      path = Paths.get(name);
-    } else {
-      path = site_path.resolve("GerritServer.properties");
-      if (!Files.exists(path)) {
-        path = Paths.get("GerritServer.properties");
-      }
-    }
-    if (Files.exists(path)) {
-      try (InputStream in = Files.newInputStream(path)) {
-        srvprop.load(in);
-      } catch (IOException e) {
-        throw new IOException("Cannot read " + name, e);
-      }
-      final Properties dbprop = new Properties();
-      for (Map.Entry<Object, Object> e : srvprop.entrySet()) {
-        final String key = (String) e.getKey();
-        if (key.startsWith("database.")) {
-          dbprop.put(key.substring("database.".length()), e.getValue());
-        }
-      }
-      return dbprop;
-    }
-    return null;
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java b/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java
index b417d05..0d73089 100644
--- a/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java
+++ b/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.server.index.account.AllAccountsIndexer;
 import com.google.gerrit.server.index.group.AllGroupsIndexer;
-import com.google.gerrit.server.index.group.GroupIndexCollection;
 import com.google.gerrit.server.index.group.GroupIndexDefinition;
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
 import com.google.inject.AbstractModule;
@@ -75,8 +74,6 @@
     // during init, hence we don't need AllGroupsIndexer.
     bind(AllGroupsIndexer.class).toProvider(Providers.of(null));
 
-    bind(GroupIndexCollection.class);
-
     bind(new TypeLiteral<Map<String, Integer>>() {})
         .annotatedWith(Names.named(SingleVersionModule.SINGLE_VERSIONS))
         .toInstance(ImmutableMap.<String, Integer>of());
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 540c5f3..ca4b6f3 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -50,7 +50,6 @@
 import com.google.gerrit.server.config.DefaultUrlFormatter;
 import com.google.gerrit.server.config.DisableReverseDnsLookup;
 import com.google.gerrit.server.config.DisableReverseDnsLookupProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.GitReceivePackGroups;
 import com.google.gerrit.server.config.GitUploadPackGroups;
 import com.google.gerrit.server.config.SysExecutorModule;
@@ -87,21 +86,13 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import org.eclipse.jgit.lib.Config;
 
-/**
- * Module for programs that perform batch operations on a site.
- *
- * <p>Any program that requires this module likely also requires using {@link ThreadLimiter} to
- * limit the number of threads accessing the database concurrently.
- */
+/** Module for programs that perform batch operations on a site. */
 public class BatchProgramModule extends FactoryModule {
-  private final Config cfg;
   private final Module reviewDbModule;
 
   @Inject
-  BatchProgramModule(@GerritServerConfig Config cfg, PerThreadReviewDbModule reviewDbModule) {
-    this.cfg = cfg;
+  BatchProgramModule(PerThreadReviewDbModule reviewDbModule) {
     this.reviewDbModule = reviewDbModule;
   }
 
@@ -173,7 +164,7 @@
     install(new H2CacheModule());
     install(new ExternalIdModule());
     install(new GroupModule());
-    install(new NoteDbModule(cfg));
+    install(new NoteDbModule());
     install(AccountCacheImpl.module());
     install(GroupCacheImpl.module());
     install(GroupIncludeCacheImpl.module());
diff --git a/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java b/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
deleted file mode 100644
index 1c7dc52..0000000
--- a/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.util;
-
-import com.google.gerrit.common.SiteLibraryLoaderUtil;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gerrit.server.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.nio.file.Path;
-import javax.sql.DataSource;
-import org.eclipse.jgit.lib.Config;
-
-/** Loads the site library if not yet loaded. */
-@Singleton
-public class SiteLibraryBasedDataSourceProvider extends DataSourceProvider {
-  private final Path libdir;
-  private boolean init;
-
-  @Inject
-  SiteLibraryBasedDataSourceProvider(
-      SitePaths site,
-      @GerritServerConfig Config cfg,
-      MetricMaker metrics,
-      ThreadSettingsConfig tsc,
-      DataSourceProvider.Context ctx,
-      DataSourceType dst) {
-    super(cfg, metrics, tsc, ctx, dst);
-    libdir = site.lib_dir;
-  }
-
-  @Override
-  public synchronized DataSource get() {
-    if (!init) {
-      SiteLibraryLoaderUtil.loadSiteLib(libdir);
-      init = true;
-    }
-    return super.get();
-  }
-}
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index 65feeab..e273c02 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -15,52 +15,34 @@
 package com.google.gerrit.pgm.util;
 
 import static com.google.gerrit.server.config.GerritServerConfigModule.getSecureStoreClassName;
-import static com.google.inject.Scopes.SINGLETON;
 import static com.google.inject.Stage.PRODUCTION;
 
 import com.google.gerrit.common.Die;
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
 import com.google.gerrit.server.config.GerritRuntime;
-import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.GerritServerConfigModule;
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.git.GitRepositoryManagerModule;
 import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.schema.DataSourceModule;
-import com.google.gerrit.server.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.DataSourceType;
 import com.google.gerrit.server.schema.DatabaseModule;
 import com.google.gerrit.server.schema.ReviewDbSchemaModule;
 import com.google.gerrit.server.securestore.SecureStoreClassName;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.AbstractModule;
-import com.google.inject.Binding;
 import com.google.inject.CreationException;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import com.google.inject.Key;
 import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
 import com.google.inject.spi.Message;
 import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
-import javax.sql.DataSource;
-import org.eclipse.jgit.lib.Config;
 import org.kohsuke.args4j.Option;
 
 public abstract class SiteProgram extends AbstractProgram {
@@ -74,8 +56,6 @@
     sitePath = Paths.get(path);
   }
 
-  protected Provider<DataSource> dsProvider;
-
   private Path sitePath = Paths.get(".");
 
   protected SiteProgram() {}
@@ -84,11 +64,6 @@
     this.sitePath = sitePath;
   }
 
-  protected SiteProgram(Path sitePath, Provider<DataSource> dsProvider) {
-    this.sitePath = sitePath;
-    this.dsProvider = dsProvider;
-  }
-
   /** @return the site path specified on the command line. */
   protected Path getSitePath() {
     return sitePath;
@@ -102,12 +77,12 @@
   }
 
   /** @return provides database connectivity and site path. */
-  protected Injector createDbInjector(DataSourceProvider.Context context) {
-    return createDbInjector(false, context);
+  protected Injector createDbInjector() {
+    return createDbInjector(false);
   }
 
   /** @return provides database connectivity and site path. */
-  protected Injector createDbInjector(boolean enableMetrics, DataSourceProvider.Context context) {
+  protected Injector createDbInjector(boolean enableMetrics) {
     List<Module> modules = new ArrayList<>();
 
     Module sitePathModule =
@@ -134,26 +109,6 @@
           });
     }
 
-    modules.add(
-        new LifecycleModule() {
-          @Override
-          protected void configure() {
-            bind(DataSourceProvider.Context.class).toInstance(context);
-            if (dsProvider != null) {
-              bind(Key.get(DataSource.class, Names.named("ReviewDb")))
-                  .toProvider(dsProvider)
-                  .in(SINGLETON);
-              if (LifecycleListener.class.isAssignableFrom(dsProvider.getClass())) {
-                listener().toInstance((LifecycleListener) dsProvider);
-              }
-            } else {
-              bind(Key.get(DataSource.class, Names.named("ReviewDb")))
-                  .toProvider(SiteLibraryBasedDataSourceProvider.class)
-                  .in(SINGLETON);
-              listener().to(SiteLibraryBasedDataSourceProvider.class);
-            }
-          }
-        });
     Module configModule = new GerritServerConfigModule();
     modules.add(configModule);
     modules.add(
@@ -164,29 +119,7 @@
           }
         });
     Injector cfgInjector = Guice.createInjector(sitePathModule, configModule);
-    Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
-    String dbType;
-    if (dsProvider != null) {
-      dbType = getDbType(dsProvider);
-    } else {
-      dbType = cfg.getString("database", null, "type");
-    }
 
-    if (dbType == null) {
-      throw new ProvisionException("database.type must be defined");
-    }
-
-    DataSourceType dst =
-        Guice.createInjector(new DataSourceModule(), configModule, sitePathModule)
-            .getInstance(Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
-
-    modules.add(
-        new AbstractModule() {
-          @Override
-          protected void configure() {
-            bind(DataSourceType.class).toInstance(dst);
-          }
-        });
     modules.add(new DatabaseModule());
     modules.add(new ReviewDbSchemaModule());
     modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class));
@@ -236,41 +169,6 @@
     return getSecureStoreClassName(sitePath);
   }
 
-  private String getDbType(Provider<DataSource> dsProvider) {
-    String dbProductName;
-    try (Connection conn = dsProvider.get().getConnection()) {
-      dbProductName = conn.getMetaData().getDatabaseProductName().toLowerCase();
-    } catch (SQLException e) {
-      throw new RuntimeException(e);
-    }
-
-    List<Module> modules = new ArrayList<>();
-    modules.add(
-        new AbstractModule() {
-          @Override
-          protected void configure() {
-            bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath());
-          }
-        });
-    modules.add(new GerritServerConfigModule());
-    modules.add(new DataSourceModule());
-    Injector i = Guice.createInjector(modules);
-    List<Binding<DataSourceType>> dsTypeBindings =
-        i.findBindingsByType(new TypeLiteral<DataSourceType>() {});
-    for (Binding<DataSourceType> binding : dsTypeBindings) {
-      Annotation annotation = binding.getKey().getAnnotation();
-      if (annotation instanceof Named) {
-        Named named = (Named) annotation;
-        if (named.value().toLowerCase().contains(dbProductName)) {
-          return named.value();
-        }
-      }
-    }
-    throw new IllegalStateException(
-        String.format(
-            "Cannot guess database type from the database product name '%s'", dbProductName));
-  }
-
   @SuppressWarnings("deprecation")
   private static boolean isCannotCreatePoolException(Throwable why) {
     return why instanceof org.apache.commons.dbcp.SQLNestedException
diff --git a/java/com/google/gerrit/pgm/util/ThreadLimiter.java b/java/com/google/gerrit/pgm/util/ThreadLimiter.java
deleted file mode 100644
index 64f703bd..0000000
--- a/java/com/google/gerrit/pgm/util/ThreadLimiter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.util;
-
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import org.eclipse.jgit.lib.Config;
-
-// TODO(dborowitz): Not necessary once we switch to NoteDb.
-/** Utility to limit threads used by a batch program. */
-public class ThreadLimiter {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  public static int limitThreads(Injector dbInjector, int threads) {
-    return limitThreads(
-        dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)),
-        dbInjector.getInstance(DataSourceType.class),
-        dbInjector.getInstance(ThreadSettingsConfig.class),
-        threads);
-  }
-
-  private static int limitThreads(
-      Config cfg, DataSourceType dst, ThreadSettingsConfig threadSettingsConfig, int threads) {
-    boolean usePool = cfg.getBoolean("database", "connectionpool", dst.usePool());
-    int poolLimit = threadSettingsConfig.getDatabasePoolLimit();
-    if (usePool && threads > poolLimit) {
-      logger.atWarning().log("Limiting program to %d threads due to database.poolLimit", poolLimit);
-      return poolLimit;
-    }
-    return threads;
-  }
-
-  private ThreadLimiter() {}
-}
diff --git a/java/com/google/gerrit/reviewdb/BUILD b/java/com/google/gerrit/reviewdb/BUILD
index a4c6601..76e35a1 100644
--- a/java/com/google/gerrit/reviewdb/BUILD
+++ b/java/com/google/gerrit/reviewdb/BUILD
@@ -5,9 +5,8 @@
 java_library(
     name = "server",
     srcs = glob(["**/*.java"]),
-    resource_strip_prefix = "resources",
-    resources = ["//resources/com/google/gerrit/reviewdb"],
     deps = [
+        "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/extensions:api",
         "//lib:guava",
         "//lib:gwtorm",
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
index 90cd066..5ff4afa 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
@@ -30,10 +30,7 @@
  * </ul>
  */
 public interface ReviewDb extends Schema {
-  /* If you change anything, update ReviewDbSchemaVersion.C to use a new version. */
-
-  @Relation(id = 1)
-  SchemaVersionAccess schemaVersion();
+  // Deleted @Relation(id = 1)
 
   // Deleted @Relation(id = 2)
 
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
index c420254..5a77e01 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
@@ -105,11 +105,6 @@
   }
 
   @Override
-  public SchemaVersionAccess schemaVersion() {
-    return delegate.schemaVersion();
-  }
-
-  @Override
   public ChangeAccess changes() {
     return delegate.changes();
   }
diff --git a/java/com/google/gerrit/server/ApprovalsUtil.java b/java/com/google/gerrit/server/ApprovalsUtil.java
index 3625de6..95b92af 100644
--- a/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -21,7 +21,6 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
@@ -44,7 +43,6 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.LabelPermission;
@@ -106,7 +104,6 @@
     return Iterables.filter(psas, a -> Objects.equals(a.getAccountId(), accountId));
   }
 
-  private final NotesMigration migration;
   private final ApprovalCopier copier;
   private final PermissionBackend permissionBackend;
   private final ProjectCache projectCache;
@@ -114,11 +111,7 @@
   @VisibleForTesting
   @Inject
   public ApprovalsUtil(
-      NotesMigration migration,
-      ApprovalCopier copier,
-      PermissionBackend permissionBackend,
-      ProjectCache projectCache) {
-    this.migration = migration;
+      ApprovalCopier copier, PermissionBackend permissionBackend, ProjectCache projectCache) {
     this.copier = copier;
     this.permissionBackend = permissionBackend;
     this.projectCache = projectCache;
@@ -127,15 +120,11 @@
   /**
    * Get all reviewers for a change.
    *
-   * @param db review database.
    * @param notes change notes.
    * @return reviewers for the change.
    * @throws OrmException if reviewers for the change could not be read.
    */
-  public ReviewerSet getReviewers(ReviewDb db, ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return ReviewerSet.fromApprovals(db.patchSetApprovals().byChange(notes.getChangeId()));
-    }
+  public ReviewerSet getReviewers(ChangeNotes notes) throws OrmException {
     return notes.load().getReviewers();
   }
 
@@ -148,23 +137,17 @@
    */
   public ReviewerSet getReviewers(ChangeNotes notes, Iterable<PatchSetApproval> allApprovals)
       throws OrmException {
-    if (!migration.readChanges()) {
-      return ReviewerSet.fromApprovals(allApprovals);
-    }
     return notes.load().getReviewers();
   }
 
   /**
-   * Get updates to reviewer set. Always returns empty list for ReviewDb.
+   * Get updates to reviewer set.
    *
    * @param notes change notes.
    * @return reviewer updates for the change.
    * @throws OrmException if reviewer updates for the change could not be read.
    */
   public List<ReviewerStatusUpdate> getReviewerUpdates(ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return ImmutableList.of();
-    }
     return notes.load().getReviewerUpdates();
   }
 
@@ -200,13 +183,7 @@
       throws OrmException {
     PatchSet.Id psId = change.currentPatchSetId();
     Collection<Account.Id> existingReviewers;
-    if (migration.readChanges()) {
-      // If using NoteDB, we only want reviewers in the REVIEWER state.
-      existingReviewers = notes.load().getReviewers().byState(REVIEWER);
-    } else {
-      // Prior to NoteDB, we gather all reviewers regardless of state.
-      existingReviewers = getReviewers(db, notes).all();
-    }
+    existingReviewers = notes.load().getReviewers().byState(REVIEWER);
     // Existing reviewers should include pending additions in the REVIEWER
     // state, taken from ChangeUpdate.
     existingReviewers = Lists.newArrayList(existingReviewers);
@@ -308,7 +285,7 @@
   }
 
   /**
-   * Adds approvals to ChangeUpdate for a new patch set, and writes to ReviewDb.
+   * Adds approvals to ChangeUpdate for a new patch set, and writes to NoteDb.
    *
    * @param db review database.
    * @param update change update.
@@ -346,7 +323,6 @@
     for (PatchSetApproval psa : cells) {
       update.putApproval(psa.getLabel(), psa.getValue());
     }
-    db.patchSetApprovals().insert(cells);
     return cells;
   }
 
@@ -377,16 +353,8 @@
     }
   }
 
-  public ListMultimap<PatchSet.Id, PatchSetApproval> byChange(ReviewDb db, ChangeNotes notes)
+  public ListMultimap<PatchSet.Id, PatchSetApproval> byChange(ChangeNotes notes)
       throws OrmException {
-    if (!migration.readChanges()) {
-      ImmutableListMultimap.Builder<PatchSet.Id, PatchSetApproval> result =
-          ImmutableListMultimap.builder();
-      for (PatchSetApproval psa : db.patchSetApprovals().byChange(notes.getChangeId())) {
-        result.put(psa.getPatchSetId(), psa);
-      }
-      return result.build();
-    }
     return notes.load().getApprovals();
   }
 
@@ -397,9 +365,6 @@
       @Nullable RevWalk rw,
       @Nullable Config repoConfig)
       throws OrmException {
-    if (!migration.readChanges()) {
-      return sortApprovals(db.patchSetApprovals().byPatchSet(psId));
-    }
     return copier.getForPatchSet(db, notes, psId, rw, repoConfig);
   }
 
@@ -411,19 +376,16 @@
       @Nullable RevWalk rw,
       @Nullable Config repoConfig)
       throws OrmException {
-    if (!migration.readChanges()) {
-      return sortApprovals(db.patchSetApprovals().byPatchSetUser(psId, accountId));
-    }
     return filterApprovals(byPatchSet(db, notes, psId, rw, repoConfig), accountId);
   }
 
-  public PatchSetApproval getSubmitter(ReviewDb db, ChangeNotes notes, PatchSet.Id c) {
+  public PatchSetApproval getSubmitter(ChangeNotes notes, PatchSet.Id c) {
     if (c == null) {
       return null;
     }
     try {
       // Submit approval is never copied, so bypass expensive byPatchSet call.
-      return getSubmitter(c, byChange(db, notes).get(c));
+      return getSubmitter(c, byChange(notes).get(c));
     } catch (OrmException e) {
       return null;
     }
diff --git a/java/com/google/gerrit/server/ChangeMessagesUtil.java b/java/com/google/gerrit/server/ChangeMessagesUtil.java
index 969cf38..c2b6b23 100644
--- a/java/com/google/gerrit/server/ChangeMessagesUtil.java
+++ b/java/com/google/gerrit/server/ChangeMessagesUtil.java
@@ -15,37 +15,24 @@
 package com.google.gerrit.server;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
-/**
- * Utility functions to manipulate ChangeMessages.
- *
- * <p>These methods either query for and update ChangeMessages in the NoteDb or ReviewDb, depending
- * on the state of the NotesMigration.
- */
+/** Utility functions to manipulate ChangeMessages. */
 @Singleton
 public class ChangeMessagesUtil {
   public static final String AUTOGENERATED_TAG_PREFIX = "autogenerated:";
@@ -100,27 +87,11 @@
     return workInProgress ? TAG_UPLOADED_WIP_PATCH_SET : TAG_UPLOADED_PATCH_SET;
   }
 
-  private static List<ChangeMessage> sortChangeMessages(Iterable<ChangeMessage> changeMessage) {
-    return ChangeNotes.MESSAGE_BY_TIME.sortedCopy(changeMessage);
-  }
-
-  private final NotesMigration migration;
-
-  @VisibleForTesting
-  @Inject
-  public ChangeMessagesUtil(NotesMigration migration) {
-    this.migration = migration;
-  }
-
-  public List<ChangeMessage> byChange(ReviewDb db, ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return sortChangeMessages(db.changeMessages().byChange(notes.getChangeId()));
-    }
+  public List<ChangeMessage> byChange(ChangeNotes notes) throws OrmException {
     return notes.load().getChangeMessages();
   }
 
-  public void addChangeMessage(ReviewDb db, ChangeUpdate update, ChangeMessage changeMessage)
-      throws OrmException {
+  public void addChangeMessage(ChangeUpdate update, ChangeMessage changeMessage) {
     checkState(
         Objects.equals(changeMessage.getAuthor(), update.getNullableAccountId()),
         "cannot store change message by %s in update by %s",
@@ -128,7 +99,6 @@
         update.getNullableAccountId());
     update.setChangeMessage(changeMessage.getMessage());
     update.setTag(changeMessage.getTag());
-    db.changeMessages().insert(Collections.singleton(changeMessage));
   }
 
   /**
@@ -139,63 +109,14 @@
    * deleted from both NoteDb and ReviewDb, the index of the change message must be used rather than
    * its ID.
    *
-   * @param db the {@code ReviewDb} instance to update.
    * @param update change update.
    * @param targetMessageIdx the index of the target change message.
    * @param newMessage the new message which is going to replace the old.
-   * @throws OrmException
    */
-  public void replaceChangeMessage(
-      ReviewDb db, ChangeUpdate update, int targetMessageIdx, String newMessage)
-      throws OrmException {
-    if (PrimaryStorage.of(update.getChange()).equals(PrimaryStorage.REVIEW_DB)) {
-      if (db instanceof BatchUpdateReviewDb) {
-        db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
-      }
-      db = unwrapDb(db);
-
-      List<ChangeMessage> messagesInReviewDb =
-          sortChangeMessages(db.changeMessages().byChange(update.getId()));
-      if (migration.readChanges()) {
-        sanityCheckForChangeMessages(messagesInReviewDb, update.getNotes().getChangeMessages());
-      }
-      ChangeMessage targetMessage = messagesInReviewDb.get(targetMessageIdx);
-      targetMessage.setMessage(newMessage);
-      db.changeMessages().upsert(Collections.singleton(targetMessage));
-    }
-
+  public void replaceChangeMessage(ChangeUpdate update, int targetMessageIdx, String newMessage) {
     update.deleteChangeMessageByRewritingHistory(targetMessageIdx, newMessage);
   }
 
-  private static void sanityCheckForChangeMessages(
-      List<ChangeMessage> messagesInReviewDb, List<ChangeMessage> messagesInNoteDb) {
-    String message =
-        String.format(
-            "Change messages in ReivewDb and NoteDb don't match: NoteDb %s; ReviewDb %s",
-            messagesInNoteDb, messagesInReviewDb);
-    if (messagesInReviewDb.size() != messagesInNoteDb.size()) {
-      throw new IllegalStateException(message);
-    }
-
-    for (int i = 0; i < messagesInReviewDb.size(); i++) {
-      ChangeMessage messageInReviewDb = messagesInReviewDb.get(i);
-      ChangeMessage messageInNoteDb = messagesInNoteDb.get(i);
-
-      // Don't compare the keys because they are different for the same change message in NoteDb and
-      // ReviewDb.
-      boolean isEqual =
-          Objects.equals(messageInReviewDb.getAuthor(), messageInNoteDb.getAuthor())
-              && Objects.equals(messageInReviewDb.getWrittenOn(), messageInNoteDb.getWrittenOn())
-              && Objects.equals(messageInReviewDb.getMessage(), messageInNoteDb.getMessage())
-              && Objects.equals(messageInReviewDb.getPatchSetId(), messageInNoteDb.getPatchSetId())
-              && Objects.equals(messageInReviewDb.getTag(), messageInNoteDb.getTag())
-              && Objects.equals(messageInReviewDb.getRealAuthor(), messageInNoteDb.getRealAuthor());
-      if (!isEqual) {
-        throw new IllegalStateException(message);
-      }
-    }
-  }
-
   /**
    * @param tag value of a tag, or null.
    * @return whether the tag starts with the autogenerated prefix.
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index 99dfbbb..b3812a0 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -16,16 +16,12 @@
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.PatchLineComment.Status.PUBLISHED;
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
-import com.google.common.collect.Streams;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.common.CommentInfo;
@@ -35,31 +31,23 @@
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.client.RobotComment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import org.eclipse.jgit.lib.BatchRefUpdate;
@@ -70,12 +58,7 @@
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.transport.ReceiveCommand;
 
-/**
- * Utility functions to manipulate Comments.
- *
- * <p>These methods either query for and update Comments in the NoteDb or ReviewDb, depending on the
- * state of the NotesMigration.
- */
+/** Utility functions to manipulate Comments. */
 @Singleton
 public class CommentsUtil {
   public static final Ordering<Comment> COMMENT_ORDER =
@@ -127,18 +110,13 @@
 
   private final GitRepositoryManager repoManager;
   private final AllUsersName allUsers;
-  private final NotesMigration migration;
   private final String serverId;
 
   @Inject
   CommentsUtil(
-      GitRepositoryManager repoManager,
-      AllUsersName allUsers,
-      NotesMigration migration,
-      @GerritServerId String serverId) {
+      GitRepositoryManager repoManager, AllUsersName allUsers, @GerritServerId String serverId) {
     this.repoManager = repoManager;
     this.allUsers = allUsers;
-    this.migration = migration;
     this.serverId = serverId;
   }
 
@@ -158,7 +136,7 @@
       } else {
         // Inherit unresolved value from inReplyTo comment if not specified.
         Comment.Key key = new Comment.Key(parentUuid, path, psId.patchSetId);
-        Optional<Comment> parent = getPublished(ctx.getDb(), ctx.getNotes(), key);
+        Optional<Comment> parent = getPublished(ctx.getNotes(), key);
         if (!parent.isPresent()) {
           throw new UnprocessableEntityException("Invalid parentUuid supplied for comment");
         }
@@ -201,119 +179,64 @@
     return c;
   }
 
-  public Optional<Comment> getPublished(ReviewDb db, ChangeNotes notes, Comment.Key key)
-      throws OrmException {
-    if (!migration.readChanges()) {
-      return getReviewDb(db, notes, key);
-    }
-    return publishedByChange(db, notes).stream().filter(c -> key.equals(c.key)).findFirst();
+  public Optional<Comment> getPublished(ChangeNotes notes, Comment.Key key) throws OrmException {
+    return publishedByChange(notes).stream().filter(c -> key.equals(c.key)).findFirst();
   }
 
-  public Optional<Comment> getDraft(
-      ReviewDb db, ChangeNotes notes, IdentifiedUser user, Comment.Key key) throws OrmException {
-    if (!migration.readChanges()) {
-      Optional<Comment> c = getReviewDb(db, notes, key);
-      if (c.isPresent() && !c.get().author.getId().equals(user.getAccountId())) {
-        throw new OrmException(
-            String.format(
-                "Expected draft %s to belong to account %s, but it belongs to %s",
-                key, user.getAccountId(), c.get().author.getId()));
-      }
-      return c;
-    }
-    return draftByChangeAuthor(db, notes, user.getAccountId())
+  public Optional<Comment> getDraft(ChangeNotes notes, IdentifiedUser user, Comment.Key key)
+      throws OrmException {
+    return draftByChangeAuthor(notes, user.getAccountId())
         .stream()
         .filter(c -> key.equals(c.key))
         .findFirst();
   }
 
-  private Optional<Comment> getReviewDb(ReviewDb db, ChangeNotes notes, Comment.Key key)
-      throws OrmException {
-    return Optional.ofNullable(
-            db.patchComments().get(PatchLineComment.Key.from(notes.getChangeId(), key)))
-        .map(plc -> plc.asComment(serverId));
-  }
-
-  public List<Comment> publishedByChange(ReviewDb db, ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return sort(byCommentStatus(db.patchComments().byChange(notes.getChangeId()), PUBLISHED));
-    }
-
+  public List<Comment> publishedByChange(ChangeNotes notes) throws OrmException {
     notes.load();
     return sort(Lists.newArrayList(notes.getComments().values()));
   }
 
   public List<RobotComment> robotCommentsByChange(ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return ImmutableList.of();
-    }
-
     notes.load();
     return sort(Lists.newArrayList(notes.getRobotComments().values()));
   }
 
-  public List<Comment> draftByChange(ReviewDb db, ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return sort(byCommentStatus(db.patchComments().byChange(notes.getChangeId()), Status.DRAFT));
-    }
-
+  public List<Comment> draftByChange(ChangeNotes notes) throws OrmException {
     List<Comment> comments = new ArrayList<>();
     for (Ref ref : getDraftRefs(notes.getChangeId())) {
       Account.Id account = Account.Id.fromRefSuffix(ref.getName());
       if (account != null) {
-        comments.addAll(draftByChangeAuthor(db, notes, account));
+        comments.addAll(draftByChangeAuthor(notes, account));
       }
     }
     return sort(comments);
   }
 
-  private List<Comment> byCommentStatus(
-      ResultSet<PatchLineComment> comments, PatchLineComment.Status status) {
-    return toComments(
-        serverId, Lists.newArrayList(Iterables.filter(comments, c -> c.getStatus() == status)));
-  }
-
-  public List<Comment> byPatchSet(ReviewDb db, ChangeNotes notes, PatchSet.Id psId)
-      throws OrmException {
-    if (!migration.readChanges()) {
-      return sort(toComments(serverId, db.patchComments().byPatchSet(psId).toList()));
-    }
+  public List<Comment> byPatchSet(ChangeNotes notes, PatchSet.Id psId) throws OrmException {
     List<Comment> comments = new ArrayList<>();
-    comments.addAll(publishedByPatchSet(db, notes, psId));
+    comments.addAll(publishedByPatchSet(notes, psId));
 
     for (Ref ref : getDraftRefs(notes.getChangeId())) {
       Account.Id account = Account.Id.fromRefSuffix(ref.getName());
       if (account != null) {
-        comments.addAll(draftByPatchSetAuthor(db, psId, account, notes));
+        comments.addAll(draftByPatchSetAuthor(psId, account, notes));
       }
     }
     return sort(comments);
   }
 
-  public List<Comment> publishedByChangeFile(
-      ReviewDb db, ChangeNotes notes, Change.Id changeId, String file) throws OrmException {
-    if (!migration.readChanges()) {
-      return sort(
-          toComments(serverId, db.patchComments().publishedByChangeFile(changeId, file).toList()));
-    }
+  public List<Comment> publishedByChangeFile(ChangeNotes notes, String file) throws OrmException {
     return commentsOnFile(notes.load().getComments().values(), file);
   }
 
-  public List<Comment> publishedByPatchSet(ReviewDb db, ChangeNotes notes, PatchSet.Id psId)
+  public List<Comment> publishedByPatchSet(ChangeNotes notes, PatchSet.Id psId)
       throws OrmException {
-    if (!migration.readChanges()) {
-      return removeCommentsOnAncestorOfCommitMessage(
-          sort(toComments(serverId, db.patchComments().publishedByPatchSet(psId).toList())));
-    }
     return removeCommentsOnAncestorOfCommitMessage(
         commentsOnPatchSet(notes.load().getComments().values(), psId));
   }
 
   public List<RobotComment> robotCommentsByPatchSet(ChangeNotes notes, PatchSet.Id psId)
       throws OrmException {
-    if (!migration.readChanges()) {
-      return ImmutableList.of();
-    }
     return commentsOnPatchSet(notes.load().getRobotComments().values(), psId);
   }
 
@@ -330,49 +253,28 @@
         .collect(toList());
   }
 
-  public List<Comment> draftByPatchSetAuthor(
-      ReviewDb db, PatchSet.Id psId, Account.Id author, ChangeNotes notes) throws OrmException {
-    if (!migration.readChanges()) {
-      return sort(
-          toComments(serverId, db.patchComments().draftByPatchSetAuthor(psId, author).toList()));
-    }
+  public List<Comment> draftByPatchSetAuthor(PatchSet.Id psId, Account.Id author, ChangeNotes notes)
+      throws OrmException {
     return commentsOnPatchSet(notes.load().getDraftComments(author).values(), psId);
   }
 
-  public List<Comment> draftByChangeFileAuthor(
-      ReviewDb db, ChangeNotes notes, String file, Account.Id author) throws OrmException {
-    if (!migration.readChanges()) {
-      return sort(
-          toComments(
-              serverId,
-              db.patchComments()
-                  .draftByChangeFileAuthor(notes.getChangeId(), file, author)
-                  .toList()));
-    }
+  public List<Comment> draftByChangeFileAuthor(ChangeNotes notes, String file, Account.Id author)
+      throws OrmException {
     return commentsOnFile(notes.load().getDraftComments(author).values(), file);
   }
 
-  public List<Comment> draftByChangeAuthor(ReviewDb db, ChangeNotes notes, Account.Id author)
+  public List<Comment> draftByChangeAuthor(ChangeNotes notes, Account.Id author)
       throws OrmException {
-    if (!migration.readChanges()) {
-      return Streams.stream(db.patchComments().draftByAuthor(author))
-          .filter(c -> c.getPatchSetId().getParentKey().equals(notes.getChangeId()))
-          .map(plc -> plc.asComment(serverId))
-          .sorted(COMMENT_ORDER)
-          .collect(toList());
-    }
     List<Comment> comments = new ArrayList<>();
     comments.addAll(notes.getDraftComments(author).values());
     return sort(comments);
   }
 
   public void putComments(
-      ReviewDb db, ChangeUpdate update, PatchLineComment.Status status, Iterable<Comment> comments)
-      throws OrmException {
+      ChangeUpdate update, PatchLineComment.Status status, Iterable<Comment> comments) {
     for (Comment c : comments) {
       update.putComment(status, c);
     }
-    db.patchComments().upsert(toPatchLineComments(update.getId(), status, comments));
   }
 
   public void putRobotComments(ChangeUpdate update, Iterable<RobotComment> comments) {
@@ -381,37 +283,14 @@
     }
   }
 
-  public void deleteComments(ReviewDb db, ChangeUpdate update, Iterable<Comment> comments)
-      throws OrmException {
+  public void deleteComments(ChangeUpdate update, Iterable<Comment> comments) {
     for (Comment c : comments) {
       update.deleteComment(c);
     }
-    db.patchComments()
-        .delete(toPatchLineComments(update.getId(), PatchLineComment.Status.DRAFT, comments));
   }
 
   public void deleteCommentByRewritingHistory(
-      ReviewDb db, ChangeUpdate update, Comment.Key commentKey, PatchSet.Id psId, String newMessage)
-      throws OrmException {
-    if (PrimaryStorage.of(update.getChange()).equals(PrimaryStorage.REVIEW_DB)) {
-      PatchLineComment.Key key =
-          new PatchLineComment.Key(new Patch.Key(psId, commentKey.filename), commentKey.uuid);
-
-      if (db instanceof BatchUpdateReviewDb) {
-        db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
-      }
-      db = ReviewDbUtil.unwrapDb(db);
-
-      PatchLineComment patchLineComment = db.patchComments().get(key);
-
-      if (!patchLineComment.getStatus().equals(PUBLISHED)) {
-        throw new OrmException(String.format("comment %s is not published", key));
-      }
-
-      patchLineComment.setMessage(newMessage);
-      db.patchComments().upsert(Collections.singleton(patchLineComment));
-    }
-
+      ChangeUpdate update, Comment.Key commentKey, String newMessage) {
     update.deleteCommentByRewritingHistory(commentKey.uuid, newMessage);
   }
 
diff --git a/java/com/google/gerrit/server/PatchSetUtil.java b/java/com/google/gerrit/server/PatchSetUtil.java
index f6c7abc..8f26082 100644
--- a/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/java/com/google/gerrit/server/PatchSetUtil.java
@@ -17,7 +17,6 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.ImmutableMap.toImmutableMap;
 import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
-import static com.google.gerrit.server.notedb.PatchSetState.PUBLISHED;
 import static java.util.Objects.requireNonNull;
 import static java.util.function.Function.identity;
 
@@ -149,12 +148,6 @@
     return ps;
   }
 
-  public void publish(ReviewDb db, ChangeUpdate update, PatchSet ps) throws OrmException {
-    ensurePatchSetMatches(ps.getId(), update);
-    update.setPatchSetState(PUBLISHED);
-    db.patchSets().update(Collections.singleton(ps));
-  }
-
   private static void ensurePatchSetMatches(PatchSet.Id psId, ChangeUpdate update) {
     Change.Id changeId = update.getChange().getId();
     checkArgument(
diff --git a/java/com/google/gerrit/server/PublishCommentUtil.java b/java/com/google/gerrit/server/PublishCommentUtil.java
index a90f3e7..9d5ecb4 100644
--- a/java/com/google/gerrit/server/PublishCommentUtil.java
+++ b/java/com/google/gerrit/server/PublishCommentUtil.java
@@ -74,7 +74,7 @@
         throw new OrmException(e);
       }
     }
-    commentsUtil.putComments(ctx.getDb(), ctx.getUpdate(psId), PUBLISHED, drafts);
+    commentsUtil.putComments(ctx.getUpdate(psId), PUBLISHED, drafts);
   }
 
   private static PatchSet.Id psId(ChangeNotes notes, Comment c) {
diff --git a/java/com/google/gerrit/server/change/AbandonOp.java b/java/com/google/gerrit/server/change/AbandonOp.java
index 5affd5c..874f5e4 100644
--- a/java/com/google/gerrit/server/change/AbandonOp.java
+++ b/java/com/google/gerrit/server/change/AbandonOp.java
@@ -105,7 +105,7 @@
 
     update.setStatus(change.getStatus());
     message = newMessage(ctx);
-    cmUtil.addChangeMessage(ctx.getDb(), update, message);
+    cmUtil.addChangeMessage(update, message);
     return true;
   }
 
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index 33c7f73..1eb1dc3 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -459,7 +459,7 @@
               patchSet.getCreatedOn(),
               message,
               ChangeMessagesUtil.uploadedPatchSetTag(workInProgress));
-      cmUtil.addChangeMessage(db, update, changeMessage);
+      cmUtil.addChangeMessage(update, changeMessage);
     }
     return true;
   }
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index b7049a7..889a20c 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -667,7 +667,7 @@
   }
 
   private Collection<ChangeMessageInfo> messages(ChangeData cd) throws OrmException {
-    List<ChangeMessage> messages = cmUtil.byChange(db.get(), cd.notes());
+    List<ChangeMessage> messages = cmUtil.byChange(cd.notes());
     if (messages.isEmpty()) {
       return Collections.emptyList();
     }
diff --git a/java/com/google/gerrit/server/change/ChangeResource.java b/java/com/google/gerrit/server/change/ChangeResource.java
index ef8b2f9..1131e94 100644
--- a/java/com/google/gerrit/server/change/ChangeResource.java
+++ b/java/com/google/gerrit/server/change/ChangeResource.java
@@ -167,7 +167,7 @@
       // set of accounts that posted a message is too expensive. However everyone who posts a
       // message is automatically added as reviewer. Hence if we include removed reviewers we can
       // be sure that we have all accounts that posted messages on the change.
-      accounts.addAll(approvalUtil.getReviewers(db.get(), notes).all());
+      accounts.addAll(approvalUtil.getReviewers(notes).all());
     } catch (OrmException e) {
       // This ETag will be invalidated if it loads next time.
     }
diff --git a/java/com/google/gerrit/server/change/ConsistencyChecker.java b/java/com/google/gerrit/server/change/ConsistencyChecker.java
index a379f2c..6e5158b 100644
--- a/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -52,7 +52,6 @@
 import com.google.gerrit.server.plugincontext.PluginItemContext;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.update.RetryHelper;
@@ -503,7 +502,7 @@
     List<ProblemInfo> currProblems = new ArrayList<>(3);
     currProblems.add(notFound);
     if (deleteOldPatchSetProblem != null) {
-      currProblems.add(insertPatchSetProblem);
+      currProblems.add(deleteOldPatchSetProblem);
     }
     currProblems.add(insertPatchSetProblem);
 
@@ -667,15 +666,9 @@
     public boolean updateChange(ChangeContext ctx)
         throws OrmException, PatchSetInfoNotAvailableException {
       // Delete dangling key references.
-      ReviewDb db = BatchUpdateReviewDb.unwrap(ctx.getDb());
       accountPatchReviewStore.run(s -> s.clearReviewed(psId), OrmException.class);
-      db.changeMessages().delete(db.changeMessages().byChange(psId.getParentKey()));
-      db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(psId));
-      db.patchComments().delete(db.patchComments().byPatchSet(psId));
-      db.patchSets().deleteKeys(Collections.singleton(psId));
 
-      // NoteDb requires no additional fiddling; setting the state to deleted is
-      // sufficient to filter everything else out.
+      // For NoteDb setting the state to deleted is sufficient to filter everything out.
       ctx.getUpdate(psId).setPatchSetState(PatchSetState.DELETED);
 
       p.status = Status.FIXED;
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index 24c4237..a906d6a 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -250,7 +250,7 @@
             description);
 
     if (notify != NotifyHandling.NONE) {
-      oldReviewers = approvalsUtil.getReviewers(db, ctx.getNotes());
+      oldReviewers = approvalsUtil.getReviewers(ctx.getNotes());
     }
 
     if (message != null) {
@@ -275,7 +275,7 @@
           db, ctx.getNotes(), patchSet, ctx.getRevWalk(), ctx.getRepoView().getConfig());
     }
     if (changeMessage != null) {
-      cmUtil.addChangeMessage(db, update, changeMessage);
+      cmUtil.addChangeMessage(update, changeMessage);
     }
     return true;
   }
diff --git a/java/com/google/gerrit/server/change/SetAssigneeOp.java b/java/com/google/gerrit/server/change/SetAssigneeOp.java
index f61e95f..dd24ff6 100644
--- a/java/com/google/gerrit/server/change/SetAssigneeOp.java
+++ b/java/com/google/gerrit/server/change/SetAssigneeOp.java
@@ -99,7 +99,7 @@
     return true;
   }
 
-  private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+  private void addMessage(ChangeContext ctx, ChangeUpdate update) {
     StringBuilder msg = new StringBuilder();
     msg.append("Assignee ");
     if (oldAssignee == null) {
@@ -113,7 +113,7 @@
     }
     ChangeMessage cmsg =
         ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_SET_ASSIGNEE);
-    cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+    cmUtil.addChangeMessage(update, cmsg);
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/change/SetHashtagsOp.java b/java/com/google/gerrit/server/change/SetHashtagsOp.java
index d11b2df..2c180ed 100644
--- a/java/com/google/gerrit/server/change/SetHashtagsOp.java
+++ b/java/com/google/gerrit/server/change/SetHashtagsOp.java
@@ -125,13 +125,13 @@
     }
   }
 
-  private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+  private void addMessage(ChangeContext ctx, ChangeUpdate update) {
     StringBuilder msg = new StringBuilder();
     appendHashtagMessage(msg, "added", toAdd);
     appendHashtagMessage(msg, "removed", toRemove);
     ChangeMessage cmsg =
         ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_SET_HASHTAGS);
-    cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+    cmUtil.addChangeMessage(update, cmsg);
   }
 
   private void appendHashtagMessage(StringBuilder b, String action, Set<String> hashtags) {
diff --git a/java/com/google/gerrit/server/change/WorkInProgressOp.java b/java/com/google/gerrit/server/change/WorkInProgressOp.java
index 35b4e6e..bc3be51 100644
--- a/java/com/google/gerrit/server/change/WorkInProgressOp.java
+++ b/java/com/google/gerrit/server/change/WorkInProgressOp.java
@@ -135,7 +135,7 @@
     return true;
   }
 
-  private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+  private void addMessage(ChangeContext ctx, ChangeUpdate update) {
     Change c = ctx.getChange();
     StringBuilder buf =
         new StringBuilder(c.isWorkInProgress() ? "Set Work In Progress" : "Set Ready For Review");
@@ -154,7 +154,7 @@
                 ? ChangeMessagesUtil.TAG_SET_WIP
                 : ChangeMessagesUtil.TAG_SET_READY);
 
-    cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+    cmUtil.addChangeMessage(update, cmsg);
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index b26e875..6eb4ad9 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -246,7 +246,7 @@
     install(new GitModule());
     install(new GroupDbModule());
     install(new GroupModule());
-    install(new NoteDbModule(cfg));
+    install(new NoteDbModule());
     install(new PrologModule());
     install(new DefaultSubmitRule.Module());
     install(new IgnoreSelfApprovalRule.Module());
diff --git a/java/com/google/gerrit/server/config/GerritServerIdProvider.java b/java/com/google/gerrit/server/config/GerritServerIdProvider.java
index c609cc4..4898f55 100644
--- a/java/com/google/gerrit/server/config/GerritServerIdProvider.java
+++ b/java/com/google/gerrit/server/config/GerritServerIdProvider.java
@@ -48,9 +48,7 @@
 
     // We're not generally supposed to do work in provider constructors, but this is a bit of a
     // special case because we really need to have the ID available by the time the dbInjector
-    // is created. This even applies during MigrateToNoteDb, which otherwise would have been a
-    // reasonable place to do the ID generation. Fortunately, it's not much work, and it happens
-    // once.
+    // is created. Fortunately, it's not much work, and it happens once.
     id = generate();
     Config newCfg = readGerritConfig(sitePaths);
     newCfg.setString(SECTION, null, KEY, id);
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 40ad144..25b96c8 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -222,9 +222,8 @@
    * @param a
    * @param notes
    */
-  public void addAllReviewers(ReviewDb db, ChangeAttribute a, ChangeNotes notes)
-      throws OrmException {
-    Collection<Account.Id> reviewers = approvalsUtil.getReviewers(db, notes).all();
+  public void addAllReviewers(ChangeAttribute a, ChangeNotes notes) throws OrmException {
+    Collection<Account.Id> reviewers = approvalsUtil.getReviewers(notes).all();
     if (!reviewers.isEmpty()) {
       a.allReviewers = Lists.newArrayListWithCapacity(reviewers.size());
       for (Account.Id id : reviewers) {
diff --git a/java/com/google/gerrit/server/git/MergedByPushOp.java b/java/com/google/gerrit/server/git/MergedByPushOp.java
index b3a1d72..03a93f1 100644
--- a/java/com/google/gerrit/server/git/MergedByPushOp.java
+++ b/java/com/google/gerrit/server/git/MergedByPushOp.java
@@ -151,7 +151,7 @@
     ChangeMessage msg =
         ChangeMessagesUtil.newMessage(
             psId, ctx.getUser(), ctx.getWhen(), msgBuf.toString(), ChangeMessagesUtil.TAG_MERGED);
-    cmUtil.addChangeMessage(ctx.getDb(), update, msg);
+    cmUtil.addChangeMessage(update, msg);
 
     PatchSetApproval submitter =
         ApprovalsUtil.newApproval(
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index c6dfabc..85a938a 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -641,7 +641,7 @@
         ObjectInserter ins = repo.newObjectInserter();
         ObjectReader reader = ins.newReader();
         RevWalk rw = new RevWalk(reader)) {
-      bu.setRepository(repo, rw, ins).updateChangesInParallel();
+      bu.setRepository(repo, rw, ins);
       bu.setRefLogMessage("push");
 
       int added = 0;
@@ -794,7 +794,7 @@
         ObjectInserter ins = repo.newObjectInserter();
         ObjectReader reader = ins.newReader();
         RevWalk rw = new RevWalk(reader)) {
-      bu.setRepository(repo, rw, ins).updateChangesInParallel();
+      bu.setRepository(repo, rw, ins);
       bu.setRefLogMessage("push");
 
       logger.atFine().log("Adding %d replace requests", newChanges.size());
@@ -3114,7 +3114,7 @@
                 ObjectInserter ins = repo.newObjectInserter();
                 ObjectReader reader = ins.newReader();
                 RevWalk rw = new RevWalk(reader)) {
-              bu.setRepository(repo, rw, ins).updateChangesInParallel();
+              bu.setRepository(repo, rw, ins);
               // TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
 
               RevCommit newTip = rw.parseCommit(cmd.getNewId());
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index a580cf6..cedc167 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -353,7 +353,7 @@
     }
 
     msg = createChangeMessage(ctx, reviewMessage);
-    cmUtil.addChangeMessage(ctx.getDb(), update, msg);
+    cmUtil.addChangeMessage(update, msg);
 
     if (mergedByPushOp == null) {
       resetChange(ctx);
@@ -512,7 +512,7 @@
   private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress)
       throws OrmException {
     List<Comment> comments =
-        commentsUtil.draftByChangeAuthor(ctx.getDb(), ctx.getNotes(), ctx.getUser().getAccountId());
+        commentsUtil.draftByChangeAuthor(ctx.getNotes(), ctx.getUser().getAccountId());
     publishCommentUtil.publish(
         ctx, patchSetId, comments, ChangeMessagesUtil.uploadedPatchSetTag(workInProgress));
     return comments;
diff --git a/java/com/google/gerrit/server/index/IndexModule.java b/java/com/google/gerrit/server/index/IndexModule.java
index 3c9538c..a84fbe9 100644
--- a/java/com/google/gerrit/server/index/IndexModule.java
+++ b/java/com/google/gerrit/server/index/IndexModule.java
@@ -125,7 +125,7 @@
     factory(ChangeIndexer.Factory.class);
 
     bind(GroupIndexRewriter.class);
-    bind(GroupIndexCollection.class);
+    // GroupIndexCollection is already bound very high up in ReviewDbSchemaModule.
     listener().to(GroupIndexCollection.class);
     factory(GroupIndexerImpl.Factory.class);
 
diff --git a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
index 37e288c..8f1433e 100644
--- a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
+++ b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
@@ -224,7 +224,7 @@
         // It does mean that reindexing after invalidating the DiffSummary cache will be expensive,
         // but the goal is to invalidate that cache as infrequently as we possibly can. And besides,
         // we don't have concrete proof that improving packfile locality would help.
-        notesFactory.scan(repo, db, project).forEach(r -> index(db, r));
+        notesFactory.scan(repo, project).forEach(r -> index(db, r));
       } catch (RepositoryNotFoundException rnfe) {
         logger.atSevere().log(rnfe.getMessage());
       }
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 064af64..7fd3185 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.index.IndexUtils;
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.logging.TraceContext.TraceTimer;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.project.NoSuchChangeException;
@@ -87,8 +86,6 @@
   @Nullable private final ChangeIndexCollection indexes;
   @Nullable private final ChangeIndex index;
   private final SchemaFactory<ReviewDb> schemaFactory;
-  private final NotesMigration notesMigration;
-  private final ChangeNotes.Factory changeNotesFactory;
   private final ChangeData.Factory changeDataFactory;
   private final ThreadLocalRequestContext context;
   private final ListeningExecutorService batchExecutor;
@@ -102,7 +99,6 @@
       @GerritServerConfig Config cfg,
       SchemaFactory<ReviewDb> schemaFactory,
       NotesMigration notesMigration,
-      ChangeNotes.Factory changeNotesFactory,
       ChangeData.Factory changeDataFactory,
       ThreadLocalRequestContext context,
       PluginSetContext<ChangeIndexedListener> indexedListeners,
@@ -112,8 +108,6 @@
       @Assisted ChangeIndex index) {
     this.executor = executor;
     this.schemaFactory = schemaFactory;
-    this.notesMigration = notesMigration;
-    this.changeNotesFactory = changeNotesFactory;
     this.changeDataFactory = changeDataFactory;
     this.context = context;
     this.indexedListeners = indexedListeners;
@@ -129,7 +123,6 @@
       SchemaFactory<ReviewDb> schemaFactory,
       @GerritServerConfig Config cfg,
       NotesMigration notesMigration,
-      ChangeNotes.Factory changeNotesFactory,
       ChangeData.Factory changeDataFactory,
       ThreadLocalRequestContext context,
       PluginSetContext<ChangeIndexedListener> indexedListeners,
@@ -139,8 +132,6 @@
       @Assisted ChangeIndexCollection indexes) {
     this.executor = executor;
     this.schemaFactory = schemaFactory;
-    this.notesMigration = notesMigration;
-    this.changeNotesFactory = changeNotesFactory;
     this.changeDataFactory = changeDataFactory;
     this.context = context;
     this.indexedListeners = indexedListeners;
@@ -239,8 +230,9 @@
    * @param db review database.
    * @param change change to index.
    */
+  // TODO(dborowitz): Remove OrmException
   public void index(ReviewDb db, Change change) throws IOException, OrmException {
-    index(newChangeData(db, change));
+    index(changeDataFactory.create(db, change));
   }
 
   /**
@@ -250,9 +242,10 @@
    * @param project the project to which the change belongs.
    * @param changeId ID of the change to index.
    */
+  // TODO(dborowitz): Remove OrmException
   public void index(ReviewDb db, Project.NameKey project, Change.Id changeId)
       throws IOException, OrmException {
-    index(newChangeData(db, project, changeId));
+    index(changeDataFactory.create(db, project, changeId));
   }
 
   /**
@@ -383,7 +376,7 @@
 
     @Override
     public Void callImpl(Provider<ReviewDb> db) throws Exception {
-      ChangeData cd = newChangeData(db.get(), project, id);
+      ChangeData cd = changeDataFactory.create(db.get(), project, id);
       index(cd);
       return null;
     }
@@ -429,7 +422,7 @@
     public Boolean callImpl(Provider<ReviewDb> db) throws Exception {
       try {
         if (stalenessChecker.isStale(id)) {
-          indexImpl(newChangeData(db.get(), project, id));
+          indexImpl(changeDataFactory.create(db.get(), project, id));
           return true;
         }
       } catch (NoSuchChangeException nsce) {
@@ -460,26 +453,4 @@
     }
     return false;
   }
-
-  // Avoid auto-rebuilding when reindexing if reading is disabled. This just
-  // increases contention on the meta ref from a background indexing thread
-  // with little benefit. The next actual write to the entity may still incur a
-  // less-contentious rebuild.
-  private ChangeData newChangeData(ReviewDb db, Change change) throws OrmException {
-    if (!notesMigration.readChanges()) {
-      ChangeNotes notes = changeNotesFactory.createWithAutoRebuildingDisabled(change, null);
-      return changeDataFactory.create(db, notes);
-    }
-    return changeDataFactory.create(db, change);
-  }
-
-  private ChangeData newChangeData(ReviewDb db, Project.NameKey project, Change.Id changeId)
-      throws OrmException {
-    if (!notesMigration.readChanges()) {
-      ChangeNotes notes =
-          changeNotesFactory.createWithAutoRebuildingDisabled(db, project, changeId);
-      return changeDataFactory.create(db, notes);
-    }
-    return changeDataFactory.create(db, project, changeId);
-  }
 }
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 262e82b..6d6f043 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -290,7 +290,7 @@
       }
 
       changeMessage = generateChangeMessage(ctx);
-      changeMessagesUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), changeMessage);
+      changeMessagesUtil.addChangeMessage(ctx.getUpdate(psId), changeMessage);
 
       comments = new ArrayList<>();
       for (MailComment c : parsedComments) {
@@ -301,10 +301,7 @@
             persistentCommentFromMailComment(ctx, c, targetPatchSetForComment(ctx, c, patchSet)));
       }
       commentsUtil.putComments(
-          ctx.getDb(),
-          ctx.getUpdate(ctx.getChange().currentPatchSetId()),
-          Status.PUBLISHED,
-          comments);
+          ctx.getUpdate(ctx.getChange().currentPatchSetId()), Status.PUBLISHED, comments);
 
       return true;
     }
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index e810397..c8aa259 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -316,7 +316,7 @@
 
     Comment.Key key = new Comment.Key(child.parentUuid, child.key.filename, child.key.patchSetId);
     try {
-      return commentsUtil.getPublished(args.db.get(), changeData.notes(), key);
+      return commentsUtil.getPublished(changeData.notes(), key);
     } catch (OrmException e) {
       logger.atWarning().log("Could not find the parent of this comment: %s", child);
       return Optional.empty();
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 72f1ba6..32d086c 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.notedb;
 
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
 import static java.util.Objects.requireNonNull;
 
@@ -27,8 +28,6 @@
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -55,9 +54,6 @@
 
     // Providers required to avoid dependency cycles.
 
-    // ChangeRebuilder -> ChangeNotes.Factory -> Args
-    final Provider<ChangeRebuilder> rebuilder;
-
     // ChangeNoteCache -> Args
     final Provider<ChangeNotesCache> cache;
 
@@ -70,7 +66,6 @@
         LegacyChangeNoteRead legacyChangeNoteRead,
         NoteDbMetrics metrics,
         Provider<ReviewDb> db,
-        Provider<ChangeRebuilder> rebuilder,
         Provider<ChangeNotesCache> cache) {
       this.repoManager = repoManager;
       this.migration = migration;
@@ -79,7 +74,6 @@
       this.changeNoteJson = changeNoteJson;
       this.metrics = metrics;
       this.db = db;
-      this.rebuilder = rebuilder;
       this.cache = cache;
     }
   }
@@ -114,22 +108,14 @@
   }
 
   protected final Args args;
-  protected final PrimaryStorage primaryStorage;
-  protected final boolean autoRebuild;
   private final Change.Id changeId;
 
   private ObjectId revision;
   private boolean loaded;
 
-  AbstractChangeNotes(
-      Args args, Change.Id changeId, @Nullable PrimaryStorage primaryStorage, boolean autoRebuild) {
+  AbstractChangeNotes(Args args, Change.Id changeId) {
     this.args = requireNonNull(args);
     this.changeId = requireNonNull(changeId);
-    this.primaryStorage = primaryStorage;
-    this.autoRebuild =
-        primaryStorage == PrimaryStorage.REVIEW_DB
-            && !args.migration.disableChangeReviewDb()
-            && autoRebuild;
   }
 
   public Change.Id getChangeId() {
@@ -146,18 +132,7 @@
       return self();
     }
 
-    boolean read = args.migration.readChanges();
-    if (!read && primaryStorage == PrimaryStorage.NOTE_DB) {
-      throw new OrmException("NoteDb is required to read change " + changeId);
-    }
-    boolean readOrWrite = read || args.migration.rawWriteChangesSetting();
-    if (!readOrWrite) {
-      // Don't even open the repo if we neither write to nor read from NoteDb. It's possible that
-      // there is some garbage in the noteDbState field and/or the repo, but at this point NoteDb is
-      // completely off so it's none of our business.
-      loadDefaults();
-      return self();
-    }
+    checkState(args.migration.readChanges(), "NoteDb is required to read changes");
     if (args.migration.failOnLoadForTest()) {
       throw new OrmException("Reading from NoteDb is disabled");
     }
@@ -166,12 +141,8 @@
         // Call openHandle even if reading is disabled, to trigger
         // auto-rebuilding before this object may get passed to a ChangeUpdate.
         LoadHandle handle = openHandle(repo)) {
-      if (read) {
-        revision = handle.id();
-        onLoad(handle);
-      } else {
-        loadDefaults();
-      }
+      revision = handle.id();
+      onLoad(handle);
       loaded = true;
     } catch (ConfigInvalidException | IOException e) {
       throw new OrmException(e);
@@ -202,7 +173,7 @@
     return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), id);
   }
 
-  public T reload() throws NoSuchChangeException, OrmException {
+  public T reload() throws OrmException {
     loaded = false;
     return load();
   }
diff --git a/java/com/google/gerrit/server/notedb/ChangeBundle.java b/java/com/google/gerrit/server/notedb/ChangeBundle.java
deleted file mode 100644
index c4d6a91..0000000
--- a/java/com/google/gerrit/server/notedb/ChangeBundle.java
+++ /dev/null
@@ -1,976 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB;
-import static com.google.gerrit.server.util.time.TimeUtil.truncateToSecond;
-import static java.util.Comparator.comparing;
-import static java.util.Comparator.naturalOrder;
-import static java.util.Comparator.nullsFirst;
-import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Strings;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSet.Id;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.server.OrmException;
-import java.lang.reflect.Field;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * A bundle of all entities rooted at a single {@link Change} entity.
- *
- * <p>See the {@link Change} Javadoc for a depiction of this tree. Bundles may be compared using
- * {@link #differencesFrom(ChangeBundle)}, which normalizes out the minor implementation differences
- * between ReviewDb and NoteDb.
- */
-public class ChangeBundle {
-  public enum Source {
-    REVIEW_DB,
-    NOTE_DB;
-  }
-
-  public static ChangeBundle fromNotes(CommentsUtil commentsUtil, ChangeNotes notes)
-      throws OrmException {
-    return new ChangeBundle(
-        notes.getChange(),
-        notes.getChangeMessages(),
-        notes.getPatchSets().values(),
-        notes.getApprovals().values(),
-        Iterables.concat(
-            CommentsUtil.toPatchLineComments(
-                notes.getChangeId(),
-                PatchLineComment.Status.DRAFT,
-                commentsUtil.draftByChange(null, notes)),
-            CommentsUtil.toPatchLineComments(
-                notes.getChangeId(),
-                PatchLineComment.Status.PUBLISHED,
-                commentsUtil.publishedByChange(null, notes))),
-        notes.getReviewers(),
-        Source.NOTE_DB);
-  }
-
-  private static ImmutableSortedMap<ChangeMessage.Key, ChangeMessage> changeMessageMap(
-      Collection<ChangeMessage> in) {
-    return in.stream()
-        .collect(
-            toImmutableSortedMap(
-                comparing((ChangeMessage.Key k) -> k.getParentKey().get())
-                    .thenComparing(k -> k.get()),
-                cm -> cm.getKey(),
-                cm -> cm));
-  }
-
-  // Unlike the *Map comparators, which are intended to make key lists diffable,
-  // this comparator sorts first on timestamp, then on every other field.
-  private static final Comparator<ChangeMessage> CHANGE_MESSAGE_COMPARATOR =
-      comparing(ChangeMessage::getWrittenOn)
-          .thenComparing(m -> m.getKey().getParentKey().get())
-          .thenComparing(
-              m -> m.getPatchSetId() != null ? m.getPatchSetId().get() : null,
-              nullsFirst(naturalOrder()))
-          .thenComparing(ChangeMessage::getAuthor, intKeyOrdering())
-          .thenComparing(ChangeMessage::getMessage, nullsFirst(naturalOrder()));
-
-  private static ImmutableList<ChangeMessage> changeMessageList(Iterable<ChangeMessage> in) {
-    return Streams.stream(in).sorted(CHANGE_MESSAGE_COMPARATOR).collect(toImmutableList());
-  }
-
-  private static ImmutableSortedMap<Id, PatchSet> patchSetMap(Iterable<PatchSet> in) {
-    return Streams.stream(in)
-        .collect(toImmutableSortedMap(patchSetIdComparator(), PatchSet::getId, ps -> ps));
-  }
-
-  private static ImmutableSortedMap<PatchSetApproval.Key, PatchSetApproval> patchSetApprovalMap(
-      Iterable<PatchSetApproval> in) {
-    return Streams.stream(in)
-        .collect(
-            toImmutableSortedMap(
-                comparing(PatchSetApproval.Key::getParentKey, patchSetIdComparator())
-                    .thenComparing(PatchSetApproval.Key::getAccountId, intKeyOrdering())
-                    .thenComparing(PatchSetApproval.Key::getLabelId),
-                PatchSetApproval::getKey,
-                a -> a));
-  }
-
-  private static ImmutableSortedMap<PatchLineComment.Key, PatchLineComment> patchLineCommentMap(
-      Iterable<PatchLineComment> in) {
-    return Streams.stream(in)
-        .collect(
-            toImmutableSortedMap(
-                comparing(
-                        (PatchLineComment.Key k) -> k.getParentKey().getParentKey(),
-                        patchSetIdComparator())
-                    .thenComparing(PatchLineComment.Key::getParentKey)
-                    .thenComparing(PatchLineComment.Key::get),
-                PatchLineComment::getKey,
-                c -> c));
-  }
-
-  private static Comparator<PatchSet.Id> patchSetIdComparator() {
-    return comparing((PatchSet.Id id) -> id.getParentKey().get()).thenComparing(id -> id.get());
-  }
-
-  static {
-    // Initialization-time checks that the column set hasn't changed since the
-    // last time this file was updated.
-    checkColumns(Change.Id.class, 1);
-
-    checkColumns(
-        Change.class, 1, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 101);
-    checkColumns(ChangeMessage.Key.class, 1, 2);
-    checkColumns(ChangeMessage.class, 1, 2, 3, 4, 5, 6, 7);
-    checkColumns(PatchSet.Id.class, 1, 2);
-    checkColumns(PatchSet.class, 1, 2, 3, 4, 6, 8, 9);
-    checkColumns(PatchSetApproval.Key.class, 1, 2, 3);
-    checkColumns(PatchSetApproval.class, 1, 2, 3, 6, 7, 8);
-    checkColumns(PatchLineComment.Key.class, 1, 2);
-    checkColumns(PatchLineComment.class, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
-  }
-
-  private final Change change;
-  private final ImmutableList<ChangeMessage> changeMessages;
-  private final ImmutableSortedMap<PatchSet.Id, PatchSet> patchSets;
-  private final ImmutableMap<PatchSetApproval.Key, PatchSetApproval> patchSetApprovals;
-  private final ImmutableMap<PatchLineComment.Key, PatchLineComment> patchLineComments;
-  private final ReviewerSet reviewers;
-  private final Source source;
-
-  public ChangeBundle(
-      Change change,
-      Iterable<ChangeMessage> changeMessages,
-      Iterable<PatchSet> patchSets,
-      Iterable<PatchSetApproval> patchSetApprovals,
-      Iterable<PatchLineComment> patchLineComments,
-      ReviewerSet reviewers,
-      Source source) {
-    this.change = requireNonNull(change);
-    this.changeMessages = changeMessageList(changeMessages);
-    this.patchSets = ImmutableSortedMap.copyOfSorted(patchSetMap(patchSets));
-    this.patchSetApprovals = ImmutableMap.copyOf(patchSetApprovalMap(patchSetApprovals));
-    this.patchLineComments = ImmutableMap.copyOf(patchLineCommentMap(patchLineComments));
-    this.reviewers = requireNonNull(reviewers);
-    this.source = requireNonNull(source);
-
-    for (ChangeMessage m : this.changeMessages) {
-      checkArgument(m.getKey().getParentKey().equals(change.getId()));
-    }
-    for (PatchSet.Id id : this.patchSets.keySet()) {
-      checkArgument(id.getParentKey().equals(change.getId()));
-    }
-    for (PatchSetApproval.Key k : this.patchSetApprovals.keySet()) {
-      checkArgument(k.getParentKey().getParentKey().equals(change.getId()));
-    }
-    for (PatchLineComment.Key k : this.patchLineComments.keySet()) {
-      checkArgument(k.getParentKey().getParentKey().getParentKey().equals(change.getId()));
-    }
-  }
-
-  public Change getChange() {
-    return change;
-  }
-
-  public ImmutableCollection<ChangeMessage> getChangeMessages() {
-    return changeMessages;
-  }
-
-  public ImmutableCollection<PatchSet> getPatchSets() {
-    return patchSets.values();
-  }
-
-  public ImmutableCollection<PatchSetApproval> getPatchSetApprovals() {
-    return patchSetApprovals.values();
-  }
-
-  public ImmutableCollection<PatchLineComment> getPatchLineComments() {
-    return patchLineComments.values();
-  }
-
-  public ReviewerSet getReviewers() {
-    return reviewers;
-  }
-
-  public Source getSource() {
-    return source;
-  }
-
-  public ImmutableList<String> differencesFrom(ChangeBundle o) {
-    List<String> diffs = new ArrayList<>();
-    diffChanges(diffs, this, o);
-    diffChangeMessages(diffs, this, o);
-    diffPatchSets(diffs, this, o);
-    diffPatchSetApprovals(diffs, this, o);
-    diffReviewers(diffs, this, o);
-    diffPatchLineComments(diffs, this, o);
-    return ImmutableList.copyOf(diffs);
-  }
-
-  private Timestamp getFirstPatchSetTime() {
-    if (patchSets.isEmpty()) {
-      return change.getCreatedOn();
-    }
-    return patchSets.firstEntry().getValue().getCreatedOn();
-  }
-
-  private Timestamp getLatestTimestamp() {
-    Ordering<Timestamp> o = Ordering.natural().nullsFirst();
-    Timestamp ts = null;
-    for (ChangeMessage cm : filterChangeMessages()) {
-      ts = o.max(ts, cm.getWrittenOn());
-    }
-    for (PatchSet ps : getPatchSets()) {
-      ts = o.max(ts, ps.getCreatedOn());
-    }
-    for (PatchSetApproval psa : filterPatchSetApprovals().values()) {
-      ts = o.max(ts, psa.getGranted());
-    }
-    for (PatchLineComment plc : filterPatchLineComments().values()) {
-      // Ignore draft comments, as they do not show up in the change meta graph.
-      if (plc.getStatus() != PatchLineComment.Status.DRAFT) {
-        ts = o.max(ts, plc.getWrittenOn());
-      }
-    }
-    return firstNonNull(ts, change.getLastUpdatedOn());
-  }
-
-  private Map<PatchSetApproval.Key, PatchSetApproval> filterPatchSetApprovals() {
-    return limitToValidPatchSets(patchSetApprovals, PatchSetApproval.Key::getParentKey);
-  }
-
-  private Map<PatchLineComment.Key, PatchLineComment> filterPatchLineComments() {
-    return limitToValidPatchSets(patchLineComments, k -> k.getParentKey().getParentKey());
-  }
-
-  private <K, V> Map<K, V> limitToValidPatchSets(Map<K, V> in, Function<K, PatchSet.Id> func) {
-    return Maps.filterKeys(in, Predicates.compose(validPatchSetPredicate(), func));
-  }
-
-  private Predicate<PatchSet.Id> validPatchSetPredicate() {
-    return patchSets::containsKey;
-  }
-
-  private Collection<ChangeMessage> filterChangeMessages() {
-    final Predicate<PatchSet.Id> validPatchSet = validPatchSetPredicate();
-    return Collections2.filter(
-        changeMessages,
-        m -> {
-          PatchSet.Id psId = m.getPatchSetId();
-          if (psId == null) {
-            return true;
-          }
-          return validPatchSet.apply(psId);
-        });
-  }
-
-  private static void diffChanges(List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
-    Change a = bundleA.change;
-    Change b = bundleB.change;
-    String desc = a.getId().equals(b.getId()) ? describe(a.getId()) : "Changes";
-
-    boolean excludeCreatedOn = false;
-    boolean excludeCurrentPatchSetId = false;
-    boolean excludeTopic = false;
-    Timestamp aCreated = a.getCreatedOn();
-    Timestamp bCreated = b.getCreatedOn();
-    Timestamp aUpdated = a.getLastUpdatedOn();
-    Timestamp bUpdated = b.getLastUpdatedOn();
-
-    boolean excludeSubject = false;
-    boolean excludeOrigSubj = false;
-    // Subject is not technically a nullable field, but we observed some null
-    // subjects in the wild on googlesource.com, so treat null as empty.
-    String aSubj = Strings.nullToEmpty(a.getSubject());
-    String bSubj = Strings.nullToEmpty(b.getSubject());
-
-    // Allow created timestamp in NoteDb to be any of:
-    //  - The created timestamp of the change.
-    //  - The timestamp of the first remaining patch set.
-    //  - The last updated timestamp, if it is less than the created timestamp.
-    //
-    // Ignore subject if the NoteDb subject starts with the ReviewDb subject.
-    // The NoteDb subject is read directly from the commit, whereas the ReviewDb
-    // subject historically may have been truncated to fit in a SQL varchar
-    // column.
-    //
-    // Ignore original subject on the ReviewDb side when comparing to NoteDb.
-    // This field may have any number of values:
-    //  - It may be null, if the change has had no new patch sets pushed since
-    //    migrating to schema 103.
-    //  - It may match the first patch set subject, if the change was created
-    //    after migrating to schema 103.
-    //  - It may match the subject of the first patch set that was pushed after
-    //    the migration to schema 103, even though that is neither the subject
-    //    of the first patch set nor the subject of the last patch set. (See
-    //    Change#setCurrentPatchSet as of 43b10f86 for this behavior.) This
-    //    subject of an intermediate patch set is not available to the
-    //    ChangeBundle; we would have to get the subject from the repo, which is
-    //    inconvenient at this point.
-    //
-    // Ignore original subject on the ReviewDb side if it equals the subject of
-    // the current patch set.
-    //
-    // For all of the above subject comparisons, first trim any leading spaces
-    // from the NoteDb strings. (We actually do represent the leading spaces
-    // faithfully during conversion, but JGit's FooterLine parser trims them
-    // when reading.)
-    //
-    // Ignore empty topic on the ReviewDb side if it is null on the NoteDb side.
-    //
-    // Ignore currentPatchSetId on NoteDb side if ReviewDb does not point to a
-    // valid patch set.
-    //
-    // Use max timestamp of all ReviewDb entities when comparing with NoteDb.
-    if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
-      boolean createdOnMatchesFirstPs =
-          !timestampsDiffer(bundleA, bundleA.getFirstPatchSetTime(), bundleB, bCreated);
-      boolean createdOnMatchesLastUpdatedOn =
-          !timestampsDiffer(bundleA, aUpdated, bundleB, bCreated);
-      boolean createdAfterUpdated = aCreated.compareTo(aUpdated) > 0;
-      excludeCreatedOn =
-          createdOnMatchesFirstPs || (createdAfterUpdated && createdOnMatchesLastUpdatedOn);
-
-      aSubj = cleanReviewDbSubject(aSubj);
-      bSubj = cleanNoteDbSubject(bSubj);
-      excludeCurrentPatchSetId = !bundleA.validPatchSetPredicate().apply(a.currentPatchSetId());
-      excludeSubject = bSubj.startsWith(aSubj) || excludeCurrentPatchSetId;
-      excludeOrigSubj = true;
-      String aTopic = trimOrNull(a.getTopic());
-      excludeTopic =
-          Objects.equals(aTopic, b.getTopic()) || ("".equals(aTopic) && b.getTopic() == null);
-      aUpdated = bundleA.getLatestTimestamp();
-    } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
-      boolean createdOnMatchesFirstPs =
-          !timestampsDiffer(bundleA, aCreated, bundleB, bundleB.getFirstPatchSetTime());
-      boolean createdOnMatchesLastUpdatedOn =
-          !timestampsDiffer(bundleA, aCreated, bundleB, bUpdated);
-      boolean createdAfterUpdated = bCreated.compareTo(bUpdated) > 0;
-      excludeCreatedOn =
-          createdOnMatchesFirstPs || (createdAfterUpdated && createdOnMatchesLastUpdatedOn);
-
-      aSubj = cleanNoteDbSubject(aSubj);
-      bSubj = cleanReviewDbSubject(bSubj);
-      excludeCurrentPatchSetId = !bundleB.validPatchSetPredicate().apply(b.currentPatchSetId());
-      excludeSubject = aSubj.startsWith(bSubj) || excludeCurrentPatchSetId;
-      excludeOrigSubj = true;
-      String bTopic = trimOrNull(b.getTopic());
-      excludeTopic =
-          Objects.equals(bTopic, a.getTopic()) || (a.getTopic() == null && "".equals(bTopic));
-      bUpdated = bundleB.getLatestTimestamp();
-    }
-
-    String subjectField = "subject";
-    String updatedField = "lastUpdatedOn";
-    List<String> exclude =
-        Lists.newArrayList(subjectField, updatedField, "noteDbState", "rowVersion");
-    if (excludeCreatedOn) {
-      exclude.add("createdOn");
-    }
-    if (excludeCurrentPatchSetId) {
-      exclude.add("currentPatchSetId");
-    }
-    if (excludeOrigSubj) {
-      exclude.add("originalSubject");
-    }
-    if (excludeTopic) {
-      exclude.add("topic");
-    }
-    diffColumnsExcluding(diffs, Change.class, desc, bundleA, a, bundleB, b, exclude);
-
-    // Allow last updated timestamps to either be exactly equal (within slop),
-    // or the NoteDb timestamp to be equal to the latest entity timestamp in the
-    // whole ReviewDb bundle (within slop).
-    if (timestampsDiffer(bundleA, a.getLastUpdatedOn(), bundleB, b.getLastUpdatedOn())) {
-      diffTimestamps(
-          diffs, desc, bundleA, aUpdated, bundleB, bUpdated, "effective last updated time");
-    }
-    if (!excludeSubject) {
-      diffValues(diffs, desc, aSubj, bSubj, subjectField);
-    }
-  }
-
-  private static String trimOrNull(String s) {
-    return s != null ? CharMatcher.whitespace().trimFrom(s) : null;
-  }
-
-  private static String cleanReviewDbSubject(String s) {
-    s = CharMatcher.is(' ').trimLeadingFrom(s);
-
-    // An old JGit bug failed to extract subjects from commits with "\r\n"
-    // terminators: https://bugs.eclipse.org/bugs/show_bug.cgi?id=400707
-    // Changes created with this bug may have "\r\n" converted to "\r " and the
-    // entire commit in the subject. The version of JGit used to read NoteDb
-    // changes parses these subjects correctly, so we need to clean up old
-    // ReviewDb subjects before comparing.
-    int rn = s.indexOf("\r \r ");
-    if (rn >= 0) {
-      s = s.substring(0, rn);
-    }
-    return NoteDbUtil.sanitizeFooter(s);
-  }
-
-  private static String cleanNoteDbSubject(String s) {
-    return NoteDbUtil.sanitizeFooter(s);
-  }
-
-  /**
-   * Set of fields that must always exactly match between ReviewDb and NoteDb.
-   *
-   * <p>Used to limit the worst-case quadratic search when pairing off matching messages below.
-   */
-  @AutoValue
-  abstract static class ChangeMessageCandidate {
-    static ChangeMessageCandidate create(ChangeMessage cm) {
-      return new AutoValue_ChangeBundle_ChangeMessageCandidate(
-          cm.getAuthor(), cm.getMessage(), cm.getTag());
-    }
-
-    @Nullable
-    abstract Account.Id author();
-
-    @Nullable
-    abstract String message();
-
-    @Nullable
-    abstract String tag();
-
-    // Exclude:
-    //  - patch set, which may be null on ReviewDb side but not NoteDb
-    //  - UUID, which is always different between ReviewDb and NoteDb
-    //  - writtenOn, which is fuzzy
-  }
-
-  private static void diffChangeMessages(
-      List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
-    if (bundleA.source == REVIEW_DB && bundleB.source == REVIEW_DB) {
-      // Both came from ReviewDb: check all fields exactly.
-      Map<ChangeMessage.Key, ChangeMessage> as = changeMessageMap(bundleA.filterChangeMessages());
-      Map<ChangeMessage.Key, ChangeMessage> bs = changeMessageMap(bundleB.filterChangeMessages());
-
-      for (ChangeMessage.Key k : diffKeySets(diffs, as, bs)) {
-        ChangeMessage a = as.get(k);
-        ChangeMessage b = bs.get(k);
-        String desc = describe(k);
-        diffColumns(diffs, ChangeMessage.class, desc, bundleA, a, bundleB, b);
-      }
-      return;
-    }
-    Change.Id id = bundleA.getChange().getId();
-    checkArgument(id.equals(bundleB.getChange().getId()));
-
-    // Try to pair up matching ChangeMessages from each side, and succeed only
-    // if both collections are empty at the end. Quadratic in the worst case,
-    // but easy to reason about.
-    List<ChangeMessage> as = new LinkedList<>(bundleA.filterChangeMessages());
-
-    ListMultimap<ChangeMessageCandidate, ChangeMessage> bs = LinkedListMultimap.create();
-    for (ChangeMessage b : bundleB.filterChangeMessages()) {
-      bs.put(ChangeMessageCandidate.create(b), b);
-    }
-
-    Iterator<ChangeMessage> ait = as.iterator();
-    A:
-    while (ait.hasNext()) {
-      ChangeMessage a = ait.next();
-      Iterator<ChangeMessage> bit = bs.get(ChangeMessageCandidate.create(a)).iterator();
-      while (bit.hasNext()) {
-        ChangeMessage b = bit.next();
-        if (changeMessagesMatch(bundleA, a, bundleB, b)) {
-          ait.remove();
-          bit.remove();
-          continue A;
-        }
-      }
-    }
-
-    if (as.isEmpty() && bs.isEmpty()) {
-      return;
-    }
-    StringBuilder sb =
-        new StringBuilder("ChangeMessages differ for Change.Id ").append(id).append('\n');
-    if (!as.isEmpty()) {
-      sb.append("Only in A:");
-      for (ChangeMessage cm : as) {
-        sb.append("\n  ").append(cm);
-      }
-      if (!bs.isEmpty()) {
-        sb.append('\n');
-      }
-    }
-    if (!bs.isEmpty()) {
-      sb.append("Only in B:");
-      bs.values()
-          .stream()
-          .sorted(CHANGE_MESSAGE_COMPARATOR)
-          .forEach(cm -> sb.append("\n  ").append(cm));
-    }
-    diffs.add(sb.toString());
-  }
-
-  private static boolean changeMessagesMatch(
-      ChangeBundle bundleA, ChangeMessage a, ChangeBundle bundleB, ChangeMessage b) {
-    List<String> tempDiffs = new ArrayList<>();
-    String temp = "temp";
-
-    // ReviewDb allows timestamps before patch set was created, but NoteDb
-    // truncates this to the patch set creation timestamp.
-    Timestamp ta = a.getWrittenOn();
-    Timestamp tb = b.getWrittenOn();
-    PatchSet psa = bundleA.patchSets.get(a.getPatchSetId());
-    PatchSet psb = bundleB.patchSets.get(b.getPatchSetId());
-    boolean excludePatchSet = false;
-    boolean excludeWrittenOn = false;
-    if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
-      excludePatchSet = a.getPatchSetId() == null;
-      excludeWrittenOn =
-          psa != null
-              && psb != null
-              && ta.before(psa.getCreatedOn())
-              && tb.equals(psb.getCreatedOn());
-    } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
-      excludePatchSet = b.getPatchSetId() == null;
-      excludeWrittenOn =
-          psa != null
-              && psb != null
-              && tb.before(psb.getCreatedOn())
-              && ta.equals(psa.getCreatedOn());
-    }
-
-    List<String> exclude = Lists.newArrayList("key");
-    if (excludePatchSet) {
-      exclude.add("patchset");
-    }
-    if (excludeWrittenOn) {
-      exclude.add("writtenOn");
-    }
-
-    diffColumnsExcluding(tempDiffs, ChangeMessage.class, temp, bundleA, a, bundleB, b, exclude);
-    return tempDiffs.isEmpty();
-  }
-
-  private static void diffPatchSets(
-      List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
-    Map<PatchSet.Id, PatchSet> as = bundleA.patchSets;
-    Map<PatchSet.Id, PatchSet> bs = bundleB.patchSets;
-    Optional<PatchSet.Id> minA = as.keySet().stream().min(intKeyOrdering());
-    Optional<PatchSet.Id> minB = bs.keySet().stream().min(intKeyOrdering());
-    Set<PatchSet.Id> ids = diffKeySets(diffs, as, bs);
-
-    // Old versions of Gerrit had a bug that created patch sets during
-    // rebase or submission with a createdOn timestamp earlier than the patch
-    // set it was replacing. (In the cases I examined, it was equal to createdOn
-    // for the change, but we're not counting on this exact behavior.)
-    //
-    // ChangeRebuilder ensures patch set events come out in order, but it's hard
-    // to predict what the resulting timestamps would look like. So, completely
-    // ignore the createdOn timestamps if both:
-    //   * ReviewDb timestamps are non-monotonic.
-    //   * NoteDb timestamps are monotonic.
-    //
-    // Allow the timestamp of the first patch set to match the creation time of
-    // the change.
-    boolean excludeAllCreatedOn = false;
-    if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
-      excludeAllCreatedOn = !createdOnIsMonotonic(as, ids) && createdOnIsMonotonic(bs, ids);
-    } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
-      excludeAllCreatedOn = createdOnIsMonotonic(as, ids) && !createdOnIsMonotonic(bs, ids);
-    }
-
-    for (PatchSet.Id id : ids) {
-      PatchSet a = as.get(id);
-      PatchSet b = bs.get(id);
-      String desc = describe(id);
-      String pushCertField = "pushCertificate";
-
-      boolean excludeCreatedOn = excludeAllCreatedOn;
-      boolean excludeDesc = false;
-      if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
-        excludeDesc = Objects.equals(trimOrNull(a.getDescription()), b.getDescription());
-        excludeCreatedOn |=
-            Optional.of(id).equals(minB) && b.getCreatedOn().equals(bundleB.change.getCreatedOn());
-      } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
-        excludeDesc = Objects.equals(a.getDescription(), trimOrNull(b.getDescription()));
-        excludeCreatedOn |=
-            Optional.of(id).equals(minA) && a.getCreatedOn().equals(bundleA.change.getCreatedOn());
-      }
-
-      List<String> exclude = Lists.newArrayList(pushCertField);
-      if (excludeCreatedOn) {
-        exclude.add("createdOn");
-      }
-      if (excludeDesc) {
-        exclude.add("description");
-      }
-
-      diffColumnsExcluding(diffs, PatchSet.class, desc, bundleA, a, bundleB, b, exclude);
-      diffValues(diffs, desc, trimPushCert(a), trimPushCert(b), pushCertField);
-    }
-  }
-
-  private static String trimPushCert(PatchSet ps) {
-    if (ps.getPushCertificate() == null) {
-      return null;
-    }
-    return CharMatcher.is('\n').trimTrailingFrom(ps.getPushCertificate());
-  }
-
-  private static boolean createdOnIsMonotonic(
-      Map<?, PatchSet> patchSets, Set<PatchSet.Id> limitToIds) {
-    List<PatchSet> orderedById =
-        patchSets
-            .values()
-            .stream()
-            .filter(ps -> limitToIds.contains(ps.getId()))
-            .sorted(ChangeUtil.PS_ID_ORDER)
-            .collect(toList());
-    return Ordering.natural().onResultOf(PatchSet::getCreatedOn).isOrdered(orderedById);
-  }
-
-  private static void diffPatchSetApprovals(
-      List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
-    Map<PatchSetApproval.Key, PatchSetApproval> as = bundleA.filterPatchSetApprovals();
-    Map<PatchSetApproval.Key, PatchSetApproval> bs = bundleB.filterPatchSetApprovals();
-    for (PatchSetApproval.Key k : diffKeySets(diffs, as, bs)) {
-      PatchSetApproval a = as.get(k);
-      PatchSetApproval b = bs.get(k);
-      String desc = describe(k);
-
-      // ReviewDb allows timestamps before patch set was created, but NoteDb
-      // truncates this to the patch set creation timestamp.
-      //
-      // ChangeRebuilder ensures all post-submit approvals happen after the
-      // actual submit, so the timestamps may not line up. This shouldn't really
-      // happen, because postSubmit shouldn't be set in ReviewDb until after the
-      // change is submitted in ReviewDb, but you never know.
-      //
-      // Due to a quirk of PostReview, post-submit 0 votes might not have the
-      // postSubmit bit set in ReviewDb. As these are only used for tombstone
-      // purposes, ignore the postSubmit bit in NoteDb in this case.
-      Timestamp ta = a.getGranted();
-      Timestamp tb = b.getGranted();
-      PatchSet psa = requireNonNull(bundleA.patchSets.get(a.getPatchSetId()));
-      PatchSet psb = requireNonNull(bundleB.patchSets.get(b.getPatchSetId()));
-      boolean excludeGranted = false;
-      boolean excludePostSubmit = false;
-      List<String> exclude = new ArrayList<>(1);
-      if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
-        excludeGranted =
-            (ta.before(psa.getCreatedOn()) && tb.equals(psb.getCreatedOn()))
-                || ta.compareTo(tb) < 0;
-        excludePostSubmit = a.getValue() == 0 && b.isPostSubmit();
-      } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
-        excludeGranted =
-            (tb.before(psb.getCreatedOn()) && ta.equals(psa.getCreatedOn()))
-                || (tb.compareTo(ta) < 0);
-        excludePostSubmit = b.getValue() == 0 && a.isPostSubmit();
-      }
-
-      // Legacy submit approvals may or may not have tags associated with them,
-      // depending on whether ChangeRebuilder happened to group them with the
-      // status change.
-      boolean excludeTag =
-          bundleA.source != bundleB.source && a.isLegacySubmit() && b.isLegacySubmit();
-
-      if (excludeGranted) {
-        exclude.add("granted");
-      }
-      if (excludePostSubmit) {
-        exclude.add("postSubmit");
-      }
-      if (excludeTag) {
-        exclude.add("tag");
-      }
-
-      diffColumnsExcluding(diffs, PatchSetApproval.class, desc, bundleA, a, bundleB, b, exclude);
-    }
-  }
-
-  private static void diffReviewers(
-      List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
-    diffSets(diffs, bundleA.reviewers.all(), bundleB.reviewers.all(), "reviewer");
-  }
-
-  private static void diffPatchLineComments(
-      List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
-    Map<PatchLineComment.Key, PatchLineComment> as = bundleA.filterPatchLineComments();
-    Map<PatchLineComment.Key, PatchLineComment> bs = bundleB.filterPatchLineComments();
-    for (PatchLineComment.Key k : diffKeySets(diffs, as, bs)) {
-      PatchLineComment a = as.get(k);
-      PatchLineComment b = bs.get(k);
-      String desc = describe(k);
-      diffColumns(diffs, PatchLineComment.class, desc, bundleA, a, bundleB, b);
-    }
-  }
-
-  private static <T> Set<T> diffKeySets(List<String> diffs, Map<T, ?> a, Map<T, ?> b) {
-    if (a.isEmpty() && b.isEmpty()) {
-      return a.keySet();
-    }
-    String clazz = keyClass((!a.isEmpty() ? a.keySet() : b.keySet()).iterator().next());
-    return diffSets(diffs, a.keySet(), b.keySet(), clazz);
-  }
-
-  private static <T> Set<T> diffSets(List<String> diffs, Set<T> as, Set<T> bs, String desc) {
-    if (as.isEmpty() && bs.isEmpty()) {
-      return as;
-    }
-
-    Set<T> aNotB = Sets.difference(as, bs);
-    Set<T> bNotA = Sets.difference(bs, as);
-    if (aNotB.isEmpty() && bNotA.isEmpty()) {
-      return as;
-    }
-    diffs.add(desc + " sets differ: " + aNotB + " only in A; " + bNotA + " only in B");
-    return Sets.intersection(as, bs);
-  }
-
-  private static <T> void diffColumns(
-      List<String> diffs,
-      Class<T> clazz,
-      String desc,
-      ChangeBundle bundleA,
-      T a,
-      ChangeBundle bundleB,
-      T b) {
-    diffColumnsExcluding(diffs, clazz, desc, bundleA, a, bundleB, b);
-  }
-
-  private static <T> void diffColumnsExcluding(
-      List<String> diffs,
-      Class<T> clazz,
-      String desc,
-      ChangeBundle bundleA,
-      T a,
-      ChangeBundle bundleB,
-      T b,
-      String... exclude) {
-    diffColumnsExcluding(diffs, clazz, desc, bundleA, a, bundleB, b, Arrays.asList(exclude));
-  }
-
-  private static <T> void diffColumnsExcluding(
-      List<String> diffs,
-      Class<T> clazz,
-      String desc,
-      ChangeBundle bundleA,
-      T a,
-      ChangeBundle bundleB,
-      T b,
-      Iterable<String> exclude) {
-    Set<String> toExclude = Sets.newLinkedHashSet(exclude);
-    for (Field f : clazz.getDeclaredFields()) {
-      Column col = f.getAnnotation(Column.class);
-      if (col == null) {
-        continue;
-      } else if (toExclude.remove(f.getName())) {
-        continue;
-      }
-      f.setAccessible(true);
-      try {
-        if (Timestamp.class.isAssignableFrom(f.getType())) {
-          diffTimestamps(diffs, desc, bundleA, a, bundleB, b, f.getName());
-        } else {
-          diffValues(diffs, desc, f.get(a), f.get(b), f.getName());
-        }
-      } catch (IllegalAccessException e) {
-        throw new IllegalArgumentException(e);
-      }
-    }
-    checkArgument(
-        toExclude.isEmpty(),
-        "requested columns to exclude not present in %s: %s",
-        clazz.getSimpleName(),
-        toExclude);
-  }
-
-  private static void diffTimestamps(
-      List<String> diffs,
-      String desc,
-      ChangeBundle bundleA,
-      Object a,
-      ChangeBundle bundleB,
-      Object b,
-      String field) {
-    checkArgument(a.getClass() == b.getClass());
-    Class<?> clazz = a.getClass();
-
-    Timestamp ta;
-    Timestamp tb;
-    try {
-      Field f = clazz.getDeclaredField(field);
-      checkArgument(f.getAnnotation(Column.class) != null);
-      f.setAccessible(true);
-      ta = (Timestamp) f.get(a);
-      tb = (Timestamp) f.get(b);
-    } catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
-      throw new IllegalArgumentException(e);
-    }
-    diffTimestamps(diffs, desc, bundleA, ta, bundleB, tb, field);
-  }
-
-  private static void diffTimestamps(
-      List<String> diffs,
-      String desc,
-      ChangeBundle bundleA,
-      Timestamp ta,
-      ChangeBundle bundleB,
-      Timestamp tb,
-      String fieldDesc) {
-    if (bundleA.source == bundleB.source || ta == null || tb == null) {
-      diffValues(diffs, desc, ta, tb, fieldDesc);
-    } else if (bundleA.source == NOTE_DB) {
-      diffTimestamps(diffs, desc, bundleA.getChange(), ta, bundleB.getChange(), tb, fieldDesc);
-    } else {
-      diffTimestamps(diffs, desc, bundleB.getChange(), tb, bundleA.getChange(), ta, fieldDesc);
-    }
-  }
-
-  private static boolean timestampsDiffer(
-      ChangeBundle bundleA, Timestamp ta, ChangeBundle bundleB, Timestamp tb) {
-    List<String> tempDiffs = new ArrayList<>(1);
-    diffTimestamps(tempDiffs, "temp", bundleA, ta, bundleB, tb, "temp");
-    return !tempDiffs.isEmpty();
-  }
-
-  private static void diffTimestamps(
-      List<String> diffs,
-      String desc,
-      Change changeFromNoteDb,
-      Timestamp tsFromNoteDb,
-      Change changeFromReviewDb,
-      Timestamp tsFromReviewDb,
-      String field) {
-    // Because ChangeRebuilder may batch events together that are several
-    // seconds apart, the timestamp in NoteDb may actually be several seconds
-    // *earlier* than the timestamp in ReviewDb that it was converted from.
-    checkArgument(
-        tsFromNoteDb.equals(truncateToSecond(tsFromNoteDb)),
-        "%s from NoteDb has non-rounded %s timestamp: %s",
-        desc,
-        field,
-        tsFromNoteDb);
-
-    if (tsFromReviewDb.before(changeFromReviewDb.getCreatedOn())
-        && tsFromNoteDb.equals(changeFromNoteDb.getCreatedOn())) {
-      // Timestamp predates change creation. These are truncated to change
-      // creation time during NoteDb conversion, so allow this if the timestamp
-      // in NoteDb matches the createdOn time in NoteDb.
-      return;
-    }
-
-    long delta = tsFromReviewDb.getTime() - tsFromNoteDb.getTime();
-    long max = ChangeRebuilderImpl.MAX_WINDOW_MS;
-    if (delta < 0 || delta > max) {
-      diffs.add(
-          field
-              + " differs for "
-              + desc
-              + " in NoteDb vs. ReviewDb:"
-              + " {"
-              + tsFromNoteDb
-              + "} != {"
-              + tsFromReviewDb
-              + "}");
-    }
-  }
-
-  private static void diffValues(
-      List<String> diffs, String desc, Object va, Object vb, String name) {
-    if (!Objects.equals(va, vb)) {
-      diffs.add(name + " differs for " + desc + ": {" + va + "} != {" + vb + "}");
-    }
-  }
-
-  private static String describe(Object key) {
-    return keyClass(key) + " " + key;
-  }
-
-  private static String keyClass(Object obj) {
-    Class<?> clazz = obj.getClass();
-    String name = clazz.getSimpleName();
-    checkArgument(name.endsWith("Key") || name.endsWith("Id"), "not an Id/Key class: %s", name);
-    if (name.equals("Key") || name.equals("Id")) {
-      return clazz.getEnclosingClass().getSimpleName() + "." + name;
-    } else if (name.startsWith("AutoValue_")) {
-      return name.substring(name.lastIndexOf('_') + 1);
-    }
-    return name;
-  }
-
-  @Override
-  public String toString() {
-    return getClass().getSimpleName()
-        + "{id="
-        + change.getId()
-        + ", ChangeMessage["
-        + changeMessages.size()
-        + "]"
-        + ", PatchSet["
-        + patchSets.size()
-        + "]"
-        + ", PatchSetApproval["
-        + patchSetApprovals.size()
-        + "]"
-        + ", PatchLineComment["
-        + patchLineComments.size()
-        + "]"
-        + "}";
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/ChangeBundleReader.java b/java/com/google/gerrit/server/notedb/ChangeBundleReader.java
deleted file mode 100644
index 3207c3b..0000000
--- a/java/com/google/gerrit/server/notedb/ChangeBundleReader.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-
-public interface ChangeBundleReader {
-  @Nullable
-  ChangeBundle fromReviewDb(ReviewDb db, Change.Id id) throws OrmException;
-}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index d2942dc..e5e0d51 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -17,7 +17,6 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
 import static java.util.Comparator.comparing;
 import static java.util.Objects.requireNonNull;
 
@@ -28,18 +27,15 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Iterators;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Ordering;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
-import com.google.common.collect.Streams;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.metrics.Timer1;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -57,9 +53,7 @@
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.ReviewerStatusUpdate;
 import com.google.gerrit.server.git.RefCache;
-import com.google.gerrit.server.git.RepoRefCache;
 import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.query.change.ChangeData;
@@ -76,8 +70,6 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -186,12 +178,6 @@
       return new ChangeNotes(args, loadChangeFromDb(db, project, changeId)).load();
     }
 
-    public ChangeNotes createWithAutoRebuildingDisabled(
-        ReviewDb db, Project.NameKey project, Change.Id changeId) throws OrmException {
-      return new ChangeNotes(args, loadChangeFromDb(db, project, changeId), true, false, null)
-          .load();
-    }
-
     /**
      * Create change notes for a change that was loaded from index. This method should only be used
      * when database access is harmful and potentially stale data from the index is acceptable.
@@ -205,12 +191,11 @@
 
     public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist)
         throws OrmException {
-      return new ChangeNotes(args, change, shouldExist, false, null).load();
+      return new ChangeNotes(args, change, shouldExist, null).load();
     }
 
-    public ChangeNotes createWithAutoRebuildingDisabled(Change change, RefCache refs)
-        throws OrmException {
-      return new ChangeNotes(args, change, true, false, refs).load();
+    public ChangeNotes create(Change change, RefCache refs) throws OrmException {
+      return new ChangeNotes(args, change, true, refs).load();
     }
 
     // TODO(ekempin): Remove when database backend is deleted
@@ -285,7 +270,7 @@
       if (args.migration.readChanges()) {
         for (Project.NameKey project : projectCache.all()) {
           try (Repository repo = args.repoManager.openRepository(project)) {
-            scanNoteDb(repo, db, project)
+            scan(repo, project)
                 .filter(r -> !r.error().isPresent())
                 .map(ChangeNotesResult::notes)
                 .filter(predicate)
@@ -303,79 +288,23 @@
       return ImmutableListMultimap.copyOf(m);
     }
 
-    public Stream<ChangeNotesResult> scan(Repository repo, ReviewDb db, Project.NameKey project)
+    public Stream<ChangeNotesResult> scan(Repository repo, Project.NameKey project)
         throws IOException {
-      return args.migration.readChanges() ? scanNoteDb(repo, db, project) : scanReviewDb(repo, db);
-    }
-
-    private Stream<ChangeNotesResult> scanReviewDb(Repository repo, ReviewDb db)
-        throws IOException {
-      // Scan IDs that might exist in ReviewDb, assuming that each change has at least one patch set
-      // ref. Not all changes might exist: some patch set refs might have been written where the
-      // corresponding ReviewDb write failed. These will be silently filtered out by the batch get
-      // call below, which is intended.
-      Set<Change.Id> ids = scanChangeIds(repo).fromPatchSetRefs();
-
-      // A batch size of N may overload get(Iterable), so use something smaller, but still >1.
-      return Streams.stream(Iterators.partition(ids.iterator(), 30))
-          .flatMap(
-              batch -> {
-                try {
-                  return Streams.stream(ReviewDbUtil.unwrapDb(db).changes().get(batch))
-                      .map(this::toResult)
-                      .filter(Objects::nonNull);
-                } catch (OrmException e) {
-                  // Return this error for each Id in the input batch.
-                  return batch.stream().map(id -> ChangeNotesResult.error(id, e));
-                }
-              });
-    }
-
-    private Stream<ChangeNotesResult> scanNoteDb(
-        Repository repo, ReviewDb db, Project.NameKey project) throws IOException {
       ScanResult sr = scanChangeIds(repo);
-      PrimaryStorage defaultStorage = args.migration.changePrimaryStorage();
 
-      return sr.all()
-          .stream()
-          .map(id -> scanOneNoteDbChange(db, project, sr, defaultStorage, id))
-          .filter(Objects::nonNull);
+      return sr.all().stream().map(id -> scanOneChange(project, sr, id)).filter(Objects::nonNull);
     }
 
-    private ChangeNotesResult scanOneNoteDbChange(
-        ReviewDb db,
-        Project.NameKey project,
-        ScanResult sr,
-        PrimaryStorage defaultStorage,
-        Change.Id id) {
-      Change change;
-      try {
-        change = readOneReviewDbChange(db, id);
-      } catch (OrmException e) {
-        return ChangeNotesResult.error(id, e);
-      }
-
-      if (change == null) {
-        if (!sr.fromMetaRefs().contains(id)) {
-          // Stray patch set refs can happen due to normal error conditions, e.g. failed
-          // push processing, so aren't worth even a warning.
-          return null;
-        }
-        if (defaultStorage == PrimaryStorage.REVIEW_DB) {
-          // If changes should exist in ReviewDb, it's worth warning about a meta ref with
-          // no corresponding ReviewDb data.
-          logger.atWarning().log(
-              "skipping change %s found in project %s but not in ReviewDb", id, project);
-          return null;
-        }
-        // TODO(dborowitz): See discussion in NoteDbBatchUpdate#newChangeContext.
-        change = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
-      } else if (!change.getProject().equals(project)) {
-        logger.atSevere().log(
-            "skipping change %s found in project %s because ReviewDb change has project %s",
-            id, project, change.getProject());
+    private ChangeNotesResult scanOneChange(Project.NameKey project, ScanResult sr, Change.Id id) {
+      if (!sr.fromMetaRefs().contains(id)) {
+        // Stray patch set refs can happen due to normal error conditions, e.g. failed
+        // push processing, so aren't worth even a warning.
         return null;
       }
+
+      // TODO(dborowitz): See discussion in NoteDbBatchUpdate#newChangeContext.
+      Change change = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
+
       logger.atFine().log("adding change %s found in project %s", id, project);
       return toResult(change);
     }
@@ -391,7 +320,7 @@
       return ChangeNotesResult.notes(n);
     }
 
-    /** Result of {@link #scan(Repository, ReviewDb, Project.NameKey)}. */
+    /** Result of {@link #scan(Repository,Project.NameKey)}. */
     @AutoValue
     public abstract static class ChangeNotesResult {
       static ChangeNotesResult error(Change.Id id, OrmException e) {
@@ -459,7 +388,6 @@
   // notes easier.
   RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
 
-  private NoteDbUpdateManager.Result rebuildResult;
   private DraftCommentNotes draftCommentNotes;
   private RobotCommentNotes robotCommentNotes;
 
@@ -471,12 +399,11 @@
 
   @VisibleForTesting
   public ChangeNotes(Args args, Change change) {
-    this(args, change, true, true, null);
+    this(args, change, true, null);
   }
 
-  private ChangeNotes(
-      Args args, Change change, boolean shouldExist, boolean autoRebuild, @Nullable RefCache refs) {
-    super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
+  private ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
+    super(args, change.getId());
     this.change = new Change(change);
     this.shouldExist = shouldExist;
     this.refs = refs;
@@ -609,8 +536,7 @@
    */
   private void loadDraftComments(Account.Id author, @Nullable Ref ref) throws OrmException {
     if (draftCommentNotes == null || !author.equals(draftCommentNotes.getAuthor()) || ref != null) {
-      draftCommentNotes =
-          new DraftCommentNotes(args, change, author, autoRebuild, rebuildResult, ref);
+      draftCommentNotes = new DraftCommentNotes(args, getChangeId(), author, ref);
       draftCommentNotes.load();
     }
   }
@@ -665,8 +591,7 @@
   }
 
   @Override
-  protected void onLoad(LoadHandle handle)
-      throws NoSuchChangeException, IOException, ConfigInvalidException {
+  protected void onLoad(LoadHandle handle) throws NoSuchChangeException, IOException {
     ObjectId rev = handle.id();
     if (rev == null) {
       if (args.migration.readChanges()
@@ -699,89 +624,4 @@
   protected ObjectId readRef(Repository repo) throws IOException {
     return refs != null ? refs.get(getRefName()).orElse(null) : super.readRef(repo);
   }
-
-  @Override
-  protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
-    if (autoRebuild) {
-      NoteDbChangeState state = NoteDbChangeState.parse(change);
-      if (args.migration.disableChangeReviewDb()) {
-        checkState(
-            state != null,
-            "shouldn't have null NoteDbChangeState when ReviewDb disabled: %s",
-            change);
-      }
-      ObjectId id = readRef(repo);
-      if (id == null) {
-        // Meta ref doesn't exist in NoteDb.
-
-        if (state == null) {
-          // Either ReviewDb change is being newly created, or it exists in ReviewDb but has not yet
-          // been rebuilt for the first time, e.g. because we just turned on write-only mode. In
-          // both cases, we don't want to auto-rebuild, just proceed with an empty ChangeNotes.
-          return super.openHandle(repo, id);
-        } else if (shouldExist && state.getPrimaryStorage() == PrimaryStorage.NOTE_DB) {
-          throw new NoSuchChangeException(getChangeId());
-        }
-
-        // ReviewDb claims NoteDb state exists, but meta ref isn't present: fall through and
-        // auto-rebuild if necessary.
-      }
-      RefCache refs = this.refs != null ? this.refs : new RepoRefCache(repo);
-      if (!NoteDbChangeState.isChangeUpToDate(state, refs, getChangeId())) {
-        return rebuildAndOpen(repo, id);
-      }
-    }
-    return super.openHandle(repo);
-  }
-
-  private LoadHandle rebuildAndOpen(Repository repo, ObjectId oldId) throws IOException {
-    Timer1.Context timer = args.metrics.autoRebuildLatency.start(CHANGES);
-    try {
-      Change.Id cid = getChangeId();
-      ReviewDb db = args.db.get();
-      ChangeRebuilder rebuilder = args.rebuilder.get();
-      NoteDbUpdateManager.Result r;
-      try (NoteDbUpdateManager manager = rebuilder.stage(db, cid)) {
-        if (manager == null) {
-          return super.openHandle(repo, oldId); // May be null in tests.
-        }
-        manager.setRefLogMessage("Auto-rebuilding change");
-        r = manager.stageAndApplyDelta(change);
-        try {
-          rebuilder.execute(db, cid, manager);
-          repo.scanForRepoChanges();
-        } catch (OrmException | IOException e) {
-          // Rebuilding failed. Most likely cause is contention on one or more
-          // change refs; there are other types of errors that can happen during
-          // rebuilding, but generally speaking they should happen during stage(),
-          // not execute(). Assume that some other worker is going to successfully
-          // store the rebuilt state, which is deterministic given an input
-          // ChangeBundle.
-          //
-          // Parse notes from the staged result so we can return something useful
-          // to the caller instead of throwing.
-          logger.atFine().log("Rebuilding change %s failed: %s", getChangeId(), e.getMessage());
-          args.metrics.autoRebuildFailureCount.increment(CHANGES);
-          rebuildResult = requireNonNull(r);
-          requireNonNull(r.newState());
-          requireNonNull(r.staged());
-          requireNonNull(r.staged().changeObjects());
-          return LoadHandle.create(
-              ChangeNotesCommit.newStagedRevWalk(repo, r.staged().changeObjects()),
-              r.newState().getChangeMetaId());
-        }
-      }
-      return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), r.newState().getChangeMetaId());
-    } catch (NoSuchChangeException e) {
-      return super.openHandle(repo, oldId);
-    } catch (OrmException e) {
-      throw new IOException(e);
-    } finally {
-      logger.atFine().log(
-          "Rebuilt change %s in project %s in %s ms",
-          getChangeId(),
-          getProjectName(),
-          TimeUnit.MILLISECONDS.convert(timer.stop(), TimeUnit.NANOSECONDS));
-    }
-  }
 }
diff --git a/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java b/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
index e122754..8720c36 100644
--- a/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
+++ b/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.server.UsedAt;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.GerritServerIdProvider;
 import com.google.inject.Inject;
@@ -50,6 +51,7 @@
 import org.eclipse.jgit.transport.ReceiveCommand;
 import org.eclipse.jgit.util.MutableInteger;
 
+@UsedAt(UsedAt.Project.GOOGLE)
 @Singleton
 public class CommentJsonMigrator {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 8acc8d4..2523c2c 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.gerrit.reviewdb.client.RefNames.refsDraftComments;
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -25,24 +24,15 @@
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.metrics.Timer1;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.StagedResult;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import java.io.IOException;
-import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
@@ -50,53 +40,29 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.notes.NoteMap;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
 
 /** View of the draft comments for a single {@link Change} based on the log of its drafts branch. */
 public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   public interface Factory {
-    DraftCommentNotes create(Change change, Account.Id accountId);
-
-    DraftCommentNotes createWithAutoRebuildingDisabled(Change.Id changeId, Account.Id accountId);
+    DraftCommentNotes create(Change.Id changeId, Account.Id accountId);
   }
 
-  private final Change change;
   private final Account.Id author;
-  private final NoteDbUpdateManager.Result rebuildResult;
   private final Ref ref;
 
   private ImmutableListMultimap<RevId, Comment> comments;
   private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
 
   @AssistedInject
-  DraftCommentNotes(Args args, @Assisted Change change, @Assisted Account.Id author) {
-    this(args, change, author, true, null, null);
-  }
-
-  @AssistedInject
   DraftCommentNotes(Args args, @Assisted Change.Id changeId, @Assisted Account.Id author) {
-    // PrimaryStorage is unknown; this should only called by
-    // PatchLineCommentsUtil#draftByAuthor, which can live with this.
-    super(args, changeId, null, false);
-    this.change = null;
-    this.author = author;
-    this.rebuildResult = null;
-    this.ref = null;
+    this(args, changeId, author, null);
   }
 
-  DraftCommentNotes(
-      Args args,
-      Change change,
-      Account.Id author,
-      boolean autoRebuild,
-      @Nullable NoteDbUpdateManager.Result rebuildResult,
-      @Nullable Ref ref) {
-    super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
-    this.change = change;
-    this.author = author;
-    this.rebuildResult = rebuildResult;
+  DraftCommentNotes(Args args, Change.Id changeId, Account.Id author, @Nullable Ref ref) {
+    super(args, changeId);
+    this.author = requireNonNull(author);
     this.ref = ref;
     if (ref != null) {
       checkArgument(
@@ -181,79 +147,6 @@
     return args.allUsers;
   }
 
-  @Override
-  protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
-    if (rebuildResult != null) {
-      StagedResult sr = requireNonNull(rebuildResult.staged());
-      return LoadHandle.create(
-          ChangeNotesCommit.newStagedRevWalk(repo, sr.allUsersObjects()),
-          findNewId(sr.allUsersCommands(), getRefName()));
-    } else if (change != null && autoRebuild) {
-      NoteDbChangeState state = NoteDbChangeState.parse(change);
-      // Only check if this particular user's drafts are up to date, to avoid
-      // reading unnecessary refs.
-      if (!NoteDbChangeState.areDraftsUpToDate(
-          state, new RepoRefCache(repo), getChangeId(), author)) {
-        return rebuildAndOpen(repo);
-      }
-    }
-    return super.openHandle(repo);
-  }
-
-  private static ObjectId findNewId(Iterable<ReceiveCommand> cmds, String refName) {
-    for (ReceiveCommand cmd : cmds) {
-      if (cmd.getRefName().equals(refName)) {
-        return cmd.getNewId();
-      }
-    }
-    return null;
-  }
-
-  private LoadHandle rebuildAndOpen(Repository repo) throws NoSuchChangeException, IOException {
-    Timer1.Context timer = args.metrics.autoRebuildLatency.start(CHANGES);
-    try {
-      Change.Id cid = getChangeId();
-      ReviewDb db = args.db.get();
-      ChangeRebuilder rebuilder = args.rebuilder.get();
-      NoteDbUpdateManager.Result r;
-      try (NoteDbUpdateManager manager = rebuilder.stage(db, cid)) {
-        if (manager == null) {
-          return super.openHandle(repo); // May be null in tests.
-        }
-        r = manager.stageAndApplyDelta(change);
-        try {
-          rebuilder.execute(db, cid, manager);
-          repo.scanForRepoChanges();
-        } catch (OrmException | IOException e) {
-          // See ChangeNotes#rebuildAndOpen.
-          logger.atFine().log(
-              "Rebuilding change %s via drafts failed: %s", getChangeId(), e.getMessage());
-          args.metrics.autoRebuildFailureCount.increment(CHANGES);
-          requireNonNull(r.staged());
-          return LoadHandle.create(
-              ChangeNotesCommit.newStagedRevWalk(repo, r.staged().allUsersObjects()), draftsId(r));
-        }
-      }
-      return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), draftsId(r));
-    } catch (NoSuchChangeException e) {
-      return super.openHandle(repo);
-    } catch (OrmException e) {
-      throw new IOException(e);
-    } finally {
-      logger.atFine().log(
-          "Rebuilt change %s in %s in %s ms via drafts",
-          getChangeId(),
-          change != null ? "project " + change.getProject() : "unknown project",
-          TimeUnit.MILLISECONDS.convert(timer.stop(), TimeUnit.NANOSECONDS));
-    }
-  }
-
-  private ObjectId draftsId(NoteDbUpdateManager.Result r) {
-    requireNonNull(r);
-    requireNonNull(r.newState());
-    return r.newState().getDraftIds().get(author);
-  }
-
   @VisibleForTesting
   NoteMap getNoteMap() {
     return revisionNoteMap != null ? revisionNoteMap.noteMap : null;
diff --git a/java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java b/java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java
deleted file mode 100644
index 347ba48..0000000
--- a/java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.notedb.ChangeBundle.Source;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.util.List;
-
-@Singleton
-public class GwtormChangeBundleReader implements ChangeBundleReader {
-  @Inject
-  GwtormChangeBundleReader() {}
-
-  @Override
-  @Nullable
-  public ChangeBundle fromReviewDb(ReviewDb db, Change.Id id) throws OrmException {
-    Change reviewDbChange = db.changes().get(id);
-    if (reviewDbChange == null) {
-      return null;
-    }
-
-    // TODO(dborowitz): Figure out how to do this more consistently, e.g. hand-written inner joins.
-    List<PatchSetApproval> approvals = db.patchSetApprovals().byChange(id).toList();
-    return new ChangeBundle(
-        reviewDbChange,
-        db.changeMessages().byChange(id),
-        db.patchSets().byChange(id),
-        approvals,
-        db.patchComments().byChange(id),
-        ReviewerSet.fromApprovals(approvals),
-        Source.REVIEW_DB);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/MutableNotesMigration.java b/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
index 7f4912b..eb41cbc 100644
--- a/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
+++ b/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.notedb;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.inject.Inject;
@@ -79,24 +80,18 @@
   /**
    * Set the in-memory values returned by this instance to match the given state.
    *
-   * <p>This method is only intended for use by {@link
-   * com.google.gerrit.server.notedb.rebuild.NoteDbMigrator}.
+   * <p>This method is only intended for use by tests.
    *
    * <p>This <em>only</em> modifies the in-memory state; if this instance was initialized from a
    * file-based config, the underlying storage is not updated. Callers are responsible for managing
    * the underlying storage on their own.
    */
+  @VisibleForTesting
   public MutableNotesMigration setFrom(NotesMigrationState state) {
     snapshot.set(state.snapshot());
     return this;
   }
 
-  /** @see #setFrom(NotesMigrationState) */
-  public MutableNotesMigration setFrom(NotesMigration other) {
-    snapshot.set(other.snapshot.get());
-    return this;
-  }
-
   private MutableNotesMigration set(Function<Snapshot.Builder, Snapshot.Builder> f) {
     snapshot.updateAndGet(s -> f.apply(s.toBuilder()).build());
     return this;
diff --git a/java/com/google/gerrit/server/notedb/NoteDbModule.java b/java/com/google/gerrit/server/notedb/NoteDbModule.java
index c76c39b..cf12d84 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbModule.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbModule.java
@@ -17,33 +17,21 @@
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
 import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Id;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gerrit.server.notedb.rebuild.NotesMigrationStateListener;
 import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
-import org.eclipse.jgit.lib.Config;
 
 public class NoteDbModule extends FactoryModule {
-  private final Config cfg;
   private final boolean useTestBindings;
 
-  static NoteDbModule forTest(Config cfg) {
-    return new NoteDbModule(cfg, true);
+  static NoteDbModule forTest() {
+    return new NoteDbModule(true);
   }
 
-  public NoteDbModule(Config cfg) {
-    this(cfg, false);
+  public NoteDbModule() {
+    this(false);
   }
 
-  private NoteDbModule(Config cfg, boolean useTestBindings) {
-    this.cfg = cfg;
+  private NoteDbModule(boolean useTestBindings) {
     this.useTestBindings = useTestBindings;
   }
 
@@ -56,57 +44,10 @@
     factory(NoteDbUpdateManager.Factory.class);
     factory(RobotCommentNotes.Factory.class);
     factory(RobotCommentUpdate.Factory.class);
-    DynamicSet.setOf(binder(), NotesMigrationStateListener.class);
 
     if (!useTestBindings) {
       install(ChangeNotesCache.module());
-      if (cfg.getBoolean("noteDb", null, "testRebuilderWrapper", false)) {
-        // Yes, another variety of test bindings with a different way of
-        // configuring it.
-        bind(ChangeRebuilder.class).to(TestChangeRebuilderWrapper.class);
-      } else {
-        bind(ChangeRebuilder.class).to(ChangeRebuilderImpl.class);
-      }
     } else {
-      bind(ChangeRebuilder.class)
-          .toInstance(
-              new ChangeRebuilder(null) {
-                @Override
-                public Result rebuild(ReviewDb db, Change.Id changeId) {
-                  return null;
-                }
-
-                @Override
-                public Result rebuildEvenIfReadOnly(ReviewDb db, Id changeId) {
-                  return null;
-                }
-
-                @Override
-                public Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle) {
-                  return null;
-                }
-
-                @Override
-                public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId) {
-                  return null;
-                }
-
-                @Override
-                public Result execute(
-                    ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager) {
-                  return null;
-                }
-
-                @Override
-                public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle) {
-                  // Do nothing.
-                }
-
-                @Override
-                public void rebuildReviewDb(ReviewDb db, Project.NameKey project, Id changeId) {
-                  // Do nothing.
-                }
-              });
       bind(new TypeLiteral<Cache<ChangeNotesCache.Key, ChangeNotesState>>() {})
           .annotatedWith(Names.named(ChangeNotesCache.CACHE_NAME))
           .toInstance(CacheBuilder.newBuilder().<ChangeNotesCache.Key, ChangeNotesState>build());
diff --git a/java/com/google/gerrit/server/notedb/NotesMigration.java b/java/com/google/gerrit/server/notedb/NotesMigration.java
index c7acaf1..28754a6 100644
--- a/java/com/google/gerrit/server/notedb/NotesMigration.java
+++ b/java/com/google/gerrit/server/notedb/NotesMigration.java
@@ -46,8 +46,7 @@
  * <p>This class controls the state of the migration according to options in {@code gerrit.config}.
  * In general, any changes to these options should only be made by adventurous administrators, who
  * know what they're doing, on non-production data, for the purposes of testing the NoteDb
- * implementation. Changing options quite likely requires re-running {@code MigrateToNoteDb}. For
- * these reasons, the options remain undocumented.
+ * implementation.
  *
  * <p><strong>Note:</strong> Callers should not assume the values returned by {@code
  * NotesMigration}'s methods will not change in a running server.
diff --git a/java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java b/java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java
deleted file mode 100644
index 7b427b4..0000000
--- a/java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java
+++ /dev/null
@@ -1,510 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.github.rholder.retry.RetryException;
-import com.github.rholder.retry.Retryer;
-import com.github.rholder.retry.RetryerBuilder;
-import com.github.rholder.retry.StopStrategies;
-import com.github.rholder.retry.WaitStrategies;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.InternalUser;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gerrit.server.update.BatchUpdateOp;
-import com.google.gerrit.server.update.ChangeContext;
-import com.google.gerrit.server.update.RetryHelper;
-import com.google.gerrit.server.update.UpdateException;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-
-/** Helper to migrate the {@link PrimaryStorage} of individual changes. */
-@Singleton
-public class PrimaryStorageMigrator {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  /**
-   * Exception thrown during migration if the change has no {@code noteDbState} field at the
-   * beginning of the migration.
-   */
-  public static class NoNoteDbStateException extends RuntimeException {
-    private static final long serialVersionUID = 1L;
-
-    private NoNoteDbStateException(Change.Id id) {
-      super("change " + id + " has no note_db_state; rebuild it first");
-    }
-  }
-
-  private final AllUsersName allUsers;
-  private final ChangeNotes.Factory changeNotesFactory;
-  private final ChangeRebuilder rebuilder;
-  private final ChangeUpdate.Factory updateFactory;
-  private final GitRepositoryManager repoManager;
-  private final InternalUser.Factory internalUserFactory;
-  private final Provider<InternalChangeQuery> queryProvider;
-  private final Provider<ReviewDb> db;
-  private final RetryHelper retryHelper;
-
-  private final long skewMs;
-  private final long timeoutMs;
-  private final Retryer<NoteDbChangeState> testEnsureRebuiltRetryer;
-
-  @Inject
-  PrimaryStorageMigrator(
-      @GerritServerConfig Config cfg,
-      Provider<ReviewDb> db,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsers,
-      ChangeRebuilder rebuilder,
-      ChangeNotes.Factory changeNotesFactory,
-      Provider<InternalChangeQuery> queryProvider,
-      ChangeUpdate.Factory updateFactory,
-      InternalUser.Factory internalUserFactory,
-      RetryHelper retryHelper) {
-    this(
-        cfg,
-        db,
-        repoManager,
-        allUsers,
-        rebuilder,
-        null,
-        changeNotesFactory,
-        queryProvider,
-        updateFactory,
-        internalUserFactory,
-        retryHelper);
-  }
-
-  @VisibleForTesting
-  public PrimaryStorageMigrator(
-      Config cfg,
-      Provider<ReviewDb> db,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsers,
-      ChangeRebuilder rebuilder,
-      @Nullable Retryer<NoteDbChangeState> testEnsureRebuiltRetryer,
-      ChangeNotes.Factory changeNotesFactory,
-      Provider<InternalChangeQuery> queryProvider,
-      ChangeUpdate.Factory updateFactory,
-      InternalUser.Factory internalUserFactory,
-      RetryHelper retryHelper) {
-    this.db = db;
-    this.repoManager = repoManager;
-    this.allUsers = allUsers;
-    this.rebuilder = rebuilder;
-    this.testEnsureRebuiltRetryer = testEnsureRebuiltRetryer;
-    this.changeNotesFactory = changeNotesFactory;
-    this.queryProvider = queryProvider;
-    this.updateFactory = updateFactory;
-    this.internalUserFactory = internalUserFactory;
-    this.retryHelper = retryHelper;
-    skewMs = NoteDbChangeState.getReadOnlySkew(cfg);
-
-    String s = "notedb";
-    timeoutMs =
-        cfg.getTimeUnit(
-            s,
-            null,
-            "primaryStorageMigrationTimeout",
-            MILLISECONDS.convert(60, SECONDS),
-            MILLISECONDS);
-  }
-
-  /**
-   * Migrate a change's primary storage from ReviewDb to NoteDb.
-   *
-   * <p>This method will return only if the primary storage of the change is NoteDb afterwards. (It
-   * may return early if the primary storage was already NoteDb.)
-   *
-   * <p>If this method throws an exception, then the primary storage of the change is probably not
-   * NoteDb. (It is possible that the primary storage of the change is NoteDb in this case, but
-   * there was an error reading the state.) Moreover, after an exception, the change may be
-   * read-only until a lease expires. If the caller chooses to retry, they should wait until the
-   * read-only lease expires; this method will fail relatively quickly if called on a read-only
-   * change.
-   *
-   * <p>Note that if the change is read-only after this method throws an exception, that does not
-   * necessarily guarantee that the read-only lease was acquired during that particular method
-   * invocation; this call may have in fact failed because another thread acquired the lease first.
-   *
-   * @param id change ID.
-   * @throws OrmException if a ReviewDb-level error occurs.
-   * @throws IOException if a repo-level error occurs.
-   */
-  public void migrateToNoteDbPrimary(Change.Id id) throws OrmException, IOException {
-    // Since there are multiple non-atomic steps in this method, we need to
-    // consider what happens when there is another writer concurrent with the
-    // thread executing this method.
-    //
-    // Let:
-    // * OR = other writer writes noteDbState & new data to ReviewDb (in one
-    //        transaction)
-    // * ON = other writer writes to NoteDb
-    // * MRO = migrator sets state to read-only
-    // * MR = ensureRebuilt writes rebuilt noteDbState to ReviewDb (but does not
-    //        otherwise update ReviewDb in this transaction)
-    // * MN = ensureRebuilt writes rebuilt state to NoteDb
-    //
-    // Consider all the interleavings of these operations.
-    //
-    // * OR,ON,MRO,...
-    //   Other writer completes before migrator begins; this is not a concurrent
-    //   write.
-    // * MRO,...,OR,...
-    //   OR will fail, since it atomically checks that the noteDbState is not
-    //   read-only before proceeding. This results in an exception, but not a
-    //   concurrent write.
-    //
-    // Thus all the "interesting" interleavings start with OR,MRO, and differ on
-    // where ON falls relative to MR/MN.
-    //
-    // * OR,MRO,ON,MR,MN
-    //   The other NoteDb write succeeds despite the noteDbState being
-    //   read-only. Because the read-only state from MRO includes the update
-    //   from OR, the change is up-to-date at this point. Thus MR,MN is a no-op.
-    //   The end result is an up-to-date, read-only change.
-    //
-    // * OR,MRO,MR,ON,MN
-    //   The change is out-of-date when ensureRebuilt begins, because OR
-    //   succeeded but the corresponding ON has not happened yet. ON will
-    //   succeed, because there have been no intervening NoteDb writes. MN will
-    //   fail, because ON updated the state in NoteDb to something other than
-    //   what MR claimed. This leaves the change in an out-of-date, read-only
-    //   state.
-    //
-    //   If this method threw an exception in this case, the change would
-    //   eventually switch back to read-write when the read-only lease expires,
-    //   so this situation is recoverable. However, it would be inconvenient for
-    //   a change to be read-only for so long.
-    //
-    //   Thus, as an optimization, we have a retry loop that attempts
-    //   ensureRebuilt while still holding the same read-only lease. This
-    //   effectively results in the interleaving OR,MR,ON,MR,MN; in contrast
-    //   with the previous case, here, MR/MN actually rebuilds the change. In
-    //   the case of a write failure, MR/MN might fail and get retried again. If
-    //   it exceeds the maximum number of retries, an exception is thrown.
-    //
-    // * OR,MRO,MR,MN,ON
-    //   The change is out-of-date when ensureRebuilt begins. The change is
-    //   rebuilt, leaving a new state in NoteDb. ON will fail, because the old
-    //   NoteDb state has changed since the ref state was read when the update
-    //   began (prior to OR). This results in an exception from ON, but the end
-    //   result is still an up-to-date, read-only change. The end user that
-    //   initiated the other write observes an error, but this is no different
-    //   from other errors that need retrying, e.g. due to a backend write
-    //   failure.
-
-    Stopwatch sw = Stopwatch.createStarted();
-    Change readOnlyChange = setReadOnlyInReviewDb(id); // MRO
-    if (readOnlyChange == null) {
-      return; // Already migrated.
-    }
-
-    NoteDbChangeState rebuiltState;
-    try {
-      // MR,MN
-      rebuiltState =
-          ensureRebuiltRetryer(sw)
-              .call(
-                  () ->
-                      ensureRebuilt(
-                          readOnlyChange.getProject(),
-                          id,
-                          NoteDbChangeState.parse(readOnlyChange)));
-    } catch (RetryException | ExecutionException e) {
-      throw new OrmException(e);
-    }
-
-    // At this point, the noteDbState in ReviewDb is read-only, and it is
-    // guaranteed to match the state actually in NoteDb. Now it is safe to set
-    // the primary storage to NoteDb.
-
-    setPrimaryStorageNoteDb(id, rebuiltState);
-    logger.atFine().log(
-        "Migrated change %s to NoteDb primary in %sms", id, sw.elapsed(MILLISECONDS));
-  }
-
-  private Change setReadOnlyInReviewDb(Change.Id id) throws OrmException {
-    AtomicBoolean alreadyMigrated = new AtomicBoolean(false);
-    Change result =
-        db().changes()
-            .atomicUpdate(
-                id,
-                new AtomicUpdate<Change>() {
-                  @Override
-                  public Change update(Change change) {
-                    NoteDbChangeState state = NoteDbChangeState.parse(change);
-                    if (state == null) {
-                      // Could rebuild the change here, but that's more complexity, and this
-                      // normally shouldn't happen.
-                      //
-                      // Known cases where this happens are described in and handled by
-                      // NoteDbMigrator#canSkipPrimaryStorageMigration.
-                      throw new NoNoteDbStateException(id);
-                    }
-                    // If the change is already read-only, then the lease is held by another
-                    // (likely failed) migrator thread. Fail early, as we can't take over
-                    // the lease.
-                    NoteDbChangeState.checkNotReadOnly(change, skewMs);
-                    if (state.getPrimaryStorage() != PrimaryStorage.NOTE_DB) {
-                      Timestamp now = TimeUtil.nowTs();
-                      Timestamp until = new Timestamp(now.getTime() + timeoutMs);
-                      change.setNoteDbState(state.withReadOnlyUntil(until).toString());
-                    } else {
-                      alreadyMigrated.set(true);
-                    }
-                    return change;
-                  }
-                });
-    return alreadyMigrated.get() ? null : result;
-  }
-
-  private Retryer<NoteDbChangeState> ensureRebuiltRetryer(Stopwatch sw) {
-    if (testEnsureRebuiltRetryer != null) {
-      return testEnsureRebuiltRetryer;
-    }
-    // Retry the ensureRebuilt step with backoff until half the timeout has
-    // expired, leaving the remaining half for the rest of the steps.
-    long remainingNanos = (MILLISECONDS.toNanos(timeoutMs) / 2) - sw.elapsed(NANOSECONDS);
-    remainingNanos = Math.max(remainingNanos, 0);
-    return RetryerBuilder.<NoteDbChangeState>newBuilder()
-        .retryIfException(e -> (e instanceof IOException) || (e instanceof OrmException))
-        .withWaitStrategy(
-            WaitStrategies.join(
-                WaitStrategies.exponentialWait(250, MILLISECONDS),
-                WaitStrategies.randomWait(50, MILLISECONDS)))
-        .withStopStrategy(StopStrategies.stopAfterDelay(remainingNanos, NANOSECONDS))
-        .build();
-  }
-
-  private NoteDbChangeState ensureRebuilt(
-      Project.NameKey project, Change.Id id, NoteDbChangeState readOnlyState)
-      throws IOException, OrmException, RepositoryNotFoundException {
-    try (Repository changeRepo = repoManager.openRepository(project);
-        Repository allUsersRepo = repoManager.openRepository(allUsers)) {
-      if (!readOnlyState.isUpToDate(new RepoRefCache(changeRepo), new RepoRefCache(allUsersRepo))) {
-        NoteDbUpdateManager.Result r = rebuilder.rebuildEvenIfReadOnly(db(), id);
-        checkState(
-            r.newState().getReadOnlyUntil().equals(readOnlyState.getReadOnlyUntil()),
-            "state after rebuilding has different read-only lease: %s != %s",
-            r.newState(),
-            readOnlyState);
-        readOnlyState = r.newState();
-      }
-    }
-    return readOnlyState;
-  }
-
-  private void setPrimaryStorageNoteDb(Change.Id id, NoteDbChangeState expectedState)
-      throws OrmException {
-    db().changes()
-        .atomicUpdate(
-            id,
-            new AtomicUpdate<Change>() {
-              @Override
-              public Change update(Change change) {
-                NoteDbChangeState state = NoteDbChangeState.parse(change);
-                if (!Objects.equals(state, expectedState)) {
-                  throw new OrmRuntimeException(badState(state, expectedState));
-                }
-                Timestamp until = state.getReadOnlyUntil().get();
-                if (TimeUtil.nowTs().after(until)) {
-                  throw new OrmRuntimeException(
-                      "read-only lease on change " + id + " expired at " + until);
-                }
-                change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
-                return change;
-              }
-            });
-  }
-
-  private ReviewDb db() {
-    return ReviewDbUtil.unwrapDb(db.get());
-  }
-
-  private String badState(NoteDbChangeState actual, NoteDbChangeState expected) {
-    return "state changed unexpectedly: " + actual + " != " + expected;
-  }
-
-  public void migrateToReviewDbPrimary(Change.Id id, @Nullable Project.NameKey project)
-      throws OrmException, IOException {
-    // Migrating back to ReviewDb primary is much simpler than the original migration to NoteDb
-    // primary, because when NoteDb is primary, each write only goes to one storage location rather
-    // than both. We only need to consider whether a concurrent writer (OR) conflicts with the first
-    // setReadOnlyInNoteDb step (MR) in this method.
-    //
-    // If OR wins, then either:
-    // * MR will set read-only after OR is completed, which is not a concurrent write.
-    // * MR will fail to set read-only with a lock failure. The caller will have to retry, but the
-    //   change is not in a read-only state, so behavior is not degraded in the meantime.
-    //
-    // If MR wins, then either:
-    // * OR will fail with a read-only exception (via AbstractChangeNotes#apply).
-    // * OR will fail with a lock failure.
-    //
-    // In all of these scenarios, the change is read-only if and only if MR succeeds.
-    //
-    // There will be no concurrent writes to ReviewDb for this change until
-    // setPrimaryStorageReviewDb completes, because ReviewDb writes are not attempted when primary
-    // storage is NoteDb. After the primary storage changes back, it is possible for subsequent
-    // NoteDb writes to conflict with the releaseReadOnlyLeaseInNoteDb step, but at this point,
-    // since ReviewDb is primary, we are back to ignoring them.
-    Stopwatch sw = Stopwatch.createStarted();
-    if (project == null) {
-      project = getProject(id);
-    }
-    ObjectId newMetaId = setReadOnlyInNoteDb(project, id);
-    rebuilder.rebuildReviewDb(db(), project, id);
-    setPrimaryStorageReviewDb(id, newMetaId);
-    releaseReadOnlyLeaseInNoteDb(project, id);
-    logger.atFine().log(
-        "Migrated change %s to ReviewDb primary in %sms", id, sw.elapsed(MILLISECONDS));
-  }
-
-  private ObjectId setReadOnlyInNoteDb(Project.NameKey project, Change.Id id)
-      throws OrmException, IOException {
-    Timestamp now = TimeUtil.nowTs();
-    Timestamp until = new Timestamp(now.getTime() + timeoutMs);
-    ChangeUpdate update =
-        updateFactory.create(
-            changeNotesFactory.createChecked(db.get(), project, id), internalUserFactory.create());
-    update.setReadOnlyUntil(until);
-    return update.commit();
-  }
-
-  private void setPrimaryStorageReviewDb(Change.Id id, ObjectId newMetaId)
-      throws OrmException, IOException {
-    ImmutableMap.Builder<Account.Id, ObjectId> draftIds = ImmutableMap.builder();
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      for (Ref draftRef :
-          repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftCommentsPrefix(id))) {
-        Account.Id accountId = Account.Id.fromRef(draftRef.getName());
-        if (accountId != null) {
-          draftIds.put(accountId, draftRef.getObjectId().copy());
-        }
-      }
-    }
-    NoteDbChangeState newState =
-        new NoteDbChangeState(
-            id,
-            PrimaryStorage.REVIEW_DB,
-            Optional.of(RefState.create(newMetaId, draftIds.build())),
-            Optional.empty());
-    db().changes()
-        .atomicUpdate(
-            id,
-            new AtomicUpdate<Change>() {
-              @Override
-              public Change update(Change change) {
-                if (PrimaryStorage.of(change) != PrimaryStorage.NOTE_DB) {
-                  throw new OrmRuntimeException(
-                      "change " + id + " is not NoteDb primary: " + change.getNoteDbState());
-                }
-                change.setNoteDbState(newState.toString());
-                return change;
-              }
-            });
-  }
-
-  private void releaseReadOnlyLeaseInNoteDb(Project.NameKey project, Change.Id id)
-      throws OrmException {
-    // Use a BatchUpdate since ReviewDb is primary at this point, so it needs to reflect the update.
-    // (In practice retrying won't happen, since we aren't using fused updates at this point.)
-    try {
-      retryHelper.execute(
-          updateFactory -> {
-            try (BatchUpdate bu =
-                updateFactory.create(
-                    db.get(), project, internalUserFactory.create(), TimeUtil.nowTs())) {
-              bu.addOp(
-                  id,
-                  new BatchUpdateOp() {
-                    @Override
-                    public boolean updateChange(ChangeContext ctx) {
-                      ctx.getUpdate(ctx.getChange().currentPatchSetId())
-                          .setReadOnlyUntil(new Timestamp(0));
-                      return true;
-                    }
-                  });
-              bu.execute();
-              return null;
-            }
-          });
-    } catch (RestApiException | UpdateException e) {
-      throw new OrmException(e);
-    }
-  }
-
-  private Project.NameKey getProject(Change.Id id) throws OrmException {
-    List<ChangeData> cds =
-        queryProvider.get().setRequestedFields(ChangeField.PROJECT).byLegacyChangeId(id);
-    Set<Project.NameKey> projects = new TreeSet<>();
-    for (ChangeData cd : cds) {
-      projects.add(cd.project());
-    }
-    if (projects.size() != 1) {
-      throw new OrmException(
-          "zero or multiple projects found for change "
-              + id
-              + ", must specify project explicitly: "
-              + projects);
-    }
-    return projects.iterator().next();
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
index a05e6a1..364ad75 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.client.RobotComment;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -49,7 +48,7 @@
 
   @Inject
   RobotCommentNotes(Args args, @Assisted Change change) {
-    super(args, change.getId(), PrimaryStorage.of(change), false);
+    super(args, change.getId());
     this.change = change;
   }
 
diff --git a/java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java b/java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java
deleted file mode 100644
index 11fef24..0000000
--- a/java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Id;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@VisibleForTesting
-@Singleton
-public class TestChangeRebuilderWrapper extends ChangeRebuilder {
-  private final ChangeRebuilderImpl delegate;
-  private final AtomicBoolean failNextUpdate;
-  private final AtomicBoolean stealNextUpdate;
-
-  @Inject
-  TestChangeRebuilderWrapper(SchemaFactory<ReviewDb> schemaFactory, ChangeRebuilderImpl rebuilder) {
-    super(schemaFactory);
-    this.delegate = rebuilder;
-    this.failNextUpdate = new AtomicBoolean();
-    this.stealNextUpdate = new AtomicBoolean();
-  }
-
-  public void failNextUpdate() {
-    failNextUpdate.set(true);
-  }
-
-  public void stealNextUpdate() {
-    stealNextUpdate.set(true);
-  }
-
-  @Override
-  public Result rebuild(ReviewDb db, Change.Id changeId) throws IOException, OrmException {
-    return rebuild(db, changeId, true);
-  }
-
-  @Override
-  public Result rebuildEvenIfReadOnly(ReviewDb db, Change.Id changeId)
-      throws IOException, OrmException {
-    return rebuild(db, changeId, false);
-  }
-
-  private Result rebuild(ReviewDb db, Change.Id changeId, boolean checkReadOnly)
-      throws IOException, OrmException {
-    if (failNextUpdate.getAndSet(false)) {
-      throw new IOException("Update failed");
-    }
-    Result result =
-        checkReadOnly
-            ? delegate.rebuild(db, changeId)
-            : delegate.rebuildEvenIfReadOnly(db, changeId);
-    if (stealNextUpdate.getAndSet(false)) {
-      throw new IOException("Update stolen");
-    }
-    return result;
-  }
-
-  @Override
-  public Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle)
-      throws IOException, OrmException {
-    // stealNextUpdate doesn't really apply in this case because the IOException
-    // would normally come from the manager.execute() method, which isn't called
-    // here.
-    return delegate.rebuild(manager, bundle);
-  }
-
-  @Override
-  public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
-      throws IOException, OrmException {
-    // Don't inspect stealNextUpdate; that happens in execute() below.
-    return delegate.stage(db, changeId);
-  }
-
-  @Override
-  public Result execute(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
-      throws OrmException, IOException {
-    if (failNextUpdate.getAndSet(false)) {
-      throw new IOException("Update failed");
-    }
-    Result result = delegate.execute(db, changeId, manager);
-    if (stealNextUpdate.getAndSet(false)) {
-      throw new IOException("Update stolen");
-    }
-    return result;
-  }
-
-  @Override
-  public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
-      throws IOException, OrmException {
-    // Don't check for manual failure; that happens in execute().
-    delegate.buildUpdates(manager, bundle);
-  }
-
-  @Override
-  public void rebuildReviewDb(ReviewDb db, Project.NameKey project, Id changeId)
-      throws OrmException {
-    if (failNextUpdate.getAndSet(false)) {
-      throw new OrmException("Update failed");
-    }
-    delegate.rebuildReviewDb(db, project, changeId);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java b/java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java
deleted file mode 100644
index 0e6d3e9..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.gwtorm.server.OrmRuntimeException;
-
-class AbortUpdateException extends OrmRuntimeException {
-  private static final long serialVersionUID = 1L;
-
-  AbortUpdateException() {
-    super("aborted");
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java b/java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java
deleted file mode 100644
index 9ecf476..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import java.sql.Timestamp;
-
-class ApprovalEvent extends Event {
-  private PatchSetApproval psa;
-
-  ApprovalEvent(PatchSetApproval psa, Timestamp changeCreatedOn) {
-    super(
-        psa.getPatchSetId(),
-        psa.getAccountId(),
-        psa.getRealAccountId(),
-        psa.getGranted(),
-        changeCreatedOn,
-        psa.getTag());
-    this.psa = psa;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return false;
-  }
-
-  @Override
-  protected boolean canHaveTag() {
-    // Legacy SUBM approvals don't have a tag field set, but the corresponding
-    // ChangeMessage for merging the change does. We need to let these be in the
-    // same meta commit so the SUBM approval isn't counted as post-submit.
-    return !psa.isLegacySubmit();
-  }
-
-  @Override
-  void apply(ChangeUpdate update) {
-    checkUpdate(update);
-    update.putApproval(psa.getLabel(), psa.getValue());
-  }
-
-  @Override
-  protected boolean isPostSubmitApproval() {
-    return psa.isPostSubmit();
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    helper.add("approval", psa);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java
deleted file mode 100644
index 53c9dc4..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.server.ChangeMessagesUtil;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.sql.Timestamp;
-import java.util.Map;
-import java.util.Optional;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class ChangeMessageEvent extends Event {
-  private static final ImmutableMap<Change.Status, Pattern> STATUS_PATTERNS =
-      ImmutableMap.of(
-          Change.Status.ABANDONED, Pattern.compile("^Abandoned(\n.*)*$"),
-          Change.Status.MERGED,
-              Pattern.compile(
-                  "^Change has been successfully (merged|cherry-picked|rebased|pushed).*$"),
-          Change.Status.NEW, Pattern.compile("^Restored(\n.*)*$"));
-
-  private static final Pattern PRIVATE_SET_REGEXP = Pattern.compile("^Set private$");
-  private static final Pattern PRIVATE_UNSET_REGEXP = Pattern.compile("^Unset private$");
-
-  private static final Pattern TOPIC_SET_REGEXP = Pattern.compile("^Topic set to (.+)$");
-  private static final Pattern TOPIC_CHANGED_REGEXP =
-      Pattern.compile("^Topic changed from (.+) to (.+)$");
-  private static final Pattern TOPIC_REMOVED_REGEXP = Pattern.compile("^Topic (.+) removed$");
-
-  private static final Pattern WIP_SET_REGEXP = Pattern.compile("^Set Work In Progress$");
-  private static final Pattern WIP_UNSET_REGEXP = Pattern.compile("^Set Ready For Review$");
-
-  private final Change change;
-  private final Change noteDbChange;
-  private final Optional<Change.Status> status;
-  private final ChangeMessage message;
-
-  ChangeMessageEvent(
-      Change change, Change noteDbChange, ChangeMessage message, Timestamp changeCreatedOn) {
-    super(
-        message.getPatchSetId(),
-        message.getAuthor(),
-        message.getRealAuthor(),
-        message.getWrittenOn(),
-        changeCreatedOn,
-        message.getTag());
-    this.change = change;
-    this.noteDbChange = noteDbChange;
-    this.message = message;
-    this.status = parseStatus(message);
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return true;
-  }
-
-  @Override
-  protected boolean isSubmit() {
-    return status.isPresent() && status.get() == Change.Status.MERGED;
-  }
-
-  @Override
-  protected boolean canHaveTag() {
-    return true;
-  }
-
-  @SuppressWarnings("deprecation")
-  @Override
-  void apply(ChangeUpdate update) throws OrmException {
-    checkUpdate(update);
-    update.setChangeMessage(message.getMessage());
-    setPrivate(update);
-    setTopic(update);
-    setWorkInProgress(update);
-
-    if (status.isPresent()) {
-      Change.Status s = status.get();
-      update.fixStatus(s);
-      noteDbChange.setStatus(s);
-      if (s == Change.Status.MERGED) {
-        update.setSubmissionId(change.getSubmissionId());
-        noteDbChange.setSubmissionId(change.getSubmissionId());
-      }
-    }
-  }
-
-  private static Optional<Change.Status> parseStatus(ChangeMessage message) {
-    String msg = message.getMessage();
-    if (msg == null) {
-      return Optional.empty();
-    }
-    for (Map.Entry<Change.Status, Pattern> e : STATUS_PATTERNS.entrySet()) {
-      if (e.getValue().matcher(msg).matches()) {
-        return Optional.of(e.getKey());
-      }
-    }
-    return Optional.empty();
-  }
-
-  private void setPrivate(ChangeUpdate update) {
-    String msg = message.getMessage();
-    if (msg == null) {
-      return;
-    }
-    Matcher m = PRIVATE_SET_REGEXP.matcher(msg);
-    if (m.matches()) {
-      update.setPrivate(true);
-      noteDbChange.setPrivate(true);
-      return;
-    }
-
-    m = PRIVATE_UNSET_REGEXP.matcher(msg);
-    if (m.matches()) {
-      update.setPrivate(false);
-      noteDbChange.setPrivate(false);
-    }
-  }
-
-  private void setTopic(ChangeUpdate update) {
-    String msg = message.getMessage();
-    if (msg == null) {
-      return;
-    }
-    Matcher m = TOPIC_SET_REGEXP.matcher(msg);
-    if (m.matches()) {
-      String topic = m.group(1);
-      update.setTopic(topic);
-      noteDbChange.setTopic(topic);
-      return;
-    }
-
-    m = TOPIC_CHANGED_REGEXP.matcher(msg);
-    if (m.matches()) {
-      String topic = m.group(2);
-      update.setTopic(topic);
-      noteDbChange.setTopic(topic);
-      return;
-    }
-
-    if (TOPIC_REMOVED_REGEXP.matcher(msg).matches()) {
-      update.setTopic(null);
-      noteDbChange.setTopic(null);
-    }
-  }
-
-  private void setWorkInProgress(ChangeUpdate update) {
-    String msg = Strings.nullToEmpty(message.getMessage());
-    String tag = message.getTag();
-    if (ChangeMessagesUtil.TAG_SET_WIP.equals(tag)
-        || ChangeMessagesUtil.TAG_UPLOADED_WIP_PATCH_SET.equals(tag)
-        || WIP_SET_REGEXP.matcher(msg).matches()) {
-      update.setWorkInProgress(true);
-      noteDbChange.setWorkInProgress(true);
-    } else if (ChangeMessagesUtil.TAG_SET_READY.equals(tag)
-        || ChangeMessagesUtil.TAG_UPLOADED_PATCH_SET.equals(tag)
-        || WIP_UNSET_REGEXP.matcher(msg).matches()) {
-      update.setWorkInProgress(false);
-      noteDbChange.setWorkInProgress(false);
-    }
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    helper.add("message", message);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java
deleted file mode 100644
index 8ce9987..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import java.io.IOException;
-
-public abstract class ChangeRebuilder {
-  public static class NoPatchSetsException extends OrmException {
-    private static final long serialVersionUID = 1L;
-
-    NoPatchSetsException(Change.Id changeId) {
-      super("Change " + changeId + " cannot be rebuilt because it has no patch sets");
-    }
-  }
-
-  private final SchemaFactory<ReviewDb> schemaFactory;
-
-  protected ChangeRebuilder(SchemaFactory<ReviewDb> schemaFactory) {
-    this.schemaFactory = schemaFactory;
-  }
-
-  public final ListenableFuture<Result> rebuildAsync(
-      Change.Id id, ListeningExecutorService executor) {
-    return executor.submit(
-        () -> {
-          try (ReviewDb db = schemaFactory.open()) {
-            return rebuild(db, id);
-          }
-        });
-  }
-
-  /**
-   * Rebuild ReviewDb contents by copying from NoteDb.
-   *
-   * <p>Requires NoteDb to be the primary storage for the change.
-   */
-  public abstract void rebuildReviewDb(ReviewDb db, Project.NameKey project, Change.Id changeId)
-      throws OrmException;
-
-  // In the following methods "rebuilding" always refers to copying the state
-  // from ReviewDb to NoteDb, i.e. assuming ReviewDb is the primary storage.
-
-  public abstract Result rebuild(ReviewDb db, Change.Id changeId) throws IOException, OrmException;
-
-  public abstract Result rebuildEvenIfReadOnly(ReviewDb db, Change.Id changeId)
-      throws IOException, OrmException;
-
-  public abstract Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle)
-      throws IOException, OrmException;
-
-  public abstract void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
-      throws IOException, OrmException;
-
-  public abstract NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
-      throws IOException, OrmException;
-
-  public abstract Result execute(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
-      throws OrmException, IOException;
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
deleted file mode 100644
index 8740710..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
+++ /dev/null
@@ -1,687 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
-import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
-import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Splitter;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Table;
-import com.google.common.primitives.Ints;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeDraftUpdate;
-import com.google.gerrit.server.notedb.ChangeNoteUtil;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.OpenRepo;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.update.ChainedReceiveCommands;
-import com.google.gwtorm.client.Key;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeMap;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-public class ChangeRebuilderImpl extends ChangeRebuilder {
-  /**
-   * The maximum amount of time between the ReviewDb timestamp of the first and last events batched
-   * together into a single NoteDb update.
-   *
-   * <p>Used to account for the fact that different records with their own timestamps (e.g. {@link
-   * PatchSetApproval} and {@link ChangeMessage}) historically didn't necessarily use the same
-   * timestamp, and tended to call {@code System.currentTimeMillis()} independently.
-   */
-  public static final long MAX_WINDOW_MS = SECONDS.toMillis(3);
-
-  /**
-   * The maximum amount of time between two consecutive events to consider them to be in the same
-   * batch.
-   */
-  static final long MAX_DELTA_MS = SECONDS.toMillis(1);
-
-  private final ChangeBundleReader bundleReader;
-  private final ChangeDraftUpdate.Factory draftUpdateFactory;
-  private final ChangeNoteUtil changeNoteUtil;
-  private final ChangeNotes.Factory notesFactory;
-  private final ChangeUpdate.Factory updateFactory;
-  private final CommentsUtil commentsUtil;
-  private final NoteDbUpdateManager.Factory updateManagerFactory;
-  private final NotesMigration migration;
-  private final PatchListCache patchListCache;
-  private final PersonIdent serverIdent;
-  private final ProjectCache projectCache;
-  private final String serverId;
-  private final long skewMs;
-
-  @Inject
-  ChangeRebuilderImpl(
-      @GerritServerConfig Config cfg,
-      SchemaFactory<ReviewDb> schemaFactory,
-      ChangeBundleReader bundleReader,
-      ChangeDraftUpdate.Factory draftUpdateFactory,
-      ChangeNoteUtil changeNoteUtil,
-      ChangeNotes.Factory notesFactory,
-      ChangeUpdate.Factory updateFactory,
-      CommentsUtil commentsUtil,
-      NoteDbUpdateManager.Factory updateManagerFactory,
-      NotesMigration migration,
-      PatchListCache patchListCache,
-      @GerritPersonIdent PersonIdent serverIdent,
-      @Nullable ProjectCache projectCache,
-      @GerritServerId String serverId) {
-    super(schemaFactory);
-    this.bundleReader = bundleReader;
-    this.draftUpdateFactory = draftUpdateFactory;
-    this.changeNoteUtil = changeNoteUtil;
-    this.notesFactory = notesFactory;
-    this.updateFactory = updateFactory;
-    this.commentsUtil = commentsUtil;
-    this.updateManagerFactory = updateManagerFactory;
-    this.migration = migration;
-    this.patchListCache = patchListCache;
-    this.serverIdent = serverIdent;
-    this.projectCache = projectCache;
-    this.serverId = serverId;
-    this.skewMs = NoteDbChangeState.getReadOnlySkew(cfg);
-  }
-
-  @Override
-  public Result rebuild(ReviewDb db, Change.Id changeId) throws IOException, OrmException {
-    return rebuild(db, changeId, true);
-  }
-
-  @Override
-  public Result rebuildEvenIfReadOnly(ReviewDb db, Change.Id changeId)
-      throws IOException, OrmException {
-    return rebuild(db, changeId, false);
-  }
-
-  private Result rebuild(ReviewDb db, Change.Id changeId, boolean checkReadOnly)
-      throws IOException, OrmException {
-    db = ReviewDbUtil.unwrapDb(db);
-    // Read change just to get project; this instance is then discarded so we can read a consistent
-    // ChangeBundle inside a transaction.
-    Change change = db.changes().get(changeId);
-    if (change == null) {
-      throw new NoSuchChangeException(changeId);
-    }
-    try (NoteDbUpdateManager manager = updateManagerFactory.create(change.getProject())) {
-      buildUpdates(manager, bundleReader.fromReviewDb(db, changeId));
-      return execute(db, changeId, manager, checkReadOnly, true);
-    }
-  }
-
-  @Override
-  public Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle)
-      throws NoSuchChangeException, IOException, OrmException {
-    Change change = new Change(bundle.getChange());
-    buildUpdates(manager, bundle);
-    return manager.stageAndApplyDelta(change);
-  }
-
-  @Override
-  public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
-      throws IOException, OrmException {
-    db = ReviewDbUtil.unwrapDb(db);
-    Change change = checkNoteDbState(ChangeNotes.readOneReviewDbChange(db, changeId));
-    if (change == null) {
-      throw new NoSuchChangeException(changeId);
-    }
-    NoteDbUpdateManager manager = updateManagerFactory.create(change.getProject());
-    buildUpdates(manager, bundleReader.fromReviewDb(db, changeId));
-    manager.stage();
-    return manager;
-  }
-
-  @Override
-  public Result execute(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
-      throws OrmException, IOException {
-    return execute(db, changeId, manager, true, true);
-  }
-
-  public Result execute(
-      ReviewDb db,
-      Change.Id changeId,
-      NoteDbUpdateManager manager,
-      boolean checkReadOnly,
-      boolean executeManager)
-      throws OrmException, IOException {
-    db = ReviewDbUtil.unwrapDb(db);
-    Change change = checkNoteDbState(ChangeNotes.readOneReviewDbChange(db, changeId));
-    if (change == null) {
-      throw new NoSuchChangeException(changeId);
-    }
-
-    String oldNoteDbStateStr = change.getNoteDbState();
-    Result r = manager.stageAndApplyDelta(change);
-    String newNoteDbStateStr = change.getNoteDbState();
-    if (newNoteDbStateStr == null) {
-      throw new OrmException(
-          String.format(
-              "Rebuilding change %s produced no writes to NoteDb: %s",
-              changeId, bundleReader.fromReviewDb(db, changeId)));
-    }
-    NoteDbChangeState newNoteDbState =
-        requireNonNull(NoteDbChangeState.parse(changeId, newNoteDbStateStr));
-    try {
-      db.changes()
-          .atomicUpdate(
-              changeId,
-              new AtomicUpdate<Change>() {
-                @Override
-                public Change update(Change change) {
-                  if (checkReadOnly) {
-                    NoteDbChangeState.checkNotReadOnly(change, skewMs);
-                  }
-                  String currNoteDbStateStr = change.getNoteDbState();
-                  if (Objects.equals(currNoteDbStateStr, newNoteDbStateStr)) {
-                    // Another thread completed the same rebuild we were about to.
-                    throw new AbortUpdateException();
-                  } else if (!Objects.equals(oldNoteDbStateStr, currNoteDbStateStr)) {
-                    // Another thread updated the state to something else.
-                    throw new ConflictingUpdateRuntimeException(change, oldNoteDbStateStr);
-                  }
-                  change.setNoteDbState(newNoteDbStateStr);
-                  return change;
-                }
-              });
-    } catch (ConflictingUpdateRuntimeException e) {
-      // Rethrow as an OrmException so the caller knows to use staged results. Strictly speaking
-      // they are not completely up to date, but result we send to the caller is the same as if this
-      // rebuild had executed before the other thread.
-      throw new ConflictingUpdateException(e);
-    } catch (AbortUpdateException e) {
-      if (newNoteDbState.isUpToDate(
-          manager.getChangeRepo().cmds.getRepoRefCache(),
-          manager.getAllUsersRepo().cmds.getRepoRefCache())) {
-        // If the state in ReviewDb matches NoteDb at this point, it means another thread
-        // successfully completed this rebuild. It's ok to not execute the update in this case,
-        // since the object referenced in the Result was flushed to the repo by whatever thread won
-        // the race.
-        return r;
-      }
-      // If the state doesn't match, that means another thread attempted this rebuild, but
-      // failed. Fall through and try to update the ref again.
-    }
-    if (migration.failChangeWrites()) {
-      // Don't even attempt to execute if read-only, it would fail anyway. But do throw an exception
-      // to the caller so they know to use the staged results instead of reading from the repo.
-      throw new OrmException(NoteDbUpdateManager.CHANGES_READ_ONLY);
-    }
-    if (executeManager) {
-      manager.execute();
-    }
-    return r;
-  }
-
-  static Change checkNoteDbState(Change c) throws OrmException {
-    // Can only rebuild a change if its primary storage is ReviewDb.
-    NoteDbChangeState s = NoteDbChangeState.parse(c);
-    if (s != null && s.getPrimaryStorage() != PrimaryStorage.REVIEW_DB) {
-      throw new OrmException(String.format("cannot rebuild change %s with state %s", c.getId(), s));
-    }
-    return c;
-  }
-
-  @Override
-  public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
-      throws IOException, OrmException {
-    manager.setCheckExpectedState(false).setRefLogMessage("Rebuilding change");
-    Change change = new Change(bundle.getChange());
-    if (bundle.getPatchSets().isEmpty()) {
-      throw new NoPatchSetsException(change.getId());
-    }
-    if (change.getLastUpdatedOn().compareTo(change.getCreatedOn()) < 0) {
-      // A bug in data migration might set created_on to the time of the migration. The
-      // correct timestamps were lost, but we can at least set it so created_on is not after
-      // last_updated_on.
-      // See https://bugs.chromium.org/p/gerrit/issues/detail?id=7397
-      change.setCreatedOn(change.getLastUpdatedOn());
-    }
-
-    // We will rebuild all events, except for draft comments, in buckets based on author and
-    // timestamp.
-    List<Event> events = new ArrayList<>();
-    ListMultimap<Account.Id, DraftCommentEvent> draftCommentEvents =
-        MultimapBuilder.hashKeys().arrayListValues().build();
-
-    events.addAll(getHashtagsEvents(change, manager));
-
-    // Delete ref only after hashtags have been read.
-    deleteChangeMetaRef(change, manager.getChangeRepo().cmds);
-    deleteDraftRefs(change, manager.getAllUsersRepo());
-
-    Integer minPsNum = getMinPatchSetNum(bundle);
-    TreeMap<PatchSet.Id, PatchSetEvent> patchSetEvents =
-        new TreeMap<>(ReviewDbUtil.intKeyOrdering());
-
-    for (PatchSet ps : bundle.getPatchSets()) {
-      PatchSetEvent pse = new PatchSetEvent(change, ps, manager.getChangeRepo().rw);
-      patchSetEvents.put(ps.getId(), pse);
-      events.add(pse);
-      for (Comment c : getComments(bundle, serverId, Status.PUBLISHED, ps)) {
-        CommentEvent e = new CommentEvent(c, change, ps, patchListCache);
-        events.add(e.addDep(pse));
-      }
-      for (Comment c : getComments(bundle, serverId, Status.DRAFT, ps)) {
-        DraftCommentEvent e = new DraftCommentEvent(c, change, ps, patchListCache);
-        draftCommentEvents.put(c.author.getId(), e);
-      }
-    }
-    ensurePatchSetOrder(patchSetEvents);
-
-    for (PatchSetApproval psa : bundle.getPatchSetApprovals()) {
-      PatchSetEvent pse = patchSetEvents.get(psa.getPatchSetId());
-      if (pse != null) {
-        events.add(new ApprovalEvent(psa, change.getCreatedOn()).addDep(pse));
-      }
-    }
-
-    for (Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> r :
-        bundle.getReviewers().asTable().cellSet()) {
-      events.add(new ReviewerEvent(r, change.getCreatedOn()));
-    }
-
-    Change noteDbChange = new Change(null, null, null, null, null);
-    for (ChangeMessage msg : bundle.getChangeMessages()) {
-      Event msgEvent = new ChangeMessageEvent(change, noteDbChange, msg, change.getCreatedOn());
-      if (msg.getPatchSetId() != null) {
-        PatchSetEvent pse = patchSetEvents.get(msg.getPatchSetId());
-        if (pse == null) {
-          continue; // Ignore events for missing patch sets.
-        }
-        msgEvent.addDep(pse);
-      }
-      events.add(msgEvent);
-    }
-
-    sortAndFillEvents(change, noteDbChange, bundle.getPatchSets(), events, minPsNum);
-
-    EventList<Event> el = new EventList<>();
-    for (Event e : events) {
-      if (!el.canAdd(e)) {
-        flushEventsToUpdate(manager, el, change);
-        checkState(el.canAdd(e));
-      }
-      el.add(e);
-    }
-    flushEventsToUpdate(manager, el, change);
-
-    EventList<DraftCommentEvent> plcel = new EventList<>();
-    for (Account.Id author : draftCommentEvents.keys()) {
-      for (DraftCommentEvent e : Ordering.natural().sortedCopy(draftCommentEvents.get(author))) {
-        if (!plcel.canAdd(e)) {
-          flushEventsToDraftUpdate(manager, plcel, change);
-          checkState(plcel.canAdd(e));
-        }
-        plcel.add(e);
-      }
-      flushEventsToDraftUpdate(manager, plcel, change);
-    }
-  }
-
-  private static Integer getMinPatchSetNum(ChangeBundle bundle) {
-    Integer minPsNum = null;
-    for (PatchSet ps : bundle.getPatchSets()) {
-      int n = ps.getId().get();
-      if (minPsNum == null || n < minPsNum) {
-        minPsNum = n;
-      }
-    }
-    return minPsNum;
-  }
-
-  private static void ensurePatchSetOrder(TreeMap<PatchSet.Id, PatchSetEvent> events) {
-    if (events.isEmpty()) {
-      return;
-    }
-    Iterator<PatchSetEvent> it = events.values().iterator();
-    PatchSetEvent curr = it.next();
-    while (it.hasNext()) {
-      PatchSetEvent next = it.next();
-      next.addDep(curr);
-      curr = next;
-    }
-  }
-
-  private static List<Comment> getComments(
-      ChangeBundle bundle, String serverId, PatchLineComment.Status status, PatchSet ps) {
-    return bundle
-        .getPatchLineComments()
-        .stream()
-        .filter(c -> c.getPatchSetId().equals(ps.getId()) && c.getStatus() == status)
-        .map(plc -> plc.asComment(serverId))
-        .sorted(CommentsUtil.COMMENT_ORDER)
-        .collect(toList());
-  }
-
-  private void sortAndFillEvents(
-      Change change,
-      Change noteDbChange,
-      ImmutableCollection<PatchSet> patchSets,
-      List<Event> events,
-      Integer minPsNum) {
-    Event finalUpdates = new FinalUpdatesEvent(change, noteDbChange, patchSets);
-    events.add(finalUpdates);
-    setPostSubmitDeps(events);
-    new EventSorter(events).sort();
-
-    // Ensure the first event in the list creates the change, setting the author and any required
-    // footers. Also force the creation time of the first patch set to match the creation time of
-    // the change.
-    Event first = events.get(0);
-    if (first instanceof PatchSetEvent && change.getOwner().equals(first.user)) {
-      first.when = change.getCreatedOn();
-      ((PatchSetEvent) first).createChange = true;
-    } else {
-      events.add(0, new CreateChangeEvent(change, minPsNum));
-    }
-
-    // Final pass to correct some inconsistencies.
-    //
-    // First, fill in any missing patch set IDs using the latest patch set of the change at the time
-    // of the event, because NoteDb can't represent actions with no associated patch set ID. This
-    // workaround is as if a user added a ChangeMessage on the change by replying from the latest
-    // patch set.
-    //
-    // Start with the first patch set that actually exists. If there are no patch sets at all,
-    // minPsNum will be null, so just bail and use 1 as the patch set ID.
-    //
-    // Second, ensure timestamps are nondecreasing, by copying the previous timestamp if this
-    // happens. This assumes that the only way this can happen is due to dependency constraints, and
-    // it is ok to give an event the same timestamp as one of its dependencies.
-    int ps = firstNonNull(minPsNum, 1);
-    for (int i = 0; i < events.size(); i++) {
-      Event e = events.get(i);
-      if (e.psId == null) {
-        e.psId = new PatchSet.Id(change.getId(), ps);
-      } else {
-        ps = Math.max(ps, e.psId.get());
-      }
-
-      if (i > 0) {
-        Event p = events.get(i - 1);
-        if (e.when.before(p.when)) {
-          e.when = p.when;
-        }
-      }
-    }
-  }
-
-  private void setPostSubmitDeps(List<Event> events) {
-    Optional<Event> submitEvent =
-        Lists.reverse(events).stream().filter(Event::isSubmit).findFirst();
-    if (submitEvent.isPresent()) {
-      events.stream().filter(Event::isPostSubmitApproval).forEach(e -> e.addDep(submitEvent.get()));
-    }
-  }
-
-  private void flushEventsToUpdate(
-      NoteDbUpdateManager manager, EventList<Event> events, Change change)
-      throws OrmException, IOException {
-    if (events.isEmpty()) {
-      return;
-    }
-    Comparator<String> labelNameComparator;
-    if (projectCache != null) {
-      labelNameComparator = projectCache.get(change.getProject()).getLabelTypes().nameComparator();
-    } else {
-      // No project cache available, bail and use natural ordering; there's no semantic difference
-      // anyway difference.
-      labelNameComparator = Ordering.natural();
-    }
-    ChangeUpdate update =
-        updateFactory.create(
-            change,
-            events.getAccountId(),
-            events.getRealAccountId(),
-            newAuthorIdent(events),
-            events.getWhen(),
-            labelNameComparator);
-    update.setAllowWriteToNewRef(true);
-    update.setPatchSetId(events.getPatchSetId());
-    update.setTag(events.getTag());
-    for (Event e : events) {
-      e.apply(update);
-    }
-    manager.add(update);
-    events.clear();
-  }
-
-  private void flushEventsToDraftUpdate(
-      NoteDbUpdateManager manager, EventList<DraftCommentEvent> events, Change change) {
-    if (events.isEmpty()) {
-      return;
-    }
-    ChangeDraftUpdate update =
-        draftUpdateFactory.create(
-            change,
-            events.getAccountId(),
-            events.getRealAccountId(),
-            newAuthorIdent(events),
-            events.getWhen());
-    update.setPatchSetId(events.getPatchSetId());
-    for (DraftCommentEvent e : events) {
-      e.applyDraft(update);
-    }
-    manager.add(update);
-    events.clear();
-  }
-
-  private PersonIdent newAuthorIdent(EventList<?> events) {
-    Account.Id id = events.getAccountId();
-    if (id == null) {
-      return new PersonIdent(serverIdent, events.getWhen());
-    }
-    return changeNoteUtil.newIdent(id, events.getWhen(), serverIdent);
-  }
-
-  private List<HashtagsEvent> getHashtagsEvents(Change change, NoteDbUpdateManager manager)
-      throws IOException {
-    String refName = changeMetaRef(change.getId());
-    Optional<ObjectId> old = manager.getChangeRepo().getObjectId(refName);
-    if (!old.isPresent()) {
-      return Collections.emptyList();
-    }
-
-    RevWalk rw = manager.getChangeRepo().rw;
-    List<HashtagsEvent> events = new ArrayList<>();
-    rw.reset();
-    rw.markStart(rw.parseCommit(old.get()));
-    for (RevCommit commit : rw) {
-      Account.Id authorId;
-      try {
-        authorId =
-            changeNoteUtil
-                .getLegacyChangeNoteRead()
-                .parseIdent(commit.getAuthorIdent(), change.getId());
-      } catch (ConfigInvalidException e) {
-        continue; // Corrupt data, no valid hashtags in this commit.
-      }
-      PatchSet.Id psId = parsePatchSetId(change, commit);
-      Set<String> hashtags = parseHashtags(commit);
-      if (authorId == null || psId == null || hashtags == null) {
-        continue;
-      }
-
-      Timestamp commitTime = new Timestamp(commit.getCommitterIdent().getWhen().getTime());
-      events.add(new HashtagsEvent(psId, authorId, commitTime, hashtags, change.getCreatedOn()));
-    }
-    return events;
-  }
-
-  private Set<String> parseHashtags(RevCommit commit) {
-    List<String> hashtagsLines = commit.getFooterLines(FOOTER_HASHTAGS);
-    if (hashtagsLines.isEmpty() || hashtagsLines.size() > 1) {
-      return null;
-    }
-
-    if (hashtagsLines.get(0).isEmpty()) {
-      return ImmutableSet.of();
-    }
-    return Sets.newHashSet(Splitter.on(',').split(hashtagsLines.get(0)));
-  }
-
-  private PatchSet.Id parsePatchSetId(Change change, RevCommit commit) {
-    List<String> psIdLines = commit.getFooterLines(FOOTER_PATCH_SET);
-    if (psIdLines.size() != 1) {
-      return null;
-    }
-    Integer psId = Ints.tryParse(psIdLines.get(0));
-    if (psId == null) {
-      return null;
-    }
-    return new PatchSet.Id(change.getId(), psId);
-  }
-
-  private void deleteChangeMetaRef(Change change, ChainedReceiveCommands cmds) throws IOException {
-    String refName = changeMetaRef(change.getId());
-    Optional<ObjectId> old = cmds.get(refName);
-    if (old.isPresent()) {
-      cmds.add(new ReceiveCommand(old.get(), ObjectId.zeroId(), refName));
-    }
-  }
-
-  private void deleteDraftRefs(Change change, OpenRepo allUsersRepo) throws IOException {
-    for (Ref r :
-        allUsersRepo
-            .repo
-            .getRefDatabase()
-            .getRefsByPrefix(RefNames.refsDraftCommentsPrefix(change.getId()))) {
-      allUsersRepo.cmds.add(new ReceiveCommand(r.getObjectId(), ObjectId.zeroId(), r.getName()));
-    }
-  }
-
-  static void createChange(ChangeUpdate update, Change change) {
-    update.setSubjectForCommit("Create change");
-    update.setChangeId(change.getKey().get());
-    update.setBranch(change.getDest().get());
-    update.setSubject(change.getOriginalSubject());
-    if (change.getRevertOf() != null) {
-      update.setRevertOf(change.getRevertOf().get());
-    }
-  }
-
-  @Override
-  public void rebuildReviewDb(ReviewDb db, Project.NameKey project, Change.Id changeId)
-      throws OrmException {
-    // TODO(dborowitz): Fail fast if changes tables are disabled in ReviewDb.
-    ChangeNotes notes = notesFactory.create(db, project, changeId);
-    ChangeBundle bundle = ChangeBundle.fromNotes(commentsUtil, notes);
-
-    db = ReviewDbUtil.unwrapDb(db);
-    db.changes().beginTransaction(changeId);
-    try {
-      Change c = db.changes().get(changeId);
-      if (c != null) {
-        PrimaryStorage ps = PrimaryStorage.of(c);
-        switch (ps) {
-          case REVIEW_DB:
-            return; // Nothing to do.
-          case NOTE_DB:
-            break; // Continue and rebuild.
-          default:
-            throw new OrmException("primary storage of " + changeId + " is " + ps);
-        }
-      } else {
-        c = notes.getChange();
-      }
-      db.changes().upsert(Collections.singleton(c));
-      putExactlyEntities(
-          db.changeMessages(), db.changeMessages().byChange(c.getId()), bundle.getChangeMessages());
-      putExactlyEntities(db.patchSets(), db.patchSets().byChange(c.getId()), bundle.getPatchSets());
-      putExactlyEntities(
-          db.patchSetApprovals(),
-          db.patchSetApprovals().byChange(c.getId()),
-          bundle.getPatchSetApprovals());
-      putExactlyEntities(
-          db.patchComments(),
-          db.patchComments().byChange(c.getId()),
-          bundle.getPatchLineComments());
-      db.commit();
-    } finally {
-      db.rollback();
-    }
-  }
-
-  private static <T, K extends Key<?>> void putExactlyEntities(
-      Access<T, K> access, Iterable<T> existing, Collection<T> ents) throws OrmException {
-    Set<K> toKeep = access.toMap(ents).keySet();
-    access.delete(
-        FluentIterable.from(existing).filter(e -> !toKeep.contains(access.primaryKey(e))));
-    access.upsert(ents);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java b/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
deleted file mode 100644
index 8f7b387..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-
-class CommentEvent extends Event {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  public final Comment c;
-  private final Change change;
-  private final PatchSet ps;
-  private final PatchListCache cache;
-
-  CommentEvent(Comment c, Change change, PatchSet ps, PatchListCache cache) {
-    super(
-        CommentsUtil.getCommentPsId(change.getId(), c),
-        c.author.getId(),
-        c.getRealAuthor().getId(),
-        c.writtenOn,
-        change.getCreatedOn(),
-        c.tag);
-    this.c = c;
-    this.change = change;
-    this.ps = ps;
-    this.cache = cache;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return false;
-  }
-
-  @Override
-  protected boolean canHaveTag() {
-    return true;
-  }
-
-  @Override
-  void apply(ChangeUpdate update) {
-    checkUpdate(update);
-    if (c.revId == null) {
-      try {
-        setCommentRevId(c, cache, change, ps);
-      } catch (PatchListNotAvailableException e) {
-        logger.atWarning().log(
-            "Unable to determine parent commit of patch set %s (%s); omitting inline comment %s",
-            ps.getId(), ps.getRevision(), c);
-        return;
-      }
-    }
-    update.putComment(PatchLineComment.Status.PUBLISHED, c);
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    helper.add("message", c.message);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java b/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java
deleted file mode 100644
index d8e7480..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.gwtorm.server.OrmException;
-
-/**
- * {@link com.google.gwtorm.server.OrmException} thrown by {@link ChangeRebuilder} when rebuilding a
- * change failed because another operation modified its {@link
- * com.google.gerrit.server.notedb.NoteDbChangeState}.
- */
-public class ConflictingUpdateException extends OrmException {
-  private static final long serialVersionUID = 1L;
-
-  // Always created from a ConflictingUpdateRuntimeException because it originates from an
-  // AtomicUpdate, which cannot throw checked exceptions.
-  ConflictingUpdateException(ConflictingUpdateRuntimeException cause) {
-    super(cause.getMessage(), cause);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java b/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java
deleted file mode 100644
index abfafa2..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmRuntimeException;
-
-class ConflictingUpdateRuntimeException extends OrmRuntimeException {
-  private static final long serialVersionUID = 1L;
-
-  ConflictingUpdateRuntimeException(Change change, String expectedNoteDbState) {
-    super(
-        String.format(
-            "Expected change %s to have noteDbState %s but was %s",
-            change.getId(), expectedNoteDbState, change.getNoteDbState()));
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java b/java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java
deleted file mode 100644
index d01071b..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-
-class CreateChangeEvent extends Event {
-  private final Change change;
-
-  private static PatchSet.Id psId(Change change, Integer minPsNum) {
-    int n;
-    if (minPsNum == null) {
-      // There were no patch sets for the change at all, so something is very
-      // wrong. Bail and use 1 as the patch set.
-      n = 1;
-    } else {
-      n = minPsNum;
-    }
-    return new PatchSet.Id(change.getId(), n);
-  }
-
-  CreateChangeEvent(Change change, Integer minPsNum) {
-    super(
-        psId(change, minPsNum),
-        change.getOwner(),
-        change.getOwner(),
-        change.getCreatedOn(),
-        change.getCreatedOn(),
-        null);
-    this.change = change;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return true;
-  }
-
-  @Override
-  void apply(ChangeUpdate update) throws IOException, OrmException {
-    checkUpdate(update);
-    ChangeRebuilderImpl.createChange(update, change);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java b/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
deleted file mode 100644
index 2a2795d..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.notedb.ChangeDraftUpdate;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-
-class DraftCommentEvent extends Event {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  public final Comment c;
-  private final Change change;
-  private final PatchSet ps;
-  private final PatchListCache cache;
-
-  DraftCommentEvent(Comment c, Change change, PatchSet ps, PatchListCache cache) {
-    super(
-        CommentsUtil.getCommentPsId(change.getId(), c),
-        c.author.getId(),
-        c.getRealAuthor().getId(),
-        c.writtenOn,
-        change.getCreatedOn(),
-        c.tag);
-    this.c = c;
-    this.change = change;
-    this.ps = ps;
-    this.cache = cache;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return false;
-  }
-
-  @Override
-  void apply(ChangeUpdate update) {
-    throw new UnsupportedOperationException();
-  }
-
-  void applyDraft(ChangeDraftUpdate draftUpdate) {
-    if (c.revId == null) {
-      try {
-        setCommentRevId(c, cache, change, ps);
-      } catch (PatchListNotAvailableException e) {
-        logger.atWarning().log(
-            "Unable to determine parent commit of patch set %s (%s);"
-                + " omitting draft inline comment %s",
-            ps.getId(), ps.getRevision(), c);
-        return;
-      }
-    }
-    draftUpdate.putComment(c);
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    helper.add("message", c.message);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/Event.java b/java/com/google/gerrit/server/notedb/rebuild/Event.java
deleted file mode 100644
index 3957c5c..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/Event.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl.MAX_WINDOW_MS;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.ComparisonChain;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.notedb.AbstractChangeUpdate;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-abstract class Event implements Comparable<Event> {
-  // NOTE: EventList only supports direct subclasses, not an arbitrary
-  // hierarchy.
-
-  final Account.Id user;
-  final Account.Id realUser;
-  final String tag;
-  final boolean predatesChange;
-
-  /** Dependencies of this event; other events that must happen before this one. */
-  final List<Event> deps;
-
-  Timestamp when;
-  PatchSet.Id psId;
-
-  protected Event(
-      PatchSet.Id psId,
-      Account.Id effectiveUser,
-      Account.Id realUser,
-      Timestamp when,
-      Timestamp changeCreatedOn,
-      String tag) {
-    this.psId = psId;
-    this.user = effectiveUser;
-    this.realUser = realUser != null ? realUser : effectiveUser;
-    this.tag = tag;
-    // Truncate timestamps at the change's createdOn timestamp.
-    predatesChange = when.before(changeCreatedOn);
-    this.when = predatesChange ? changeCreatedOn : when;
-    deps = new ArrayList<>();
-  }
-
-  protected void checkUpdate(AbstractChangeUpdate update) {
-    checkState(
-        Objects.equals(update.getPatchSetId(), psId),
-        "cannot apply event for %s to update for %s",
-        update.getPatchSetId(),
-        psId);
-    checkState(
-        when.getTime() - update.getWhen().getTime() <= MAX_WINDOW_MS,
-        "event at %s outside update window starting at %s",
-        when,
-        update.getWhen());
-    checkState(
-        Objects.equals(update.getNullableAccountId(), user),
-        "cannot apply event by %s to update by %s",
-        user,
-        update.getNullableAccountId());
-  }
-
-  Event addDep(Event e) {
-    deps.add(e);
-    return this;
-  }
-
-  /**
-   * @return whether this event type must be unique per {@link ChangeUpdate}, i.e. there may be at
-   *     most one of this type.
-   */
-  abstract boolean uniquePerUpdate();
-
-  abstract void apply(ChangeUpdate update) throws OrmException, IOException;
-
-  protected boolean isPostSubmitApproval() {
-    return false;
-  }
-
-  protected boolean isSubmit() {
-    return false;
-  }
-
-  protected boolean canHaveTag() {
-    return false;
-  }
-
-  @Override
-  public String toString() {
-    ToStringHelper helper =
-        MoreObjects.toStringHelper(this)
-            .add("psId", psId)
-            .add("effectiveUser", user)
-            .add("realUser", realUser)
-            .add("when", when)
-            .add("tag", tag);
-    addToString(helper);
-    return helper.toString();
-  }
-
-  /** @param helper toString helper to add fields to */
-  protected void addToString(ToStringHelper helper) {}
-
-  @Override
-  public int compareTo(Event other) {
-    return ComparisonChain.start()
-        .compareFalseFirst(this.isFinalUpdates(), other.isFinalUpdates())
-        .compare(this.when, other.when)
-        .compareTrueFirst(isPatchSet(), isPatchSet())
-        .compareTrueFirst(this.predatesChange, other.predatesChange)
-        .compare(this.user, other.user, ReviewDbUtil.intKeyOrdering())
-        .compare(this.realUser, other.realUser, ReviewDbUtil.intKeyOrdering())
-        .compare(this.psId, other.psId, ReviewDbUtil.intKeyOrdering().nullsLast())
-        .result();
-  }
-
-  private boolean isPatchSet() {
-    return this instanceof PatchSetEvent;
-  }
-
-  private boolean isFinalUpdates() {
-    return this instanceof FinalUpdatesEvent;
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/EventList.java b/java/com/google/gerrit/server/notedb/rebuild/EventList.java
deleted file mode 100644
index e83814d..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/EventList.java
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.collect.Lists;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Objects;
-
-class EventList<E extends Event> implements Iterable<E> {
-  private final ArrayList<E> list = new ArrayList<>();
-  private boolean isSubmit;
-
-  @Override
-  public Iterator<E> iterator() {
-    return list.iterator();
-  }
-
-  void add(E e) {
-    list.add(e);
-    if (e.isSubmit()) {
-      isSubmit = true;
-    }
-  }
-
-  void clear() {
-    list.clear();
-    isSubmit = false;
-  }
-
-  boolean isEmpty() {
-    return list.isEmpty();
-  }
-
-  boolean canAdd(E e) {
-    if (isEmpty()) {
-      return true;
-    }
-    if (e instanceof FinalUpdatesEvent) {
-      return false; // FinalUpdatesEvent always gets its own update.
-    }
-
-    Event last = getLast();
-    if (!Objects.equals(e.user, last.user)
-        || !Objects.equals(e.realUser, last.realUser)
-        || !e.psId.equals(last.psId)) {
-      return false; // Different patch set or author.
-    }
-    if (e.canHaveTag() && canHaveTag() && !Objects.equals(e.tag, getTag())) {
-      // We should trust the tag field, and it doesn't match.
-      return false;
-    }
-    if (e.isPostSubmitApproval() && isSubmit) {
-      // Post-submit approvals must come after the update that submits.
-      return false;
-    }
-
-    long t = e.when.getTime();
-    long tFirst = getFirstTime();
-    long tLast = getLastTime();
-    checkArgument(t >= tLast, "event %s is before previous event in list %s", e, last);
-    if (t - tLast > ChangeRebuilderImpl.MAX_DELTA_MS
-        || t - tFirst > ChangeRebuilderImpl.MAX_WINDOW_MS) {
-      return false; // Too much time elapsed.
-    }
-
-    if (!e.uniquePerUpdate()) {
-      return true;
-    }
-    for (Event o : this) {
-      if (e.getClass() == o.getClass()) {
-        return false; // Only one event of this type allowed per update.
-      }
-    }
-
-    // TODO(dborowitz): Additional heuristics, like keeping events separate if
-    // they affect overlapping fields within a single entity.
-
-    return true;
-  }
-
-  Timestamp getWhen() {
-    return get(0).when;
-  }
-
-  PatchSet.Id getPatchSetId() {
-    PatchSet.Id id = requireNonNull(get(0).psId);
-    for (int i = 1; i < size(); i++) {
-      checkState(
-          get(i).psId.equals(id), "mismatched patch sets in EventList: %s != %s", id, get(i).psId);
-    }
-    return id;
-  }
-
-  Account.Id getAccountId() {
-    Account.Id id = get(0).user;
-    for (int i = 1; i < size(); i++) {
-      checkState(
-          Objects.equals(id, get(i).user),
-          "mismatched users in EventList: %s != %s",
-          id,
-          get(i).user);
-    }
-    return id;
-  }
-
-  Account.Id getRealAccountId() {
-    Account.Id id = get(0).realUser;
-    for (int i = 1; i < size(); i++) {
-      checkState(
-          Objects.equals(id, get(i).realUser),
-          "mismatched real users in EventList: %s != %s",
-          id,
-          get(i).realUser);
-    }
-    return id;
-  }
-
-  String getTag() {
-    for (E e : Lists.reverse(list)) {
-      if (e.tag != null) {
-        return e.tag;
-      }
-    }
-    return null;
-  }
-
-  private boolean canHaveTag() {
-    return list.stream().anyMatch(Event::canHaveTag);
-  }
-
-  private E get(int i) {
-    return list.get(i);
-  }
-
-  private int size() {
-    return list.size();
-  }
-
-  private E getLast() {
-    return list.get(list.size() - 1);
-  }
-
-  private long getLastTime() {
-    return getLast().when.getTime();
-  }
-
-  private long getFirstTime() {
-    return list.get(0).when.getTime();
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/EventSorter.java b/java/com/google/gerrit/server/notedb/rebuild/EventSorter.java
deleted file mode 100644
index 077a027..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/EventSorter.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * Helper to sort a list of events.
- *
- * <p>Events are sorted in two passes:
- *
- * <ol>
- *   <li>Sort by natural order (timestamp, patch set, author, etc.)
- *   <li>Postpone any events with dependencies to occur only after all of their dependencies, where
- *       this violates natural order.
- * </ol>
- *
- * {@link #sort()} modifies the event list in place (similar to {@link Collections#sort(List)}), but
- * does not modify any event. In particular, events might end up out of order with respect to
- * timestamp; callers are responsible for adjusting timestamps later if they prefer monotonicity.
- */
-class EventSorter {
-  private final List<Event> out;
-  private final LinkedHashSet<Event> sorted;
-  private ListMultimap<Event, Event> waiting;
-  private SetMultimap<Event, Event> deps;
-
-  EventSorter(List<Event> events) {
-    LinkedHashSet<Event> all = new LinkedHashSet<>(events);
-    out = events;
-
-    for (Event e : events) {
-      for (Event d : e.deps) {
-        checkArgument(all.contains(d), "dep %s of %s not in input list", d, e);
-      }
-    }
-
-    all.clear();
-    sorted = all; // Presized.
-  }
-
-  void sort() {
-    // First pass: sort by natural order.
-    PriorityQueue<Event> todo = new PriorityQueue<>(out);
-
-    // Populate waiting map after initial sort to preserve natural order.
-    waiting = MultimapBuilder.hashKeys().arrayListValues().build();
-    deps = MultimapBuilder.hashKeys().hashSetValues().build();
-    for (Event e : todo) {
-      for (Event d : e.deps) {
-        deps.put(e, d);
-        waiting.put(d, e);
-      }
-    }
-
-    // Second pass: enforce dependencies.
-    int size = out.size();
-    while (!todo.isEmpty()) {
-      process(todo.remove(), todo);
-    }
-    checkState(
-        sorted.size() == size, "event sort expected %s elements, got %s", size, sorted.size());
-
-    // Modify out in-place a la Collections#sort.
-    out.clear();
-    out.addAll(sorted);
-  }
-
-  void process(Event e, PriorityQueue<Event> todo) {
-    if (sorted.contains(e)) {
-      return; // Already emitted.
-    }
-    if (!deps.get(e).isEmpty()) {
-      // Not all events that e depends on have been emitted yet. Ignore e for
-      // now; it will get added back to the queue in the block below once its
-      // last dependency is processed.
-      return;
-    }
-
-    // All events that e depends on have been emitted, so e can be emitted.
-    sorted.add(e);
-
-    // Remove e from the dependency set of all events waiting on e, and add
-    // those events back to the queue in the original priority order for
-    // reconsideration.
-    for (Event w : waiting.get(e)) {
-      deps.get(w).remove(e);
-      todo.add(w);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java b/java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java
deleted file mode 100644
index 55d5a31..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.ImmutableCollection;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.util.Objects;
-
-class FinalUpdatesEvent extends Event {
-  private final Change change;
-  private final Change noteDbChange;
-  private final ImmutableCollection<PatchSet> patchSets;
-
-  FinalUpdatesEvent(Change change, Change noteDbChange, ImmutableCollection<PatchSet> patchSets) {
-    super(
-        change.currentPatchSetId(),
-        change.getOwner(),
-        change.getOwner(),
-        change.getLastUpdatedOn(),
-        change.getCreatedOn(),
-        null);
-    this.change = change;
-    this.noteDbChange = noteDbChange;
-    this.patchSets = patchSets;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return true;
-  }
-
-  @SuppressWarnings("deprecation")
-  @Override
-  void apply(ChangeUpdate update) throws OrmException {
-    if (!Objects.equals(change.getTopic(), noteDbChange.getTopic())) {
-      update.setTopic(change.getTopic());
-    }
-    if (!statusMatches()) {
-      // TODO(dborowitz): Stamp approximate approvals at this time.
-      update.fixStatus(change.getStatus());
-    }
-    if (change.isPrivate() != noteDbChange.isPrivate()) {
-      update.setPrivate(change.isPrivate());
-    }
-    if (change.isWorkInProgress() != noteDbChange.isWorkInProgress()) {
-      update.setWorkInProgress(change.isWorkInProgress());
-    }
-    if (change.getSubmissionId() != null && noteDbChange.getSubmissionId() == null) {
-      update.setSubmissionId(change.getSubmissionId());
-    }
-    if (!Objects.equals(change.getAssignee(), noteDbChange.getAssignee())) {
-      // TODO(dborowitz): Parse intermediate values out from messages.
-      update.setAssignee(change.getAssignee());
-    }
-    if (!patchSets.isEmpty() && !highestNumberedPatchSetIsCurrent()) {
-      update.setCurrentPatchSet();
-    }
-    if (!update.isEmpty()) {
-      update.setSubjectForCommit("Final NoteDb migration updates");
-    }
-  }
-
-  private boolean statusMatches() {
-    return Objects.equals(change.getStatus(), noteDbChange.getStatus());
-  }
-
-  private boolean highestNumberedPatchSetIsCurrent() {
-    PatchSet.Id max = patchSets.stream().map(PatchSet::getId).max(intKeyOrdering()).get();
-    return max.equals(change.currentPatchSetId());
-  }
-
-  @Override
-  protected boolean isSubmit() {
-    return change.getStatus() == Change.Status.MERGED;
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    if (!statusMatches()) {
-      helper.add("status", change.getStatus());
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java b/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java
deleted file mode 100644
index f2a5cc6..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_GC_SECTION;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_AUTO;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GarbageCollectionResult;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GarbageCollection;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.function.Consumer;
-import org.eclipse.jgit.lib.Repository;
-
-@Singleton
-public class GcAllUsers {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private final AllUsersName allUsers;
-  private final GarbageCollection.Factory gcFactory;
-  private final GitRepositoryManager repoManager;
-
-  @Inject
-  GcAllUsers(
-      AllUsersName allUsers,
-      GarbageCollection.Factory gcFactory,
-      GitRepositoryManager repoManager) {
-    this.allUsers = allUsers;
-    this.gcFactory = gcFactory;
-    this.repoManager = repoManager;
-  }
-
-  public void runWithLogger() {
-    // Print log messages using logger, and skip progress.
-    run(s -> logger.atInfo().log(s), null);
-  }
-
-  public void run(PrintWriter writer) {
-    // Print both log messages and progress to given writer.
-    run(requireNonNull(writer)::println, writer);
-  }
-
-  private void run(Consumer<String> logOneLine, @Nullable PrintWriter progressWriter) {
-    if (!(repoManager instanceof LocalDiskRepositoryManager)) {
-      logOneLine.accept("Skipping GC of " + allUsers + "; not a local disk repo");
-      return;
-    }
-    if (!enableAutoGc(logOneLine)) {
-      logOneLine.accept(
-          "Skipping GC of "
-              + allUsers
-              + " due to disabling "
-              + CONFIG_GC_SECTION
-              + "."
-              + CONFIG_KEY_AUTO);
-      logOneLine.accept(
-          "If loading accounts is slow after the NoteDb migration, run `git gc` on "
-              + allUsers
-              + " manually");
-      return;
-    }
-
-    if (progressWriter == null) {
-      // Mimic log line from GarbageCollection.
-      logOneLine.accept("collecting garbage for \"" + allUsers + "\":\n");
-    }
-    GarbageCollectionResult result =
-        gcFactory.create().run(ImmutableList.of(allUsers), progressWriter);
-    if (!result.hasErrors()) {
-      return;
-    }
-    for (GarbageCollectionResult.Error e : result.getErrors()) {
-      switch (e.getType()) {
-        case GC_ALREADY_SCHEDULED:
-          logOneLine.accept("GC already scheduled for " + e.getProjectName());
-          break;
-        case GC_FAILED:
-          logOneLine.accept("GC failed for " + e.getProjectName());
-          break;
-        case REPOSITORY_NOT_FOUND:
-          logOneLine.accept(e.getProjectName() + " repo not found");
-          break;
-        default:
-          logOneLine.accept("GC failed for " + e.getProjectName() + ": " + e.getType());
-          break;
-      }
-    }
-  }
-
-  private boolean enableAutoGc(Consumer<String> logOneLine) {
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      return repo.getConfig().getInt(CONFIG_GC_SECTION, CONFIG_KEY_AUTO, -1) != 0;
-    } catch (IOException e) {
-      logOneLine.accept(
-          "Error reading config for " + allUsers + ":\n" + Throwables.getStackTraceAsString(e));
-      return false;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java b/java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java
deleted file mode 100644
index 4f6f6ad..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.sql.Timestamp;
-import java.util.Set;
-
-class HashtagsEvent extends Event {
-  private final Set<String> hashtags;
-
-  HashtagsEvent(
-      PatchSet.Id psId,
-      Account.Id who,
-      Timestamp when,
-      Set<String> hashtags,
-      Timestamp changeCreatdOn) {
-    super(
-        psId,
-        who,
-        who,
-        when,
-        changeCreatdOn,
-        // Somewhat confusingly, hashtags do not use the setTag method on
-        // AbstractChangeUpdate, so pass null as the tag.
-        null);
-    this.hashtags = hashtags;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    // Since these are produced from existing commits in the old NoteDb graph,
-    // we know that there must be one per commit in the rebuilt graph.
-    return true;
-  }
-
-  @Override
-  void apply(ChangeUpdate update) throws OrmException {
-    update.setHashtags(hashtags);
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    helper.add("hashtags", hashtags);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/MigrationException.java b/java/com/google/gerrit/server/notedb/rebuild/MigrationException.java
deleted file mode 100644
index 0cf78be..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/MigrationException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import java.io.IOException;
-
-/** Exception thrown by {@link NoteDbMigrator} when migration fails. */
-public class MigrationException extends IOException {
-  private static final long serialVersionUID = 1L;
-
-  MigrationException(String message) {
-    super(message);
-  }
-
-  MigrationException(String message, Throwable why) {
-    super(message, why);
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
deleted file mode 100644
index f60744f..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ /dev/null
@@ -1,1035 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.WRITE;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Comparator.comparing;
-import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Streams;
-import com.google.common.flogger.FluentLogger;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.common.FormatUtil;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.git.LockFailureException;
-import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.InternalUser;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfigProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.NoteDbSchemaVersionManager;
-import com.google.gerrit.server.notedb.NoteDbTable;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.gerrit.server.notedb.PrimaryStorageMigrator;
-import com.google.gerrit.server.notedb.PrimaryStorageMigrator.NoNoteDbStateException;
-import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
-import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.update.ChainedReceiveCommands;
-import com.google.gerrit.server.util.ManualRequestContext;
-import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.storage.file.PackInserter;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.io.NullOutputStream;
-
-/** One stop shop for migrating a site's change storage from ReviewDb to NoteDb. */
-public class NoteDbMigrator implements AutoCloseable {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private static final String AUTO_MIGRATE = "autoMigrate";
-  private static final String TRIAL = "trial";
-
-  public static boolean getAutoMigrate(Config cfg) {
-    return cfg.getBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), AUTO_MIGRATE, false);
-  }
-
-  private static void setAutoMigrate(Config cfg, boolean autoMigrate) {
-    cfg.setBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), AUTO_MIGRATE, autoMigrate);
-  }
-
-  public static boolean getTrialMode(Config cfg) {
-    return cfg.getBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), TRIAL, false);
-  }
-
-  public static void setTrialMode(Config cfg, boolean trial) {
-    cfg.setBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), TRIAL, trial);
-  }
-
-  public static class Builder {
-    private final Config cfg;
-    private final SitePaths sitePaths;
-    private final Provider<PersonIdent> serverIdent;
-    private final AllUsersName allUsers;
-    private final SchemaFactory<ReviewDb> schemaFactory;
-    private final GitRepositoryManager repoManager;
-    private final NoteDbUpdateManager.Factory updateManagerFactory;
-    private final ChangeBundleReader bundleReader;
-    private final AllProjectsName allProjects;
-    private final InternalUser.Factory userFactory;
-    private final ThreadLocalRequestContext requestContext;
-    private final ChangeRebuilderImpl rebuilder;
-    private final WorkQueue workQueue;
-    private final MutableNotesMigration globalNotesMigration;
-    private final PrimaryStorageMigrator primaryStorageMigrator;
-    private final PluginSetContext<NotesMigrationStateListener> listeners;
-    private final NoteDbSchemaVersionManager versionManager;
-
-    private int threads;
-    private ImmutableList<Project.NameKey> projects = ImmutableList.of();
-    private ImmutableList<Change.Id> changes = ImmutableList.of();
-    private OutputStream progressOut = NullOutputStream.INSTANCE;
-    private NotesMigrationState stopAtState;
-    private boolean trial;
-    private boolean forceRebuild;
-    private int sequenceGap = -1;
-    private boolean autoMigrate;
-
-    @Inject
-    Builder(
-        GerritServerConfigProvider configProvider,
-        SitePaths sitePaths,
-        @GerritPersonIdent Provider<PersonIdent> serverIdent,
-        AllUsersName allUsers,
-        SchemaFactory<ReviewDb> schemaFactory,
-        GitRepositoryManager repoManager,
-        NoteDbUpdateManager.Factory updateManagerFactory,
-        ChangeBundleReader bundleReader,
-        AllProjectsName allProjects,
-        ThreadLocalRequestContext requestContext,
-        InternalUser.Factory userFactory,
-        ChangeRebuilderImpl rebuilder,
-        WorkQueue workQueue,
-        MutableNotesMigration globalNotesMigration,
-        PrimaryStorageMigrator primaryStorageMigrator,
-        PluginSetContext<NotesMigrationStateListener> listeners,
-        NoteDbSchemaVersionManager versionManager) {
-      // Reload gerrit.config/notedb.config on each migrator invocation, in case a previous
-      // migration in the same process modified the on-disk contents. This ensures the defaults for
-      // trial/autoMigrate get set correctly below.
-      this.cfg = configProvider.loadConfig();
-      this.sitePaths = sitePaths;
-      this.serverIdent = serverIdent;
-      this.allUsers = allUsers;
-      this.schemaFactory = schemaFactory;
-      this.repoManager = repoManager;
-      this.updateManagerFactory = updateManagerFactory;
-      this.bundleReader = bundleReader;
-      this.allProjects = allProjects;
-      this.requestContext = requestContext;
-      this.userFactory = userFactory;
-      this.rebuilder = rebuilder;
-      this.workQueue = workQueue;
-      this.globalNotesMigration = globalNotesMigration;
-      this.primaryStorageMigrator = primaryStorageMigrator;
-      this.listeners = listeners;
-      this.versionManager = versionManager;
-      this.trial = getTrialMode(cfg);
-      this.autoMigrate = getAutoMigrate(cfg);
-    }
-
-    /**
-     * Set the number of threads used by parallelizable phases of the migration, such as rebuilding
-     * all changes.
-     *
-     * <p>Not all phases are parallelizable, and calling {@link #rebuild()} directly will do
-     * substantial work in the calling thread regardless of the number of threads configured.
-     *
-     * <p>By default, all work is done in the calling thread.
-     *
-     * @param threads thread count; if less than 2, all work happens in the calling thread.
-     * @return this.
-     */
-    public Builder setThreads(int threads) {
-      this.threads = threads;
-      return this;
-    }
-
-    /**
-     * Limit the set of projects that are processed.
-     *
-     * <p>Incompatible with {@link #setChanges(Collection)}.
-     *
-     * <p>By default, all projects will be processed.
-     *
-     * @param projects set of projects; if null or empty, all projects will be processed.
-     * @return this.
-     */
-    public Builder setProjects(@Nullable Collection<Project.NameKey> projects) {
-      this.projects = projects != null ? ImmutableList.copyOf(projects) : ImmutableList.of();
-      return this;
-    }
-
-    /**
-     * Limit the set of changes that are processed.
-     *
-     * <p>Incompatible with {@link #setProjects(Collection)}.
-     *
-     * <p>By default, all changes will be processed.
-     *
-     * @param changes set of changes; if null or empty, all changes will be processed.
-     * @return this.
-     */
-    public Builder setChanges(@Nullable Collection<Change.Id> changes) {
-      this.changes = changes != null ? ImmutableList.copyOf(changes) : ImmutableList.of();
-      return this;
-    }
-
-    /**
-     * Set output stream for progress monitors.
-     *
-     * <p>By default, there is no progress monitor output (although there may be other logs).
-     *
-     * @param progressOut output stream.
-     * @return this.
-     */
-    public Builder setProgressOut(OutputStream progressOut) {
-      this.progressOut = requireNonNull(progressOut);
-      return this;
-    }
-
-    /**
-     * Stop at a specific migration state, for testing only.
-     *
-     * @param stopAtState state to stop at.
-     * @return this.
-     */
-    @VisibleForTesting
-    public Builder setStopAtStateForTesting(NotesMigrationState stopAtState) {
-      this.stopAtState = stopAtState;
-      return this;
-    }
-
-    /**
-     * Rebuild in "trial mode": configure Gerrit to write to and read from NoteDb, but leave
-     * ReviewDb as the source of truth for all changes.
-     *
-     * <p>By default, trial mode is off, and NoteDb is the source of truth for all changes following
-     * the migration.
-     *
-     * @param trial whether to rebuild in trial mode.
-     * @return this.
-     */
-    public Builder setTrialMode(boolean trial) {
-      this.trial = trial;
-      return this;
-    }
-
-    /**
-     * Rebuild all changes in NoteDb from ReviewDb, even if Gerrit is currently configured to read
-     * from NoteDb.
-     *
-     * <p>Only supported if ReviewDb is still the source of truth for all changes.
-     *
-     * <p>By default, force rebuilding is off.
-     *
-     * @param forceRebuild whether to force rebuilding.
-     * @return this.
-     */
-    public Builder setForceRebuild(boolean forceRebuild) {
-      this.forceRebuild = forceRebuild;
-      return this;
-    }
-
-    /**
-     * Gap between ReviewDb change sequence numbers and NoteDb.
-     *
-     * <p>If NoteDb sequences are enabled in a running server, there is a race between the migration
-     * step that calls {@code nextChangeId()} to seed the ref, and other threads that call {@code
-     * nextChangeId()} to create new changes. In order to prevent these operations stepping on one
-     * another, we use this value to skip some predefined sequence numbers. This is strongly
-     * recommended in a running server.
-     *
-     * <p>If the migration takes place offline, there is no race with other threads, and this option
-     * may be set to 0. However, admins may still choose to use a gap, for example to make it easier
-     * to distinguish changes that were created before and after the NoteDb migration.
-     *
-     * <p>By default, uses the value from {@code noteDb.changes.initialSequenceGap} in {@code
-     * gerrit.config}, which defaults to 1000.
-     *
-     * @param sequenceGap sequence gap size; if negative, use the default.
-     * @return this.
-     */
-    public Builder setSequenceGap(int sequenceGap) {
-      this.sequenceGap = sequenceGap;
-      return this;
-    }
-
-    /**
-     * Enable auto-migration on subsequent daemon launches.
-     *
-     * <p>If true, prior to running any migration steps, sets the necessary configuration in {@code
-     * gerrit.config} to make {@code gerrit.war daemon} retry the migration on next startup, if it
-     * fails.
-     *
-     * @param autoMigrate whether to set auto-migration config.
-     * @return this.
-     */
-    public Builder setAutoMigrate(boolean autoMigrate) {
-      this.autoMigrate = autoMigrate;
-      return this;
-    }
-
-    public NoteDbMigrator build() throws MigrationException {
-      return new NoteDbMigrator(
-          sitePaths,
-          schemaFactory,
-          serverIdent,
-          allUsers,
-          repoManager,
-          updateManagerFactory,
-          bundleReader,
-          allProjects,
-          requestContext,
-          userFactory,
-          rebuilder,
-          globalNotesMigration,
-          primaryStorageMigrator,
-          listeners,
-          versionManager,
-          threads > 1
-              ? MoreExecutors.listeningDecorator(
-                  workQueue.createQueue(threads, "RebuildChange", true))
-              : MoreExecutors.newDirectExecutorService(),
-          projects,
-          changes,
-          progressOut,
-          stopAtState,
-          trial,
-          forceRebuild,
-          sequenceGap >= 0 ? sequenceGap : Sequences.getChangeSequenceGap(cfg),
-          autoMigrate);
-    }
-  }
-
-  private final FileBasedConfig gerritConfig;
-  private final FileBasedConfig noteDbConfig;
-  private final SchemaFactory<ReviewDb> schemaFactory;
-  private final Provider<PersonIdent> serverIdent;
-  private final AllUsersName allUsers;
-  private final GitRepositoryManager repoManager;
-  private final NoteDbUpdateManager.Factory updateManagerFactory;
-  private final ChangeBundleReader bundleReader;
-  private final AllProjectsName allProjects;
-  private final ThreadLocalRequestContext requestContext;
-  private final InternalUser.Factory userFactory;
-  private final ChangeRebuilderImpl rebuilder;
-  private final MutableNotesMigration globalNotesMigration;
-  private final PrimaryStorageMigrator primaryStorageMigrator;
-  private final PluginSetContext<NotesMigrationStateListener> listeners;
-  private final NoteDbSchemaVersionManager versionManager;
-
-  private final ListeningExecutorService executor;
-  private final ImmutableList<Project.NameKey> projects;
-  private final ImmutableList<Change.Id> changes;
-  private final OutputStream progressOut;
-  private final NotesMigrationState stopAtState;
-  private final boolean trial;
-  private final boolean forceRebuild;
-  private final int sequenceGap;
-  private final boolean autoMigrate;
-
-  private NoteDbMigrator(
-      SitePaths sitePaths,
-      SchemaFactory<ReviewDb> schemaFactory,
-      Provider<PersonIdent> serverIdent,
-      AllUsersName allUsers,
-      GitRepositoryManager repoManager,
-      NoteDbUpdateManager.Factory updateManagerFactory,
-      ChangeBundleReader bundleReader,
-      AllProjectsName allProjects,
-      ThreadLocalRequestContext requestContext,
-      InternalUser.Factory userFactory,
-      ChangeRebuilderImpl rebuilder,
-      MutableNotesMigration globalNotesMigration,
-      PrimaryStorageMigrator primaryStorageMigrator,
-      PluginSetContext<NotesMigrationStateListener> listeners,
-      NoteDbSchemaVersionManager versionManager,
-      ListeningExecutorService executor,
-      ImmutableList<Project.NameKey> projects,
-      ImmutableList<Change.Id> changes,
-      OutputStream progressOut,
-      NotesMigrationState stopAtState,
-      boolean trial,
-      boolean forceRebuild,
-      int sequenceGap,
-      boolean autoMigrate)
-      throws MigrationException {
-    if (!changes.isEmpty() && !projects.isEmpty()) {
-      throw new MigrationException("Cannot set both changes and projects");
-    }
-    if (sequenceGap < 0) {
-      throw new MigrationException("Sequence gap must be non-negative: " + sequenceGap);
-    }
-
-    this.schemaFactory = schemaFactory;
-    this.serverIdent = serverIdent;
-    this.allUsers = allUsers;
-    this.rebuilder = rebuilder;
-    this.repoManager = repoManager;
-    this.updateManagerFactory = updateManagerFactory;
-    this.bundleReader = bundleReader;
-    this.allProjects = allProjects;
-    this.requestContext = requestContext;
-    this.userFactory = userFactory;
-    this.globalNotesMigration = globalNotesMigration;
-    this.primaryStorageMigrator = primaryStorageMigrator;
-    this.listeners = listeners;
-    this.versionManager = versionManager;
-    this.executor = executor;
-    this.projects = projects;
-    this.changes = changes;
-    this.progressOut = progressOut;
-    this.stopAtState = stopAtState;
-    this.trial = trial;
-    this.forceRebuild = forceRebuild;
-    this.sequenceGap = sequenceGap;
-    this.autoMigrate = autoMigrate;
-
-    // Stack notedb.config over gerrit.config, in the same way as GerritServerConfigProvider.
-    this.gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
-    this.noteDbConfig =
-        new FileBasedConfig(gerritConfig, sitePaths.notedb_config.toFile(), FS.detect());
-  }
-
-  @Override
-  public void close() {
-    executor.shutdownNow();
-  }
-
-  public void migrate() throws OrmException, IOException {
-    if (!changes.isEmpty() || !projects.isEmpty()) {
-      throw new MigrationException(
-          "Cannot set changes or projects during full migration; call rebuild() instead");
-    }
-    Optional<NotesMigrationState> maybeState = loadState();
-    if (!maybeState.isPresent()) {
-      throw new MigrationException("Could not determine initial migration state");
-    }
-
-    NotesMigrationState state = maybeState.get();
-    if (trial && state.compareTo(READ_WRITE_NO_SEQUENCE) > 0) {
-      throw new MigrationException(
-          "Migration has already progressed past the endpoint of the \"trial mode\" state;"
-              + " NoteDb is already the primary storage for some changes");
-    }
-    if (forceRebuild && state.compareTo(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY) > 0) {
-      throw new MigrationException(
-          "Cannot force rebuild changes; NoteDb is already the primary storage for some changes");
-    }
-    setControlFlags();
-
-    boolean rebuilt = false;
-    while (state.compareTo(NOTE_DB) < 0) {
-      if (state.equals(stopAtState)) {
-        return;
-      }
-      boolean stillNeedsRebuild = forceRebuild && !rebuilt;
-      if (trial && state.compareTo(READ_WRITE_NO_SEQUENCE) >= 0) {
-        if (stillNeedsRebuild && state == READ_WRITE_NO_SEQUENCE) {
-          // We're at the end state of trial mode, but still need a rebuild due to forceRebuild. Let
-          // the loop go one more time.
-        } else {
-          return;
-        }
-      }
-      switch (state) {
-        case REVIEW_DB:
-          state = turnOnWrites(state);
-          break;
-        case WRITE:
-          state = rebuildAndEnableReads(state);
-          rebuilt = true;
-          break;
-        case READ_WRITE_NO_SEQUENCE:
-          if (stillNeedsRebuild) {
-            state = rebuildAndEnableReads(state);
-            rebuilt = true;
-          } else {
-            state = enableSequences(state);
-          }
-          break;
-        case READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY:
-          if (stillNeedsRebuild) {
-            state = rebuildAndEnableReads(state);
-            rebuilt = true;
-          } else {
-            state = setNoteDbPrimary(state);
-          }
-          break;
-        case READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY:
-          // The only way we can get here is if there was a failure on a previous run of
-          // setNoteDbPrimary, since that method moves to NOTE_DB if it completes
-          // successfully. Assume that not all changes were converted and re-run the step.
-          // migrateToNoteDbPrimary is a relatively fast no-op for already-migrated changes, so this
-          // isn't actually repeating work.
-          state = setNoteDbPrimary(state);
-          break;
-        case NOTE_DB:
-          // Done!
-          break;
-        default:
-          throw new MigrationException(
-              "Migration out of the following state is not supported:\n" + state.toText());
-      }
-    }
-  }
-
-  private NotesMigrationState turnOnWrites(NotesMigrationState prev)
-      throws OrmException, IOException {
-    versionManager.init();
-    return saveState(prev, WRITE);
-  }
-
-  private NotesMigrationState rebuildAndEnableReads(NotesMigrationState prev)
-      throws OrmException, IOException {
-    rebuild();
-    return saveState(prev, READ_WRITE_NO_SEQUENCE);
-  }
-
-  private NotesMigrationState enableSequences(NotesMigrationState prev)
-      throws OrmException, IOException {
-    try (ReviewDb db = schemaFactory.open()) {
-      @SuppressWarnings("deprecation")
-      final int nextChangeId = db.nextChangeId();
-
-      RepoSequence seq =
-          new RepoSequence(
-              repoManager,
-              GitReferenceUpdated.DISABLED,
-              allProjects,
-              Sequences.NAME_CHANGES,
-              // If sequenceGap is 0, this writes into the sequence ref the same ID that is returned
-              // by the call to seq.next() below. If we actually used this as a change ID, that
-              // would be a problem, but we just discard it, so this is safe.
-              () -> nextChangeId + sequenceGap - 1,
-              1,
-              nextChangeId);
-      seq.next();
-    }
-    return saveState(prev, READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);
-  }
-
-  private NotesMigrationState setNoteDbPrimary(NotesMigrationState prev)
-      throws MigrationException, OrmException, IOException {
-    checkState(
-        projects.isEmpty() && changes.isEmpty(),
-        "Should not have attempted setNoteDbPrimary with a subset of changes");
-    checkState(
-        prev == READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY
-            || prev == READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY,
-        "Unexpected start state for setNoteDbPrimary: %s",
-        prev);
-
-    // Before changing the primary storage of old changes, ensure new changes are created with
-    // NoteDb primary.
-    prev = saveState(prev, READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY);
-
-    Stopwatch sw = Stopwatch.createStarted();
-    logger.atInfo().log("Setting primary storage to NoteDb");
-    List<Change.Id> allChanges;
-    try (ReviewDb db = unwrapDb(schemaFactory.open())) {
-      allChanges = Streams.stream(db.changes().all()).map(Change::getId).collect(toList());
-    }
-
-    try (ContextHelper contextHelper = new ContextHelper()) {
-      List<ListenableFuture<Boolean>> futures =
-          allChanges
-              .stream()
-              .map(
-                  id ->
-                      executor.submit(
-                          () -> {
-                            try (ManualRequestContext ctx = contextHelper.open()) {
-                              try {
-                                primaryStorageMigrator.migrateToNoteDbPrimary(id);
-                              } catch (NoNoteDbStateException e) {
-                                if (canSkipPrimaryStorageMigration(
-                                    ctx.getReviewDbProvider().get(), id)) {
-                                  logger.atWarning().withCause(e).log(
-                                      "Change %s previously failed to rebuild;"
-                                          + " skipping primary storage migration",
-                                      id);
-                                } else {
-                                  throw e;
-                                }
-                              }
-                              return true;
-                            } catch (Exception e) {
-                              logger.atSevere().withCause(e).log(
-                                  "Error migrating primary storage for %s", id);
-                              return false;
-                            }
-                          }))
-              .collect(toList());
-
-      boolean ok = futuresToBoolean(futures, "Error migrating primary storage");
-      double t = sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
-      logger.atInfo().log(
-          "Migrated primary storage of %d changes in %.01fs (%.01f/s)\n",
-          allChanges.size(), t, allChanges.size() / t);
-      if (!ok) {
-        throw new MigrationException("Migrating primary storage for some changes failed, see log");
-      }
-    }
-
-    return disableReviewDb(prev);
-  }
-
-  /**
-   * Checks whether a change is so corrupt that it can be completely skipped by the primary storage
-   * migration step.
-   *
-   * <p>To get to the point where this method is called from {@link #setNoteDbPrimary}, it means we
-   * attempted to rebuild it, and encountered an error that was then caught in {@link
-   * #rebuildProject} and skipped. As a result, there is no {@code noteDbState} field in the change
-   * by the time we get to {@link #setNoteDbPrimary}, so {@code migrateToNoteDbPrimary} throws an
-   * exception.
-   *
-   * <p>We have to do this hacky double-checking because we don't have a way for the rebuilding
-   * phase to communicate to the primary storage migration phase that the change is skippable. It
-   * would be possible to store this info in some field in this class, but there is no guarantee
-   * that the rebuild and primary storage migration phases are run in the same JVM invocation.
-   *
-   * <p>In an ideal world, we could do this through the {@link
-   * com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage} enum, having a separate value
-   * for errors. However, that would be an invasive change touching many non-migration-related parts
-   * of the NoteDb migration code, which is too risky to attempt in the stable branch where this bug
-   * had to be fixed.
-   *
-   * <p>As of this writing, the only case where this happens is when a change has no patch sets.
-   */
-  private static boolean canSkipPrimaryStorageMigration(ReviewDb db, Change.Id id) {
-    try {
-      return Iterables.isEmpty(unwrapDb(db).patchSets().byChange(id));
-    } catch (Exception e) {
-      logger.atSevere().withCause(e).log(
-          "Error checking if change %s can be skipped, assuming no", id);
-      return false;
-    }
-  }
-
-  private NotesMigrationState disableReviewDb(NotesMigrationState prev) throws IOException {
-    return saveState(prev, NOTE_DB, c -> setAutoMigrate(c, false));
-  }
-
-  private Optional<NotesMigrationState> loadState() throws IOException {
-    try {
-      gerritConfig.load();
-      noteDbConfig.load();
-      return NotesMigrationState.forConfig(noteDbConfig);
-    } catch (ConfigInvalidException | IllegalArgumentException e) {
-      logger.atWarning().withCause(e).log(
-          "error reading NoteDb migration options from %s", noteDbConfig.getFile());
-      return Optional.empty();
-    }
-  }
-
-  private NotesMigrationState saveState(
-      NotesMigrationState expectedOldState, NotesMigrationState newState) throws IOException {
-    return saveState(expectedOldState, newState, c -> {});
-  }
-
-  private NotesMigrationState saveState(
-      NotesMigrationState expectedOldState,
-      NotesMigrationState newState,
-      Consumer<Config> additionalUpdates)
-      throws IOException {
-    synchronized (globalNotesMigration) {
-      // This read-modify-write is racy. We're counting on the fact that no other Gerrit operation
-      // modifies gerrit.config, and hoping that admins don't either.
-      Optional<NotesMigrationState> actualOldState = loadState();
-      if (!actualOldState.equals(Optional.of(expectedOldState))) {
-        throw new MigrationException(
-            "Cannot move to new state:\n"
-                + newState.toText()
-                + "\n\n"
-                + "Expected this state in gerrit.config:\n"
-                + expectedOldState.toText()
-                + "\n\n"
-                + (actualOldState.isPresent()
-                    ? "But found this state:\n" + actualOldState.get().toText()
-                    : "But could not parse the current state"));
-      }
-
-      preStateChange(expectedOldState, newState);
-
-      newState.setConfigValues(noteDbConfig);
-      additionalUpdates.accept(noteDbConfig);
-      noteDbConfig.save();
-
-      // Only set in-memory state once it's been persisted to storage.
-      globalNotesMigration.setFrom(newState);
-      logger.atInfo().log("Migration state: %s => %s", expectedOldState, newState);
-
-      return newState;
-    }
-  }
-
-  private void preStateChange(NotesMigrationState oldState, NotesMigrationState newState)
-      throws IOException {
-    listeners.runEach(l -> l.preStateChange(oldState, newState), IOException.class);
-  }
-
-  private void setControlFlags() throws MigrationException {
-    synchronized (globalNotesMigration) {
-      try {
-        noteDbConfig.load();
-        setAutoMigrate(noteDbConfig, autoMigrate);
-        setTrialMode(noteDbConfig, trial);
-        noteDbConfig.save();
-      } catch (ConfigInvalidException | IOException e) {
-        throw new MigrationException("Error saving auto-migration config", e);
-      }
-    }
-  }
-
-  public void rebuild() throws MigrationException, OrmException {
-    if (!globalNotesMigration.commitChangeWrites()) {
-      throw new MigrationException("Cannot rebuild without noteDb.changes.write=true");
-    }
-    Stopwatch sw = Stopwatch.createStarted();
-    logger.atInfo().log("Rebuilding changes in NoteDb");
-
-    ImmutableListMultimap<Project.NameKey, Change.Id> changesByProject = getChangesByProject();
-    List<ListenableFuture<Boolean>> futures = new ArrayList<>();
-    try (ContextHelper contextHelper = new ContextHelper()) {
-      List<Project.NameKey> projectNames =
-          Ordering.usingToString().sortedCopy(changesByProject.keySet());
-      for (Project.NameKey project : projectNames) {
-        ListenableFuture<Boolean> future =
-            executor.submit(
-                () -> {
-                  try {
-                    return rebuildProject(contextHelper.getReviewDb(), changesByProject, project);
-                  } catch (Exception e) {
-                    logger.atSevere().withCause(e).log("Error rebuilding project %s", project);
-                    return false;
-                  }
-                });
-        futures.add(future);
-      }
-
-      boolean ok = futuresToBoolean(futures, "Error rebuilding projects");
-      double t = sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
-      logger.atInfo().log(
-          "Rebuilt %d changes in %.01fs (%.01f/s)\n",
-          changesByProject.size(), t, changesByProject.size() / t);
-      if (!ok) {
-        throw new MigrationException("Rebuilding some changes failed, see log");
-      }
-    }
-  }
-
-  private ImmutableListMultimap<Project.NameKey, Change.Id> getChangesByProject()
-      throws OrmException {
-    // Memoize all changes so we can close the db connection and allow other threads to use the full
-    // connection pool.
-    SetMultimap<Project.NameKey, Change.Id> out =
-        MultimapBuilder.treeKeys(comparing(Project.NameKey::get))
-            .treeSetValues(comparing(Change.Id::get))
-            .build();
-    try (ReviewDb db = unwrapDb(schemaFactory.open())) {
-      if (!projects.isEmpty()) {
-        return byProject(db.changes().all(), c -> projects.contains(c.getProject()), out);
-      }
-      if (!changes.isEmpty()) {
-        return byProject(db.changes().get(changes), c -> true, out);
-      }
-      return byProject(db.changes().all(), c -> true, out);
-    }
-  }
-
-  private static ImmutableListMultimap<Project.NameKey, Change.Id> byProject(
-      Iterable<Change> changes,
-      Predicate<Change> pred,
-      SetMultimap<Project.NameKey, Change.Id> out) {
-    Streams.stream(changes).filter(pred).forEach(c -> out.put(c.getProject(), c.getId()));
-    return ImmutableListMultimap.copyOf(out);
-  }
-
-  private static ObjectInserter newPackInserter(Repository repo) {
-    if (!(repo instanceof FileRepository)) {
-      return repo.newObjectInserter();
-    }
-    PackInserter ins = ((FileRepository) repo).getObjectDatabase().newPackInserter();
-    ins.checkExisting(false);
-    return ins;
-  }
-
-  private boolean rebuildProject(
-      ReviewDb db,
-      ImmutableListMultimap<Project.NameKey, Change.Id> allChanges,
-      Project.NameKey project) {
-    checkArgument(allChanges.containsKey(project));
-    boolean ok = true;
-    ProgressMonitor pm =
-        new TextProgressMonitor(
-            new PrintWriter(new BufferedWriter(new OutputStreamWriter(progressOut, UTF_8))));
-    try (Repository changeRepo = repoManager.openRepository(project);
-        // Only use a PackInserter for the change repo, not All-Users.
-        //
-        // It's not possible to share a single inserter for All-Users across all project tasks, and
-        // we don't want to add one pack per project to All-Users. Adding many loose objects is
-        // preferable to many packs.
-        //
-        // Anyway, the number of objects inserted into All-Users is proportional to the number
-        // of pending draft comments, which should not be high (relative to the total number of
-        // changes), so the number of loose objects shouldn't be too unreasonable.
-        ObjectInserter changeIns = newPackInserter(changeRepo);
-        ObjectReader changeReader = changeIns.newReader();
-        RevWalk changeRw = new RevWalk(changeReader);
-        Repository allUsersRepo = repoManager.openRepository(allUsers);
-        ObjectInserter allUsersIns = allUsersRepo.newObjectInserter();
-        ObjectReader allUsersReader = allUsersIns.newReader();
-        RevWalk allUsersRw = new RevWalk(allUsersReader)) {
-      ChainedReceiveCommands changeCmds = new ChainedReceiveCommands(changeRepo);
-      ChainedReceiveCommands allUsersCmds = new ChainedReceiveCommands(allUsersRepo);
-
-      Collection<Change.Id> changes = allChanges.get(project);
-      pm.beginTask(FormatUtil.elide("Rebuilding " + project.get(), 50), changes.size());
-      int toSave = 0;
-      try {
-        for (Change.Id changeId : changes) {
-          // NoteDbUpdateManager assumes that all commands in its OpenRepo were added by itself, so
-          // we can't share the top-level ChainedReceiveCommands. Use a new set of commands sharing
-          // the same underlying repo, and copy commands back to the top-level
-          // ChainedReceiveCommands later. This also assumes that each ref in the final list of
-          // commands was only modified by a single NoteDbUpdateManager; since we use one manager
-          // per change, and each ref corresponds to exactly one change, this assumption should be
-          // safe.
-          ChainedReceiveCommands tmpChangeCmds =
-              new ChainedReceiveCommands(changeCmds.getRepoRefCache());
-          ChainedReceiveCommands tmpAllUsersCmds =
-              new ChainedReceiveCommands(allUsersCmds.getRepoRefCache());
-
-          try (NoteDbUpdateManager manager =
-              updateManagerFactory
-                  .create(project)
-                  .setAtomicRefUpdates(false)
-                  .setSaveObjects(false)
-                  .setChangeRepo(changeRepo, changeRw, changeIns, tmpChangeCmds)
-                  .setAllUsersRepo(allUsersRepo, allUsersRw, allUsersIns, tmpAllUsersCmds)) {
-            rebuild(db, changeId, manager);
-
-            // Executing with dryRun=true writes all objects to the underlying inserters and adds
-            // commands to the ChainedReceiveCommands. Afterwards, we can discard the manager, so we
-            // don't keep using any memory beyond what may be buffered in the PackInserter.
-            manager.execute(true);
-
-            tmpChangeCmds.getCommands().values().forEach(c -> addCommand(changeCmds, c));
-            tmpAllUsersCmds.getCommands().values().forEach(c -> addCommand(allUsersCmds, c));
-
-            toSave++;
-          } catch (NoPatchSetsException e) {
-            logger.atWarning().log(e.getMessage());
-          } catch (ConflictingUpdateException ex) {
-            logger.atWarning().log(
-                "Rebuilding detected a conflicting ReviewDb update for change %s;"
-                    + " will be auto-rebuilt at runtime",
-                changeId);
-          } catch (Throwable t) {
-            logger.atSevere().withCause(t).log("Failed to rebuild change %s", changeId);
-            ok = false;
-          }
-          pm.update(1);
-        }
-      } finally {
-        pm.endTask();
-      }
-
-      pm.beginTask(FormatUtil.elide("Saving " + project.get(), 50), ProgressMonitor.UNKNOWN);
-      try {
-        save(changeRepo, changeRw, changeIns, changeCmds);
-        save(allUsersRepo, allUsersRw, allUsersIns, allUsersCmds);
-        // This isn't really useful progress. If we passed a real ProgressMonitor to
-        // BatchRefUpdate#execute we might get something more incremental, but that doesn't allow us
-        // to specify the repo name in the task text.
-        pm.update(toSave);
-      } catch (LockFailureException e) {
-        logger.atWarning().log(
-            "Rebuilding detected a conflicting NoteDb update for the following refs, which will"
-                + " be auto-rebuilt at runtime: %s",
-            e.getFailedRefs().stream().distinct().sorted().collect(joining(", ")));
-      } catch (IOException e) {
-        logger.atSevere().withCause(e).log("Failed to save NoteDb state for %s", project);
-      } finally {
-        pm.endTask();
-      }
-    } catch (RepositoryNotFoundException e) {
-      logger.atWarning().log("Repository %s not found", project);
-    } catch (IOException e) {
-      logger.atSevere().withCause(e).log("Failed to rebuild project %s", project);
-    }
-    return ok;
-  }
-
-  private void rebuild(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
-      throws OrmException, IOException {
-    // Match ChangeRebuilderImpl#stage, but without calling manager.stage(), since that can only be
-    // called after building updates for all changes.
-    Change change =
-        ChangeRebuilderImpl.checkNoteDbState(ChangeNotes.readOneReviewDbChange(db, changeId));
-    if (change == null) {
-      // Could log here instead, but this matches the behavior of ChangeRebuilderImpl#stage.
-      throw new NoSuchChangeException(changeId);
-    }
-    rebuilder.buildUpdates(manager, bundleReader.fromReviewDb(db, changeId));
-
-    rebuilder.execute(db, changeId, manager, true, false);
-  }
-
-  private static void addCommand(ChainedReceiveCommands cmds, ReceiveCommand cmd) {
-    // ChainedReceiveCommands doesn't allow no-ops, but these occur when rebuilding a
-    // previously-rebuilt change.
-    if (!cmd.getOldId().equals(cmd.getNewId())) {
-      cmds.add(cmd);
-    }
-  }
-
-  private void save(Repository repo, RevWalk rw, ObjectInserter ins, ChainedReceiveCommands cmds)
-      throws IOException {
-    if (cmds.isEmpty()) {
-      return;
-    }
-    ins.flush();
-    BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
-    bru.setRefLogMessage("Migrate changes to NoteDb", false);
-    bru.setRefLogIdent(serverIdent.get());
-    bru.setAtomic(false);
-    bru.setAllowNonFastForwards(true);
-    cmds.addTo(bru);
-    RefUpdateUtil.executeChecked(bru, rw);
-  }
-
-  private static boolean futuresToBoolean(List<ListenableFuture<Boolean>> futures, String errMsg) {
-    try {
-      return Futures.allAsList(futures).get().stream().allMatch(b -> b);
-    } catch (InterruptedException | ExecutionException e) {
-      logger.atSevere().withCause(e).log(errMsg);
-      return false;
-    }
-  }
-
-  private class ContextHelper implements AutoCloseable {
-    private final Thread callingThread;
-    private ReviewDb db;
-    private Runnable closeDb;
-
-    ContextHelper() {
-      callingThread = Thread.currentThread();
-    }
-
-    ManualRequestContext open() throws OrmException {
-      return new ManualRequestContext(
-          userFactory.create(),
-          // Reuse the same lazily-opened ReviewDb on the original calling thread, otherwise open
-          // SchemaFactory in the normal way.
-          Thread.currentThread().equals(callingThread) ? this::getReviewDb : schemaFactory,
-          requestContext);
-    }
-
-    synchronized ReviewDb getReviewDb() throws OrmException {
-      if (db == null) {
-        ReviewDb actual = schemaFactory.open();
-        closeDb = actual::close;
-        db =
-            new ReviewDbWrapper(unwrapDb(actual)) {
-              @Override
-              public void close() {
-                // Closed by ContextHelper#close.
-              }
-            };
-      }
-      return db;
-    }
-
-    @Override
-    public synchronized void close() {
-      if (db != null) {
-        closeDb.run();
-        db = null;
-        closeDb = null;
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java b/java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java
deleted file mode 100644
index aef30a2..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import java.io.IOException;
-
-/** Listener for state changes performed by {@link OnlineNoteDbMigrator}. */
-@ExtensionPoint
-public interface NotesMigrationStateListener {
-  /**
-   * Invoked just before saving the new migration state.
-   *
-   * @param oldState state prior to this state change.
-   * @param newState state after this state change.
-   * @throws IOException if an error occurred, which will cause the migration to abort. Exceptions
-   *     that should be considered non-fatal must be caught (and ideally logged) by the
-   *     implementation rather than thrown.
-   */
-  void preStateChange(NotesMigrationState oldState, NotesMigrationState newState)
-      throws IOException;
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
deleted file mode 100644
index b5a8236..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.index.OnlineUpgrader;
-import com.google.gerrit.server.index.VersionManager;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.Config;
-
-@Singleton
-public class OnlineNoteDbMigrator implements LifecycleListener {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private static final String TRIAL = "OnlineNoteDbMigrator/trial";
-
-  public static class Module extends LifecycleModule {
-    private final boolean trial;
-
-    public Module(boolean trial) {
-      this.trial = trial;
-    }
-
-    @Override
-    public void configure() {
-      listener().to(OnlineNoteDbMigrator.class);
-      bindConstant().annotatedWith(Names.named(TRIAL)).to(trial);
-    }
-  }
-
-  private final GcAllUsers gcAllUsers;
-  private final OnlineUpgrader indexUpgrader;
-  private final Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
-  private final boolean upgradeIndex;
-  private final boolean trial;
-
-  @Inject
-  OnlineNoteDbMigrator(
-      @GerritServerConfig Config cfg,
-      GcAllUsers gcAllUsers,
-      OnlineUpgrader indexUpgrader,
-      Provider<NoteDbMigrator.Builder> migratorBuilderProvider,
-      @Named(TRIAL) boolean trial) {
-    this.gcAllUsers = gcAllUsers;
-    this.indexUpgrader = indexUpgrader;
-    this.migratorBuilderProvider = migratorBuilderProvider;
-    this.upgradeIndex = VersionManager.getOnlineUpgrade(cfg);
-    this.trial = trial || NoteDbMigrator.getTrialMode(cfg);
-  }
-
-  @Override
-  public void start() {
-    Thread t = new Thread(this::migrate);
-    t.setDaemon(true);
-    t.setName(getClass().getSimpleName());
-    t.start();
-  }
-
-  private void migrate() {
-    logger.atInfo().log("Starting online NoteDb migration");
-    if (upgradeIndex) {
-      logger.atInfo().log(
-          "Online index schema upgrades will be deferred until NoteDb migration is complete");
-    }
-    Stopwatch sw = Stopwatch.createStarted();
-    // TODO(dborowitz): Tune threads, maybe expose a progress monitor somewhere.
-    try (NoteDbMigrator migrator =
-        migratorBuilderProvider.get().setAutoMigrate(true).setTrialMode(trial).build()) {
-      migrator.migrate();
-    } catch (Exception e) {
-      logger.atSevere().withCause(e).log("Error in online NoteDb migration");
-    }
-    gcAllUsers.runWithLogger();
-    logger.atInfo().log("Online NoteDb migration completed in %ss", sw.elapsed(TimeUnit.SECONDS));
-
-    if (upgradeIndex) {
-      logger.atInfo().log("Starting deferred index schema upgrades");
-      indexUpgrader.start();
-    }
-  }
-
-  @Override
-  public void stop() {
-    // Do nothing; upgrade process uses daemon threads and knows how to recover from failures on
-    // next attempt.
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java b/java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java
deleted file mode 100644
index acb80c0..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.errors.InvalidObjectIdException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-class PatchSetEvent extends Event {
-  private final Change change;
-  private final PatchSet ps;
-  private final RevWalk rw;
-  boolean createChange;
-
-  PatchSetEvent(Change change, PatchSet ps, RevWalk rw) {
-    super(
-        ps.getId(),
-        ps.getUploader(),
-        ps.getUploader(),
-        ps.getCreatedOn(),
-        change.getCreatedOn(),
-        null);
-    this.change = change;
-    this.ps = ps;
-    this.rw = rw;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return true;
-  }
-
-  @Override
-  void apply(ChangeUpdate update) throws IOException, OrmException {
-    checkUpdate(update);
-    if (createChange) {
-      ChangeRebuilderImpl.createChange(update, change);
-    } else {
-      update.setSubject(change.getSubject());
-      update.setSubjectForCommit("Create patch set " + ps.getPatchSetId());
-    }
-    setRevision(update, ps);
-    update.setPsDescription(ps.getDescription());
-    List<String> groups = ps.getGroups();
-    if (!groups.isEmpty()) {
-      update.setGroups(ps.getGroups());
-    }
-  }
-
-  private void setRevision(ChangeUpdate update, PatchSet ps) throws IOException {
-    String rev = ps.getRevision().get();
-    String cert = ps.getPushCertificate();
-    ObjectId id;
-    try {
-      id = ObjectId.fromString(rev);
-    } catch (InvalidObjectIdException e) {
-      update.setRevisionForMissingCommit(rev, cert);
-      return;
-    }
-    try {
-      update.setCommit(rw, id, cert);
-    } catch (MissingObjectException e) {
-      update.setRevisionForMissingCommit(rev, cert);
-      return;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java b/java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java
deleted file mode 100644
index 2ecf969..0000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.Table;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.sql.Timestamp;
-
-class ReviewerEvent extends Event {
-  private Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer;
-
-  ReviewerEvent(
-      Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer,
-      Timestamp changeCreatedOn) {
-    super(
-        // Reviewers aren't generally associated with a particular patch set
-        // (although as an implementation detail they were in ReviewDb). Just
-        // use the latest patch set at the time of the event.
-        null,
-        reviewer.getColumnKey(),
-        // TODO(dborowitz): Real account ID shouldn't really matter for
-        // reviewers, but we might have to deal with this to avoid ChangeBundle
-        // diffs when run against real data.
-        reviewer.getColumnKey(),
-        reviewer.getValue(),
-        changeCreatedOn,
-        null);
-    this.reviewer = reviewer;
-  }
-
-  @Override
-  boolean uniquePerUpdate() {
-    return false;
-  }
-
-  @Override
-  void apply(ChangeUpdate update) throws IOException, OrmException {
-    checkUpdate(update);
-    update.putReviewer(reviewer.getColumnKey(), reviewer.getRowKey());
-  }
-
-  @Override
-  protected void addToString(ToStringHelper helper) {
-    helper.add("account", reviewer.getColumnKey()).add("state", reviewer.getRowKey());
-  }
-}
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index b1e0e3c..dca5098 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -391,7 +391,7 @@
   }
 
   private void loadPublished(Map<Patch.Key, Patch> byKey, String file) throws OrmException {
-    for (Comment c : commentsUtil.publishedByChangeFile(db, notes, changeId, file)) {
+    for (Comment c : commentsUtil.publishedByChangeFile(notes, file)) {
       comments.include(notes.getChangeId(), c);
       PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
       Patch.Key pKey = new Patch.Key(psId, c.key.filename);
@@ -404,7 +404,7 @@
 
   private void loadDrafts(Map<Patch.Key, Patch> byKey, Account.Id me, String file)
       throws OrmException {
-    for (Comment c : commentsUtil.draftByChangeFileAuthor(db, notes, file, me)) {
+    for (Comment c : commentsUtil.draftByChangeFileAuthor(notes, file, me)) {
       comments.include(notes.getChangeId(), c);
       PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
       Patch.Key pKey = new Patch.Key(psId, c.key.filename);
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 47be6e3..249c872 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -345,7 +345,7 @@
     Project.NameKey p = projectState.getNameKey();
     Stream<ChangeNotesResult> s;
     try {
-      s = changeNotesFactory.scan(repo, db.get(), p);
+      s = changeNotesFactory.scan(repo, p);
     } catch (IOException e) {
       logger.atSevere().withCause(e).log(
           "Cannot load changes for project %s, assuming no changes are visible", p);
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 0f5d938..8864de1 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -750,7 +750,7 @@
       if (!lazyLoad) {
         return ImmutableListMultimap.of();
       }
-      allApprovals = approvalsUtil.byChange(db, notes());
+      allApprovals = approvalsUtil.byChange(notes());
     }
     return allApprovals;
   }
@@ -858,7 +858,7 @@
       if (!lazyLoad) {
         return Collections.emptyList();
       }
-      publishedComments = commentsUtil.publishedByChange(db, notes());
+      publishedComments = commentsUtil.publishedByChange(notes());
     }
     return publishedComments;
   }
@@ -949,7 +949,7 @@
       if (!lazyLoad) {
         return Collections.emptyList();
       }
-      messages = cmUtil.byChange(db, notes());
+      messages = cmUtil.byChange(notes());
     }
     return messages;
   }
@@ -1097,7 +1097,7 @@
           }
         }
       } else {
-        for (Comment sc : commentsUtil.draftByChange(db, notes())) {
+        for (Comment sc : commentsUtil.draftByChange(notes())) {
           draftsByUser.put(sc.author.getId(), null);
         }
       }
diff --git a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 17c23b6..a190ac5 100644
--- a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -250,7 +250,7 @@
     }
 
     if (includeAllReviewers) {
-      eventFactory.addAllReviewers(db, c, d.notes());
+      eventFactory.addAllReviewers(c, d.notes());
     }
 
     if (includeSubmitRecords) {
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
index 108ee0e..3c553a1 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
@@ -184,12 +184,12 @@
         throws OrmException, PatchListNotAvailableException, PermissionBackendException {
       ImmutableList.Builder<CommentInfo> comments = ImmutableList.builder();
       boolean dirty = false;
-      for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getDb(), ctx.getNotes(), accountId)) {
+      for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), accountId)) {
         dirty = true;
         PatchSet.Id psId = new PatchSet.Id(ctx.getChange().getId(), c.key.patchSetId);
         setCommentRevId(
             c, patchListCache, ctx.getChange(), psUtil.get(ctx.getDb(), ctx.getNotes(), psId));
-        commentsUtil.deleteComments(ctx.getDb(), ctx.getUpdate(psId), Collections.singleton(c));
+        commentsUtil.deleteComments(ctx.getUpdate(psId), Collections.singleton(c));
         comments.add(commentFormatter.format(c));
       }
       if (dirty) {
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 6399cde..c7a8a96 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -361,7 +361,7 @@
     if (input.keepReviewers && sourceChange != null) {
       ReviewerSet reviewerSet =
           approvalsUtil.getReviewers(
-              dbProvider.get(), changeNotesFactory.createChecked(dbProvider.get(), sourceChange));
+              changeNotesFactory.createChecked(dbProvider.get(), sourceChange));
       Set<Account.Id> reviewers =
           new HashSet<>(reviewerSet.byState(ReviewerStateInternal.REVIEWER));
       reviewers.add(sourceChange.getOwner());
diff --git a/java/com/google/gerrit/server/restapi/change/Comments.java b/java/com/google/gerrit/server/restapi/change/Comments.java
index f563cc6..22f376b 100644
--- a/java/com/google/gerrit/server/restapi/change/Comments.java
+++ b/java/com/google/gerrit/server/restapi/change/Comments.java
@@ -20,32 +20,27 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.CommentResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
 @Singleton
 public class Comments implements ChildCollection<RevisionResource, CommentResource> {
   private final DynamicMap<RestView<CommentResource>> views;
   private final ListRevisionComments list;
-  private final Provider<ReviewDb> dbProvider;
   private final CommentsUtil commentsUtil;
 
   @Inject
   Comments(
       DynamicMap<RestView<CommentResource>> views,
       ListRevisionComments list,
-      Provider<ReviewDb> dbProvider,
       CommentsUtil commentsUtil) {
     this.views = views;
     this.list = list;
-    this.dbProvider = dbProvider;
     this.commentsUtil = commentsUtil;
   }
 
@@ -65,8 +60,7 @@
     String uuid = id.get();
     ChangeNotes notes = rev.getNotes();
 
-    for (Comment c :
-        commentsUtil.publishedByPatchSet(dbProvider.get(), notes, rev.getPatchSet().getId())) {
+    for (Comment c : commentsUtil.publishedByPatchSet(notes, rev.getPatchSet().getId())) {
       if (uuid.equals(c.key.uuid)) {
         return new CommentResource(rev, c);
       }
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index 0e93c55..773b914 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -126,8 +126,7 @@
 
       setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
 
-      commentsUtil.putComments(
-          ctx.getDb(), ctx.getUpdate(psId), Status.DRAFT, Collections.singleton(comment));
+      commentsUtil.putComments(ctx.getUpdate(psId), Status.DRAFT, Collections.singleton(comment));
       ctx.dontBumpLastUpdatedOn();
       return true;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 7d68022..1e9b695 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -114,14 +114,14 @@
       return deletedAssignee != null ? deletedAssignee.getAccount().getId() : null;
     }
 
-    private void addMessage(ChangeContext ctx, ChangeUpdate update, IdentifiedUser deletedAssignee)
-        throws OrmException {
+    private void addMessage(
+        ChangeContext ctx, ChangeUpdate update, IdentifiedUser deletedAssignee) {
       ChangeMessage cmsg =
           ChangeMessagesUtil.newMessage(
               ctx,
               "Assignee deleted: " + deletedAssignee.getNameEmail(),
               ChangeMessagesUtil.TAG_DELETE_ASSIGNEE);
-      cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+      cmUtil.addChangeMessage(update, cmsg);
     }
 
     @Override
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
index 881d106..c57f9ec 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
@@ -109,8 +109,7 @@
 
   private ChangeMessageInfo createUpdatedChangeMessageInfo(Change.Id id, int targetIdx)
       throws OrmException, PermissionBackendException {
-    List<ChangeMessage> messages =
-        changeMessagesUtil.byChange(dbProvider.get(), notesFactory.createChecked(id));
+    List<ChangeMessage> messages = changeMessagesUtil.byChange(notesFactory.createChecked(id));
     ChangeMessage updatedChangeMessage = messages.get(targetIdx);
     AccountLoader accountLoader = accountLoaderFactory.create(true);
     ChangeMessageInfo info = createChangeMessageInfo(updatedChangeMessage, accountLoader);
@@ -145,10 +144,9 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws OrmException {
+    public boolean updateChange(ChangeContext ctx) {
       PatchSet.Id psId = ctx.getChange().currentPatchSetId();
-      changeMessagesUtil.replaceChangeMessage(
-          ctx.getDb(), ctx.getUpdate(psId), targetMessageIdx, newMessage);
+      changeMessagesUtil.replaceChangeMessage(ctx.getUpdate(psId), targetMessageIdx, newMessage);
       return true;
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
index 2ddf359..6279357 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -97,7 +97,7 @@
 
     ChangeNotes updatedNotes =
         notesFactory.createChecked(rsrc.getRevisionResource().getChange().getId());
-    List<Comment> changeComments = commentsUtil.publishedByChange(dbProvider.get(), updatedNotes);
+    List<Comment> changeComments = commentsUtil.publishedByChange(updatedNotes);
     Optional<Comment> updatedComment =
         changeComments.stream().filter(c -> c.key.equals(rsrc.getComment().key)).findFirst();
     if (!updatedComment.isPresent()) {
@@ -127,14 +127,10 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws ResourceConflictException, OrmException, ResourceNotFoundException {
+        throws ResourceConflictException, ResourceNotFoundException {
       PatchSet.Id psId = ctx.getChange().currentPatchSetId();
       commentsUtil.deleteCommentByRewritingHistory(
-          ctx.getDb(),
-          ctx.getUpdate(psId),
-          rsrc.getComment().key,
-          rsrc.getPatchSet().getId(),
-          newMessage);
+          ctx.getUpdate(psId), rsrc.getComment().key, newMessage);
       return true;
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index f8e3add..0d9629f 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -91,7 +91,7 @@
     public boolean updateChange(ChangeContext ctx)
         throws ResourceNotFoundException, OrmException, PatchListNotAvailableException {
       Optional<Comment> maybeComment =
-          commentsUtil.getDraft(ctx.getDb(), ctx.getNotes(), ctx.getIdentifiedUser(), key);
+          commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
       if (!maybeComment.isPresent()) {
         return false; // Nothing to do.
       }
@@ -102,7 +102,7 @@
       }
       Comment c = maybeComment.get();
       setCommentRevId(c, patchListCache, ctx.getChange(), ps);
-      commentsUtil.deleteComments(ctx.getDb(), ctx.getUpdate(psId), Collections.singleton(c));
+      commentsUtil.deleteComments(ctx.getUpdate(psId), Collections.singleton(c));
       ctx.dontBumpLastUpdatedOn();
       return true;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java
index 2cc4ce4..b130e62 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewerOp.java
@@ -125,7 +125,7 @@
     // Check of removing this reviewer (even if there is no vote processed by the loop below) is OK
     removeReviewerControl.checkRemoveReviewer(ctx.getNotes(), ctx.getUser(), reviewerId);
 
-    if (!approvalsUtil.getReviewers(ctx.getDb(), ctx.getNotes()).all().contains(reviewerId)) {
+    if (!approvalsUtil.getReviewers(ctx.getNotes()).all().contains(reviewerId)) {
       throw new ResourceNotFoundException();
     }
     currChange = ctx.getChange();
@@ -171,7 +171,7 @@
 
     changeMessage =
         ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_DELETE_REVIEWER);
-    cmUtil.addChangeMessage(ctx.getDb(), update, changeMessage);
+    cmUtil.addChangeMessage(update, changeMessage);
 
     return true;
   }
@@ -217,7 +217,7 @@
       db = ReviewDbUtil.unwrapDb(db);
       approvals = db.patchSetApprovals().byChange(changeId);
     } else {
-      approvals = approvalsUtil.byChange(ctx.getDb(), ctx.getNotes()).values();
+      approvals = approvalsUtil.byChange(ctx.getNotes()).values();
     }
 
     return Iterables.filter(approvals, psa -> accountId.equals(psa.getAccountId()));
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 57649fad..e4d067b 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -218,7 +218,7 @@
       msg.append(" by ").append(userFactory.create(accountId).getNameEmail()).append("\n");
       changeMessage =
           ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_DELETE_VOTE);
-      cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), changeMessage);
+      cmUtil.addChangeMessage(ctx.getUpdate(psId), changeMessage);
 
       return true;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/DraftComments.java b/java/com/google/gerrit/server/restapi/change/DraftComments.java
index b8e24a5..9f06252 100644
--- a/java/com/google/gerrit/server/restapi/change/DraftComments.java
+++ b/java/com/google/gerrit/server/restapi/change/DraftComments.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.change.DraftCommentResource;
@@ -36,7 +35,6 @@
   private final DynamicMap<RestView<DraftCommentResource>> views;
   private final Provider<CurrentUser> user;
   private final ListRevisionDrafts list;
-  private final Provider<ReviewDb> dbProvider;
   private final CommentsUtil commentsUtil;
 
   @Inject
@@ -44,12 +42,10 @@
       DynamicMap<RestView<DraftCommentResource>> views,
       Provider<CurrentUser> user,
       ListRevisionDrafts list,
-      Provider<ReviewDb> dbProvider,
       CommentsUtil commentsUtil) {
     this.views = views;
     this.user = user;
     this.list = list;
-    this.dbProvider = dbProvider;
     this.commentsUtil = commentsUtil;
   }
 
@@ -71,7 +67,7 @@
     String uuid = id.get();
     for (Comment c :
         commentsUtil.draftByPatchSetAuthor(
-            dbProvider.get(), rev.getPatchSet().getId(), rev.getAccountId(), rev.getNotes())) {
+            rev.getPatchSet().getId(), rev.getAccountId(), rev.getNotes())) {
       if (uuid.equals(c.key.uuid)) {
         return new DraftCommentResource(rev, c);
       }
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index 40f4642..42af722 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -57,6 +57,6 @@
         .setFillAccounts(true)
         .setFillPatchSet(true)
         .newCommentFormatter()
-        .format(commentsUtil.publishedByChange(db.get(), cd.notes()));
+        .format(commentsUtil.publishedByChange(cd.notes()));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
index a524f6d..280277c 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
@@ -57,7 +57,7 @@
     }
     ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
     List<Comment> drafts =
-        commentsUtil.draftByChangeAuthor(db.get(), cd.notes(), rsrc.getUser().getAccountId());
+        commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
     return commentJson
         .get()
         .setFillAccounts(false)
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
index 39c12f7..ba09281 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
@@ -19,30 +19,24 @@
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.util.List;
 import java.util.stream.Collectors;
 
 @Singleton
 public class ListChangeMessages implements RestReadView<ChangeResource> {
-  private final Provider<ReviewDb> dbProvider;
   private final ChangeMessagesUtil changeMessagesUtil;
   private final AccountLoader accountLoader;
 
   @Inject
   public ListChangeMessages(
-      Provider<ReviewDb> dbProvider,
-      ChangeMessagesUtil changeMessagesUtil,
-      AccountLoader.Factory accountLoaderFactory) {
-    this.dbProvider = dbProvider;
+      ChangeMessagesUtil changeMessagesUtil, AccountLoader.Factory accountLoaderFactory) {
     this.changeMessagesUtil = changeMessagesUtil;
     this.accountLoader = accountLoaderFactory.create(true);
   }
@@ -50,8 +44,7 @@
   @Override
   public List<ChangeMessageInfo> apply(ChangeResource resource)
       throws OrmException, PermissionBackendException {
-    List<ChangeMessage> messages =
-        changeMessagesUtil.byChange(dbProvider.get(), resource.getNotes());
+    List<ChangeMessage> messages = changeMessagesUtil.byChange(resource.getNotes());
     List<ChangeMessageInfo> messageInfos =
         messages
             .stream()
diff --git a/java/com/google/gerrit/server/restapi/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
index 99d8746..725a89b 100644
--- a/java/com/google/gerrit/server/restapi/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.ReviewerJson;
@@ -26,7 +25,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -34,18 +32,13 @@
 
 @Singleton
 class ListReviewers implements RestReadView<ChangeResource> {
-  private final Provider<ReviewDb> dbProvider;
   private final ApprovalsUtil approvalsUtil;
   private final ReviewerJson json;
   private final ReviewerResource.Factory resourceFactory;
 
   @Inject
   ListReviewers(
-      Provider<ReviewDb> dbProvider,
-      ApprovalsUtil approvalsUtil,
-      ReviewerResource.Factory resourceFactory,
-      ReviewerJson json) {
-    this.dbProvider = dbProvider;
+      ApprovalsUtil approvalsUtil, ReviewerResource.Factory resourceFactory, ReviewerJson json) {
     this.approvalsUtil = approvalsUtil;
     this.resourceFactory = resourceFactory;
     this.json = json;
@@ -55,8 +48,7 @@
   public List<ReviewerInfo> apply(ChangeResource rsrc)
       throws OrmException, PermissionBackendException {
     Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
-    ReviewDb db = dbProvider.get();
-    for (Account.Id accountId : approvalsUtil.getReviewers(db, rsrc.getNotes()).all()) {
+    for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
       if (!reviewers.containsKey(accountId.toString())) {
         reviewers.put(accountId.toString(), resourceFactory.create(rsrc, accountId));
       }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
index 964e560..f10d92b 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -27,9 +26,8 @@
 @Singleton
 public class ListRevisionComments extends ListRevisionDrafts {
   @Inject
-  ListRevisionComments(
-      Provider<ReviewDb> db, Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
-    super(db, commentJson, commentsUtil);
+  ListRevisionComments(Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
+    super(commentJson, commentsUtil);
   }
 
   @Override
@@ -40,6 +38,6 @@
   @Override
   protected Iterable<Comment> listComments(RevisionResource rsrc) throws OrmException {
     ChangeNotes notes = rsrc.getNotes();
-    return commentsUtil.publishedByPatchSet(db.get(), notes, rsrc.getPatchSet().getId());
+    return commentsUtil.publishedByPatchSet(notes, rsrc.getPatchSet().getId());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index dbd0ccf..3df7e9c 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,21 +30,18 @@
 
 @Singleton
 public class ListRevisionDrafts implements RestReadView<RevisionResource> {
-  protected final Provider<ReviewDb> db;
   protected final Provider<CommentJson> commentJson;
   protected final CommentsUtil commentsUtil;
 
   @Inject
-  ListRevisionDrafts(
-      Provider<ReviewDb> db, Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
-    this.db = db;
+  ListRevisionDrafts(Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
     this.commentJson = commentJson;
     this.commentsUtil = commentsUtil;
   }
 
   protected Iterable<Comment> listComments(RevisionResource rsrc) throws OrmException {
     return commentsUtil.draftByPatchSetAuthor(
-        db.get(), rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes());
+        rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes());
   }
 
   protected boolean includeAuthorInfo() {
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 7add548..6e7ffd9 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ReviewerJson;
 import com.google.gerrit.server.change.ReviewerResource;
@@ -27,7 +26,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -35,18 +33,13 @@
 
 @Singleton
 class ListRevisionReviewers implements RestReadView<RevisionResource> {
-  private final Provider<ReviewDb> dbProvider;
   private final ApprovalsUtil approvalsUtil;
   private final ReviewerJson json;
   private final ReviewerResource.Factory resourceFactory;
 
   @Inject
   ListRevisionReviewers(
-      Provider<ReviewDb> dbProvider,
-      ApprovalsUtil approvalsUtil,
-      ReviewerResource.Factory resourceFactory,
-      ReviewerJson json) {
-    this.dbProvider = dbProvider;
+      ApprovalsUtil approvalsUtil, ReviewerResource.Factory resourceFactory, ReviewerJson json) {
     this.approvalsUtil = approvalsUtil;
     this.resourceFactory = resourceFactory;
     this.json = json;
@@ -60,8 +53,7 @@
     }
 
     Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
-    ReviewDb db = dbProvider.get();
-    for (Account.Id accountId : approvalsUtil.getReviewers(db, rsrc.getNotes()).all()) {
+    for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
       if (!reviewers.containsKey(accountId.toString())) {
         reviewers.put(accountId.toString(), resourceFactory.create(rsrc, accountId));
       }
diff --git a/java/com/google/gerrit/server/restapi/change/Module.java b/java/com/google/gerrit/server/restapi/change/Module.java
index a45a6d8..c7fe653 100644
--- a/java/com/google/gerrit/server/restapi/change/Module.java
+++ b/java/com/google/gerrit/server/restapi/change/Module.java
@@ -96,7 +96,6 @@
     get(CHANGE_KIND, "submitted_together").to(SubmittedTogether.class);
     post(CHANGE_KIND, "rebase").to(Rebase.CurrentRevision.class);
     post(CHANGE_KIND, "index").to(Index.class);
-    post(CHANGE_KIND, "rebuild.notedb").to(Rebuild.class);
     post(CHANGE_KIND, "move").to(Move.class);
     post(CHANGE_KIND, "private").to(PostPrivate.class);
     post(CHANGE_KIND, "private.delete").to(DeletePrivateByPost.class);
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index df1d9b9..0f650dd 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -252,7 +252,7 @@
       }
       ChangeMessage cmsg =
           ChangeMessagesUtil.newMessage(ctx, msgBuf.toString(), ChangeMessagesUtil.TAG_MOVE);
-      cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+      cmUtil.addChangeMessage(update, cmsg);
 
       return true;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index d06766d..ac19cce 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -322,7 +322,7 @@
       if (!ccOrReviewer) {
         // Check if user was already CCed or reviewing prior to this review.
         ReviewerSet currentReviewers =
-            approvalsUtil.getReviewers(db.get(), revision.getChangeResource().getNotes());
+            approvalsUtil.getReviewers(revision.getChangeResource().getNotes());
         ccOrReviewer = currentReviewers.all().contains(id);
       }
 
@@ -990,7 +990,7 @@
           break;
       }
       ChangeUpdate u = ctx.getUpdate(psId);
-      commentsUtil.putComments(ctx.getDb(), u, Status.PUBLISHED, toPublish);
+      commentsUtil.putComments(u, Status.PUBLISHED, toPublish);
       comments.addAll(toPublish);
       return !toPublish.isEmpty();
     }
@@ -1079,7 +1079,7 @@
 
     private Set<CommentSetEntry> readExistingComments(ChangeContext ctx) throws OrmException {
       return commentsUtil
-          .publishedByChange(ctx.getDb(), ctx.getNotes())
+          .publishedByChange(ctx.getNotes())
           .stream()
           .map(CommentSetEntry::create)
           .collect(toSet());
@@ -1095,8 +1095,7 @@
 
     private Map<String, Comment> changeDrafts(ChangeContext ctx) throws OrmException {
       Map<String, Comment> drafts = new HashMap<>();
-      for (Comment c :
-          commentsUtil.draftByChangeAuthor(ctx.getDb(), ctx.getNotes(), user.getAccountId())) {
+      for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), user.getAccountId())) {
         c.tag = in.tag;
         drafts.put(c.key.uuid, c);
       }
@@ -1106,8 +1105,7 @@
     private Map<String, Comment> patchSetDrafts(ChangeContext ctx) throws OrmException {
       Map<String, Comment> drafts = new HashMap<>();
       for (Comment c :
-          commentsUtil.draftByPatchSetAuthor(
-              ctx.getDb(), psId, user.getAccountId(), ctx.getNotes())) {
+          commentsUtil.draftByPatchSetAuthor(psId, user.getAccountId(), ctx.getNotes())) {
         drafts.put(c.key.uuid, c);
       }
       return drafts;
@@ -1387,7 +1385,7 @@
       return current;
     }
 
-    private boolean insertMessage(ChangeContext ctx) throws OrmException {
+    private boolean insertMessage(ChangeContext ctx) {
       String msg = Strings.nullToEmpty(in.message).trim();
 
       StringBuilder buf = new StringBuilder();
@@ -1411,7 +1409,7 @@
       message =
           ChangeMessagesUtil.newMessage(
               psId, user, ctx.getWhen(), "Patch Set " + psId.get() + ":" + buf, in.tag);
-      cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), message);
+      cmUtil.addChangeMessage(ctx.getUpdate(psId), message);
       return true;
     }
 
diff --git a/java/com/google/gerrit/server/restapi/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
index 3b5edb2..a116fc1 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -117,7 +117,7 @@
       ChangeMessage cmsg =
           ChangeMessagesUtil.newMessage(
               psId, ctx.getUser(), ctx.getWhen(), summary, ChangeMessagesUtil.TAG_SET_DESCRIPTION);
-      cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+      cmUtil.addChangeMessage(update, cmsg);
       return true;
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 72358bd..dfa32bf 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -118,7 +118,7 @@
     public boolean updateChange(ChangeContext ctx)
         throws ResourceNotFoundException, OrmException, PatchListNotAvailableException {
       Optional<Comment> maybeComment =
-          commentsUtil.getDraft(ctx.getDb(), ctx.getNotes(), ctx.getIdentifiedUser(), key);
+          commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
       if (!maybeComment.isPresent()) {
         // Disappeared out from under us. Can't easily fall back to insert,
         // because the input might be missing required fields. Just give up.
@@ -141,15 +141,12 @@
         // Updating the path alters the primary key, which isn't possible.
         // Delete then recreate the comment instead of an update.
 
-        commentsUtil.deleteComments(ctx.getDb(), update, Collections.singleton(origComment));
+        commentsUtil.deleteComments(update, Collections.singleton(origComment));
         comment.key.filename = in.path;
       }
       setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
       commentsUtil.putComments(
-          ctx.getDb(),
-          update,
-          Status.DRAFT,
-          Collections.singleton(update(comment, in, ctx.getWhen())));
+          update, Status.DRAFT, Collections.singleton(update(comment, in, ctx.getWhen())));
       ctx.dontBumpLastUpdatedOn();
       return true;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
index 7f56c91..60d5088 100644
--- a/java/com/google/gerrit/server/restapi/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -123,7 +123,7 @@
 
       ChangeMessage cmsg =
           ChangeMessagesUtil.newMessage(ctx, summary, ChangeMessagesUtil.TAG_SET_TOPIC);
-      cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+      cmUtil.addChangeMessage(update, cmsg);
       return true;
     }
 
diff --git a/java/com/google/gerrit/server/restapi/change/Rebuild.java b/java/com/google/gerrit/server/restapi/change/Rebuild.java
deleted file mode 100644
index dc390cc..0000000
--- a/java/com/google/gerrit/server/restapi/change/Rebuild.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.restapi.change;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.gerrit.extensions.common.Input;
-import com.google.gerrit.extensions.restapi.BinaryResult;
-import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-@Singleton
-public class Rebuild implements RestModifyView<ChangeResource, Input> {
-
-  private final Provider<ReviewDb> db;
-  private final NotesMigration migration;
-  private final ChangeRebuilder rebuilder;
-  private final ChangeBundleReader bundleReader;
-  private final CommentsUtil commentsUtil;
-  private final ChangeNotes.Factory notesFactory;
-
-  @Inject
-  Rebuild(
-      Provider<ReviewDb> db,
-      NotesMigration migration,
-      ChangeRebuilder rebuilder,
-      ChangeBundleReader bundleReader,
-      CommentsUtil commentsUtil,
-      ChangeNotes.Factory notesFactory) {
-    this.db = db;
-    this.migration = migration;
-    this.rebuilder = rebuilder;
-    this.bundleReader = bundleReader;
-    this.commentsUtil = commentsUtil;
-    this.notesFactory = notesFactory;
-  }
-
-  @Override
-  public BinaryResult apply(ChangeResource rsrc, Input input)
-      throws ResourceNotFoundException, IOException, OrmException, ConfigInvalidException,
-          ResourceConflictException {
-    if (!migration.commitChangeWrites()) {
-      throw new ResourceNotFoundException();
-    }
-    if (!migration.readChanges()) {
-      // ChangeBundle#fromNotes currently doesn't work if reading isn't enabled,
-      // so don't attempt a diff.
-      rebuild(rsrc);
-      return BinaryResult.create("Rebuilt change successfully");
-    }
-
-    // Not the same transaction as the rebuild, so may result in spurious diffs
-    // in the case of races. This should be easy enough to detect by rerunning.
-    ChangeBundle reviewDbBundle =
-        bundleReader.fromReviewDb(ReviewDbUtil.unwrapDb(db.get()), rsrc.getId());
-    if (reviewDbBundle == null) {
-      throw new ResourceConflictException("change is missing in ReviewDb");
-    }
-    rebuild(rsrc);
-    ChangeNotes notes = notesFactory.create(db.get(), rsrc.getChange().getProject(), rsrc.getId());
-    ChangeBundle noteDbBundle = ChangeBundle.fromNotes(commentsUtil, notes);
-    List<String> diffs = reviewDbBundle.differencesFrom(noteDbBundle);
-    if (diffs.isEmpty()) {
-      return BinaryResult.create("No differences between ReviewDb and NoteDb");
-    }
-    return BinaryResult.create(
-        diffs.stream().collect(joining("\n", "Differences between ReviewDb and NoteDb:\n", "\n")));
-  }
-
-  private void rebuild(ChangeResource rsrc)
-      throws ResourceNotFoundException, OrmException, IOException {
-    try {
-      rebuilder.rebuild(db.get(), rsrc.getId());
-    } catch (NoSuchChangeException e) {
-      throw new ResourceNotFoundException(IdString.fromDecoded(rsrc.getId().toString()));
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/restapi/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
index d6f9e2b..4411766 100644
--- a/java/com/google/gerrit/server/restapi/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -130,7 +130,7 @@
       update.setStatus(change.getStatus());
 
       message = newMessage(ctx);
-      cmUtil.addChangeMessage(ctx.getDb(), update, message);
+      cmUtil.addChangeMessage(update, message);
       return true;
     }
 
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index 7309fde..dfaad5a 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -231,7 +231,7 @@
       ins.setNotify(input.notify);
       ins.setAccountsToNotify(accountsToNotify);
 
-      ReviewerSet reviewerSet = approvalsUtil.getReviewers(db.get(), notes);
+      ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
 
       Set<Account.Id> reviewers = new HashSet<>();
       reviewers.add(changeToRevert.getOwner());
@@ -318,7 +318,7 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws Exception {
+    public boolean updateChange(ChangeContext ctx) {
       Change change = ctx.getChange();
       PatchSet.Id patchSetId = change.currentPatchSetId();
       ChangeMessage changeMessage =
@@ -326,7 +326,7 @@
               ctx,
               "Created a revert of this change as I" + computedChangeId.name(),
               ChangeMessagesUtil.TAG_REVERT);
-      cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(patchSetId), changeMessage);
+      cmUtil.addChangeMessage(ctx.getUpdate(patchSetId), changeMessage);
       return true;
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index d88489e..eb79b79 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.FanOutExecutor;
 import com.google.gerrit.server.change.ReviewerSuggestion;
@@ -80,7 +79,6 @@
   private final PluginMapContext<ReviewerSuggestion> reviewerSuggestionPluginMap;
   private final Provider<InternalChangeQuery> queryProvider;
   private final ExecutorService executor;
-  private final Provider<ReviewDb> dbProvider;
   private final ApprovalsUtil approvalsUtil;
 
   @Inject
@@ -89,7 +87,6 @@
       PluginMapContext<ReviewerSuggestion> reviewerSuggestionPluginMap,
       Provider<InternalChangeQuery> queryProvider,
       @FanOutExecutor ExecutorService executor,
-      Provider<ReviewDb> dbProvider,
       ApprovalsUtil approvalsUtil,
       @GerritServerConfig Config config) {
     this.changeQueryBuilder = changeQueryBuilder;
@@ -97,7 +94,6 @@
     this.queryProvider = queryProvider;
     this.reviewerSuggestionPluginMap = reviewerSuggestionPluginMap;
     this.executor = executor;
-    this.dbProvider = dbProvider;
     this.approvalsUtil = approvalsUtil;
   }
 
@@ -183,7 +179,7 @@
 
       // Remove existing reviewers
       approvalsUtil
-          .getReviewers(dbProvider.get(), changeNotes)
+          .getReviewers(changeNotes)
           .byState(REVIEWER)
           .forEach(
               r -> {
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewers.java b/java/com/google/gerrit/server/restapi/change/Reviewers.java
index f0aef13..cf69080 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewers.java
@@ -23,14 +23,12 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.Collection;
@@ -39,7 +37,6 @@
 @Singleton
 public class Reviewers implements ChildCollection<ChangeResource, ReviewerResource> {
   private final DynamicMap<RestView<ReviewerResource>> views;
-  private final Provider<ReviewDb> dbProvider;
   private final ApprovalsUtil approvalsUtil;
   private final AccountsCollection accounts;
   private final ReviewerResource.Factory resourceFactory;
@@ -47,13 +44,11 @@
 
   @Inject
   Reviewers(
-      Provider<ReviewDb> dbProvider,
       ApprovalsUtil approvalsUtil,
       AccountsCollection accounts,
       ReviewerResource.Factory resourceFactory,
       DynamicMap<RestView<ReviewerResource>> views,
       ListReviewers list) {
-    this.dbProvider = dbProvider;
     this.approvalsUtil = approvalsUtil;
     this.accounts = accounts;
     this.resourceFactory = resourceFactory;
@@ -99,6 +94,6 @@
   }
 
   private Collection<Account.Id> fetchAccountIds(ChangeResource rsrc) throws OrmException {
-    return approvalsUtil.getReviewers(dbProvider.get(), rsrc.getNotes()).all();
+    return approvalsUtil.getReviewers(rsrc.getNotes()).all();
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
index b9b7a4f..60c9a54 100644
--- a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
@@ -24,14 +24,12 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.restapi.account.AccountsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.Collection;
@@ -40,7 +38,6 @@
 @Singleton
 public class RevisionReviewers implements ChildCollection<RevisionResource, ReviewerResource> {
   private final DynamicMap<RestView<ReviewerResource>> views;
-  private final Provider<ReviewDb> dbProvider;
   private final ApprovalsUtil approvalsUtil;
   private final AccountsCollection accounts;
   private final ReviewerResource.Factory resourceFactory;
@@ -48,13 +45,11 @@
 
   @Inject
   RevisionReviewers(
-      Provider<ReviewDb> dbProvider,
       ApprovalsUtil approvalsUtil,
       AccountsCollection accounts,
       ReviewerResource.Factory resourceFactory,
       DynamicMap<RestView<ReviewerResource>> views,
       ListRevisionReviewers list) {
-    this.dbProvider = dbProvider;
     this.approvalsUtil = approvalsUtil;
     this.accounts = accounts;
     this.resourceFactory = resourceFactory;
@@ -89,8 +84,7 @@
         throw e;
       }
     }
-    Collection<Account.Id> reviewers =
-        approvalsUtil.getReviewers(dbProvider.get(), rsrc.getNotes()).all();
+    Collection<Account.Id> reviewers = approvalsUtil.getReviewers(rsrc.getNotes()).all();
     // See if the id exists as a reviewer for this change
     if (reviewers.contains(accountId)) {
       return resourceFactory.create(rsrc, accountId);
diff --git a/java/com/google/gerrit/server/restapi/change/SetPrivateOp.java b/java/com/google/gerrit/server/restapi/change/SetPrivateOp.java
index 8aac92c..47427dd 100644
--- a/java/com/google/gerrit/server/restapi/change/SetPrivateOp.java
+++ b/java/com/google/gerrit/server/restapi/change/SetPrivateOp.java
@@ -87,7 +87,7 @@
     privateStateChanged.fire(change, ps, ctx.getAccount(), ctx.getWhen());
   }
 
-  private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+  private void addMessage(ChangeContext ctx, ChangeUpdate update) {
     Change c = ctx.getChange();
     StringBuilder buf = new StringBuilder(c.isPrivate() ? "Set private" : "Unset private");
 
@@ -104,6 +104,6 @@
             c.isPrivate()
                 ? ChangeMessagesUtil.TAG_SET_PRIVATE
                 : ChangeMessagesUtil.TAG_UNSET_PRIVATE);
-    cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+    cmUtil.addChangeMessage(update, cmsg);
   }
 }
diff --git a/java/com/google/gerrit/server/schema/BaseDataSourceType.java b/java/com/google/gerrit/server/schema/BaseDataSourceType.java
deleted file mode 100644
index 4b3a570..0000000
--- a/java/com/google/gerrit/server/schema/BaseDataSourceType.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import java.io.IOException;
-import java.io.InputStream;
-
-public abstract class BaseDataSourceType implements DataSourceType {
-
-  private static final String DEFAULT_VALIDATION_QUERY = "select 1";
-  private final String driver;
-
-  protected BaseDataSourceType(String driver) {
-    this.driver = driver;
-  }
-
-  @Override
-  public final String getDriver() {
-    return driver;
-  }
-
-  @Override
-  public boolean usePool() {
-    return true;
-  }
-
-  @Override
-  public String getValidationQuery() {
-    return DEFAULT_VALIDATION_QUERY;
-  }
-
-  @Override
-  public ScriptRunner getIndexScript() throws IOException {
-    return getScriptRunner("index_generic.sql");
-  }
-
-  protected static final ScriptRunner getScriptRunner(String path) throws IOException {
-    if (path == null) {
-      return ScriptRunner.NOOP;
-    }
-    ScriptRunner runner;
-    try (InputStream in = ReviewDb.class.getResourceAsStream(path)) {
-      if (in == null) {
-        throw new IllegalStateException("SQL script " + path + " not found");
-      }
-      runner = new ScriptRunner(path, in);
-    }
-    return runner;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/DB2.java b/java/com/google/gerrit/server/schema/DB2.java
deleted file mode 100644
index fcf8c1f..0000000
--- a/java/com/google/gerrit/server/schema/DB2.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-public class DB2 extends BaseDataSourceType {
-  private Config cfg;
-
-  @Inject
-  public DB2(@GerritServerConfig Config cfg) {
-    super("com.ibm.db2.jcc.DB2Driver");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    final StringBuilder b = new StringBuilder();
-    final ConfigSection dbc = new ConfigSection(cfg, "database");
-    b.append("jdbc:db2://");
-    b.append(hostname(dbc.optional("hostname")));
-    b.append(port(dbc.optional("port")));
-    b.append("/");
-    b.append(dbc.required("database"));
-    return b.toString();
-  }
-
-  @Override
-  public String getValidationQuery() {
-    return "SELECT 1 FROM SYSIBM.SYSDUMMY1";
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/DataSourceModule.java b/java/com/google/gerrit/server/schema/DataSourceModule.java
deleted file mode 100644
index ee57c8b..0000000
--- a/java/com/google/gerrit/server/schema/DataSourceModule.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.name.Names;
-
-public class DataSourceModule extends AbstractModule {
-
-  @Override
-  protected void configure() {
-    bind(DataSourceType.class).annotatedWith(Names.named("db2")).to(DB2.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("derby")).to(Derby.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("h2")).to(H2.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("jdbc")).to(JDBC.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("mariadb")).to(MariaDb.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("mysql")).to(MySql.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("oracle")).to(Oracle.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("postgresql")).to(PostgreSQL.class);
-    /*
-     * DatabaseMetaData.getDatabaseProductName() returns "sap db" for MaxDB.
-     * For auto-detection of the DB type (com.google.gerrit.pgm.util.SiteProgram#getDbType)
-     * we have to map "sap db" additionally to "maxdb", which is used for explicit configuration.
-     */
-    bind(DataSourceType.class).annotatedWith(Names.named("maxdb")).to(MaxDb.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("sap db")).to(MaxDb.class);
-    bind(DataSourceType.class).annotatedWith(Names.named("hana")).to(HANA.class);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/DataSourceProvider.java b/java/com/google/gerrit/server/schema/DataSourceProvider.java
deleted file mode 100644
index d4cfaa6..0000000
--- a/java/com/google/gerrit/server/schema/DataSourceProvider.java
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.extensions.persistence.DataSourceInterceptor;
-import com.google.gerrit.metrics.CallbackMetric1;
-import com.google.gerrit.metrics.Description;
-import com.google.gerrit.metrics.Field;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.jdbc.SimpleDataSource;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.sql.SQLException;
-import java.util.Properties;
-import javax.sql.DataSource;
-import org.apache.commons.dbcp.BasicDataSource;
-import org.eclipse.jgit.lib.Config;
-
-/** Provides access to the DataSource. */
-@Singleton
-public class DataSourceProvider implements Provider<DataSource>, LifecycleListener {
-  private static final String DATABASE_KEY = "database";
-
-  private final Config cfg;
-  private final MetricMaker metrics;
-  private final Context ctx;
-  private final DataSourceType dst;
-  private final ThreadSettingsConfig threadSettingsConfig;
-  private DataSource ds;
-
-  @Inject
-  protected DataSourceProvider(
-      @GerritServerConfig Config cfg,
-      MetricMaker metrics,
-      ThreadSettingsConfig threadSettingsConfig,
-      Context ctx,
-      DataSourceType dst) {
-    this.cfg = cfg;
-    this.metrics = metrics;
-    this.threadSettingsConfig = threadSettingsConfig;
-    this.ctx = ctx;
-    this.dst = dst;
-  }
-
-  @Override
-  public synchronized DataSource get() {
-    if (ds == null) {
-      ds = open(cfg, ctx, dst);
-    }
-    return ds;
-  }
-
-  @Override
-  public void start() {}
-
-  @Override
-  public synchronized void stop() {
-    if (ds instanceof BasicDataSource) {
-      try {
-        ((BasicDataSource) ds).close();
-      } catch (SQLException e) {
-        // Ignore the close failure.
-      }
-    }
-  }
-
-  public enum Context {
-    SINGLE_USER,
-    MULTI_USER
-  }
-
-  private DataSource open(Config cfg, Context context, DataSourceType dst) {
-    ConfigSection dbs = new ConfigSection(cfg, DATABASE_KEY);
-    String driver = dbs.optional("driver");
-    if (Strings.isNullOrEmpty(driver)) {
-      driver = dst.getDriver();
-    }
-
-    String url = dbs.optional("url");
-    if (Strings.isNullOrEmpty(url)) {
-      url = dst.getUrl();
-    }
-
-    String username = dbs.optional("username");
-    String password = dbs.optional("password");
-    String interceptor = dbs.optional("dataSourceInterceptorClass");
-
-    boolean usePool;
-    if (context == Context.SINGLE_USER) {
-      usePool = false;
-    } else {
-      usePool = cfg.getBoolean(DATABASE_KEY, "connectionpool", dst.usePool());
-    }
-
-    if (usePool) {
-      final BasicDataSource lds = new BasicDataSource();
-      lds.setDriverClassName(driver);
-      lds.setUrl(url);
-      if (username != null && !username.isEmpty()) {
-        lds.setUsername(username);
-      }
-      if (password != null && !password.isEmpty()) {
-        lds.setPassword(password);
-      }
-      int poolLimit = threadSettingsConfig.getDatabasePoolLimit();
-      lds.setMaxActive(poolLimit);
-      lds.setMinIdle(cfg.getInt(DATABASE_KEY, "poolminidle", 4));
-      lds.setMaxIdle(cfg.getInt(DATABASE_KEY, "poolmaxidle", Math.min(poolLimit, 16)));
-      lds.setMaxWait(
-          ConfigUtil.getTimeUnit(
-              cfg,
-              DATABASE_KEY,
-              null,
-              "poolmaxwait",
-              MILLISECONDS.convert(30, SECONDS),
-              MILLISECONDS));
-      lds.setInitialSize(lds.getMinIdle());
-      long evictIdleTimeMs = 1000L * 60;
-      lds.setMinEvictableIdleTimeMillis(evictIdleTimeMs);
-      lds.setTimeBetweenEvictionRunsMillis(evictIdleTimeMs / 2);
-      lds.setTestOnBorrow(true);
-      lds.setTestOnReturn(true);
-      lds.setValidationQuery(dst.getValidationQuery());
-      lds.setValidationQueryTimeout(5);
-      exportPoolMetrics(lds);
-      return intercept(interceptor, lds);
-    }
-    // Don't use the connection pool.
-    //
-    try {
-      final Properties p = new Properties();
-      p.setProperty("driver", driver);
-      p.setProperty("url", url);
-      if (username != null) {
-        p.setProperty("user", username);
-      }
-      if (password != null) {
-        p.setProperty("password", password);
-      }
-      return intercept(interceptor, new SimpleDataSource(p));
-    } catch (SQLException se) {
-      throw new ProvisionException("Database unavailable", se);
-    }
-  }
-
-  private void exportPoolMetrics(BasicDataSource pool) {
-    CallbackMetric1<Boolean, Integer> cnt =
-        metrics.newCallbackMetric(
-            "sql/connection_pool/connections",
-            Integer.class,
-            new Description("SQL database connections").setGauge().setUnit("connections"),
-            Field.ofBoolean("active"));
-    metrics.newTrigger(
-        cnt,
-        () -> {
-          synchronized (pool) {
-            cnt.set(true, pool.getNumActive());
-            cnt.set(false, pool.getNumIdle());
-          }
-        });
-  }
-
-  private DataSource intercept(String interceptor, DataSource ds) {
-    if (interceptor == null) {
-      return ds;
-    }
-    try {
-      Constructor<?> c = Class.forName(interceptor).getConstructor();
-      DataSourceInterceptor datasourceInterceptor = (DataSourceInterceptor) c.newInstance();
-      return datasourceInterceptor.intercept("reviewDb", ds);
-    } catch (ClassNotFoundException
-        | SecurityException
-        | NoSuchMethodException
-        | IllegalArgumentException
-        | InstantiationException
-        | IllegalAccessException
-        | InvocationTargetException e) {
-      throw new ProvisionException("Cannot intercept datasource", e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/DataSourceType.java b/java/com/google/gerrit/server/schema/DataSourceType.java
deleted file mode 100644
index cbdcf0f..0000000
--- a/java/com/google/gerrit/server/schema/DataSourceType.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import java.io.IOException;
-
-/** Abstraction of a supported database platform */
-public interface DataSourceType {
-
-  String getDriver();
-
-  String getUrl();
-
-  String getValidationQuery();
-
-  boolean usePool();
-
-  /**
-   * Return a ScriptRunner that runs the index script. Must not return {@code null}, but may return
-   * a ScriptRunner that does nothing.
-   *
-   * @throws IOException
-   */
-  ScriptRunner getIndexScript() throws IOException;
-}
diff --git a/java/com/google/gerrit/server/schema/DatabaseModule.java b/java/com/google/gerrit/server/schema/DatabaseModule.java
index 38a7751..c65b20e 100644
--- a/java/com/google/gerrit/server/schema/DatabaseModule.java
+++ b/java/com/google/gerrit/server/schema/DatabaseModule.java
@@ -14,13 +14,9 @@
 
 package com.google.gerrit.server.schema;
 
-import static com.google.inject.Scopes.SINGLETON;
-
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
-import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Key;
 import com.google.inject.TypeLiteral;
@@ -31,11 +27,11 @@
   protected void configure() {
     TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
         new TypeLiteral<SchemaFactory<ReviewDb>>() {};
-    TypeLiteral<Database<ReviewDb>> database = new TypeLiteral<Database<ReviewDb>>() {};
-
     bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
-    bind(Key.get(schemaFactory, ReviewDbFactory.class)).to(database).in(SINGLETON);
-    bind(database).toProvider(ReviewDbDatabaseProvider.class);
-    bind(ChangeBundleReader.class).to(GwtormChangeBundleReader.class);
+    bind(Key.get(schemaFactory, ReviewDbFactory.class))
+        .toInstance(
+            () -> {
+              throw new OrmException("ReviewDb no longer exists");
+            });
   }
 }
diff --git a/java/com/google/gerrit/server/schema/Derby.java b/java/com/google/gerrit/server/schema/Derby.java
deleted file mode 100644
index 9fb761d..0000000
--- a/java/com/google/gerrit/server/schema/Derby.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class Derby extends BaseDataSourceType {
-
-  protected final Config cfg;
-  private final SitePaths site;
-
-  @Inject
-  Derby(@GerritServerConfig Config cfg, SitePaths site) {
-    super("org.apache.derby.jdbc.EmbeddedDriver");
-    this.cfg = cfg;
-    this.site = site;
-  }
-
-  @Override
-  public String getUrl() {
-    String database = cfg.getString("database", null, "database");
-    if (database == null || database.isEmpty()) {
-      database = "db/ReviewDB";
-    }
-    return "jdbc:derby:" + site.resolve(database).toString() + ";create=true";
-  }
-
-  @Override
-  public String getValidationQuery() {
-    return "values 1";
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/GroupBundle.java b/java/com/google/gerrit/server/schema/GroupBundle.java
deleted file mode 100644
index 26cd96a..0000000
--- a/java/com/google/gerrit/server/schema/GroupBundle.java
+++ /dev/null
@@ -1,778 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
-import static java.util.Comparator.naturalOrder;
-import static java.util.Comparator.nullsLast;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Streams;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.db.AuditLogReader;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * A bundle of all entities rooted at a single {@link AccountGroup} entity.
- *
- * <p>Used primarily during the migration process. Most callers should prefer {@link InternalGroup}
- * instead.
- */
-@AutoValue
-abstract class GroupBundle {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  static {
-    // Initialization-time checks that the column set hasn't changed since the
-    // last time this file was updated.
-    checkColumns(AccountGroup.NameKey.class, 1);
-    checkColumns(AccountGroup.UUID.class, 1);
-    checkColumns(AccountGroup.Id.class, 1);
-    checkColumns(AccountGroup.class, 1, 2, 4, 7, 9, 10, 11);
-
-    checkColumns(AccountGroupById.Key.class, 1, 2);
-    checkColumns(AccountGroupById.class, 1);
-
-    checkColumns(AccountGroupByIdAud.Key.class, 1, 2, 3);
-    checkColumns(AccountGroupByIdAud.class, 1, 2, 3, 4);
-
-    checkColumns(AccountGroupMember.Key.class, 1, 2);
-    checkColumns(AccountGroupMember.class, 1);
-
-    checkColumns(AccountGroupMemberAudit.Key.class, 1, 2, 3);
-    checkColumns(AccountGroupMemberAudit.class, 1, 2, 3, 4);
-  }
-
-  public enum Source {
-    REVIEW_DB("ReviewDb"),
-    NOTE_DB("NoteDb");
-
-    private final String name;
-
-    private Source(String name) {
-      this.name = name;
-    }
-
-    @Override
-    public String toString() {
-      return name;
-    }
-  }
-
-  @Singleton
-  public static class Factory {
-    private final AuditLogReader auditLogReader;
-
-    @Inject
-    Factory(AuditLogReader auditLogReader) {
-      this.auditLogReader = auditLogReader;
-    }
-
-    public GroupBundle fromNoteDb(
-        Project.NameKey projectName, Repository repo, AccountGroup.UUID uuid)
-        throws ConfigInvalidException, IOException {
-      GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repo, uuid);
-      InternalGroup internalGroup = groupConfig.getLoadedGroup().get();
-      AccountGroup.Id groupId = internalGroup.getId();
-
-      AccountGroup accountGroup =
-          new AccountGroup(
-              internalGroup.getNameKey(),
-              internalGroup.getId(),
-              internalGroup.getGroupUUID(),
-              internalGroup.getCreatedOn());
-      accountGroup.setDescription(internalGroup.getDescription());
-      accountGroup.setOwnerGroupUUID(internalGroup.getOwnerGroupUUID());
-      accountGroup.setVisibleToAll(internalGroup.isVisibleToAll());
-
-      return create(
-          Source.NOTE_DB,
-          accountGroup,
-          internalGroup
-              .getMembers()
-              .stream()
-              .map(
-                  accountId ->
-                      new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)))
-              .collect(toImmutableSet()),
-          auditLogReader.getMembersAudit(repo, uuid),
-          internalGroup
-              .getSubgroups()
-              .stream()
-              .map(
-                  subgroupUuid ->
-                      new AccountGroupById(new AccountGroupById.Key(groupId, subgroupUuid)))
-              .collect(toImmutableSet()),
-          auditLogReader.getSubgroupsAudit(repo, uuid));
-    }
-
-    public static GroupBundle fromReviewDb(ReviewDb db, AccountGroup.UUID groupUuid)
-        throws OrmException {
-      JdbcSchema jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
-      AccountGroup group = readAccountGroupFromReviewDb(jdbcSchema, groupUuid);
-      AccountGroup.Id groupId = group.getId();
-
-      return create(
-          Source.REVIEW_DB,
-          group,
-          readAccountGroupMembersFromReviewDb(jdbcSchema, groupId),
-          readAccountGroupMemberAuditsFromReviewDb(jdbcSchema, groupId),
-          readAccountGroupSubgroupsFromReviewDb(jdbcSchema, groupId),
-          readAccountGroupSubgroupAuditsFromReviewDb(jdbcSchema, groupId));
-    }
-
-    private static AccountGroup readAccountGroupFromReviewDb(
-        JdbcSchema jdbcSchema, AccountGroup.UUID groupUuid) throws OrmException {
-      try (Statement stmt = jdbcSchema.getConnection().createStatement();
-          ResultSet rs =
-              stmt.executeQuery(
-                  "SELECT group_id,"
-                      + " name,"
-                      + " created_on,"
-                      + " description,"
-                      + " owner_group_uuid,"
-                      + " visible_to_all"
-                      + " FROM account_groups"
-                      + " WHERE group_uuid = '"
-                      + groupUuid.get()
-                      + "'")) {
-        if (!rs.next()) {
-          throw new OrmException(String.format("Group %s not found", groupUuid));
-        }
-
-        AccountGroup.Id groupId = new AccountGroup.Id(rs.getInt(1));
-        AccountGroup.NameKey groupName = new AccountGroup.NameKey(rs.getString(2));
-        Timestamp createdOn = rs.getTimestamp(3);
-        String description = rs.getString(4);
-        AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID(rs.getString(5));
-        boolean visibleToAll = "Y".equals(rs.getString(6));
-
-        AccountGroup group = new AccountGroup(groupName, groupId, groupUuid, createdOn);
-        group.setDescription(description);
-        group.setOwnerGroupUUID(ownerGroupUuid);
-        group.setVisibleToAll(visibleToAll);
-
-        if (rs.next()) {
-          throw new OrmException(String.format("Group UUID %s is ambiguous", groupUuid));
-        }
-
-        return group;
-      } catch (SQLException e) {
-        throw new OrmException(
-            String.format("Failed to read account group %s from ReviewDb", groupUuid.get()), e);
-      }
-    }
-
-    private static List<AccountGroupMember> readAccountGroupMembersFromReviewDb(
-        JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
-      try (Statement stmt = jdbcSchema.getConnection().createStatement();
-          ResultSet rs =
-              stmt.executeQuery(
-                  "SELECT account_id"
-                      + " FROM account_group_members"
-                      + " WHERE group_id = '"
-                      + groupId.get()
-                      + "'")) {
-        List<AccountGroupMember> members = new ArrayList<>();
-        while (rs.next()) {
-          Account.Id accountId = new Account.Id(rs.getInt(1));
-          members.add(new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)));
-        }
-        return members;
-      } catch (SQLException e) {
-        throw new OrmException(
-            String.format(
-                "Failed to read members of account group %s from ReviewDb", groupId.get()),
-            e);
-      }
-    }
-
-    private static List<AccountGroupMemberAudit> readAccountGroupMemberAuditsFromReviewDb(
-        JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
-      try (Statement stmt = jdbcSchema.getConnection().createStatement();
-          ResultSet rs =
-              stmt.executeQuery(
-                  "SELECT account_id, added_by, added_on, removed_by, removed_on"
-                      + " FROM account_group_members_audit"
-                      + " WHERE group_id = '"
-                      + groupId.get()
-                      + "'")) {
-        List<AccountGroupMemberAudit> audits = new ArrayList<>();
-        while (rs.next()) {
-          Account.Id accountId = new Account.Id(rs.getInt(1));
-
-          Account.Id addedBy = new Account.Id(rs.getInt(2));
-          Timestamp addedOn = rs.getTimestamp(3);
-
-          Timestamp removedOn = rs.getTimestamp(5);
-          Account.Id removedBy = removedOn != null ? new Account.Id(rs.getInt(4)) : null;
-
-          AccountGroupMemberAudit.Key key =
-              new AccountGroupMemberAudit.Key(accountId, groupId, addedOn);
-          AccountGroupMemberAudit audit = new AccountGroupMemberAudit(key, addedBy);
-          audit.removed(removedBy, removedOn);
-          audits.add(audit);
-        }
-        return audits;
-      } catch (SQLException e) {
-        throw new OrmException(
-            String.format(
-                "Failed to read member audits of account group %s from ReviewDb", groupId.get()),
-            e);
-      }
-    }
-
-    private static List<AccountGroupById> readAccountGroupSubgroupsFromReviewDb(
-        JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
-      try (Statement stmt = jdbcSchema.getConnection().createStatement();
-          ResultSet rs =
-              stmt.executeQuery(
-                  "SELECT include_uuid"
-                      + " FROM account_group_by_id"
-                      + " WHERE group_id = '"
-                      + groupId.get()
-                      + "'")) {
-        List<AccountGroupById> subgroups = new ArrayList<>();
-        while (rs.next()) {
-          AccountGroup.UUID includedGroupUuid = new AccountGroup.UUID(rs.getString(1));
-          subgroups.add(new AccountGroupById(new AccountGroupById.Key(groupId, includedGroupUuid)));
-        }
-        return subgroups;
-      } catch (SQLException e) {
-        throw new OrmException(
-            String.format(
-                "Failed to read subgroups of account group %s from ReviewDb", groupId.get()),
-            e);
-      }
-    }
-
-    private static List<AccountGroupByIdAud> readAccountGroupSubgroupAuditsFromReviewDb(
-        JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
-      try (Statement stmt = jdbcSchema.getConnection().createStatement();
-          ResultSet rs =
-              stmt.executeQuery(
-                  "SELECT include_uuid, added_by, added_on, removed_by, removed_on"
-                      + " FROM account_group_by_id_aud"
-                      + " WHERE group_id = '"
-                      + groupId.get()
-                      + "'")) {
-        List<AccountGroupByIdAud> audits = new ArrayList<>();
-        while (rs.next()) {
-          AccountGroup.UUID includedGroupUuid = new AccountGroup.UUID(rs.getString(1));
-
-          Account.Id addedBy = new Account.Id(rs.getInt(2));
-          Timestamp addedOn = rs.getTimestamp(3);
-
-          Timestamp removedOn = rs.getTimestamp(5);
-          Account.Id removedBy = removedOn != null ? new Account.Id(rs.getInt(4)) : null;
-
-          AccountGroupByIdAud.Key key =
-              new AccountGroupByIdAud.Key(groupId, includedGroupUuid, addedOn);
-          AccountGroupByIdAud audit = new AccountGroupByIdAud(key, addedBy);
-          audit.removed(removedBy, removedOn);
-          audits.add(audit);
-        }
-        return audits;
-      } catch (SQLException e) {
-        throw new OrmException(
-            String.format(
-                "Failed to read subgroup audits of account group %s from ReviewDb", groupId.get()),
-            e);
-      }
-    }
-  }
-
-  private static final Comparator<AccountGroupMember> ACCOUNT_GROUP_MEMBER_COMPARATOR =
-      Comparator.comparingInt((AccountGroupMember m) -> m.getAccountGroupId().get())
-          .thenComparingInt(m -> m.getAccountId().get());
-
-  private static final Comparator<AccountGroupMemberAudit> ACCOUNT_GROUP_MEMBER_AUDIT_COMPARATOR =
-      Comparator.comparingInt((AccountGroupMemberAudit a) -> a.getGroupId().get())
-          .thenComparing(AccountGroupMemberAudit::getAddedOn)
-          .thenComparingInt(a -> a.getAddedBy().get())
-          .thenComparingInt(a -> a.getMemberId().get())
-          .thenComparing(
-              a -> a.getRemovedBy() != null ? a.getRemovedBy().get() : null,
-              nullsLast(naturalOrder()))
-          .thenComparing(AccountGroupMemberAudit::getRemovedOn, nullsLast(naturalOrder()));
-
-  private static final Comparator<AccountGroupById> ACCOUNT_GROUP_BY_ID_COMPARATOR =
-      Comparator.comparingInt((AccountGroupById m) -> m.getGroupId().get())
-          .thenComparing(AccountGroupById::getIncludeUUID);
-
-  private static final Comparator<AccountGroupByIdAud> ACCOUNT_GROUP_BY_ID_AUD_COMPARATOR =
-      Comparator.comparingInt((AccountGroupByIdAud a) -> a.getGroupId().get())
-          .thenComparing(AccountGroupByIdAud::getAddedOn)
-          .thenComparingInt(a -> a.getAddedBy().get())
-          .thenComparing(AccountGroupByIdAud::getIncludeUUID)
-          .thenComparing(
-              a -> a.getRemovedBy() != null ? a.getRemovedBy().get() : null,
-              nullsLast(naturalOrder()))
-          .thenComparing(AccountGroupByIdAud::getRemovedOn, nullsLast(naturalOrder()));
-
-  private static final Comparator<AuditEntry> AUDIT_ENTRY_COMPARATOR =
-      Comparator.comparing(AuditEntry::getTimestamp)
-          .thenComparing(AuditEntry::getAction, Comparator.comparingInt(Action::getOrder));
-
-  public static GroupBundle create(
-      Source source,
-      AccountGroup group,
-      Iterable<AccountGroupMember> members,
-      Iterable<AccountGroupMemberAudit> memberAudit,
-      Iterable<AccountGroupById> byId,
-      Iterable<AccountGroupByIdAud> byIdAudit) {
-    AccountGroup.UUID uuid = group.getGroupUUID();
-    return new AutoValue_GroupBundle.Builder()
-        .source(source)
-        .group(group)
-        .members(
-            logIfNotUnique(
-                source, uuid, members, ACCOUNT_GROUP_MEMBER_COMPARATOR, AccountGroupMember.class))
-        .memberAudit(
-            logIfNotUnique(
-                source,
-                uuid,
-                memberAudit,
-                ACCOUNT_GROUP_MEMBER_AUDIT_COMPARATOR,
-                AccountGroupMemberAudit.class))
-        .byId(
-            logIfNotUnique(
-                source, uuid, byId, ACCOUNT_GROUP_BY_ID_COMPARATOR, AccountGroupById.class))
-        .byIdAudit(
-            logIfNotUnique(
-                source,
-                uuid,
-                byIdAudit,
-                ACCOUNT_GROUP_BY_ID_AUD_COMPARATOR,
-                AccountGroupByIdAud.class))
-        .build();
-  }
-
-  private static <T> ImmutableSet<T> logIfNotUnique(
-      Source source,
-      AccountGroup.UUID uuid,
-      Iterable<T> iterable,
-      Comparator<T> comparator,
-      Class<T> clazz) {
-    List<T> list = Streams.stream(iterable).sorted(comparator).collect(toList());
-    ImmutableSet<T> set = ImmutableSet.copyOf(list);
-    if (set.size() != list.size()) {
-      // One way this can happen is that distinct audit entities can compare equal, because
-      // AccountGroup{MemberAudit,ByIdAud}.Key does not include the addedOn timestamp in its
-      // members() list. However, this particular issue only applies to pure adds, since removedOn
-      // *is* included in equality. As a result, if this happens, it means the audit log is already
-      // corrupt, and it's not clear if we can programmatically repair it. For migrating to NoteDb,
-      // we'll try our best to recreate it, but no guarantees it will match the real sequence of
-      // attempted operations, which is in any case lost in the mists of time.
-      logger.atWarning().log(
-          "group %s in %s has duplicate %s entities: %s",
-          uuid, source, clazz.getSimpleName(), iterable);
-    }
-    return set;
-  }
-
-  static Builder builder() {
-    return new AutoValue_GroupBundle.Builder().members().memberAudit().byId().byIdAudit();
-  }
-
-  public static ImmutableList<String> compareWithAudits(
-      GroupBundle reviewDbBundle, GroupBundle noteDbBundle) {
-    return compare(reviewDbBundle, noteDbBundle, true);
-  }
-
-  public static ImmutableList<String> compareWithoutAudits(
-      GroupBundle reviewDbBundle, GroupBundle noteDbBundle) {
-    return compare(reviewDbBundle, noteDbBundle, false);
-  }
-
-  private static ImmutableList<String> compare(
-      GroupBundle reviewDbBundle, GroupBundle noteDbBundle, boolean compareAudits) {
-    // Normalize the ReviewDb bundle to what we expect in NoteDb. This means that values in error
-    // messages will not reflect the actual data in ReviewDb, but it will make it easier for humans
-    // to see the difference.
-    reviewDbBundle = reviewDbBundle.truncateToSecond();
-    AccountGroup reviewDbGroup = new AccountGroup(reviewDbBundle.group());
-    reviewDbGroup.setDescription(Strings.emptyToNull(reviewDbGroup.getDescription()));
-    reviewDbBundle = reviewDbBundle.toBuilder().group(reviewDbGroup).build();
-
-    checkArgument(
-        reviewDbBundle.source() == Source.REVIEW_DB,
-        "first bundle's source must be %s: %s",
-        Source.REVIEW_DB,
-        reviewDbBundle);
-    checkArgument(
-        noteDbBundle.source() == Source.NOTE_DB,
-        "second bundle's source must be %s: %s",
-        Source.NOTE_DB,
-        noteDbBundle);
-
-    ImmutableList.Builder<String> result = ImmutableList.builder();
-    if (!reviewDbBundle.group().equals(noteDbBundle.group())) {
-      result.add(
-          "AccountGroups differ\n"
-              + ("ReviewDb: " + reviewDbBundle.group() + "\n")
-              + ("NoteDb  : " + noteDbBundle.group()));
-    }
-    if (!reviewDbBundle.members().equals(noteDbBundle.members())) {
-      result.add(
-          "AccountGroupMembers differ\n"
-              + ("ReviewDb: " + reviewDbBundle.members() + "\n")
-              + ("NoteDb  : " + noteDbBundle.members()));
-    }
-    if (compareAudits
-        && !areMemberAuditsConsideredEqual(
-            reviewDbBundle.memberAudit(), noteDbBundle.memberAudit())) {
-      result.add(
-          "AccountGroupMemberAudits differ\n"
-              + ("ReviewDb: " + reviewDbBundle.memberAudit() + "\n")
-              + ("NoteDb  : " + noteDbBundle.memberAudit()));
-    }
-    if (!reviewDbBundle.byId().equals(noteDbBundle.byId())) {
-      result.add(
-          "AccountGroupByIds differ\n"
-              + ("ReviewDb: " + reviewDbBundle.byId() + "\n")
-              + ("NoteDb  : " + noteDbBundle.byId()));
-    }
-    if (compareAudits
-        && !areByIdAuditsConsideredEqual(reviewDbBundle.byIdAudit(), noteDbBundle.byIdAudit())) {
-      result.add(
-          "AccountGroupByIdAudits differ\n"
-              + ("ReviewDb: " + reviewDbBundle.byIdAudit() + "\n")
-              + ("NoteDb  : " + noteDbBundle.byIdAudit()));
-    }
-    return result.build();
-  }
-
-  private static boolean areMemberAuditsConsideredEqual(
-      ImmutableSet<AccountGroupMemberAudit> reviewDbMemberAudits,
-      ImmutableSet<AccountGroupMemberAudit> noteDbMemberAudits) {
-    ListMultimap<String, AuditEntry> reviewDbMemberAuditsByMemberId =
-        toMemberAuditEntriesByMemberId(reviewDbMemberAudits);
-    ListMultimap<String, AuditEntry> noteDbMemberAuditsByMemberId =
-        toMemberAuditEntriesByMemberId(noteDbMemberAudits);
-
-    return areConsideredEqual(reviewDbMemberAuditsByMemberId, noteDbMemberAuditsByMemberId);
-  }
-
-  private static boolean areByIdAuditsConsideredEqual(
-      ImmutableSet<AccountGroupByIdAud> reviewDbByIdAudits,
-      ImmutableSet<AccountGroupByIdAud> noteDbByIdAudits) {
-    ListMultimap<String, AuditEntry> reviewDbByIdAuditsById =
-        toByIdAuditEntriesById(reviewDbByIdAudits);
-    ListMultimap<String, AuditEntry> noteDbByIdAuditsById =
-        toByIdAuditEntriesById(noteDbByIdAudits);
-
-    return areConsideredEqual(reviewDbByIdAuditsById, noteDbByIdAuditsById);
-  }
-
-  private static ListMultimap<String, AuditEntry> toMemberAuditEntriesByMemberId(
-      ImmutableSet<AccountGroupMemberAudit> memberAudits) {
-    return memberAudits
-        .stream()
-        .flatMap(GroupBundle::toAuditEntries)
-        .collect(
-            Multimaps.toMultimap(
-                AuditEntry::getTarget,
-                Function.identity(),
-                MultimapBuilder.hashKeys().arrayListValues()::build));
-  }
-
-  private static Stream<AuditEntry> toAuditEntries(AccountGroupMemberAudit memberAudit) {
-    AuditEntry additionAuditEntry =
-        AuditEntry.create(
-            Action.ADD,
-            memberAudit.getAddedBy(),
-            memberAudit.getMemberId(),
-            memberAudit.getAddedOn());
-    if (memberAudit.isActive()) {
-      return Stream.of(additionAuditEntry);
-    }
-
-    AuditEntry removalAuditEntry =
-        AuditEntry.create(
-            Action.REMOVE,
-            memberAudit.getRemovedBy(),
-            memberAudit.getMemberId(),
-            memberAudit.getRemovedOn());
-    return Stream.of(additionAuditEntry, removalAuditEntry);
-  }
-
-  private static ListMultimap<String, AuditEntry> toByIdAuditEntriesById(
-      ImmutableSet<AccountGroupByIdAud> byIdAudits) {
-    return byIdAudits
-        .stream()
-        .flatMap(GroupBundle::toAuditEntries)
-        .collect(
-            Multimaps.toMultimap(
-                AuditEntry::getTarget,
-                Function.identity(),
-                MultimapBuilder.hashKeys().arrayListValues()::build));
-  }
-
-  private static Stream<AuditEntry> toAuditEntries(AccountGroupByIdAud byIdAudit) {
-    AuditEntry additionAuditEntry =
-        AuditEntry.create(
-            Action.ADD, byIdAudit.getAddedBy(), byIdAudit.getIncludeUUID(), byIdAudit.getAddedOn());
-    if (byIdAudit.isActive()) {
-      return Stream.of(additionAuditEntry);
-    }
-
-    AuditEntry removalAuditEntry =
-        AuditEntry.create(
-            Action.REMOVE,
-            byIdAudit.getRemovedBy(),
-            byIdAudit.getIncludeUUID(),
-            byIdAudit.getRemovedOn());
-    return Stream.of(additionAuditEntry, removalAuditEntry);
-  }
-
-  /**
-   * Determines whether the audit log entries are equal except for redundant entries. Entries of the
-   * same type (addition/removal) which follow directly on each other according to their timestamp
-   * are considered redundant.
-   */
-  private static boolean areConsideredEqual(
-      ListMultimap<String, AuditEntry> reviewDbMemberAuditsByTarget,
-      ListMultimap<String, AuditEntry> noteDbMemberAuditsByTarget) {
-    for (String target : reviewDbMemberAuditsByTarget.keySet()) {
-      ImmutableList<AuditEntry> reviewDbAuditEntries =
-          reviewDbMemberAuditsByTarget
-              .get(target)
-              .stream()
-              .sorted(AUDIT_ENTRY_COMPARATOR)
-              .collect(toImmutableList());
-      ImmutableSet<AuditEntry> noteDbAuditEntries =
-          noteDbMemberAuditsByTarget
-              .get(target)
-              .stream()
-              .sorted(AUDIT_ENTRY_COMPARATOR)
-              .collect(toImmutableSet());
-
-      int reviewDbIndex = 0;
-      for (AuditEntry noteDbAuditEntry : noteDbAuditEntries) {
-        Set<AuditEntry> redundantReviewDbAuditEntries = new HashSet<>();
-        while (reviewDbIndex < reviewDbAuditEntries.size()) {
-          AuditEntry reviewDbAuditEntry = reviewDbAuditEntries.get(reviewDbIndex);
-          if (!reviewDbAuditEntry.getAction().equals(noteDbAuditEntry.getAction())) {
-            break;
-          }
-          redundantReviewDbAuditEntries.add(reviewDbAuditEntry);
-          reviewDbIndex++;
-        }
-
-        // The order of the entries is not perfect as ReviewDb included milliseconds for timestamps
-        // and we cut off everything below seconds due to NoteDb/git. Consequently, we don't have a
-        // way to know in this method in which exact order additions/removals within the same second
-        // happened. The best we can do is to group all additions within the same second as
-        // redundant entries and the removals afterward. To compensate that we possibly group
-        // non-redundant additions/removals, we also accept NoteDb audit entries which just occur
-        // anywhere as ReviewDb audit entries.
-        if (!redundantReviewDbAuditEntries.contains(noteDbAuditEntry)
-            && !reviewDbAuditEntries.contains(noteDbAuditEntry)) {
-          return false;
-        }
-      }
-
-      if (reviewDbIndex < reviewDbAuditEntries.size()) {
-        // Some of the ReviewDb audit log entries aren't matched by NoteDb audit log entries.
-        return false;
-      }
-    }
-    return true;
-  }
-
-  public AccountGroup.Id id() {
-    return group().getId();
-  }
-
-  public AccountGroup.UUID uuid() {
-    return group().getGroupUUID();
-  }
-
-  public abstract Source source();
-
-  public abstract AccountGroup group();
-
-  public abstract ImmutableSet<AccountGroupMember> members();
-
-  public abstract ImmutableSet<AccountGroupMemberAudit> memberAudit();
-
-  public abstract ImmutableSet<AccountGroupById> byId();
-
-  public abstract ImmutableSet<AccountGroupByIdAud> byIdAudit();
-
-  public abstract Builder toBuilder();
-
-  public GroupBundle truncateToSecond() {
-    AccountGroup newGroup = new AccountGroup(group());
-    if (newGroup.getCreatedOn() != null) {
-      newGroup.setCreatedOn(TimeUtil.truncateToSecond(newGroup.getCreatedOn()));
-    }
-    return toBuilder()
-        .group(newGroup)
-        .memberAudit(
-            memberAudit().stream().map(GroupBundle::truncateToSecond).collect(toImmutableSet()))
-        .byIdAudit(
-            byIdAudit().stream().map(GroupBundle::truncateToSecond).collect(toImmutableSet()))
-        .build();
-  }
-
-  private static AccountGroupMemberAudit truncateToSecond(AccountGroupMemberAudit a) {
-    AccountGroupMemberAudit result =
-        new AccountGroupMemberAudit(
-            new AccountGroupMemberAudit.Key(
-                a.getKey().getParentKey(),
-                a.getKey().getGroupId(),
-                TimeUtil.truncateToSecond(a.getKey().getAddedOn())),
-            a.getAddedBy());
-    if (a.getRemovedOn() != null) {
-      result.removed(a.getRemovedBy(), TimeUtil.truncateToSecond(a.getRemovedOn()));
-    }
-    return result;
-  }
-
-  private static AccountGroupByIdAud truncateToSecond(AccountGroupByIdAud a) {
-    AccountGroupByIdAud result =
-        new AccountGroupByIdAud(
-            new AccountGroupByIdAud.Key(
-                a.getKey().getParentKey(),
-                a.getKey().getIncludeUUID(),
-                TimeUtil.truncateToSecond(a.getKey().getAddedOn())),
-            a.getAddedBy());
-    if (a.getRemovedOn() != null) {
-      result.removed(a.getRemovedBy(), TimeUtil.truncateToSecond(a.getRemovedOn()));
-    }
-    return result;
-  }
-
-  public InternalGroup toInternalGroup() {
-    return InternalGroup.create(
-        group(),
-        members().stream().map(AccountGroupMember::getAccountId).collect(toImmutableSet()),
-        byId().stream().map(AccountGroupById::getIncludeUUID).collect(toImmutableSet()));
-  }
-
-  @Override
-  public int hashCode() {
-    throw new UnsupportedOperationException(
-        "hashCode is not supported because equals is not supported");
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    throw new UnsupportedOperationException("Use GroupBundle.compare(a, b) instead of equals");
-  }
-
-  @AutoValue
-  abstract static class AuditEntry {
-    private static AuditEntry create(
-        Action action, Account.Id userId, Account.Id memberId, Timestamp timestamp) {
-      return new AutoValue_GroupBundle_AuditEntry(
-          action, userId, String.valueOf(memberId.get()), timestamp);
-    }
-
-    private static AuditEntry create(
-        Action action, Account.Id userId, AccountGroup.UUID subgroupId, Timestamp timestamp) {
-      return new AutoValue_GroupBundle_AuditEntry(action, userId, subgroupId.get(), timestamp);
-    }
-
-    abstract Action getAction();
-
-    abstract Account.Id getUserId();
-
-    abstract String getTarget();
-
-    abstract Timestamp getTimestamp();
-  }
-
-  enum Action {
-    ADD(1),
-    REMOVE(2);
-
-    private final int order;
-
-    Action(int order) {
-      this.order = order;
-    }
-
-    public int getOrder() {
-      return order;
-    }
-  }
-
-  @AutoValue.Builder
-  abstract static class Builder {
-    abstract Builder source(Source source);
-
-    abstract Builder group(AccountGroup group);
-
-    abstract Builder members(AccountGroupMember... member);
-
-    abstract Builder members(Iterable<AccountGroupMember> member);
-
-    abstract Builder memberAudit(AccountGroupMemberAudit... audit);
-
-    abstract Builder memberAudit(Iterable<AccountGroupMemberAudit> audit);
-
-    abstract Builder byId(AccountGroupById... byId);
-
-    abstract Builder byId(Iterable<AccountGroupById> byId);
-
-    abstract Builder byIdAudit(AccountGroupByIdAud... audit);
-
-    abstract Builder byIdAudit(Iterable<AccountGroupByIdAud> audit);
-
-    abstract GroupBundle build();
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/GroupRebuilder.java b/java/com/google/gerrit/server/schema/GroupRebuilder.java
deleted file mode 100644
index 0157025a..0000000
--- a/java/com/google/gerrit/server/schema/GroupRebuilder.java
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Sets;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.git.meta.VersionedMetaData.BatchMetaDataUpdate;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.group.db.InternalGroupCreation;
-import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gerrit.server.group.db.InternalGroupUpdate.MemberModification;
-import com.google.gerrit.server.group.db.InternalGroupUpdate.SubgroupModification;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Helper for rebuilding an entire group's NoteDb refs. */
-class GroupRebuilder {
-  private final PersonIdent serverIdent;
-  private final AllUsersName allUsers;
-  private final AuditLogFormatter auditLogFormatter;
-
-  public GroupRebuilder(
-      PersonIdent serverIdent, AllUsersName allUsers, AuditLogFormatter auditLogFormatter) {
-    this.serverIdent = serverIdent;
-    this.allUsers = allUsers;
-    this.auditLogFormatter = auditLogFormatter;
-  }
-
-  public void rebuild(Repository allUsersRepo, GroupBundle bundle, @Nullable BatchRefUpdate bru)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
-    AccountGroup group = bundle.group();
-    InternalGroupCreation groupCreation =
-        InternalGroupCreation.builder()
-            .setId(bundle.id())
-            .setNameKey(group.getNameKey())
-            .setGroupUUID(group.getGroupUUID())
-            .build();
-    GroupConfig groupConfig = GroupConfig.createForNewGroup(allUsers, allUsersRepo, groupCreation);
-    groupConfig.setAllowSaveEmptyName();
-
-    InternalGroupUpdate.Builder updateBuilder =
-        InternalGroupUpdate.builder()
-            .setOwnerGroupUUID(group.getOwnerGroupUUID())
-            .setVisibleToAll(group.isVisibleToAll())
-            .setUpdatedOn(group.getCreatedOn());
-    if (bundle.group().getDescription() != null) {
-      updateBuilder.setDescription(group.getDescription());
-    }
-    groupConfig.setGroupUpdate(updateBuilder.build(), auditLogFormatter);
-
-    Map<Key, Collection<Event>> events = toEvents(bundle).asMap();
-    PersonIdent nowServerIdent = getServerIdent(events);
-
-    MetaDataUpdate md = createMetaDataUpdate(allUsers, allUsersRepo, bru);
-
-    // Creation is done by the server (unlike later audit events).
-    PersonIdent created = new PersonIdent(nowServerIdent, group.getCreatedOn());
-    md.getCommitBuilder().setAuthor(created);
-    md.getCommitBuilder().setCommitter(created);
-
-    // Rebuild group ref.
-    try (BatchMetaDataUpdate batch = groupConfig.openUpdate(md)) {
-      batch.write(groupConfig, md.getCommitBuilder());
-
-      for (Map.Entry<Key, Collection<Event>> e : events.entrySet()) {
-        InternalGroupUpdate.Builder ub = InternalGroupUpdate.builder();
-        e.getValue().forEach(event -> event.update().accept(ub));
-        ub.setUpdatedOn(e.getKey().when());
-        groupConfig.setGroupUpdate(ub.build(), auditLogFormatter);
-
-        PersonIdent currServerIdent = new PersonIdent(nowServerIdent, e.getKey().when());
-        CommitBuilder cb = new CommitBuilder();
-        cb.setAuthor(
-            e.getKey()
-                .accountId()
-                .map(id -> auditLogFormatter.getParsableAuthorIdent(id, currServerIdent))
-                .orElse(currServerIdent));
-        cb.setCommitter(currServerIdent);
-        batch.write(groupConfig, cb);
-      }
-
-      batch.createRef(groupConfig.getRefName());
-    }
-  }
-
-  private ListMultimap<Key, Event> toEvents(GroupBundle bundle) {
-    ListMultimap<Key, Event> result =
-        MultimapBuilder.treeKeys(Key.COMPARATOR).arrayListValues(1).build();
-    Event e;
-
-    for (AccountGroupMemberAudit a : bundle.memberAudit()) {
-      checkArgument(
-          a.getKey().getGroupId().equals(bundle.id()),
-          "key %s does not match group %s",
-          a.getKey(),
-          bundle.id());
-      Account.Id accountId = a.getKey().getParentKey();
-      e = event(Type.ADD_MEMBER, a.getAddedBy(), a.getKey().getAddedOn(), addMember(accountId));
-      result.put(e.key(), e);
-      if (!a.isActive()) {
-        e = event(Type.REMOVE_MEMBER, a.getRemovedBy(), a.getRemovedOn(), removeMember(accountId));
-        result.put(e.key(), e);
-      }
-    }
-
-    for (AccountGroupByIdAud a : bundle.byIdAudit()) {
-      checkArgument(
-          a.getKey().getParentKey().equals(bundle.id()),
-          "key %s does not match group %s",
-          a.getKey(),
-          bundle.id());
-      AccountGroup.UUID uuid = a.getKey().getIncludeUUID();
-      e = event(Type.ADD_GROUP, a.getAddedBy(), a.getKey().getAddedOn(), addGroup(uuid));
-      result.put(e.key(), e);
-      if (!a.isActive()) {
-        e = event(Type.REMOVE_GROUP, a.getRemovedBy(), a.getRemovedOn(), removeGroup(uuid));
-        result.put(e.key(), e);
-      }
-    }
-
-    // Due to clock skew, audit events may be in the future relative to this machine. Ensure the
-    // fixup event happens after any other events, both for the purposes of sorting Keys correctly
-    // and to avoid non-monotonic timestamps in the commit history.
-    Timestamp maxTs =
-        Stream.concat(result.keySet().stream().map(Key::when), Stream.of(TimeUtil.nowTs()))
-            .max(Comparator.naturalOrder())
-            .get();
-    Timestamp fixupTs = new Timestamp(maxTs.getTime() + 1);
-    e = serverEvent(Type.FIXUP, fixupTs, setCurrentMembership(bundle));
-    result.put(e.key(), e);
-
-    return result;
-  }
-
-  private PersonIdent getServerIdent(Map<Key, Collection<Event>> events) {
-    // Created with MultimapBuilder.treeKeys, so the keySet is navigable.
-    Key lastKey = ((NavigableSet<Key>) events.keySet()).last();
-    checkState(lastKey.type() == Type.FIXUP);
-    return new PersonIdent(
-        serverIdent.getName(),
-        serverIdent.getEmailAddress(),
-        Iterables.getOnlyElement(events.get(lastKey)).when(),
-        serverIdent.getTimeZone());
-  }
-
-  private static MetaDataUpdate createMetaDataUpdate(
-      Project.NameKey projectName, Repository repository, @Nullable BatchRefUpdate batchRefUpdate) {
-    return new MetaDataUpdate(
-        GitReferenceUpdated.DISABLED, projectName, repository, batchRefUpdate);
-  }
-
-  private static Consumer<InternalGroupUpdate.Builder> addMember(Account.Id toAdd) {
-    return b -> {
-      MemberModification prev = b.getMemberModification();
-      b.setMemberModification(in -> Sets.union(prev.apply(in), ImmutableSet.of(toAdd)));
-    };
-  }
-
-  private static Consumer<InternalGroupUpdate.Builder> removeMember(Account.Id toRemove) {
-    return b -> {
-      MemberModification prev = b.getMemberModification();
-      b.setMemberModification(in -> Sets.difference(prev.apply(in), ImmutableSet.of(toRemove)));
-    };
-  }
-
-  private static Consumer<InternalGroupUpdate.Builder> addGroup(AccountGroup.UUID toAdd) {
-    return b -> {
-      SubgroupModification prev = b.getSubgroupModification();
-      b.setSubgroupModification(in -> Sets.union(prev.apply(in), ImmutableSet.of(toAdd)));
-    };
-  }
-
-  private static Consumer<InternalGroupUpdate.Builder> removeGroup(AccountGroup.UUID toRemove) {
-    return b -> {
-      SubgroupModification prev = b.getSubgroupModification();
-      b.setSubgroupModification(in -> Sets.difference(prev.apply(in), ImmutableSet.of(toRemove)));
-    };
-  }
-
-  private static Consumer<InternalGroupUpdate.Builder> setCurrentMembership(GroupBundle bundle) {
-    // Overwrite members and subgroups with the current values. The storage layer will do the
-    // set differences to compute the appropriate delta, if any.
-    return b ->
-        b.setMemberModification(
-                in ->
-                    bundle
-                        .members()
-                        .stream()
-                        .map(AccountGroupMember::getAccountId)
-                        .collect(toImmutableSet()))
-            .setSubgroupModification(
-                in ->
-                    bundle
-                        .byId()
-                        .stream()
-                        .map(AccountGroupById::getIncludeUUID)
-                        .collect(toImmutableSet()));
-  }
-
-  private static Event event(
-      Type type,
-      Account.Id accountId,
-      Timestamp when,
-      Consumer<InternalGroupUpdate.Builder> update) {
-    return new AutoValue_GroupRebuilder_Event(type, Optional.of(accountId), when, update);
-  }
-
-  private static Event serverEvent(
-      Type type, Timestamp when, Consumer<InternalGroupUpdate.Builder> update) {
-    return new AutoValue_GroupRebuilder_Event(type, Optional.empty(), when, update);
-  }
-
-  @AutoValue
-  abstract static class Event {
-    abstract Type type();
-
-    abstract Optional<Account.Id> accountId();
-
-    abstract Timestamp when();
-
-    abstract Consumer<InternalGroupUpdate.Builder> update();
-
-    Key key() {
-      return new AutoValue_GroupRebuilder_Key(accountId(), when(), type());
-    }
-  }
-
-  /**
-   * Distinct event types.
-   *
-   * <p>Events at the same time by the same user are batched together by type. The types should
-   * correspond to the possible batch operations supported by AuditService.
-   */
-  enum Type {
-    ADD_MEMBER,
-    REMOVE_MEMBER,
-    ADD_GROUP,
-    REMOVE_GROUP,
-    FIXUP;
-  }
-
-  @AutoValue
-  abstract static class Key {
-    static final Comparator<Key> COMPARATOR =
-        Comparator.comparing(Key::when)
-            .thenComparing(
-                k -> k.accountId().map(Account.Id::get).orElse(null),
-                Comparator.nullsFirst(Comparator.naturalOrder()))
-            .thenComparing(Key::type);
-
-    abstract Optional<Account.Id> accountId();
-
-    abstract Timestamp when();
-
-    abstract Type type();
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/H2.java b/java/com/google/gerrit/server/schema/H2.java
deleted file mode 100644
index 840eaf0..0000000
--- a/java/com/google/gerrit/server/schema/H2.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.nio.file.Path;
-import org.eclipse.jgit.lib.Config;
-
-class H2 extends BaseDataSourceType {
-
-  protected final Config cfg;
-  private final SitePaths site;
-
-  @Inject
-  H2(SitePaths site, @GerritServerConfig Config cfg) {
-    super("org.h2.Driver");
-    this.cfg = cfg;
-    this.site = site;
-  }
-
-  @Override
-  public String getUrl() {
-    String database = cfg.getString("database", null, "database");
-    if (database == null || database.isEmpty()) {
-      database = "db/ReviewDB";
-    }
-    return appendUrlOptions(cfg, createUrl(site.resolve(database)));
-  }
-
-  public static String createUrl(Path path) {
-    return new StringBuilder().append("jdbc:h2:").append(path.toUri().toString()).toString();
-  }
-
-  public static String appendUrlOptions(Config cfg, String url) {
-    long h2CacheSize = cfg.getLong("database", "h2", "cacheSize", -1);
-    boolean h2AutoServer = cfg.getBoolean("database", "h2", "autoServer", false);
-
-    StringBuilder urlBuilder = new StringBuilder().append(url);
-
-    if (h2CacheSize >= 0) {
-      // H2 CACHE_SIZE is always given in KB
-      urlBuilder.append(";CACHE_SIZE=").append(h2CacheSize / 1024);
-    }
-    if (h2AutoServer) {
-      urlBuilder.append(";AUTO_SERVER=TRUE");
-    }
-    return urlBuilder.toString();
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/HANA.java b/java/com/google/gerrit/server/schema/HANA.java
deleted file mode 100644
index f9811c6..0000000
--- a/java/com/google/gerrit/server/schema/HANA.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.lib.Config;
-
-class HANA extends BaseDataSourceType {
-
-  private Config cfg;
-
-  @Inject
-  HANA(@GerritServerConfig Config cfg) {
-    super("com.sap.db.jdbc.Driver");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    final StringBuilder b = new StringBuilder();
-    final ConfigSection dbs = new ConfigSection(cfg, "database");
-    b.append("jdbc:sap://");
-    b.append(hostname(dbs.required("hostname")));
-    b.append(port(dbs.optional("port")));
-    String database = dbs.optional("database");
-    if (!Strings.isNullOrEmpty(database)) {
-      b.append("?databaseName=").append(database);
-    }
-    return b.toString();
-  }
-
-  @Override
-  public ScriptRunner getIndexScript() throws IOException {
-    // HANA uses column tables and should not require additional indices
-    return ScriptRunner.NOOP;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/JDBC.java b/java/com/google/gerrit/server/schema/JDBC.java
deleted file mode 100644
index d188df4..0000000
--- a/java/com/google/gerrit/server/schema/JDBC.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class JDBC extends BaseDataSourceType {
-
-  protected final Config cfg;
-
-  @Inject
-  JDBC(@GerritServerConfig Config cfg) {
-    super(ConfigUtil.getRequired(cfg, "database", "driver"));
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    return ConfigUtil.getRequired(cfg, "database", "url");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index 83a0986..1ef69db 100644
--- a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.server.config.ThreadSettingsConfig;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
+import java.nio.file.Path;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -115,7 +116,7 @@
   private static String getUrl(@GerritServerConfig Config cfg, SitePaths sitePaths) {
     String url = cfg.getString(ACCOUNT_PATCH_REVIEW_DB, null, URL);
     if (url == null) {
-      return H2.createUrl(sitePaths.db_dir.resolve("account_patch_reviews"));
+      return createH2Url(sitePaths.db_dir.resolve("account_patch_reviews"));
     }
     return url;
   }
@@ -352,4 +353,8 @@
     }
     return 0;
   }
+
+  private static String createH2Url(Path path) {
+    return new StringBuilder().append("jdbc:h2:").append(path.toUri().toString()).toString();
+  }
 }
diff --git a/java/com/google/gerrit/server/schema/JdbcUtil.java b/java/com/google/gerrit/server/schema/JdbcUtil.java
index dddf23a..3995339 100644
--- a/java/com/google/gerrit/server/schema/JdbcUtil.java
+++ b/java/com/google/gerrit/server/schema/JdbcUtil.java
@@ -28,6 +28,7 @@
     return hostname;
   }
 
+  // TODO(dborowitz): Still used by plugins post-ReviewDb?
   @UsedAt(UsedAt.Project.PLUGINS_ALL)
   public static String port(String port) {
     if (port != null && !port.isEmpty()) {
diff --git a/java/com/google/gerrit/server/schema/MariaDb.java b/java/com/google/gerrit/server/schema/MariaDb.java
deleted file mode 100644
index 6c5dd35..0000000
--- a/java/com/google/gerrit/server/schema/MariaDb.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class MariaDb extends BaseDataSourceType {
-  private final Config cfg;
-
-  @Inject
-  MariaDb(@GerritServerConfig Config cfg) {
-    super("org.mariadb.jdbc.Driver");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    StringBuilder b = new StringBuilder();
-    ConfigSection dbs = new ConfigSection(cfg, "database");
-    b.append("jdbc:mariadb://");
-    b.append(hostname(dbs.optional("hostname")));
-    b.append(port(dbs.optional("port")));
-    b.append("/");
-    b.append(dbs.required("database"));
-    b.append("?useBulkStmts=false");
-    return b.toString();
-  }
-
-  @Override
-  public boolean usePool() {
-    // MariaDB has given us trouble with the connection pool,
-    // sometimes the backend disconnects and the pool winds
-    // up with a stale connection. Fortunately opening up
-    // a new MariaDB connection is usually very fast.
-    return false;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/MaxDb.java b/java/com/google/gerrit/server/schema/MaxDb.java
deleted file mode 100644
index d552eb65..0000000
--- a/java/com/google/gerrit/server/schema/MaxDb.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.lib.Config;
-
-class MaxDb extends BaseDataSourceType {
-
-  private Config cfg;
-
-  @Inject
-  MaxDb(@GerritServerConfig Config cfg) {
-    super("com.sap.dbtech.jdbc.DriverSapDB");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    final StringBuilder b = new StringBuilder();
-    final ConfigSection dbs = new ConfigSection(cfg, "database");
-    b.append("jdbc:sapdb://");
-    b.append(hostname(dbs.optional("hostname")));
-    b.append("/");
-    b.append(dbs.required("database"));
-    return b.toString();
-  }
-
-  @Override
-  public ScriptRunner getIndexScript() throws IOException {
-    return getScriptRunner("index_maxdb.sql");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/MySql.java b/java/com/google/gerrit/server/schema/MySql.java
deleted file mode 100644
index e5f59d7..0000000
--- a/java/com/google/gerrit/server/schema/MySql.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class MySql extends BaseDataSourceType {
-
-  private Config cfg;
-
-  @Inject
-  MySql(@GerritServerConfig Config cfg) {
-    super("com.mysql.jdbc.Driver");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    final StringBuilder b = new StringBuilder();
-    final ConfigSection dbs = new ConfigSection(cfg, "database");
-    b.append("jdbc:mysql://");
-    b.append(hostname(dbs.optional("hostname")));
-    b.append(port(dbs.optional("port")));
-    b.append("/");
-    b.append(dbs.required("database"));
-    // See
-    // https://stackoverflow.com/questions/42084633/table-name-pattern-can-not-be-null-or-empty-in-java
-    b.append("?nullNamePatternMatchesAll=true");
-    return b.toString();
-  }
-
-  @Override
-  public boolean usePool() {
-    // MySQL has given us trouble with the connection pool,
-    // sometimes the backend disconnects and the pool winds
-    // up with a stale connection. Fortunately opening up
-    // a new MySQL connection is usually very fast.
-    return false;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/NoChangesReviewDb.java b/java/com/google/gerrit/server/schema/NoChangesReviewDb.java
index 80a18b9..bdb2b0d 100644
--- a/java/com/google/gerrit/server/schema/NoChangesReviewDb.java
+++ b/java/com/google/gerrit/server/schema/NoChangesReviewDb.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.reviewdb.server.PatchSetAccess;
 import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
 import com.google.gwtorm.server.Access;
 import com.google.gwtorm.server.ListResultSet;
 import com.google.gwtorm.server.ResultSet;
@@ -60,11 +59,6 @@
   }
 
   @Override
-  public SchemaVersionAccess schemaVersion() {
-    throw new UnsupportedOperationException(GONE);
-  }
-
-  @Override
   public ChangeAccess changes() {
     return changes;
   }
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
index 436b67f..602b639 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -28,6 +28,7 @@
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -38,13 +39,17 @@
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.stream.IntStream;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Repository;
 
 public class NoteDbSchemaUpdater {
   private final Config cfg;
+  private final AllProjectsName allProjectsName;
   private final AllUsersName allUsersName;
   private final GitRepositoryManager repoManager;
+  private final SchemaCreator schemaCreator;
   private final NotesMigration notesMigration;
   private final NoteDbSchemaVersionManager versionManager;
   private final NoteDbSchemaVersion.Arguments args;
@@ -53,15 +58,19 @@
   @Inject
   NoteDbSchemaUpdater(
       @GerritServerConfig Config cfg,
+      AllUsersName allUsersName,
+      AllProjectsName allProjectsName,
+      GitRepositoryManager repoManager,
+      SchemaCreator schemaCreator,
       NotesMigration notesMigration,
       NoteDbSchemaVersionManager versionManager,
-      NoteDbSchemaVersion.Arguments args,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName) {
+      NoteDbSchemaVersion.Arguments args) {
     this(
         cfg,
+        allProjectsName,
         allUsersName,
         repoManager,
+        schemaCreator,
         notesMigration,
         versionManager,
         args,
@@ -70,15 +79,19 @@
 
   NoteDbSchemaUpdater(
       Config cfg,
+      AllProjectsName allProjectsName,
       AllUsersName allUsersName,
       GitRepositoryManager repoManager,
+      SchemaCreator schemaCreator,
       NotesMigration notesMigration,
       NoteDbSchemaVersionManager versionManager,
       NoteDbSchemaVersion.Arguments args,
       ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> schemaVersions) {
     this.cfg = cfg;
+    this.allProjectsName = allProjectsName;
     this.allUsersName = allUsersName;
     this.repoManager = repoManager;
+    this.schemaCreator = schemaCreator;
     this.notesMigration = notesMigration;
     this.versionManager = versionManager;
     this.args = args;
@@ -91,6 +104,9 @@
       // only option.
       return;
     }
+
+    ensureSchemaCreated();
+
     int currentVersion = versionManager.read();
     if (currentVersion == 0) {
       // The only valid case where there is no refs/meta/version is when running 3.x init for the
@@ -111,6 +127,18 @@
     }
   }
 
+  private void ensureSchemaCreated() throws OrmException {
+    try {
+      try {
+        repoManager.openRepository(allProjectsName).close();
+      } catch (RepositoryNotFoundException e) {
+        schemaCreator.create();
+      }
+    } catch (IOException | ConfigInvalidException e) {
+      throw new OrmException("Cannot initialize Gerrit site");
+    }
+  }
+
   private void checkNoteDbConfigFor216() throws OrmException {
     // Check that the NoteDb migration config matches what we expect from a site that both:
     // * Completed the change migration to NoteDB.
diff --git a/java/com/google/gerrit/server/schema/Oracle.java b/java/com/google/gerrit/server/schema/Oracle.java
deleted file mode 100644
index 4ff7243..0000000
--- a/java/com/google/gerrit/server/schema/Oracle.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-public class Oracle extends BaseDataSourceType {
-  private Config cfg;
-
-  @Inject
-  public Oracle(@GerritServerConfig Config cfg) {
-    super("oracle.jdbc.driver.OracleDriver");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    final StringBuilder b = new StringBuilder();
-    final ConfigSection dbc = new ConfigSection(cfg, "database");
-    b.append("jdbc:oracle:thin:@");
-    b.append(hostname(dbc.optional("hostname")));
-    b.append(port(dbc.optional("port")));
-    b.append(":");
-    b.append(dbc.required("instance"));
-    return b.toString();
-  }
-
-  @Override
-  public String getValidationQuery() {
-    return "select 1 from dual";
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/PostgreSQL.java b/java/com/google/gerrit/server/schema/PostgreSQL.java
deleted file mode 100644
index d6aee94..0000000
--- a/java/com/google/gerrit/server/schema/PostgreSQL.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.lib.Config;
-
-class PostgreSQL extends BaseDataSourceType {
-
-  private Config cfg;
-
-  @Inject
-  PostgreSQL(@GerritServerConfig Config cfg) {
-    super("org.postgresql.Driver");
-    this.cfg = cfg;
-  }
-
-  @Override
-  public String getUrl() {
-    final StringBuilder b = new StringBuilder();
-    final ConfigSection dbc = new ConfigSection(cfg, "database");
-    b.append("jdbc:postgresql://");
-    b.append(hostname(dbc.optional("hostname")));
-    b.append(port(dbc.optional("port")));
-    b.append("/");
-    b.append(dbc.required("database"));
-    return b.toString();
-  }
-
-  @Override
-  public ScriptRunner getIndexScript() throws IOException {
-    return getScriptRunner("index_postgres.sql");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java b/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java
deleted file mode 100644
index 0fbaeca..0000000
--- a/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.Database;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.name.Named;
-import javax.sql.DataSource;
-
-/** Provides the {@code Database<ReviewDb>} database handle. */
-final class ReviewDbDatabaseProvider implements Provider<Database<ReviewDb>> {
-  private final DataSource datasource;
-
-  @Inject
-  ReviewDbDatabaseProvider(@Named("ReviewDb") final DataSource ds) {
-    datasource = ds;
-  }
-
-  @Override
-  public Database<ReviewDb> get() {
-    try {
-      return new Database<>(datasource, ReviewDb.class);
-    } catch (OrmException e) {
-      throw new ProvisionException("Cannot create ReviewDb", e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/ReviewDbSchemaModule.java b/java/com/google/gerrit/server/schema/ReviewDbSchemaModule.java
index da8898a4..c777133 100644
--- a/java/com/google/gerrit/server/schema/ReviewDbSchemaModule.java
+++ b/java/com/google/gerrit/server/schema/ReviewDbSchemaModule.java
@@ -27,8 +27,10 @@
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
 import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.config.GerritServerIdProvider;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
 import org.eclipse.jgit.lib.PersonIdent;
 
+// TODO(dborowitz): Rename, since this no longer has anything to do with ReviewDb.
 /** Validate the schema and connect to Git. */
 public class ReviewDbSchemaModule extends FactoryModule {
   @Override
@@ -49,5 +51,11 @@
         .annotatedWith(GerritServerId.class)
         .toProvider(GerritServerIdProvider.class)
         .in(SINGLETON);
+
+    // It feels wrong to have this binding in a seemingly unrelated module, but it's a dependency of
+    // SchemaCreatorImpl, so it's needed.
+    // TODO(dborowitz): Is there any way to untangle this?
+    bind(GroupIndexCollection.class);
+    bind(SchemaCreator.class).to(SchemaCreatorImpl.class);
   }
 }
diff --git a/java/com/google/gerrit/server/schema/ReviewDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/ReviewDbSchemaUpdater.java
deleted file mode 100644
index 86f4560..0000000
--- a/java/com/google/gerrit/server/schema/ReviewDbSchemaUpdater.java
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AnonymousCowardName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.Stage;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-
-/** Creates or updates the current database schema. */
-public class ReviewDbSchemaUpdater {
-  private final SchemaFactory<ReviewDb> schema;
-  private final ReviewDbSchemaCreator creator;
-  private final Provider<ReviewDbSchemaVersion> updater;
-
-  @Inject
-  ReviewDbSchemaUpdater(
-      @ReviewDbFactory SchemaFactory<ReviewDb> schema,
-      ReviewDbSchemaCreator creator,
-      Injector parent) {
-    this.schema = schema;
-    this.creator = creator;
-    this.updater = buildInjector(parent).getProvider(ReviewDbSchemaVersion.class);
-  }
-
-  private static Injector buildInjector(Injector parent) {
-    // Use DEVELOPMENT mode to allow lazy initialization of the
-    // graph. This avoids touching ancient schema versions that
-    // are behind this installation's current version.
-    return Guice.createInjector(
-        Stage.DEVELOPMENT,
-        new AbstractModule() {
-          @Override
-          protected void configure() {
-            bind(ReviewDbSchemaVersion.class).to(ReviewDbSchemaVersion.C);
-
-            for (Key<?> k :
-                new Key<?>[] {
-                  Key.get(PersonIdent.class, GerritPersonIdent.class),
-                  Key.get(String.class, AnonymousCowardName.class),
-                  Key.get(Config.class, GerritServerConfig.class),
-                }) {
-              rebind(parent, k);
-            }
-
-            for (Class<?> c :
-                new Class<?>[] {
-                  AllProjectsName.class,
-                  AllUsersCreator.class,
-                  AllUsersName.class,
-                  GitRepositoryManager.class,
-                  SitePaths.class,
-                  SystemGroupBackend.class,
-                }) {
-              rebind(parent, Key.get(c));
-            }
-          }
-
-          private <T> void rebind(Injector parent, Key<T> c) {
-            bind(c).toProvider(parent.getProvider(c));
-          }
-        });
-  }
-
-  public void update(UpdateUI ui) throws OrmException {
-    try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
-
-      final ReviewDbSchemaVersion u = updater.get();
-      final CurrentSchemaVersion version = getSchemaVersion(db);
-      if (version == null) {
-        try {
-          creator.create(db);
-        } catch (IOException | ConfigInvalidException e) {
-          throw new OrmException("Cannot initialize schema", e);
-        }
-
-      } else {
-        try {
-          u.check(ui, version, db);
-        } catch (SQLException e) {
-          throw new OrmException("Cannot upgrade schema", e);
-        }
-      }
-    }
-  }
-
-  @VisibleForTesting
-  public ReviewDbSchemaVersion getLatestSchemaVersion() {
-    return updater.get();
-  }
-
-  private CurrentSchemaVersion getSchemaVersion(ReviewDb db) {
-    try {
-      return db.schemaVersion().get(new CurrentSchemaVersion.Key());
-    } catch (OrmException e) {
-      return null;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/ReviewDbSchemaVersion.java b/java/com/google/gerrit/server/schema/ReviewDbSchemaVersion.java
deleted file mode 100644
index f911a8f..0000000
--- a/java/com/google/gerrit/server/schema/ReviewDbSchemaVersion.java
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Lists;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.UsedAt;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** A version of the database schema. */
-public abstract class ReviewDbSchemaVersion {
-  /** The current schema version. */
-  // DO NOT upgrade this version in the master branch. Future versions must all be implemented as
-  // NoteDbSchemaVersions. It may be upgraded on the stable-2.16 branch, in which case this will
-  // need to be updated upon merging. In any case, this number must not exceed the first NoteDb
-  // schema version (180).
-  public static final Class<Schema_170> C = Schema_170.class;
-
-  static {
-    checkState(C.equals(Schema_170.class));
-    checkState(guessVersion(C) < 180);
-  }
-
-  public static int getBinaryVersion() {
-    return guessVersion(C);
-  }
-
-  private final Provider<? extends ReviewDbSchemaVersion> prior;
-  private final int versionNbr;
-
-  protected ReviewDbSchemaVersion(Provider<? extends ReviewDbSchemaVersion> prior) {
-    this.prior = prior;
-    this.versionNbr = guessVersion(getClass());
-  }
-
-  @UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
-  public static int guessVersion(Class<?> c) {
-    String n = c.getName();
-    n = n.substring(n.lastIndexOf('_') + 1);
-    while (n.startsWith("0")) {
-      n = n.substring(1);
-    }
-    return Integer.parseInt(n);
-  }
-
-  /** @return the {@link CurrentSchemaVersion#versionNbr} this step targets. */
-  public final int getVersionNbr() {
-    return versionNbr;
-  }
-
-  @VisibleForTesting
-  public final ReviewDbSchemaVersion getPrior() {
-    return prior.get();
-  }
-
-  public final void check(UpdateUI ui, CurrentSchemaVersion curr, ReviewDb db)
-      throws OrmException, SQLException {
-    if (curr.versionNbr == versionNbr) {
-      // Nothing to do, we are at the correct schema.
-    } else if (curr.versionNbr > versionNbr) {
-      throw new OrmException(
-          "Cannot downgrade database schema from version "
-              + curr.versionNbr
-              + " to "
-              + versionNbr
-              + ".");
-    } else {
-      upgradeFrom(ui, curr, db);
-    }
-  }
-
-  /** Runs check on the prior schema version, and then upgrades. */
-  private void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr, ReviewDb db)
-      throws OrmException, SQLException {
-    List<ReviewDbSchemaVersion> pending = pending(curr.versionNbr);
-    updateSchema(pending, ui, db);
-    migrateData(pending, ui, curr, db);
-
-    JdbcSchema s = (JdbcSchema) db;
-    final List<String> pruneList = new ArrayList<>();
-    s.pruneSchema(
-        new StatementExecutor() {
-          @Override
-          public void execute(String sql) {
-            pruneList.add(sql);
-          }
-
-          @Override
-          public void close() {
-            // Do nothing.
-          }
-        });
-
-    try (JdbcExecutor e = new JdbcExecutor(s)) {
-      if (!pruneList.isEmpty()) {
-        ui.pruneSchema(e, pruneList);
-      }
-    }
-  }
-
-  private List<ReviewDbSchemaVersion> pending(int curr) {
-    List<ReviewDbSchemaVersion> r = Lists.newArrayListWithCapacity(versionNbr - curr);
-    for (ReviewDbSchemaVersion v = this; curr < v.getVersionNbr(); v = v.prior.get()) {
-      r.add(v);
-    }
-    Collections.reverse(r);
-    return r;
-  }
-
-  private void updateSchema(List<ReviewDbSchemaVersion> pending, UpdateUI ui, ReviewDb db)
-      throws OrmException, SQLException {
-    for (ReviewDbSchemaVersion v : pending) {
-      ui.message(String.format("Upgrading schema to %d ...", v.getVersionNbr()));
-      v.preUpdateSchema(db);
-    }
-
-    JdbcSchema s = (JdbcSchema) db;
-    try (JdbcExecutor e = new JdbcExecutor(s)) {
-      s.updateSchema(e);
-    }
-  }
-
-  /**
-   * Invoked before updateSchema adds new columns/tables.
-   *
-   * @param db open database handle.
-   * @throws OrmException if a Gerrit-specific exception occurred.
-   * @throws SQLException if an underlying SQL exception occurred.
-   */
-  protected void preUpdateSchema(ReviewDb db) throws OrmException, SQLException {}
-
-  private void migrateData(
-      List<ReviewDbSchemaVersion> pending, UpdateUI ui, CurrentSchemaVersion curr, ReviewDb db)
-      throws OrmException, SQLException {
-    for (ReviewDbSchemaVersion v : pending) {
-      Stopwatch sw = Stopwatch.createStarted();
-      ui.message(String.format("Migrating data to schema %d ...", v.getVersionNbr()));
-      v.migrateData(db, ui);
-      v.finish(curr, db);
-      ui.message(String.format("\t> Done (%.3f s)", sw.elapsed(TimeUnit.MILLISECONDS) / 1000d));
-    }
-  }
-
-  /**
-   * Invoked between updateSchema (adds new columns/tables) and pruneSchema (removes deleted
-   * columns/tables).
-   *
-   * @param db open database handle.
-   * @param ui interface for interacting with the user.
-   * @throws OrmException if a Gerrit-specific exception occurred.
-   * @throws SQLException if an underlying SQL exception occurred.
-   */
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {}
-
-  /** Mark the current schema version. */
-  protected void finish(CurrentSchemaVersion curr, ReviewDb db) throws OrmException {
-    curr.versionNbr = versionNbr;
-    db.schemaVersion().update(Collections.singleton(curr));
-  }
-
-  /** Rename an existing table. */
-  protected static void renameTable(ReviewDb db, String from, String to) throws OrmException {
-    JdbcSchema s = (JdbcSchema) db;
-    try (JdbcExecutor e = new JdbcExecutor(s)) {
-      s.renameTable(e, from, to);
-    }
-  }
-
-  /** Rename an existing column. */
-  protected static void renameColumn(ReviewDb db, String table, String from, String to)
-      throws OrmException {
-    JdbcSchema s = (JdbcSchema) db;
-    try (JdbcExecutor e = new JdbcExecutor(s)) {
-      s.renameColumn(e, table, from, to);
-    }
-  }
-
-  /** Execute an SQL statement. */
-  protected static void execute(ReviewDb db, String sql) throws SQLException {
-    try (Statement s = newStatement(db)) {
-      s.execute(sql);
-    }
-  }
-
-  /** Open a new single statement. */
-  protected static Statement newStatement(ReviewDb db) throws SQLException {
-    return ((JdbcSchema) db).getConnection().createStatement();
-  }
-
-  /** Open a new prepared statement. */
-  protected static PreparedStatement prepareStatement(ReviewDb db, String sql) throws SQLException {
-    return ((JdbcSchema) db).getConnection().prepareStatement(sql);
-  }
-
-  /** Open a new statement executor. */
-  protected static JdbcExecutor newExecutor(ReviewDb db) throws OrmException {
-    return new JdbcExecutor(((JdbcSchema) db).getConnection());
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_168.java b/java/com/google/gerrit/server/schema/SchemaCreator.java
similarity index 70%
rename from java/com/google/gerrit/server/schema/Schema_168.java
rename to java/com/google/gerrit/server/schema/SchemaCreator.java
index fff4049..78a8422 100644
--- a/java/com/google/gerrit/server/schema/Schema_168.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -14,13 +14,11 @@
 
 package com.google.gerrit.server.schema;
 
-import com.google.inject.Inject;
-import com.google.inject.Provider;
+import com.google.gwtorm.server.OrmException;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 
-/** Drop group tables. */
-public class Schema_168 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_168(Provider<Schema_167> prior) {
-    super(prior);
-  }
+/** Populates initial repository data. */
+public interface SchemaCreator {
+  void create() throws OrmException, IOException, ConfigInvalidException;
 }
diff --git a/java/com/google/gerrit/server/schema/ReviewDbSchemaCreator.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
similarity index 89%
rename from java/com/google/gerrit/server/schema/ReviewDbSchemaCreator.java
rename to java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
index 3954d67..0d184a3 100644
--- a/java/com/google/gerrit/server/schema/ReviewDbSchemaCreator.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -20,8 +20,6 @@
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.Sequences;
 import com.google.gerrit.server.account.GroupUUID;
@@ -38,27 +36,26 @@
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.index.group.GroupIndex;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
-import java.util.Collections;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
 
-/** Creates the current database schema and populates initial code rows. */
-public class ReviewDbSchemaCreator {
+// TODO(dborowitz): The current NoteDb implementation mirrors the old ReviewDb code: this class is
+// called to create the site early on in NoteDbSchemaUpdater#update. This logic is a little
+// confusing and could stand to be reworked. Another smell is that this is an interface only for
+// testing purposes.
+public class SchemaCreatorImpl implements SchemaCreator {
   private final GitRepositoryManager repoManager;
   private final AllProjectsCreator allProjectsCreator;
   private final AllUsersCreator allUsersCreator;
   private final AllUsersName allUsersName;
   private final PersonIdent serverUser;
-  private final DataSourceType dataSourceType;
   private final GroupIndexCollection indexCollection;
   private final String serverId;
 
@@ -67,13 +64,12 @@
   private final AllProjectsName allProjectsName;
 
   @Inject
-  public ReviewDbSchemaCreator(
+  public SchemaCreatorImpl(
       GitRepositoryManager repoManager,
       AllProjectsCreator ap,
       AllUsersCreator auc,
       AllUsersName allUsersName,
       @GerritPersonIdent PersonIdent au,
-      DataSourceType dst,
       GroupIndexCollection ic,
       String serverId,
       Config config,
@@ -84,7 +80,6 @@
     allUsersCreator = auc;
     this.allUsersName = allUsersName;
     serverUser = au;
-    dataSourceType = dst;
     indexCollection = ic;
     this.serverId = serverId;
 
@@ -93,16 +88,8 @@
     this.metricMaker = metricMaker;
   }
 
-  public void create(ReviewDb db) throws OrmException, IOException, ConfigInvalidException {
-    final JdbcSchema jdbc = (JdbcSchema) db;
-    try (JdbcExecutor e = new JdbcExecutor(jdbc)) {
-      jdbc.updateSchema(e);
-    }
-
-    final CurrentSchemaVersion sVer = CurrentSchemaVersion.create();
-    sVer.versionNbr = ReviewDbSchemaVersion.getBinaryVersion();
-    db.schemaVersion().insert(Collections.singleton(sVer));
-
+  @Override
+  public void create() throws OrmException, IOException, ConfigInvalidException {
     GroupReference admins = createGroupReference("Administrators");
     GroupReference batchUsers = createGroupReference("Non-Interactive Users");
 
@@ -123,8 +110,6 @@
       createAdminsGroup(seqs, allUsersRepo, admins);
       createBatchUsersGroup(seqs, allUsersRepo, batchUsers, admins.getUUID());
     }
-
-    dataSourceType.getIndexScript().run(db);
   }
 
   private void createAdminsGroup(
diff --git a/java/com/google/gerrit/server/schema/Schema_100.java b/java/com/google/gerrit/server/schema/Schema_100.java
deleted file mode 100644
index b5105a9..0000000
--- a/java/com/google/gerrit/server/schema/Schema_100.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_100 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_100(Provider<Schema_99> prior) {
-    super(prior);
-  }
-
-  // No database migration; merges are rechecked on reindex.
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_101.java b/java/com/google/gerrit/server/schema/Schema_101.java
deleted file mode 100644
index 753d992..0000000
--- a/java/com/google/gerrit/server/schema/Schema_101.java
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.base.Joiner;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.ColumnModel;
-import com.google.gwtorm.schema.RelationModel;
-import com.google.gwtorm.schema.java.JavaSchemaModel;
-import com.google.gwtorm.schema.sql.DialectPostgreSQL;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class Schema_101 extends ReviewDbSchemaVersion {
-
-  private static class PrimaryKey {
-    String oldNameInDb;
-    List<String> cols;
-  }
-
-  private Connection conn;
-  private SqlDialect dialect;
-
-  @Inject
-  Schema_101(Provider<Schema_100> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    conn = ((JdbcSchema) db).getConnection();
-    dialect = ((JdbcSchema) db).getDialect();
-    Map<String, PrimaryKey> corrections = findPKUpdates();
-    if (corrections.isEmpty()) {
-      return;
-    }
-
-    ui.message("Wrong Primary Key Column Order Detected");
-    ui.message("The following tables are affected:");
-    ui.message(Joiner.on(", ").join(corrections.keySet()));
-    ui.message("fixing primary keys...");
-    try (JdbcExecutor executor = new JdbcExecutor(conn)) {
-      for (Map.Entry<String, PrimaryKey> c : corrections.entrySet()) {
-        ui.message(String.format("  table: %s ... ", c.getKey()));
-        recreatePK(executor, c.getKey(), c.getValue(), ui);
-        ui.message("done");
-      }
-      ui.message("done");
-    }
-  }
-
-  private Map<String, PrimaryKey> findPKUpdates() throws OrmException, SQLException {
-    Map<String, PrimaryKey> corrections = new TreeMap<>();
-    DatabaseMetaData meta = conn.getMetaData();
-    JavaSchemaModel jsm = new JavaSchemaModel(ReviewDb.class);
-    for (RelationModel rm : jsm.getRelations()) {
-      String tableName = rm.getRelationName();
-      List<String> expectedPKCols = relationPK(rm);
-      PrimaryKey actualPK = dbTablePK(meta, tableName);
-      if (!expectedPKCols.equals(actualPK.cols)) {
-        actualPK.cols = expectedPKCols;
-        corrections.put(tableName, actualPK);
-      }
-    }
-    return corrections;
-  }
-
-  private List<String> relationPK(RelationModel rm) {
-    Collection<ColumnModel> cols = rm.getPrimaryKeyColumns();
-    List<String> pk = new ArrayList<>(cols.size());
-    for (ColumnModel cm : cols) {
-      pk.add(cm.getColumnName().toLowerCase(Locale.US));
-    }
-    return pk;
-  }
-
-  private PrimaryKey dbTablePK(DatabaseMetaData meta, String tableName) throws SQLException {
-    if (meta.storesUpperCaseIdentifiers()) {
-      tableName = tableName.toUpperCase();
-    } else if (meta.storesLowerCaseIdentifiers()) {
-      tableName = tableName.toLowerCase();
-    }
-
-    try (ResultSet cols = meta.getPrimaryKeys(null, null, tableName)) {
-      PrimaryKey pk = new PrimaryKey();
-      Map<Short, String> seqToName = new TreeMap<>();
-      while (cols.next()) {
-        seqToName.put(cols.getShort("KEY_SEQ"), cols.getString("COLUMN_NAME"));
-        if (pk.oldNameInDb == null) {
-          pk.oldNameInDb = cols.getString("PK_NAME");
-        }
-      }
-
-      pk.cols = new ArrayList<>(seqToName.size());
-      for (String name : seqToName.values()) {
-        pk.cols.add(name.toLowerCase(Locale.US));
-      }
-      return pk;
-    }
-  }
-
-  private void recreatePK(StatementExecutor executor, String tableName, PrimaryKey pk, UpdateUI ui)
-      throws OrmException {
-    if (pk.oldNameInDb == null) {
-      ui.message(String.format("warning: primary key for table %s didn't exist ... ", tableName));
-    } else {
-      if (dialect instanceof DialectPostgreSQL) {
-        // postgresql doesn't support the ALTER TABLE foo DROP PRIMARY KEY form
-        executor.execute("ALTER TABLE " + tableName + " DROP CONSTRAINT " + pk.oldNameInDb);
-      } else {
-        executor.execute("ALTER TABLE " + tableName + " DROP PRIMARY KEY");
-      }
-    }
-    executor.execute(
-        "ALTER TABLE " + tableName + " ADD PRIMARY KEY(" + Joiner.on(",").join(pk.cols) + ")");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_102.java b/java/com/google/gerrit/server/schema/Schema_102.java
deleted file mode 100644
index 2bd52c2..0000000
--- a/java/com/google/gerrit/server/schema/Schema_102.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectPostgreSQL;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-public class Schema_102 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_102(Provider<Schema_101> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    SqlDialect dialect = schema.getDialect();
-    try (StatementExecutor e = newExecutor(db)) {
-      // Drop left over indexes that were missed to be removed in schema 84.
-      // See "Delete SQL index support" commit for more details:
-      // d4ae3a16d5e1464574bd04f429a63eb9c02b3b43
-      Pattern pattern =
-          Pattern.compile("^changes_(allOpen|allClosed|byBranchClosed)$", Pattern.CASE_INSENSITIVE);
-      String table = "changes";
-      Set<String> listIndexes = dialect.listIndexes(schema.getConnection(), table);
-      for (String index : listIndexes) {
-        if (pattern.matcher(index).matches()) {
-          dialect.dropIndex(e, table, index);
-        }
-      }
-
-      dialect.dropIndex(e, table, "changes_byProjectOpen");
-      if (dialect instanceof DialectPostgreSQL) {
-        e.execute(
-            "CREATE INDEX changes_byProjectOpen"
-                + " ON "
-                + table
-                + " (dest_project_name, last_updated_on)"
-                + " WHERE open = 'Y'");
-      } else {
-        e.execute(
-            "CREATE INDEX changes_byProjectOpen"
-                + " ON "
-                + table
-                + " (open, dest_project_name, last_updated_on)");
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_103.java b/java/com/google/gerrit/server/schema/Schema_103.java
deleted file mode 100644
index 1906b66..0000000
--- a/java/com/google/gerrit/server/schema/Schema_103.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_103 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_103(Provider<Schema_102> prior) {
-    super(prior);
-  }
-
-  // Adds originalSubject column
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_104.java b/java/com/google/gerrit/server/schema/Schema_104.java
deleted file mode 100644
index 9b4d67c..0000000
--- a/java/com/google/gerrit/server/schema/Schema_104.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_104 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_104(Provider<Schema_103> prior) {
-    super(prior);
-  }
-
-  // Remove old change screen
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_105.java b/java/com/google/gerrit/server/schema/Schema_105.java
deleted file mode 100644
index 56e19ef..0000000
--- a/java/com/google/gerrit/server/schema/Schema_105.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class Schema_105 extends ReviewDbSchemaVersion {
-  private static final String TABLE = "changes";
-
-  @Inject
-  Schema_105(Provider<Schema_104> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException, OrmException {
-    JdbcSchema schema = (JdbcSchema) db;
-    SqlDialect dialect = schema.getDialect();
-
-    Map<String, OrmException> errors = new HashMap<>();
-    try (StatementExecutor e = newExecutor(db)) {
-      for (String index : listChangesIndexes(schema)) {
-        ui.message("Dropping index " + index + " on table " + TABLE);
-        try {
-          dialect.dropIndex(e, TABLE, index);
-        } catch (OrmException err) {
-          errors.put(index, err);
-        }
-      }
-    }
-
-    for (String index : listChangesIndexes(schema)) {
-      String msg = "Failed to drop index " + index;
-      OrmException err = errors.get(index);
-      if (err != null) {
-        msg += ": " + err.getMessage();
-      }
-      ui.message(msg);
-    }
-  }
-
-  private Set<String> listChangesIndexes(JdbcSchema schema) throws SQLException {
-    // List of all changes indexes ever created or dropped, found with the
-    // following command:
-    //   find g* -name \*.sql | xargs git log -i -p -S' index changes_' | grep -io ' index
-    // changes_\w*' | cut -d' ' -f3 | tr A-Z a-z | sort -u
-    // Used rather than listIndexes as we're not sure whether it might include
-    // primary key indexes.
-    Set<String> allChanges =
-        ImmutableSet.of(
-            "changes_allclosed",
-            "changes_allopen",
-            "changes_bybranchclosed",
-            "changes_byownerclosed",
-            "changes_byowneropen",
-            "changes_byproject",
-            "changes_byprojectopen",
-            "changes_key",
-            "changes_submitted");
-    return Sets.intersection(
-        schema.getDialect().listIndexes(schema.getConnection(), TABLE), allChanges);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_106.java b/java/com/google/gerrit/server/schema/Schema_106.java
deleted file mode 100644
index a3f99fc..0000000
--- a/java/com/google/gerrit/server/schema/Schema_106.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_106 extends ReviewDbSchemaVersion {
-  // we can use multiple threads per CPU as we can expect that threads will be
-  // waiting for IO
-  private static final int THREADS_PER_CPU = 4;
-  private final GitRepositoryManager repoManager;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_106(
-      Provider<Schema_105> prior,
-      GitRepositoryManager repoManager,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    if (!(repoManager instanceof LocalDiskRepositoryManager)) {
-      return;
-    }
-
-    ui.message("listing all repositories ...");
-    SortedSet<Project.NameKey> repoList = repoManager.list();
-    ui.message("done");
-
-    ui.message(String.format("creating reflog files for %s branches ...", RefNames.REFS_CONFIG));
-
-    ExecutorService executorPool = createExecutor(ui, repoList.size());
-    List<Future<Void>> futures = new ArrayList<>();
-
-    for (Project.NameKey project : repoList) {
-      Callable<Void> callable = new ReflogCreator(project);
-      futures.add(executorPool.submit(callable));
-    }
-
-    executorPool.shutdown();
-    try {
-      for (Future<Void> future : futures) {
-        try {
-          future.get();
-        } catch (ExecutionException e) {
-          ui.message(e.getCause().getMessage());
-        }
-      }
-      ui.message("done");
-    } catch (InterruptedException ex) {
-      String msg =
-          String.format(
-              "Migration step 106 was interrupted. "
-                  + "Reflog created in %d of %d repositories only.",
-              countDone(futures), repoList.size());
-      ui.message(msg);
-    }
-  }
-
-  private static int countDone(List<Future<Void>> futures) {
-    int count = 0;
-    for (Future<Void> future : futures) {
-      if (future.isDone()) {
-        count++;
-      }
-    }
-
-    return count;
-  }
-
-  private ExecutorService createExecutor(UpdateUI ui, int repoCount) {
-    int procs = Runtime.getRuntime().availableProcessors();
-    int threads = Math.min(procs * THREADS_PER_CPU, repoCount);
-    ui.message(String.format("... using %d threads ...", threads));
-    return Executors.newFixedThreadPool(threads);
-  }
-
-  private class ReflogCreator implements Callable<Void> {
-    private final Project.NameKey project;
-
-    ReflogCreator(Project.NameKey project) {
-      this.project = project;
-    }
-
-    @Override
-    public Void call() throws IOException {
-      try (Repository repo = repoManager.openRepository(project)) {
-        File metaConfigLog = new File(repo.getDirectory(), "logs/" + RefNames.REFS_CONFIG);
-        if (metaConfigLog.exists()) {
-          return null;
-        }
-
-        if (!metaConfigLog.getParentFile().mkdirs() || !metaConfigLog.createNewFile()) {
-          throw new IOException();
-        }
-
-        ObjectId metaConfigId = repo.resolve(RefNames.REFS_CONFIG);
-        if (metaConfigId != null) {
-          try (PrintWriter writer = new PrintWriter(metaConfigLog, UTF_8.name())) {
-            writer.print(ObjectId.zeroId().name());
-            writer.print(" ");
-            writer.print(metaConfigId.name());
-            writer.print(" ");
-            writer.print(serverUser.toExternalString());
-            writer.print("\t");
-            writer.print("create reflog");
-            writer.println();
-          }
-        }
-        return null;
-      } catch (IOException e) {
-        throw new IOException(
-            String.format(
-                "ERROR: Failed to create reflog file for the %s branch in repository %s",
-                RefNames.REFS_CONFIG, project.get()));
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_107.java b/java/com/google/gerrit/server/schema/Schema_107.java
deleted file mode 100644
index bef8c65..0000000
--- a/java/com/google/gerrit/server/schema/Schema_107.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_107 extends ReviewDbSchemaVersion {
-
-  @Inject
-  Schema_107(Provider<Schema_106> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement()) {
-      stmt.executeUpdate("UPDATE accounts set mute_common_path_prefixes = 'Y'");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_108.java b/java/com/google/gerrit/server/schema/Schema_108.java
deleted file mode 100644
index b2ab042..0000000
--- a/java/com/google/gerrit/server/schema/Schema_108.java
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Status;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.GroupCollector;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_108 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-
-  @Inject
-  Schema_108(Provider<Schema_107> prior, GitRepositoryManager repoManager) {
-    super(prior);
-    this.repoManager = repoManager;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    ui.message("Listing all changes ...");
-    SetMultimap<Project.NameKey, Change.Id> openByProject = getOpenChangesByProject(db, ui);
-    ui.message("done");
-
-    ui.message("Updating groups for open changes ...");
-    int i = 0;
-    for (Map.Entry<Project.NameKey, Collection<Change.Id>> e : openByProject.asMap().entrySet()) {
-      try (Repository repo = repoManager.openRepository(e.getKey());
-          RevWalk rw = new RevWalk(repo)) {
-        updateProjectGroups(db, repo, rw, (Set<Change.Id>) e.getValue(), ui);
-      } catch (IOException | NoSuchChangeException err) {
-        throw new OrmException(err);
-      }
-      if (++i % 100 == 0) {
-        ui.message("  done " + i + " projects ...");
-      }
-    }
-    ui.message("done");
-  }
-
-  private void updateProjectGroups(
-      ReviewDb db, Repository repo, RevWalk rw, Set<Change.Id> changes, UpdateUI ui)
-      throws OrmException, IOException {
-    // Match sorting in ReceiveCommits.
-    rw.reset();
-    rw.sort(RevSort.TOPO);
-    rw.sort(RevSort.REVERSE, true);
-
-    RefDatabase refdb = repo.getRefDatabase();
-    for (Ref ref : refdb.getRefsByPrefix(Constants.R_HEADS)) {
-      RevCommit c = maybeParseCommit(rw, ref.getObjectId(), ui);
-      if (c != null) {
-        rw.markUninteresting(c);
-      }
-    }
-
-    ListMultimap<ObjectId, Ref> changeRefsBySha =
-        MultimapBuilder.hashKeys().arrayListValues().build();
-    ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha =
-        MultimapBuilder.hashKeys().arrayListValues().build();
-    for (Ref ref : refdb.getRefsByPrefix(RefNames.REFS_CHANGES)) {
-      ObjectId id = ref.getObjectId();
-      if (ref.getObjectId() == null) {
-        continue;
-      }
-      id = id.copy();
-      changeRefsBySha.put(id, ref);
-      PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
-      if (psId != null && changes.contains(psId.getParentKey())) {
-        patchSetsBySha.put(id, psId);
-        RevCommit c = maybeParseCommit(rw, id, ui);
-        if (c != null) {
-          rw.markStart(c);
-        }
-      }
-    }
-
-    GroupCollector collector = GroupCollector.createForSchemaUpgradeOnly(changeRefsBySha, db);
-    RevCommit c;
-    while ((c = rw.next()) != null) {
-      collector.visit(c);
-    }
-
-    updateGroups(db, collector, patchSetsBySha);
-  }
-
-  private static void updateGroups(
-      ReviewDb db, GroupCollector collector, ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha)
-      throws OrmException {
-    Map<PatchSet.Id, PatchSet> patchSets =
-        db.patchSets().toMap(db.patchSets().get(patchSetsBySha.values()));
-    for (Map.Entry<ObjectId, Collection<String>> e : collector.getGroups().asMap().entrySet()) {
-      for (PatchSet.Id psId : patchSetsBySha.get(e.getKey())) {
-        PatchSet ps = patchSets.get(psId);
-        if (ps != null) {
-          ps.setGroups(ImmutableList.copyOf(e.getValue()));
-        }
-      }
-    }
-
-    db.patchSets().update(patchSets.values());
-  }
-
-  private SetMultimap<Project.NameKey, Change.Id> getOpenChangesByProject(ReviewDb db, UpdateUI ui)
-      throws OrmException {
-    SortedSet<Project.NameKey> projects = repoManager.list();
-    SortedSet<Project.NameKey> nonExistentProjects = Sets.newTreeSet();
-    SetMultimap<Project.NameKey, Change.Id> openByProject =
-        MultimapBuilder.hashKeys().hashSetValues().build();
-    for (Change c : db.changes().all()) {
-      Status status = c.getStatus();
-      if (status != null && status.isClosed()) {
-        continue;
-      }
-
-      Project.NameKey projectKey = c.getProject();
-      if (!projects.contains(projectKey)) {
-        nonExistentProjects.add(projectKey);
-      } else {
-        // The old "submitted" state is not supported anymore
-        // (thus status is null) but it was an opened state and needs
-        // to be migrated as such
-        openByProject.put(projectKey, c.getId());
-      }
-    }
-
-    if (!nonExistentProjects.isEmpty()) {
-      ui.message("Detected open changes referring to the following non-existent projects:");
-      ui.message(Joiner.on(", ").join(nonExistentProjects));
-      ui.message(
-          "It is highly recommended to remove\n"
-              + "the obsolete open changes, comments and patch-sets from your DB.\n");
-    }
-    return openByProject;
-  }
-
-  private static RevCommit maybeParseCommit(RevWalk rw, ObjectId id, UpdateUI ui)
-      throws IOException {
-    if (id != null) {
-      try {
-        RevObject obj = rw.parseAny(id);
-        return (obj instanceof RevCommit) ? (RevCommit) obj : null;
-      } catch (MissingObjectException moe) {
-        ui.message("Missing object: " + id.getName() + "\n");
-      }
-    }
-    return null;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_109.java b/java/com/google/gerrit/server/schema/Schema_109.java
deleted file mode 100644
index f582741..0000000
--- a/java/com/google/gerrit/server/schema/Schema_109.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_109 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_109(Provider<Schema_108> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (StatementExecutor e = newExecutor(db)) {
-      e.execute("UPDATE changes SET status = 'n', created_on = created_on WHERE status = 's'");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_110.java b/java/com/google/gerrit/server/schema/Schema_110.java
deleted file mode 100644
index 6da10f6..0000000
--- a/java/com/google/gerrit/server/schema/Schema_110.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_110 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_110(Provider<Schema_109> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_111.java b/java/com/google/gerrit/server/schema/Schema_111.java
deleted file mode 100644
index 7ba48c2..0000000
--- a/java/com/google/gerrit/server/schema/Schema_111.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_111 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_111(Provider<Schema_110> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_112.java b/java/com/google/gerrit/server/schema/Schema_112.java
deleted file mode 100644
index 5a764d0..0000000
--- a/java/com/google/gerrit/server/schema/Schema_112.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_112 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_112(Provider<Schema_111> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_113.java b/java/com/google/gerrit/server/schema/Schema_113.java
deleted file mode 100644
index 2876636..0000000
--- a/java/com/google/gerrit/server/schema/Schema_113.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_113 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_113(Provider<Schema_112> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_114.java b/java/com/google/gerrit/server/schema/Schema_114.java
deleted file mode 100644
index 7bee00c..0000000
--- a/java/com/google/gerrit/server/schema/Schema_114.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_114 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_114(Provider<Schema_113> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_115.java b/java/com/google/gerrit/server/schema/Schema_115.java
deleted file mode 100644
index 28cfe22..0000000
--- a/java/com/google/gerrit/server/schema/Schema_115.java
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.config.ConfigUtil.storeSection;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.UserConfigSections;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.patch.PatchListKey;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_115 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager mgr;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_115(
-      Provider<Schema_114> prior,
-      GitRepositoryManager mgr,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.mgr = mgr;
-    this.allUsersName = allUsersName;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    Map<Account.Id, DiffPreferencesInfo> imports = new HashMap<>();
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs = stmt.executeQuery("SELECT * FROM account_diff_preferences")) {
-      Set<String> availableColumns = getColumns(rs);
-      while (rs.next()) {
-        Account.Id accountId = new Account.Id(rs.getInt("id"));
-        DiffPreferencesInfo prefs = new DiffPreferencesInfo();
-        if (availableColumns.contains("context")) {
-          prefs.context = (int) rs.getShort("context");
-        }
-        if (availableColumns.contains("expand_all_comments")) {
-          prefs.expandAllComments = toBoolean(rs.getString("expand_all_comments"));
-        }
-        if (availableColumns.contains("hide_line_numbers")) {
-          prefs.hideLineNumbers = toBoolean(rs.getString("hide_line_numbers"));
-        }
-        if (availableColumns.contains("hide_top_menu")) {
-          prefs.hideTopMenu = toBoolean(rs.getString("hide_top_menu"));
-        }
-        if (availableColumns.contains("ignore_whitespace")) {
-          // Enum with char as value
-          prefs.ignoreWhitespace = toWhitespace(rs.getString("ignore_whitespace"));
-        }
-        if (availableColumns.contains("intraline_difference")) {
-          prefs.intralineDifference = toBoolean(rs.getString("intraline_difference"));
-        }
-        if (availableColumns.contains("line_length")) {
-          prefs.lineLength = rs.getInt("line_length");
-        }
-        if (availableColumns.contains("manual_review")) {
-          prefs.manualReview = toBoolean(rs.getString("manual_review"));
-        }
-        if (availableColumns.contains("render_entire_file")) {
-          prefs.renderEntireFile = toBoolean(rs.getString("render_entire_file"));
-        }
-        if (availableColumns.contains("retain_header")) {
-          prefs.retainHeader = toBoolean(rs.getString("retain_header"));
-        }
-        if (availableColumns.contains("show_line_endings")) {
-          prefs.showLineEndings = toBoolean(rs.getString("show_line_endings"));
-        }
-        if (availableColumns.contains("show_tabs")) {
-          prefs.showTabs = toBoolean(rs.getString("show_tabs"));
-        }
-        if (availableColumns.contains("show_whitespace_errors")) {
-          prefs.showWhitespaceErrors = toBoolean(rs.getString("show_whitespace_errors"));
-        }
-        if (availableColumns.contains("skip_deleted")) {
-          prefs.skipDeleted = toBoolean(rs.getString("skip_deleted"));
-        }
-        if (availableColumns.contains("skip_uncommented")) {
-          prefs.skipUncommented = toBoolean(rs.getString("skip_uncommented"));
-        }
-        if (availableColumns.contains("syntax_highlighting")) {
-          prefs.syntaxHighlighting = toBoolean(rs.getString("syntax_highlighting"));
-        }
-        if (availableColumns.contains("tab_size")) {
-          prefs.tabSize = rs.getInt("tab_size");
-        }
-        if (availableColumns.contains("theme")) {
-          // Enum with name as values; can be null
-          prefs.theme = toTheme(rs.getString("theme"));
-        }
-        if (availableColumns.contains("hide_empty_pane")) {
-          prefs.hideEmptyPane = toBoolean(rs.getString("hide_empty_pane"));
-        }
-        if (availableColumns.contains("auto_hide_diff_table_header")) {
-          prefs.autoHideDiffTableHeader = toBoolean(rs.getString("auto_hide_diff_table_header"));
-        }
-        imports.put(accountId, prefs);
-      }
-    }
-
-    if (imports.isEmpty()) {
-      return;
-    }
-
-    try (Repository git = mgr.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-      for (Map.Entry<Account.Id, DiffPreferencesInfo> e : imports.entrySet()) {
-        try (MetaDataUpdate md =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
-          md.getCommitBuilder().setAuthor(serverUser);
-          md.getCommitBuilder().setCommitter(serverUser);
-          VersionedAccountPreferences p = VersionedAccountPreferences.forUser(e.getKey());
-          p.load(md);
-          storeSection(
-              p.getConfig(),
-              UserConfigSections.DIFF,
-              null,
-              e.getValue(),
-              DiffPreferencesInfo.defaults());
-          p.commit(md);
-        }
-      }
-
-      bru.execute(rw, NullProgressMonitor.INSTANCE);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-
-  private Set<String> getColumns(ResultSet rs) throws SQLException {
-    ResultSetMetaData metaData = rs.getMetaData();
-    int columnCount = metaData.getColumnCount();
-    Set<String> columns = new HashSet<>(columnCount);
-    for (int i = 1; i <= columnCount; i++) {
-      columns.add(metaData.getColumnLabel(i).toLowerCase());
-    }
-    return columns;
-  }
-
-  private static Theme toTheme(String v) {
-    if (v == null) {
-      return Theme.DEFAULT;
-    }
-    return Theme.valueOf(v);
-  }
-
-  private static Whitespace toWhitespace(String v) {
-    requireNonNull(v);
-    if (v.isEmpty()) {
-      return Whitespace.IGNORE_NONE;
-    }
-    Whitespace r = PatchListKey.WHITESPACE_TYPES.inverse().get(v.charAt(0));
-    if (r == null) {
-      throw new IllegalArgumentException("Cannot find Whitespace type for: " + v);
-    }
-    return r;
-  }
-
-  private static boolean toBoolean(String v) {
-    checkState(!Strings.isNullOrEmpty(v));
-    return v.equals("Y");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_116.java b/java/com/google/gerrit/server/schema/Schema_116.java
deleted file mode 100644
index 5174d92..0000000
--- a/java/com/google/gerrit/server/schema/Schema_116.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_116 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_116(Provider<Schema_115> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_117.java b/java/com/google/gerrit/server/schema/Schema_117.java
deleted file mode 100644
index d848bec..0000000
--- a/java/com/google/gerrit/server/schema/Schema_117.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Set;
-
-public class Schema_117 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_117(Provider<Schema_116> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void preUpdateSchema(ReviewDb db) throws OrmException, SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    Connection connection = schema.getConnection();
-    String tableName = "patch_sets";
-    String oldColumnName = "push_certficate";
-    String newColumnName = "push_certificate";
-    Set<String> columns = schema.getDialect().listColumns(connection, tableName);
-    if (columns.contains(oldColumnName)) {
-      renameColumn(db, tableName, oldColumnName, newColumnName);
-    }
-    try (Statement stmt = schema.getConnection().createStatement()) {
-      stmt.execute("ALTER TABLE " + tableName + " MODIFY " + newColumnName + " clob");
-    } catch (SQLException e) {
-      // Ignore.  Type may have already been modified manually.
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_118.java b/java/com/google/gerrit/server/schema/Schema_118.java
deleted file mode 100644
index e55afda..0000000
--- a/java/com/google/gerrit/server/schema/Schema_118.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_118 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_118(Provider<Schema_117> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_119.java b/java/com/google/gerrit/server/schema/Schema_119.java
deleted file mode 100644
index 9423d81..0000000
--- a/java/com/google/gerrit/server/schema/Schema_119.java
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.ANON_GIT;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.ANON_HTTP;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.HTTP;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.REPO_DOWNLOAD;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.SSH;
-import static com.google.gerrit.server.config.ConfigUtil.storeSection;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DateFormat;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DownloadCommand;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.ReviewCategoryStrategy;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.UserConfigSections;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_119 extends ReviewDbSchemaVersion {
-  private static final ImmutableMap<String, String> LEGACY_DISPLAYNAME_MAP =
-      ImmutableMap.<String, String>of(
-          "ANON_GIT", ANON_GIT,
-          "ANON_HTTP", ANON_HTTP,
-          "HTTP", HTTP,
-          "SSH", SSH,
-          "REPO_DOWNLOAD", REPO_DOWNLOAD);
-
-  private final GitRepositoryManager mgr;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_119(
-      Provider<Schema_118> prior,
-      GitRepositoryManager mgr,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.mgr = mgr;
-    this.allUsersName = allUsersName;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    Connection connection = schema.getConnection();
-    String tableName = "accounts";
-    String emailStrategy = "email_strategy";
-    Set<String> columns = schema.getDialect().listColumns(connection, tableName);
-    Map<Account.Id, GeneralPreferencesInfo> imports = new HashMap<>();
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs =
-            stmt.executeQuery(
-                "select "
-                    + "account_id, "
-                    + "maximum_page_size, "
-                    + "show_site_header, "
-                    + "use_flash_clipboard, "
-                    + "download_url, "
-                    + "download_command, "
-                    + (columns.contains(emailStrategy)
-                        ? emailStrategy + ", "
-                        : "copy_self_on_email, ")
-                    + "date_format, "
-                    + "time_format, "
-                    + "relative_date_in_change_table, "
-                    + "diff_view, "
-                    + "size_bar_in_change_table, "
-                    + "legacycid_in_change_table, "
-                    + "review_category_strategy, "
-                    + "mute_common_path_prefixes "
-                    + "from "
-                    + tableName)) {
-      while (rs.next()) {
-        GeneralPreferencesInfo p = new GeneralPreferencesInfo();
-        Account.Id accountId = new Account.Id(rs.getInt(1));
-        p.changesPerPage = (int) rs.getShort(2);
-        p.showSiteHeader = toBoolean(rs.getString(3));
-        p.useFlashClipboard = toBoolean(rs.getString(4));
-        p.downloadScheme = convertToModernNames(rs.getString(5));
-        p.downloadCommand = toDownloadCommand(rs.getString(6));
-        p.emailStrategy = toEmailStrategy(rs.getString(7), columns.contains(emailStrategy));
-        p.dateFormat = toDateFormat(rs.getString(8));
-        p.timeFormat = toTimeFormat(rs.getString(9));
-        p.relativeDateInChangeTable = toBoolean(rs.getString(10));
-        p.diffView = toDiffView(rs.getString(11));
-        p.sizeBarInChangeTable = toBoolean(rs.getString(12));
-        p.legacycidInChangeTable = toBoolean(rs.getString(13));
-        p.reviewCategoryStrategy = toReviewCategoryStrategy(rs.getString(14));
-        p.muteCommonPathPrefixes = toBoolean(rs.getString(15));
-        p.defaultBaseForMerges = GeneralPreferencesInfo.defaults().defaultBaseForMerges;
-        imports.put(accountId, p);
-      }
-    }
-
-    if (imports.isEmpty()) {
-      return;
-    }
-
-    try (Repository git = mgr.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-      for (Map.Entry<Account.Id, GeneralPreferencesInfo> e : imports.entrySet()) {
-        try (MetaDataUpdate md =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
-          md.getCommitBuilder().setAuthor(serverUser);
-          md.getCommitBuilder().setCommitter(serverUser);
-          VersionedAccountPreferences p = VersionedAccountPreferences.forUser(e.getKey());
-          p.load(md);
-          storeSection(
-              p.getConfig(),
-              UserConfigSections.GENERAL,
-              null,
-              e.getValue(),
-              GeneralPreferencesInfo.defaults());
-          p.commit(md);
-        }
-      }
-
-      bru.execute(rw, NullProgressMonitor.INSTANCE);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-
-  private String convertToModernNames(String s) {
-    return !Strings.isNullOrEmpty(s) && LEGACY_DISPLAYNAME_MAP.containsKey(s)
-        ? LEGACY_DISPLAYNAME_MAP.get(s)
-        : s;
-  }
-
-  private static DownloadCommand toDownloadCommand(String v) {
-    if (v == null) {
-      return DownloadCommand.CHECKOUT;
-    }
-    return DownloadCommand.valueOf(v);
-  }
-
-  private static DateFormat toDateFormat(String v) {
-    if (v == null) {
-      return DateFormat.STD;
-    }
-    return DateFormat.valueOf(v);
-  }
-
-  private static TimeFormat toTimeFormat(String v) {
-    if (v == null) {
-      return TimeFormat.HHMM_12;
-    }
-    return TimeFormat.valueOf(v);
-  }
-
-  private static DiffView toDiffView(String v) {
-    if (v == null) {
-      return DiffView.SIDE_BY_SIDE;
-    }
-    return DiffView.valueOf(v);
-  }
-
-  private static EmailStrategy toEmailStrategy(String v, boolean emailStrategyColumnExists)
-      throws OrmException {
-    if (v == null) {
-      return EmailStrategy.ENABLED;
-    }
-    if (emailStrategyColumnExists) {
-      return EmailStrategy.valueOf(v);
-    }
-    if (v.equals("N")) {
-      // EMAIL_STRATEGY='ENABLED' WHERE (COPY_SELF_ON_EMAIL='N')
-      return EmailStrategy.ENABLED;
-    } else if (v.equals("Y")) {
-      // EMAIL_STRATEGY='CC_ON_OWN_COMMENTS' WHERE (COPY_SELF_ON_EMAIL='Y')
-      return EmailStrategy.CC_ON_OWN_COMMENTS;
-    } else {
-      throw new OrmException("invalid value in accounts.copy_self_on_email: " + v);
-    }
-  }
-
-  private static ReviewCategoryStrategy toReviewCategoryStrategy(String v) {
-    if (v == null) {
-      return ReviewCategoryStrategy.NONE;
-    }
-    return ReviewCategoryStrategy.valueOf(v);
-  }
-
-  private static boolean toBoolean(String v) {
-    checkState(!Strings.isNullOrEmpty(v));
-    return v.equals("Y");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_120.java b/java/com/google/gerrit/server/schema/Schema_120.java
deleted file mode 100644
index 9a1c15b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_120.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.common.data.SubscribeSection;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.RefSpec;
-
-public class Schema_120 extends ReviewDbSchemaVersion {
-
-  private final GitRepositoryManager mgr;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_120(
-      Provider<Schema_119> prior,
-      GitRepositoryManager mgr,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.mgr = mgr;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  private void allowSubmoduleSubscription(Branch.NameKey subbranch, Branch.NameKey superBranch)
-      throws OrmException {
-    try (Repository git = mgr.openRepository(subbranch.getParentKey());
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-      try (MetaDataUpdate md =
-          new MetaDataUpdate(GitReferenceUpdated.DISABLED, subbranch.getParentKey(), git, bru)) {
-        md.getCommitBuilder().setAuthor(serverUser);
-        md.getCommitBuilder().setCommitter(serverUser);
-        md.setMessage("Added superproject subscription during upgrade");
-        ProjectConfig pc = projectConfigFactory.read(md);
-
-        SubscribeSection s = null;
-        for (SubscribeSection s1 : pc.getSubscribeSections(subbranch)) {
-          if (s1.getProject().equals(superBranch.getParentKey())) {
-            s = s1;
-          }
-        }
-        if (s == null) {
-          s = new SubscribeSection(superBranch.getParentKey());
-          pc.addSubscribeSection(s);
-        }
-        RefSpec newRefSpec = new RefSpec(subbranch.get() + ":" + superBranch.get());
-
-        if (!s.getMatchingRefSpecs().contains(newRefSpec)) {
-          // For the migration we use only exact RefSpecs, we're not trying to
-          // generalize it.
-          s.addMatchingRefSpec(newRefSpec);
-        }
-
-        pc.commit(md);
-      }
-      bru.execute(rw, NullProgressMonitor.INSTANCE);
-    } catch (ConfigInvalidException | IOException e) {
-      throw new OrmException(e);
-    }
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    ui.message("Generating Superproject subscriptions table to submodule ACLs");
-
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs =
-            stmt.executeQuery(
-                "SELECT "
-                    + "super_project_project_name, "
-                    + "super_project_branch_name, "
-                    + "submodule_project_name, "
-                    + "submodule_branch_name "
-                    + "FROM submodule_subscriptions")) {
-      while (rs.next()) {
-        Project.NameKey superproject = new Project.NameKey(rs.getString(1));
-        Branch.NameKey superbranch = new Branch.NameKey(superproject, rs.getString(2));
-
-        Project.NameKey submodule = new Project.NameKey(rs.getString(3));
-        Branch.NameKey subbranch = new Branch.NameKey(submodule, rs.getString(4));
-
-        allowSubmoduleSubscription(subbranch, superbranch);
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_121.java b/java/com/google/gerrit/server/schema/Schema_121.java
deleted file mode 100644
index bb994ef..0000000
--- a/java/com/google/gerrit/server/schema/Schema_121.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_121 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_121(Provider<Schema_120> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_122.java b/java/com/google/gerrit/server/schema/Schema_122.java
deleted file mode 100644
index cce1419..0000000
--- a/java/com/google/gerrit/server/schema/Schema_122.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_122 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_122(Provider<Schema_121> prior) {
-    super(prior);
-  }
-
-  // Adds tag column
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_123.java b/java/com/google/gerrit/server/schema/Schema_123.java
deleted file mode 100644
index cd84188..0000000
--- a/java/com/google/gerrit/server/schema/Schema_123.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.StarredChangesUtil;
-import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Map;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-public class Schema_123 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-
-  @Inject
-  Schema_123(
-      Provider<Schema_122> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    ListMultimap<Account.Id, Change.Id> imports =
-        MultimapBuilder.hashKeys().arrayListValues().build();
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs = stmt.executeQuery("SELECT account_id, change_id FROM starred_changes")) {
-      while (rs.next()) {
-        Account.Id accountId = new Account.Id(rs.getInt(1));
-        Change.Id changeId = new Change.Id(rs.getInt(2));
-        imports.put(accountId, changeId);
-      }
-    }
-
-    if (imports.isEmpty()) {
-      return;
-    }
-
-    try (Repository git = repoManager.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-      ObjectId id = StarredChangesUtil.writeLabels(git, StarredChangesUtil.DEFAULT_LABELS);
-      for (Map.Entry<Account.Id, Change.Id> e : imports.entries()) {
-        bru.addCommand(
-            new ReceiveCommand(
-                ObjectId.zeroId(), id, RefNames.refsStarredChanges(e.getValue(), e.getKey())));
-      }
-      bru.execute(rw, new TextProgressMonitor());
-    } catch (IOException | IllegalLabelException ex) {
-      throw new OrmException(ex);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_124.java b/java/com/google/gerrit/server/schema/Schema_124.java
deleted file mode 100644
index 7e2fa42..0000000
--- a/java/com/google/gerrit/server/schema/Schema_124.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.Comparator.comparing;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountSshKey;
-import com.google.gerrit.server.account.VersionedAuthorizedKeys;
-import com.google.gerrit.server.account.VersionedAuthorizedKeys.SimpleSshKeyCreator;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_124 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_124(
-      Provider<Schema_123> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    ListMultimap<Account.Id, AccountSshKey> imports =
-        MultimapBuilder.hashKeys().arrayListValues().build();
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs =
-            stmt.executeQuery(
-                "SELECT "
-                    + "account_id, "
-                    + "seq, "
-                    + "ssh_public_key, "
-                    + "valid "
-                    + "FROM account_ssh_keys")) {
-      while (rs.next()) {
-        Account.Id accountId = new Account.Id(rs.getInt(1));
-        int seq = rs.getInt(2);
-        String sshPublicKey = rs.getString(3);
-        boolean valid = toBoolean(rs.getString(4));
-        AccountSshKey key = AccountSshKey.create(accountId, seq, sshPublicKey, valid);
-        imports.put(accountId, key);
-      }
-    }
-
-    if (imports.isEmpty()) {
-      return;
-    }
-
-    try (Repository git = repoManager.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-
-      for (Map.Entry<Account.Id, Collection<AccountSshKey>> e : imports.asMap().entrySet()) {
-        try (MetaDataUpdate md =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
-          md.getCommitBuilder().setAuthor(serverUser);
-          md.getCommitBuilder().setCommitter(serverUser);
-
-          VersionedAuthorizedKeys authorizedKeys =
-              new VersionedAuthorizedKeys(new SimpleSshKeyCreator(), e.getKey());
-          authorizedKeys.load(md);
-          authorizedKeys.setKeys(fixInvalidSequenceNumbers(e.getValue()));
-          authorizedKeys.commit(md);
-        }
-      }
-
-      bru.execute(rw, NullProgressMonitor.INSTANCE);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-
-  private Collection<AccountSshKey> fixInvalidSequenceNumbers(Collection<AccountSshKey> keys) {
-    Ordering<AccountSshKey> o = Ordering.from(comparing(AccountSshKey::seq));
-    List<AccountSshKey> fixedKeys = new ArrayList<>(keys);
-    AccountSshKey minKey = o.min(keys);
-    while (minKey.seq() <= 0) {
-      AccountSshKey fixedKey =
-          AccountSshKey.create(
-              minKey.accountId(), Math.max(o.max(keys).seq() + 1, 1), minKey.sshPublicKey());
-      Collections.replaceAll(fixedKeys, minKey, fixedKey);
-      minKey = o.min(fixedKeys);
-    }
-    return fixedKeys;
-  }
-
-  private static boolean toBoolean(String v) {
-    return !Strings.isNullOrEmpty(v) && v.equalsIgnoreCase("Y");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_125.java b/java/com/google/gerrit/server/schema/Schema_125.java
deleted file mode 100644
index cc0d9e7..0000000
--- a/java/com/google/gerrit/server/schema/Schema_125.java
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_125 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG =
-      "Assign default permissions on user branches\n"
-          + "\n"
-          + "By default each user should be able to read and update the own user\n"
-          + "branch. Also the user should be able to approve and submit changes for\n"
-          + "the own user branch. Assign default permissions for this and remove the\n"
-          + "old exclusive read protection from the user branches.\n";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final AllProjectsName allProjectsName;
-  private final SystemGroupBackend systemGroupBackend;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_125(
-      Provider<Schema_124> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      AllProjectsName allProjectsName,
-      SystemGroupBackend systemGroupBackend,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.allProjectsName = allProjectsName;
-    this.systemGroupBackend = systemGroupBackend;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (Repository git = repoManager.openRepository(allUsersName);
-        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
-      ProjectConfig config = projectConfigFactory.read(md);
-
-      config
-          .getAccessSection(RefNames.REFS_USERS + "*", true)
-          .remove(new Permission(Permission.READ));
-      GroupReference registered = systemGroupBackend.getGroup(REGISTERED_USERS);
-      AccessSection users =
-          config.getAccessSection(
-              RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true);
-      grant(config, users, Permission.READ, true, registered);
-      grant(config, users, Permission.PUSH, true, registered);
-      grant(config, users, Permission.SUBMIT, true, registered);
-
-      for (LabelType lt : getLabelTypes(config)) {
-        if ("Code-Review".equals(lt.getName()) || "Verified".equals(lt.getName())) {
-          grant(config, users, lt, lt.getMin().getValue(), lt.getMax().getValue(), registered);
-        }
-      }
-
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(COMMIT_MSG);
-      config.commit(md);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-
-  private Collection<LabelType> getLabelTypes(ProjectConfig config)
-      throws IOException, ConfigInvalidException {
-    Map<String, LabelType> labelTypes = new HashMap<>(config.getLabelSections());
-    Project.NameKey parent = config.getProject().getParent(allProjectsName);
-    while (parent != null) {
-      try (Repository git = repoManager.openRepository(parent);
-          MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, parent, git)) {
-        ProjectConfig parentConfig = projectConfigFactory.read(md);
-        for (LabelType lt : parentConfig.getLabelSections().values()) {
-          if (!labelTypes.containsKey(lt.getName())) {
-            labelTypes.put(lt.getName(), lt);
-          }
-        }
-        parent = parentConfig.getProject().getParent(allProjectsName);
-      }
-    }
-    return labelTypes.values();
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_126.java b/java/com/google/gerrit/server/schema/Schema_126.java
deleted file mode 100644
index f3004ba..0000000
--- a/java/com/google/gerrit/server/schema/Schema_126.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_126 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG = "Fix default permissions on user branches";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final SystemGroupBackend systemGroupBackend;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_126(
-      Provider<Schema_125> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      SystemGroupBackend systemGroupBackend,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.systemGroupBackend = systemGroupBackend;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (Repository git = repoManager.openRepository(allUsersName);
-        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
-      ProjectConfig config = projectConfigFactory.read(md);
-
-      String refsUsersShardedId = RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}";
-      config.remove(config.getAccessSection(refsUsersShardedId));
-
-      GroupReference registered = systemGroupBackend.getGroup(REGISTERED_USERS);
-      AccessSection users = config.getAccessSection(refsUsersShardedId, true);
-      grant(config, users, Permission.READ, false, true, registered);
-      grant(config, users, Permission.PUSH, false, true, registered);
-      grant(config, users, Permission.SUBMIT, false, true, registered);
-
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(COMMIT_MSG);
-      config.commit(md);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_127.java b/java/com/google/gerrit/server/schema/Schema_127.java
deleted file mode 100644
index 8e4175c..0000000
--- a/java/com/google/gerrit/server/schema/Schema_127.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import org.eclipse.jgit.lib.Config;
-
-public class Schema_127 extends ReviewDbSchemaVersion {
-  private static final int MAX_BATCH_SIZE = 1000;
-
-  private final SitePaths sitePaths;
-  private final Config cfg;
-  private final ThreadSettingsConfig threadSettingsConfig;
-
-  @Inject
-  Schema_127(
-      Provider<Schema_126> prior,
-      SitePaths sitePaths,
-      @GerritServerConfig Config cfg,
-      ThreadSettingsConfig threadSettingsConfig) {
-    super(prior);
-    this.sitePaths = sitePaths;
-    this.cfg = cfg;
-    this.threadSettingsConfig = threadSettingsConfig;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    JdbcAccountPatchReviewStore jdbcAccountPatchReviewStore =
-        JdbcAccountPatchReviewStore.createAccountPatchReviewStore(
-            cfg, sitePaths, threadSettingsConfig);
-    jdbcAccountPatchReviewStore.dropTableIfExists();
-    jdbcAccountPatchReviewStore.createTableIfNotExists();
-    try (Connection con = jdbcAccountPatchReviewStore.getConnection();
-        PreparedStatement stmt =
-            con.prepareStatement(
-                "INSERT INTO account_patch_reviews "
-                    + "(account_id, change_id, patch_set_id, file_name) VALUES "
-                    + "(?, ?, ?, ?)")) {
-      int batchCount = 0;
-
-      try (Statement s = newStatement(db);
-          ResultSet rs = s.executeQuery("SELECT * from account_patch_reviews")) {
-        while (rs.next()) {
-          stmt.setInt(1, rs.getInt("account_id"));
-          stmt.setInt(2, rs.getInt("change_id"));
-          stmt.setInt(3, rs.getInt("patch_set_id"));
-          stmt.setString(4, rs.getString("file_name"));
-          stmt.addBatch();
-          batchCount++;
-          if (batchCount >= MAX_BATCH_SIZE) {
-            stmt.executeBatch();
-            batchCount = 0;
-          }
-        }
-      }
-      if (batchCount > 0) {
-        stmt.executeBatch();
-      }
-    } catch (SQLException e) {
-      throw jdbcAccountPatchReviewStore.convertError("insert", e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_128.java b/java/com/google/gerrit/server/schema/Schema_128.java
deleted file mode 100644
index cd2718b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_128.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_128 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG = "Add addPatchSet permission to all projects";
-
-  private final GitRepositoryManager repoManager;
-  private final AllProjectsName allProjectsName;
-  private final SystemGroupBackend systemGroupBackend;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_128(
-      Provider<Schema_127> prior,
-      GitRepositoryManager repoManager,
-      AllProjectsName allProjectsName,
-      SystemGroupBackend systemGroupBackend,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allProjectsName = allProjectsName;
-    this.systemGroupBackend = systemGroupBackend;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (Repository git = repoManager.openRepository(allProjectsName);
-        MetaDataUpdate md =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git)) {
-      ProjectConfig config = projectConfigFactory.read(md);
-
-      GroupReference registered = systemGroupBackend.getGroup(REGISTERED_USERS);
-      AccessSection refsFor = config.getAccessSection("refs/for/*", true);
-      grant(config, refsFor, Permission.ADD_PATCH_SET, false, false, registered);
-
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(COMMIT_MSG);
-      config.commit(md);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_129.java b/java/com/google/gerrit/server/schema/Schema_129.java
deleted file mode 100644
index f7cd8c3..0000000
--- a/java/com/google/gerrit/server/schema/Schema_129.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_129 extends ReviewDbSchemaVersion {
-
-  @Inject
-  Schema_129(Provider<Schema_128> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void preUpdateSchema(ReviewDb db) throws OrmException {
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement()) {
-      stmt.execute("ALTER TABLE patch_sets MODIFY groups clob");
-    } catch (SQLException e) {
-      // Ignore.  Type may have already been modified manually.
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_130.java b/java/com/google/gerrit/server/schema/Schema_130.java
deleted file mode 100644
index e550121..0000000
--- a/java/com/google/gerrit/server/schema/Schema_130.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_130 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG =
-      "Remove force option from 'Push Annotated Tag' permission\n"
-          + "\n"
-          + "The force option on 'Push Annotated Tag' had no effect and is no longer\n"
-          + "supported.";
-
-  private final GitRepositoryManager repoManager;
-  private final PersonIdent serverUser;
-  private final ProjectConfigSchemaUpdate.Factory projectConfigSchemaUpdateFactory;
-
-  @Inject
-  Schema_130(
-      Provider<Schema_129> prior,
-      GitRepositoryManager repoManager,
-      @GerritPersonIdent PersonIdent serverUser,
-      ProjectConfigSchemaUpdate.Factory projectConfigSchemaUpdateFactory) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.serverUser = serverUser;
-    this.projectConfigSchemaUpdateFactory = projectConfigSchemaUpdateFactory;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    SortedSet<Project.NameKey> repoList = repoManager.list();
-    SortedSet<Project.NameKey> repoUpgraded = new TreeSet<>();
-    ui.message("\tMigrating " + repoList.size() + " repositories ...");
-    for (Project.NameKey projectName : repoList) {
-      try (Repository git = repoManager.openRepository(projectName);
-          MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, projectName, git)) {
-        ProjectConfigSchemaUpdate cfg = projectConfigSchemaUpdateFactory.read(md);
-        cfg.removeForceFromPermission("pushTag");
-        if (cfg.isUpdated()) {
-          repoUpgraded.add(projectName);
-        }
-        cfg.save(serverUser, COMMIT_MSG);
-      } catch (ConfigInvalidException | IOException ex) {
-        throw new OrmException("Cannot migrate project " + projectName, ex);
-      }
-    }
-    ui.message("\tMigration completed:  " + repoUpgraded.size() + " repositories updated:");
-    ui.message("\t" + repoUpgraded.stream().map(Project.NameKey::get).collect(joining(" ")));
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_131.java b/java/com/google/gerrit/server/schema/Schema_131.java
deleted file mode 100644
index b37ae4b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_131.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_131 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG =
-      "Rename 'Push Annotated/Signed Tag' permission to 'Create Annotated/Signed Tag'";
-
-  private final GitRepositoryManager repoManager;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_131(
-      Provider<Schema_130> prior,
-      GitRepositoryManager repoManager,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    SortedSet<Project.NameKey> repoList = repoManager.list();
-    SortedSet<Project.NameKey> repoUpgraded = new TreeSet<>();
-    ui.message("\tMigrating " + repoList.size() + " repositories ...");
-    for (Project.NameKey projectName : repoList) {
-      try (Repository git = repoManager.openRepository(projectName);
-          MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, projectName, git)) {
-        ProjectConfig config = projectConfigFactory.read(md);
-        if (config.hasLegacyPermissions()) {
-          md.getCommitBuilder().setAuthor(serverUser);
-          md.getCommitBuilder().setCommitter(serverUser);
-          md.setMessage(COMMIT_MSG);
-          config.commit(md);
-          repoUpgraded.add(projectName);
-        }
-      } catch (ConfigInvalidException | IOException ex) {
-        throw new OrmException("Cannot migrate project " + projectName, ex);
-      }
-    }
-    ui.message("\tMigration completed:  " + repoUpgraded.size() + " repositories updated:");
-    ui.message("\t" + repoUpgraded.stream().map(Project.NameKey::get).collect(joining(" ")));
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_132.java b/java/com/google/gerrit/server/schema/Schema_132.java
deleted file mode 100644
index 72ec590..0000000
--- a/java/com/google/gerrit/server/schema/Schema_132.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_132 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_132(Provider<Schema_131> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_133.java b/java/com/google/gerrit/server/schema/Schema_133.java
deleted file mode 100644
index 29cd6e9..0000000
--- a/java/com/google/gerrit/server/schema/Schema_133.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_133 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_133(Provider<Schema_132> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_134.java b/java/com/google/gerrit/server/schema/Schema_134.java
deleted file mode 100644
index 298ac03..0000000
--- a/java/com/google/gerrit/server/schema/Schema_134.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_134 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_134(Provider<Schema_133> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_135.java b/java/com/google/gerrit/server/schema/Schema_135.java
deleted file mode 100644
index 03ef84a..0000000
--- a/java/com/google/gerrit/server/schema/Schema_135.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
-import static java.util.stream.Collectors.toSet;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Set;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_135 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG =
-      "Allow admins and project owners to create refs/meta/config";
-
-  private final GitRepositoryManager repoManager;
-  private final AllProjectsName allProjectsName;
-  private final SystemGroupBackend systemGroupBackend;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_135(
-      Provider<Schema_134> prior,
-      GitRepositoryManager repoManager,
-      AllProjectsName allProjectsName,
-      SystemGroupBackend systemGroupBackend,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allProjectsName = allProjectsName;
-    this.systemGroupBackend = systemGroupBackend;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (Repository git = repoManager.openRepository(allProjectsName);
-        MetaDataUpdate md =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git)) {
-      ProjectConfig config = projectConfigFactory.read(md);
-
-      AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
-      Permission createRefsMetaConfigPermission = meta.getPermission(Permission.CREATE, true);
-
-      Set<GroupReference> groups =
-          Stream.concat(
-                  config
-                      .getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
-                      .getPermission(GlobalCapability.ADMINISTRATE_SERVER, true)
-                      .getRules()
-                      .stream()
-                      .map(PermissionRule::getGroup),
-                  Stream.of(systemGroupBackend.getGroup(PROJECT_OWNERS)))
-              .filter(g -> createRefsMetaConfigPermission.getRule(g) == null)
-              .collect(toSet());
-
-      for (GroupReference group : groups) {
-        createRefsMetaConfigPermission.add(new PermissionRule(config.resolve(group)));
-      }
-
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(COMMIT_MSG);
-      config.commit(md);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_136.java b/java/com/google/gerrit/server/schema/Schema_136.java
deleted file mode 100644
index 11eda7b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_136.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_136 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_136(Provider<Schema_135> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_137.java b/java/com/google/gerrit/server/schema/Schema_137.java
deleted file mode 100644
index a6ea83b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_137.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/* change the type of SystemConfig#sitePath to CLOB */
-public class Schema_137 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_137(Provider<Schema_136> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_138.java b/java/com/google/gerrit/server/schema/Schema_138.java
deleted file mode 100644
index 61d55ec..0000000
--- a/java/com/google/gerrit/server/schema/Schema_138.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/* Add resolved field to PatchLineComment */
-public class Schema_138 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_138(Provider<Schema_137> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_139.java b/java/com/google/gerrit/server/schema/Schema_139.java
deleted file mode 100644
index a234be5..0000000
--- a/java/com/google/gerrit/server/schema/Schema_139.java
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Strings;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountConfig;
-import com.google.gerrit.server.account.InternalAccountUpdate;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_139 extends ReviewDbSchemaVersion {
-  private static final String MSG = "Migrate project watches to git";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_139(
-      Provider<Schema_138> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    ListMultimap<Account.Id, ProjectWatch> imports =
-        MultimapBuilder.hashKeys().arrayListValues().build();
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs =
-            stmt.executeQuery(
-                "SELECT "
-                    + "account_id, "
-                    + "project_name, "
-                    + "filter, "
-                    + "notify_abandoned_changes, "
-                    + "notify_all_comments, "
-                    + "notify_new_changes, "
-                    + "notify_new_patch_sets, "
-                    + "notify_submitted_changes "
-                    + "FROM account_project_watches")) {
-      while (rs.next()) {
-        Account.Id accountId = new Account.Id(rs.getInt(1));
-        ProjectWatch.Builder b =
-            ProjectWatch.builder()
-                .project(new Project.NameKey(rs.getString(2)))
-                .filter(rs.getString(3))
-                .notifyAbandonedChanges(toBoolean(rs.getString(4)))
-                .notifyAllComments(toBoolean(rs.getString(5)))
-                .notifyNewChanges(toBoolean(rs.getString(6)))
-                .notifyNewPatchSets(toBoolean(rs.getString(7)))
-                .notifySubmittedChanges(toBoolean(rs.getString(8)));
-        imports.put(accountId, b.build());
-      }
-    }
-
-    if (imports.isEmpty()) {
-      return;
-    }
-
-    try (Repository git = repoManager.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-      bru.setRefLogIdent(serverUser);
-      bru.setRefLogMessage(MSG, false);
-
-      for (Map.Entry<Account.Id, Collection<ProjectWatch>> e : imports.asMap().entrySet()) {
-        Map<ProjectWatchKey, Set<NotifyType>> projectWatches = new HashMap<>();
-        for (ProjectWatch projectWatch : e.getValue()) {
-          ProjectWatchKey key =
-              ProjectWatchKey.create(projectWatch.project(), projectWatch.filter());
-          if (projectWatches.containsKey(key)) {
-            throw new OrmDuplicateKeyException(
-                "Duplicate key for watched project: " + key.toString());
-          }
-          Set<NotifyType> notifyValues = EnumSet.noneOf(NotifyType.class);
-          if (projectWatch.notifyAbandonedChanges()) {
-            notifyValues.add(NotifyType.ABANDONED_CHANGES);
-          }
-          if (projectWatch.notifyAllComments()) {
-            notifyValues.add(NotifyType.ALL_COMMENTS);
-          }
-          if (projectWatch.notifyNewChanges()) {
-            notifyValues.add(NotifyType.NEW_CHANGES);
-          }
-          if (projectWatch.notifyNewPatchSets()) {
-            notifyValues.add(NotifyType.NEW_PATCHSETS);
-          }
-          if (projectWatch.notifySubmittedChanges()) {
-            notifyValues.add(NotifyType.SUBMITTED_CHANGES);
-          }
-          projectWatches.put(key, notifyValues);
-        }
-
-        try (MetaDataUpdate md =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
-          md.getCommitBuilder().setAuthor(serverUser);
-          md.getCommitBuilder().setCommitter(serverUser);
-          md.setMessage(MSG);
-
-          AccountConfig accountConfig = new AccountConfig(e.getKey(), allUsersName, git);
-          accountConfig.load(md);
-          accountConfig.setAccountUpdate(
-              InternalAccountUpdate.builder()
-                  .deleteProjectWatches(accountConfig.getProjectWatches().keySet())
-                  .updateProjectWatches(projectWatches)
-                  .build());
-          accountConfig.commit(md);
-        }
-      }
-      bru.execute(rw, NullProgressMonitor.INSTANCE);
-    } catch (IOException | ConfigInvalidException ex) {
-      throw new OrmException(ex);
-    }
-  }
-
-  @AutoValue
-  abstract static class ProjectWatch {
-    abstract Project.NameKey project();
-
-    abstract @Nullable String filter();
-
-    abstract boolean notifyAbandonedChanges();
-
-    abstract boolean notifyAllComments();
-
-    abstract boolean notifyNewChanges();
-
-    abstract boolean notifyNewPatchSets();
-
-    abstract boolean notifySubmittedChanges();
-
-    static Builder builder() {
-      return new AutoValue_Schema_139_ProjectWatch.Builder();
-    }
-
-    @AutoValue.Builder
-    abstract static class Builder {
-      abstract Builder project(Project.NameKey project);
-
-      abstract Builder filter(@Nullable String filter);
-
-      abstract Builder notifyAbandonedChanges(boolean notifyAbandonedChanges);
-
-      abstract Builder notifyAllComments(boolean notifyAllComments);
-
-      abstract Builder notifyNewChanges(boolean notifyNewChanges);
-
-      abstract Builder notifyNewPatchSets(boolean notifyNewPatchSets);
-
-      abstract Builder notifySubmittedChanges(boolean notifySubmittedChanges);
-
-      abstract ProjectWatch build();
-    }
-  }
-
-  private static boolean toBoolean(String v) {
-    checkState(!Strings.isNullOrEmpty(v));
-    return v.equals("Y");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_140.java b/java/com/google/gerrit/server/schema/Schema_140.java
deleted file mode 100644
index f59368c..0000000
--- a/java/com/google/gerrit/server/schema/Schema_140.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Remove ChangeMessage sequence. */
-public class Schema_140 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_140(Provider<Schema_139> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_141.java b/java/com/google/gerrit/server/schema/Schema_141.java
deleted file mode 100644
index ce1c910..0000000
--- a/java/com/google/gerrit/server/schema/Schema_141.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add status field to account. */
-public class Schema_141 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_141(Provider<Schema_140> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_142.java b/java/com/google/gerrit/server/schema/Schema_142.java
deleted file mode 100644
index 9e06c52..0000000
--- a/java/com/google/gerrit/server/schema/Schema_142.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.HashedPassword;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_142 extends ReviewDbSchemaVersion {
-  private static final int MAX_BATCH_SIZE = 1000;
-
-  @Inject
-  Schema_142(Provider<Schema_141> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (PreparedStatement updateStmt =
-        ((JdbcSchema) db)
-            .getConnection()
-            .prepareStatement(
-                "UPDATE account_external_ids " + "SET password = ? " + "WHERE external_id = ?")) {
-      int batchCount = 0;
-
-      try (Statement stmt = newStatement(db);
-          ResultSet rs =
-              stmt.executeQuery("SELECT external_id, password FROM account_external_ids")) {
-        while (rs.next()) {
-          String externalId = rs.getString("external_id");
-          String password = rs.getString("password");
-          if (!ExternalId.Key.parse(externalId).isScheme(SCHEME_USERNAME)
-              || Strings.isNullOrEmpty(password)) {
-            continue;
-          }
-
-          HashedPassword hashed = HashedPassword.fromPassword(password);
-          updateStmt.setString(1, hashed.encode());
-          updateStmt.setString(2, externalId);
-          updateStmt.addBatch();
-          batchCount++;
-          if (batchCount >= MAX_BATCH_SIZE) {
-            updateStmt.executeBatch();
-            batchCount = 0;
-          }
-        }
-      }
-
-      if (batchCount > 0) {
-        updateStmt.executeBatch();
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_143.java b/java/com/google/gerrit/server/schema/Schema_143.java
deleted file mode 100644
index 10d108f..0000000
--- a/java/com/google/gerrit/server/schema/Schema_143.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add isPrivate field to change. */
-public class Schema_143 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_143(Provider<Schema_142> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_144.java b/java/com/google/gerrit/server/schema/Schema_144.java
deleted file mode 100644
index 0aad504..0000000
--- a/java/com/google/gerrit/server/schema/Schema_144.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.account.externalids.ExternalIdNotes;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashSet;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_144 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG = "Import external IDs from ReviewDb";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverIdent;
-
-  @Inject
-  Schema_144(
-      Provider<Schema_143> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverIdent) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverIdent = serverIdent;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    Set<ExternalId> toAdd = new HashSet<>();
-    try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
-        ResultSet rs =
-            stmt.executeQuery(
-                "SELECT "
-                    + "account_id, "
-                    + "email_address, "
-                    + "password, "
-                    + "external_id "
-                    + "FROM account_external_ids")) {
-      while (rs.next()) {
-        Account.Id accountId = new Account.Id(rs.getInt(1));
-        String email = rs.getString(2);
-        String password = rs.getString(3);
-        String externalId = rs.getString(4);
-
-        toAdd.add(ExternalId.create(ExternalId.Key.parse(externalId), accountId, email, password));
-      }
-    }
-
-    try {
-      try (Repository repo = repoManager.openRepository(allUsersName)) {
-        ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsersName, repo);
-        extIdNotes.upsert(toAdd);
-        try (MetaDataUpdate metaDataUpdate =
-            new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, repo)) {
-          metaDataUpdate.getCommitBuilder().setAuthor(serverIdent);
-          metaDataUpdate.getCommitBuilder().setCommitter(serverIdent);
-          metaDataUpdate.getCommitBuilder().setMessage(COMMIT_MSG);
-          extIdNotes.commit(metaDataUpdate);
-        }
-      }
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException("Failed to migrate external IDs to NoteDb", e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_145.java b/java/com/google/gerrit/server/schema/Schema_145.java
deleted file mode 100644
index 1207632..0000000
--- a/java/com/google/gerrit/server/schema/Schema_145.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Create account_external_ids_byEmail index. */
-public class Schema_145 extends ReviewDbSchemaVersion {
-
-  @Inject
-  Schema_145(Provider<Schema_144> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    SqlDialect dialect = schema.getDialect();
-    try (StatementExecutor e = newExecutor(db)) {
-      try {
-        dialect.dropIndex(e, "account_external_ids", "account_external_ids_byEmail");
-      } catch (OrmException ex) {
-        // Ignore.  The index did not exist.
-      }
-      e.execute(
-          "CREATE INDEX account_external_ids_byEmail"
-              + " ON account_external_ids"
-              + " (email_address)");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_146.java b/java/com/google/gerrit/server/schema/Schema_146.java
deleted file mode 100644
index 2930408..0000000
--- a/java/com/google/gerrit/server/schema/Schema_146.java
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.HashMap;
-import java.util.Map;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-/**
- * Make sure that for every account a user branch exists that has an initial empty commit with the
- * registration date as commit time.
- *
- * <p>For accounts that don't have a user branch yet the user branch is created with an initial
- * empty commit that has the registration date as commit time.
- *
- * <p>For accounts that already have a user branch the user branch is rewritten and an initial empty
- * commit with the registration date as commit time is inserted (if such a commit doesn't exist
- * yet).
- */
-public class Schema_146 extends ReviewDbSchemaVersion {
-  private static final String CREATE_ACCOUNT_MSG = "Create Account";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverIdent;
-
-  @Inject
-  Schema_146(
-      Provider<Schema_145> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverIdent) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverIdent = serverIdent;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (Repository repo = repoManager.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(repo);
-        ObjectInserter oi = repo.newObjectInserter()) {
-      ObjectId emptyTree = emptyTree(oi);
-
-      for (Map.Entry<Account.Id, Timestamp> e : scanAccounts(db).entrySet()) {
-        String refName = RefNames.refsUsers(e.getKey());
-        Ref ref = repo.exactRef(refName);
-        if (ref != null) {
-          rewriteUserBranch(repo, rw, oi, emptyTree, ref, e.getValue());
-        } else {
-          createUserBranch(repo, oi, emptyTree, e.getKey(), e.getValue());
-        }
-      }
-    } catch (IOException e) {
-      throw new OrmException("Failed to rewrite user branches.", e);
-    }
-  }
-
-  private void rewriteUserBranch(
-      Repository repo,
-      RevWalk rw,
-      ObjectInserter oi,
-      ObjectId emptyTree,
-      Ref ref,
-      Timestamp registeredOn)
-      throws IOException {
-    ObjectId current = createInitialEmptyCommit(oi, emptyTree, registeredOn);
-
-    rw.reset();
-    rw.sort(RevSort.TOPO);
-    rw.sort(RevSort.REVERSE, true);
-    rw.markStart(rw.parseCommit(ref.getObjectId()));
-
-    RevCommit c;
-    while ((c = rw.next()) != null) {
-      if (isInitialEmptyCommit(emptyTree, c)) {
-        return;
-      }
-
-      CommitBuilder cb = new CommitBuilder();
-      cb.setParentId(current);
-      cb.setTreeId(c.getTree());
-      cb.setAuthor(c.getAuthorIdent());
-      cb.setCommitter(c.getCommitterIdent());
-      cb.setMessage(c.getFullMessage());
-      cb.setEncoding(c.getEncoding());
-      current = oi.insert(cb);
-    }
-
-    oi.flush();
-
-    RefUpdate ru = repo.updateRef(ref.getName());
-    ru.setExpectedOldObjectId(ref.getObjectId());
-    ru.setNewObjectId(current);
-    ru.setForceUpdate(true);
-    ru.setRefLogIdent(serverIdent);
-    ru.setRefLogMessage(getClass().getSimpleName(), true);
-    Result result = ru.update();
-    if (result != Result.FORCED) {
-      throw new IOException(
-          String.format("Failed to update ref %s: %s", ref.getName(), result.name()));
-    }
-  }
-
-  public void createUserBranch(
-      Repository repo,
-      ObjectInserter oi,
-      ObjectId emptyTree,
-      Account.Id accountId,
-      Timestamp registeredOn)
-      throws IOException {
-    ObjectId id = createInitialEmptyCommit(oi, emptyTree, registeredOn);
-
-    String refName = RefNames.refsUsers(accountId);
-    RefUpdate ru = repo.updateRef(refName);
-    ru.setExpectedOldObjectId(ObjectId.zeroId());
-    ru.setNewObjectId(id);
-    ru.setRefLogIdent(serverIdent);
-    ru.setRefLogMessage(CREATE_ACCOUNT_MSG, false);
-    Result result = ru.update();
-    if (result != Result.NEW) {
-      throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
-    }
-  }
-
-  private ObjectId createInitialEmptyCommit(
-      ObjectInserter oi, ObjectId emptyTree, Timestamp registrationDate) throws IOException {
-    PersonIdent ident = new PersonIdent(serverIdent, registrationDate);
-
-    CommitBuilder cb = new CommitBuilder();
-    cb.setTreeId(emptyTree);
-    cb.setCommitter(ident);
-    cb.setAuthor(ident);
-    cb.setMessage(CREATE_ACCOUNT_MSG);
-    return oi.insert(cb);
-  }
-
-  private boolean isInitialEmptyCommit(ObjectId emptyTree, RevCommit c) {
-    return c.getParentCount() == 0
-        && c.getTree().equals(emptyTree)
-        && c.getShortMessage().equals(CREATE_ACCOUNT_MSG);
-  }
-
-  private static ObjectId emptyTree(ObjectInserter oi) throws IOException {
-    return oi.insert(Constants.OBJ_TREE, new byte[] {});
-  }
-
-  private Map<Account.Id, Timestamp> scanAccounts(ReviewDb db) throws SQLException {
-    try (Statement stmt = newStatement(db);
-        ResultSet rs = stmt.executeQuery("SELECT account_id, registered_on FROM accounts")) {
-      HashMap<Account.Id, Timestamp> m = new HashMap<>();
-      while (rs.next()) {
-        m.put(new Account.Id(rs.getInt(1)), rs.getTimestamp(2));
-      }
-      return m;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_147.java b/java/com/google/gerrit/server/schema/Schema_147.java
deleted file mode 100644
index f317e9a..0000000
--- a/java/com/google/gerrit/server/schema/Schema_147.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.stream.Collectors.toSet;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.Repository;
-
-/** Delete user branches for which no account exists. */
-public class Schema_147 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-
-  @Inject
-  Schema_147(
-      Provider<Schema_146> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (Repository repo = repoManager.openRepository(allUsersName)) {
-      Set<Account.Id> accountIdsFromReviewDb = scanAccounts(db);
-      Set<Account.Id> accountIdsFromUserBranches =
-          repo.getRefDatabase()
-              .getRefsByPrefix(RefNames.REFS_USERS)
-              .stream()
-              .map(r -> Account.Id.fromRef(r.getName()))
-              .filter(Objects::nonNull)
-              .collect(toSet());
-      accountIdsFromUserBranches.removeAll(accountIdsFromReviewDb);
-      for (Account.Id accountId : accountIdsFromUserBranches) {
-        deleteUserBranch(repo, accountId);
-      }
-    } catch (IOException e) {
-      throw new OrmException("Failed to delete user branches for non-existing accounts.", e);
-    }
-  }
-
-  private Set<Account.Id> scanAccounts(ReviewDb db) throws SQLException {
-    try (Statement stmt = newStatement(db);
-        ResultSet rs = stmt.executeQuery("SELECT account_id FROM accounts")) {
-      Set<Account.Id> ids = new HashSet<>();
-      while (rs.next()) {
-        ids.add(new Account.Id(rs.getInt(1)));
-      }
-      return ids;
-    }
-  }
-
-  private void deleteUserBranch(Repository allUsersRepo, Account.Id accountId) throws IOException {
-    String refName = RefNames.refsUsers(accountId);
-    Ref ref = allUsersRepo.exactRef(refName);
-    if (ref == null) {
-      return;
-    }
-
-    RefUpdate ru = allUsersRepo.updateRef(refName);
-    ru.setExpectedOldObjectId(ref.getObjectId());
-    ru.setNewObjectId(ObjectId.zeroId());
-    ru.setForceUpdate(true);
-    Result result = ru.delete();
-    if (result != Result.FORCED) {
-      throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_148.java b/java/com/google/gerrit/server/schema/Schema_148.java
deleted file mode 100644
index 949dd5a..0000000
--- a/java/com/google/gerrit/server/schema/Schema_148.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.primitives.Ints;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.account.externalids.ExternalIdNotes;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_148 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG = "Make account IDs of external IDs human-readable";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_148(
-      Provider<Schema_147> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (Repository repo = repoManager.openRepository(allUsersName)) {
-      ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsersName, repo);
-      for (ExternalId extId : extIdNotes.all()) {
-        if (needsUpdate(extId)) {
-          extIdNotes.upsert(extId);
-        }
-      }
-
-      try (MetaDataUpdate metaDataUpdate =
-          new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, repo)) {
-        metaDataUpdate.getCommitBuilder().setAuthor(serverUser);
-        metaDataUpdate.getCommitBuilder().setCommitter(serverUser);
-        metaDataUpdate.getCommitBuilder().setMessage(COMMIT_MSG);
-        extIdNotes.commit(metaDataUpdate);
-      }
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException("Failed to update external IDs", e);
-    }
-  }
-
-  private static boolean needsUpdate(ExternalId extId) {
-    Config cfg = new Config();
-    cfg.setInt("externalId", extId.key().get(), "accountId", extId.accountId().get());
-    return Ints.tryParse(cfg.getString("externalId", extId.key().get(), "accountId")) == null;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_149.java b/java/com/google/gerrit/server/schema/Schema_149.java
deleted file mode 100644
index 156091b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_149.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add workInProgress field to change. */
-public class Schema_149 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_149(Provider<Schema_148> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_150.java b/java/com/google/gerrit/server/schema/Schema_150.java
deleted file mode 100644
index 71736f3..0000000
--- a/java/com/google/gerrit/server/schema/Schema_150.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Drop ACCOUNT_EXTERNAL_IDS table. */
-public class Schema_150 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_150(Provider<Schema_149> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_151.java b/java/com/google/gerrit/server/schema/Schema_151.java
deleted file mode 100644
index 0e8700f..0000000
--- a/java/com/google/gerrit/server/schema/Schema_151.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-/** A schema which adds the 'created on' field to groups. */
-public class Schema_151 extends ReviewDbSchemaVersion {
-  @Inject
-  protected Schema_151(Provider<Schema_150> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (PreparedStatement groupUpdate =
-            prepareStatement(db, "UPDATE account_groups SET created_on = ? WHERE group_id = ?");
-        PreparedStatement addedOnRetrieval =
-            prepareStatement(
-                db,
-                "SELECT added_on FROM account_group_members_audit WHERE group_id = ?"
-                    + " ORDER BY added_on ASC")) {
-      List<AccountGroup.Id> accountGroups = getAllGroupIds(db);
-      for (AccountGroup.Id groupId : accountGroups) {
-        Optional<Timestamp> firstTimeMentioned = getFirstTimeMentioned(addedOnRetrieval, groupId);
-        Timestamp createdOn = firstTimeMentioned.orElseGet(AccountGroup::auditCreationInstantTs);
-
-        groupUpdate.setTimestamp(1, createdOn);
-        groupUpdate.setInt(2, groupId.get());
-        groupUpdate.executeUpdate();
-      }
-    }
-  }
-
-  private static Optional<Timestamp> getFirstTimeMentioned(
-      PreparedStatement addedOnRetrieval, AccountGroup.Id groupId) throws SQLException {
-    addedOnRetrieval.setInt(1, groupId.get());
-    try (ResultSet resultSet = addedOnRetrieval.executeQuery()) {
-      if (resultSet.next()) {
-        return Optional.of(resultSet.getTimestamp(1));
-      }
-    }
-    return Optional.empty();
-  }
-
-  private static List<AccountGroup.Id> getAllGroupIds(ReviewDb db) throws SQLException {
-    try (Statement stmt = newStatement(db);
-        ResultSet rs = stmt.executeQuery("SELECT group_id FROM account_groups")) {
-      List<AccountGroup.Id> groupIds = new ArrayList<>();
-      while (rs.next()) {
-        groupIds.add(new AccountGroup.Id(rs.getInt(1)));
-      }
-      return groupIds;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_152.java b/java/com/google/gerrit/server/schema/Schema_152.java
deleted file mode 100644
index b605e90..0000000
--- a/java/com/google/gerrit/server/schema/Schema_152.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Drop unused indexes from accounts table. */
-public class Schema_152 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_152(Provider<Schema_151> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    SqlDialect dialect = schema.getDialect();
-    try (StatementExecutor e = newExecutor(db)) {
-      dialect.dropIndex(e, "accounts", "accounts_byFullName");
-    } catch (OrmException ex) {
-      // Ignore. The index did not exist.
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_153.java b/java/com/google/gerrit/server/schema/Schema_153.java
deleted file mode 100644
index 7d1f63e..0000000
--- a/java/com/google/gerrit/server/schema/Schema_153.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add reviewStarted field to change. */
-public class Schema_153 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_153(Provider<Schema_152> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (StatementExecutor e = newExecutor(db)) {
-      // Initialize review_started to a sensible default value according to
-      // whether change is currently WIP. No migration is needed in NoteDb,
-      // where the value of review_started is always derived from the history
-      // of assignments to work_in_progress.
-      e.execute(
-          "UPDATE changes SET review_started = 'Y', created_on = created_on WHERE work_in_progress = 'N'");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_154.java b/java/com/google/gerrit/server/schema/Schema_154.java
deleted file mode 100644
index 8c97010..0000000
--- a/java/com/google/gerrit/server/schema/Schema_154.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.stream.Collectors.toMap;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountConfig;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-
-/** Migrate accounts to NoteDb. */
-public class Schema_154 extends ReviewDbSchemaVersion {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private static final String TABLE = "accounts";
-  private static final ImmutableMap<String, AccountSetter> ACCOUNT_FIELDS_MAP =
-      ImmutableMap.<String, AccountSetter>builder()
-          .put("full_name", (a, rs, field) -> a.setFullName(rs.getString(field)))
-          .put("preferred_email", (a, rs, field) -> a.setPreferredEmail(rs.getString(field)))
-          .put("status", (a, rs, field) -> a.setStatus(rs.getString(field)))
-          .put("inactive", (a, rs, field) -> a.setActive(rs.getString(field).equals("N")))
-          .build();
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final Provider<PersonIdent> serverIdent;
-
-  @Inject
-  Schema_154(
-      Provider<Schema_153> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent Provider<PersonIdent> serverIdent) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverIdent = serverIdent;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try {
-      try (Repository repo = repoManager.openRepository(allUsersName)) {
-        ProgressMonitor pm = new TextProgressMonitor();
-        pm.beginTask("Collecting accounts", ProgressMonitor.UNKNOWN);
-        Set<Account> accounts = scanAccounts(db, pm);
-        pm.endTask();
-        pm.beginTask("Migrating accounts to NoteDb", accounts.size());
-        for (Account account : accounts) {
-          updateAccountInNoteDb(repo, account);
-          pm.update(1);
-        }
-        pm.endTask();
-      }
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException("Migrating accounts to NoteDb failed", e);
-    }
-  }
-
-  private Set<Account> scanAccounts(ReviewDb db, ProgressMonitor pm) throws SQLException {
-    Map<String, AccountSetter> fields = getFields(db);
-    if (fields.isEmpty()) {
-      logger.atWarning().log("Only account_id and registered_on fields are migrated for accounts");
-    }
-
-    List<String> queryFields = new ArrayList<>();
-    queryFields.add("account_id");
-    queryFields.add("registered_on");
-    queryFields.addAll(fields.keySet());
-    String query = "SELECT " + String.join(", ", queryFields) + String.format(" FROM %s", TABLE);
-    try (Statement stmt = newStatement(db);
-        ResultSet rs = stmt.executeQuery(query)) {
-      Set<Account> s = new HashSet<>();
-      while (rs.next()) {
-        Account a = new Account(new Account.Id(rs.getInt(1)), rs.getTimestamp(2));
-        for (Map.Entry<String, AccountSetter> field : fields.entrySet()) {
-          field.getValue().set(a, rs, field.getKey());
-        }
-        s.add(a);
-        pm.update(1);
-      }
-      return s;
-    }
-  }
-
-  private Map<String, AccountSetter> getFields(ReviewDb db) throws SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    Connection connection = schema.getConnection();
-    Set<String> columns = schema.getDialect().listColumns(connection, TABLE);
-    return ACCOUNT_FIELDS_MAP
-        .entrySet()
-        .stream()
-        .filter(e -> columns.contains(e.getKey()))
-        .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
-  }
-
-  private void updateAccountInNoteDb(Repository allUsersRepo, Account account)
-      throws IOException, ConfigInvalidException {
-    MetaDataUpdate md =
-        new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo);
-    PersonIdent ident = serverIdent.get();
-    md.getCommitBuilder().setAuthor(ident);
-    md.getCommitBuilder().setCommitter(ident);
-    new AccountConfig(account.getId(), allUsersName, allUsersRepo)
-        .load()
-        .setAccount(account)
-        .commit(md);
-  }
-
-  @FunctionalInterface
-  private interface AccountSetter {
-    void set(Account a, ResultSet rs, String field) throws SQLException;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_155.java b/java/com/google/gerrit/server/schema/Schema_155.java
deleted file mode 100644
index e9372a5..0000000
--- a/java/com/google/gerrit/server/schema/Schema_155.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Create account sequence in NoteDb */
-public class Schema_155 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-
-  @Inject
-  Schema_155(
-      Provider<Schema_154> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    RepoSequence accountSeq =
-        new RepoSequence(
-            repoManager,
-            GitReferenceUpdated.DISABLED,
-            allUsersName,
-            Sequences.NAME_ACCOUNTS,
-            () -> ReviewDb.FIRST_ACCOUNT_ID,
-            1);
-
-    // consume one account ID to ensure that the account sequence is initialized in NoteDb
-    accountSeq.next();
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_156.java b/java/com/google/gerrit/server/schema/Schema_156.java
deleted file mode 100644
index 237161c..0000000
--- a/java/com/google/gerrit/server/schema/Schema_156.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add revertOf field to change. */
-public class Schema_156 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_156(Provider<Schema_155> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_157.java b/java/com/google/gerrit/server/schema/Schema_157.java
deleted file mode 100644
index 20a8c4b..0000000
--- a/java/com/google/gerrit/server/schema/Schema_157.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Drop unused indexes from accounts table. */
-public class Schema_157 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_157(Provider<Schema_156> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    JdbcSchema schema = (JdbcSchema) db;
-    SqlDialect dialect = schema.getDialect();
-    try (StatementExecutor e = newExecutor(db)) {
-      dialect.dropIndex(e, "accounts", "accounts_byPreferredEmail");
-    } catch (OrmException ex) {
-      // Ignore. The index did not exist.
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_158.java b/java/com/google/gerrit/server/schema/Schema_158.java
deleted file mode 100644
index 3b7d9e8..0000000
--- a/java/com/google/gerrit/server/schema/Schema_158.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Drop ACCOUNTS table. */
-public class Schema_158 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_158(Provider<Schema_157> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_159.java b/java/com/google/gerrit/server/schema/Schema_159.java
deleted file mode 100644
index ddb5765..0000000
--- a/java/com/google/gerrit/server/schema/Schema_159.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Migrate draft changes to private or wip changes. */
-public class Schema_159 extends ReviewDbSchemaVersion {
-
-  private enum DraftWorkflowMigrationStrategy {
-    PRIVATE,
-    WORK_IN_PROGRESS
-  }
-
-  @Inject
-  Schema_159(Provider<Schema_158> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    DraftWorkflowMigrationStrategy strategy = DraftWorkflowMigrationStrategy.WORK_IN_PROGRESS;
-    if (ui.yesno(false, "Migrate draft changes to private changes (default is work-in-progress)")) {
-      strategy = DraftWorkflowMigrationStrategy.PRIVATE;
-    }
-    ui.message(
-        String.format("Replace draft changes with %s changes ...", strategy.name().toLowerCase()));
-    try (StatementExecutor e = newExecutor(db)) {
-      String column =
-          strategy == DraftWorkflowMigrationStrategy.PRIVATE ? "is_private" : "work_in_progress";
-      // Mark changes private/WIP and NEW if either:
-      // * they have status DRAFT
-      // * they have status NEW and have any draft patch sets
-      e.execute(
-          String.format(
-              "UPDATE changes "
-                  + "SET %s = 'Y', "
-                  + "    status = 'n', "
-                  + "    created_on = created_on "
-                  + "WHERE status = 'd' "
-                  + "  OR (status = 'n' "
-                  + "      AND EXISTS "
-                  + "        (SELECT * "
-                  + "         FROM patch_sets "
-                  + "         WHERE patch_sets.change_id = changes.change_id "
-                  + "           AND patch_sets.draft = 'Y')) ",
-              column));
-    }
-    ui.message("done");
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_160.java b/java/com/google/gerrit/server/schema/Schema_160.java
deleted file mode 100644
index 10f5c9d..0000000
--- a/java/com/google/gerrit/server/schema/Schema_160.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static com.google.gerrit.server.git.UserConfigSections.MY;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.Accounts;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-
-/**
- * Remove "My Drafts" menu items for all users and server-wide default preferences.
- *
- * <p>Since draft changes no longer exist, these menu items are obsolete.
- *
- * <p>Only matches menu items (with any name) where the URL exactly matches one of the following,
- * with or without leading {@code #}:
- *
- * <ul>
- *   <li>/q/is:draft
- *   <li>/q/owner:self+is:draft
- * </ul>
- *
- * In particular, this includes the <a
- * href="https://gerrit.googlesource.com/gerrit/+/v2.14.4/gerrit-server/src/main/java/com/google/gerrit/server/account/GeneralPreferencesLoader.java#144">default
- * from version 2.14 and earlier</a>.
- *
- * <p>Other menus containing {@code is:draft} in other positions are not affected; this is still a
- * valid predicate that matches no changes.
- */
-public class Schema_160 extends ReviewDbSchemaVersion {
-  @VisibleForTesting static final ImmutableList<String> DEFAULT_DRAFT_ITEMS;
-
-  static {
-    String ownerSelfIsDraft = "/q/owner:self+is:draft";
-    String isDraft = "/q/is:draft";
-    DEFAULT_DRAFT_ITEMS =
-        ImmutableList.of(ownerSelfIsDraft, '#' + ownerSelfIsDraft, isDraft, '#' + isDraft);
-  }
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final Provider<PersonIdent> serverIdent;
-
-  @Inject
-  Schema_160(
-      Provider<Schema_159> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritPersonIdent Provider<PersonIdent> serverIdent) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.serverIdent = serverIdent;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try {
-      try (Repository repo = repoManager.openRepository(allUsersName)) {
-        ProgressMonitor pm = new TextProgressMonitor();
-        pm.beginTask("Removing \"My Drafts\" menu items", ProgressMonitor.UNKNOWN);
-        for (Account.Id id : (Iterable<Account.Id>) Accounts.readUserRefs(repo)::iterator) {
-          removeMyDrafts(repo, RefNames.refsUsers(id), pm);
-        }
-        removeMyDrafts(repo, RefNames.REFS_USERS_DEFAULT, pm);
-        pm.endTask();
-      }
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException("Removing \"My Drafts\" menu items failed", e);
-    }
-  }
-
-  private void removeMyDrafts(Repository repo, String ref, ProgressMonitor pm)
-      throws IOException, ConfigInvalidException {
-    MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, repo);
-    PersonIdent ident = serverIdent.get();
-    md.getCommitBuilder().setAuthor(ident);
-    md.getCommitBuilder().setCommitter(ident);
-    Prefs prefs = new Prefs(ref);
-    prefs.load(allUsersName, repo);
-    prefs.removeMyDrafts();
-    prefs.commit(md);
-    if (prefs.dirty()) {
-      pm.update(1);
-    }
-  }
-
-  private static class Prefs extends VersionedAccountPreferences {
-    private boolean dirty;
-
-    Prefs(String ref) {
-      super(ref);
-    }
-
-    @Override
-    protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
-      if (!dirty) {
-        return false;
-      }
-      commit.setMessage("Remove \"My Drafts\" menu items");
-      return super.onSave(commit);
-    }
-
-    void removeMyDrafts() {
-      Config cfg = getConfig();
-      for (String item : cfg.getSubsections(MY)) {
-        String value = cfg.getString(MY, item, KEY_URL);
-        if (DEFAULT_DRAFT_ITEMS.contains(value)) {
-          cfg.unsetSection(MY, item);
-          dirty = true;
-        }
-      }
-    }
-
-    boolean dirty() {
-      return dirty;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_161.java b/java/com/google/gerrit/server/schema/Schema_161.java
deleted file mode 100644
index 4627abe..0000000
--- a/java/com/google/gerrit/server/schema/Schema_161.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.primitives.Ints;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.StarredChangesUtil;
-import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
-import com.google.gerrit.server.StarredChangesUtil.StarRef;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-public class Schema_161 extends ReviewDbSchemaVersion {
-  private static final String MUTE_LABEL = "mute";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-
-  @Inject
-  Schema_161(
-      Provider<Schema_160> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (Repository git = repoManager.openRepository(allUsersName);
-        RevWalk rw = new RevWalk(git)) {
-      BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-      bru.setAllowNonFastForwards(true);
-
-      for (Ref ref : git.getRefDatabase().getRefsByPrefix(RefNames.REFS_STARRED_CHANGES)) {
-        StarRef starRef = StarredChangesUtil.readLabels(git, ref.getName());
-
-        Set<Integer> mutedPatchSets =
-            StarredChangesUtil.getStarredPatchSets(starRef.labels(), MUTE_LABEL);
-        if (mutedPatchSets.isEmpty()) {
-          continue;
-        }
-
-        Set<Integer> reviewedPatchSets =
-            StarredChangesUtil.getStarredPatchSets(
-                starRef.labels(), StarredChangesUtil.REVIEWED_LABEL);
-        Set<Integer> unreviewedPatchSets =
-            StarredChangesUtil.getStarredPatchSets(
-                starRef.labels(), StarredChangesUtil.UNREVIEWED_LABEL);
-
-        List<String> newLabels =
-            starRef
-                .labels()
-                .stream()
-                .map(
-                    l -> {
-                      if (l.startsWith(MUTE_LABEL)) {
-                        Integer mutedPatchSet = Ints.tryParse(l.substring(MUTE_LABEL.length() + 1));
-                        if (mutedPatchSet == null) {
-                          // unexpected format of mute label, must be a label that was manually
-                          // set, just leave it alone
-                          return l;
-                        }
-                        if (!reviewedPatchSets.contains(mutedPatchSet)
-                            && !unreviewedPatchSets.contains(mutedPatchSet)) {
-                          // convert mute label to reviewed label
-                          return StarredChangesUtil.REVIEWED_LABEL + "/" + mutedPatchSet;
-                        }
-                        // else patch set is muted but has either reviewed or unreviewed label
-                        // -> just drop the mute label
-                        return null;
-                      }
-                      return l;
-                    })
-                .filter(Objects::nonNull)
-                .collect(toList());
-
-        ObjectId id = StarredChangesUtil.writeLabels(git, newLabels);
-        bru.addCommand(new ReceiveCommand(ref.getTarget().getObjectId(), id, ref.getName()));
-      }
-      bru.execute(rw, new TextProgressMonitor());
-    } catch (IOException | IllegalLabelException ex) {
-      throw new OrmException(ex);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_162.java b/java/com/google/gerrit/server/schema/Schema_162.java
deleted file mode 100644
index 3d3a192..0000000
--- a/java/com/google/gerrit/server/schema/Schema_162.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_162 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllProjectsName allProjectsName;
-  private final AllUsersName allUsersName;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_162(
-      Provider<Schema_161> prior,
-      GitRepositoryManager repoManager,
-      AllProjectsName allProjectsName,
-      AllUsersName allUsersName,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allProjectsName = allProjectsName;
-    this.allUsersName = allUsersName;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    try (Repository git = repoManager.openRepository(allUsersName);
-        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
-      ProjectConfig cfg = projectConfigFactory.read(md);
-      if (allProjectsName.equals(cfg.getProject().getParent(allProjectsName))) {
-        return;
-      }
-      cfg.getProject().setParentName(allProjectsName);
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(
-          String.format("Make %s inherit from %s", allUsersName.get(), allProjectsName.get()));
-      cfg.commit(md);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_163.java b/java/com/google/gerrit/server/schema/Schema_163.java
deleted file mode 100644
index 4b3659de..0000000
--- a/java/com/google/gerrit/server/schema/Schema_163.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Create group sequence in NoteDb */
-public class Schema_163 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-
-  @Inject
-  Schema_163(
-      Provider<Schema_162> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    RepoSequence groupSeq =
-        new RepoSequence(
-            repoManager,
-            GitReferenceUpdated.DISABLED,
-            allUsersName,
-            Sequences.NAME_GROUPS,
-            () -> ReviewDb.FIRST_GROUP_ID,
-            1);
-
-    // consume one account ID to ensure that the group sequence is initialized in NoteDb
-    groupSeq.next();
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_164.java b/java/com/google/gerrit/server/schema/Schema_164.java
deleted file mode 100644
index b6c3118..0000000
--- a/java/com/google/gerrit/server/schema/Schema_164.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Grant read on group branches */
-public class Schema_164 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG = "Grant read permissions on group branches";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final SystemGroupBackend systemGroupBackend;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_164(
-      Provider<Schema_163> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      SystemGroupBackend systemGroupBackend,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.systemGroupBackend = systemGroupBackend;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (Repository git = repoManager.openRepository(allUsersName);
-        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(COMMIT_MSG);
-
-      ProjectConfig config = projectConfigFactory.read(md);
-      AccessSection groups = config.getAccessSection(RefNames.REFS_GROUPS + "*", true);
-      grant(
-          config,
-          groups,
-          Permission.READ,
-          false,
-          true,
-          systemGroupBackend.getGroup(REGISTERED_USERS));
-      config.commit(md);
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException("Failed to grant read permissions on group branches", e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_165.java b/java/com/google/gerrit/server/schema/Schema_165.java
deleted file mode 100644
index d80c770..0000000
--- a/java/com/google/gerrit/server/schema/Schema_165.java
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.data.PermissionRule.Action;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import java.util.Optional;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Make default Label-Code-Review permission on user branches exclusive. */
-public class Schema_165 extends ReviewDbSchemaVersion {
-  private static final String COMMIT_MSG =
-      "Make default Label-Code-Review permission on user branches exclusive";
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final SystemGroupBackend systemGroupBackend;
-  private final ProjectConfig.Factory projectConfigFactory;
-  private final PersonIdent serverUser;
-
-  @Inject
-  Schema_165(
-      Provider<Schema_164> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      SystemGroupBackend systemGroupBackend,
-      ProjectConfig.Factory projectConfigFactory,
-      @GerritPersonIdent PersonIdent serverUser) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.systemGroupBackend = systemGroupBackend;
-    this.projectConfigFactory = projectConfigFactory;
-    this.serverUser = serverUser;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (Repository git = repoManager.openRepository(allUsersName);
-        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
-      ProjectConfig config = projectConfigFactory.read(md);
-      Optional<Permission> permission = findDefaultPermission(config);
-      if (!permission.isPresent()) {
-        // the default permission was not found, hence it cannot be fixed
-        return;
-      }
-
-      permission.get().setExclusiveGroup(true);
-
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.setMessage(COMMIT_MSG);
-      config.commit(md);
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException(
-          "Failed to make default Label-Code-Review permission on user branches exclusive", e);
-    }
-  }
-
-  /**
-   * Searches for the default "Label-Code-Review" permission on the user branch and returns it if it
-   * was found. If it was not found (e.g. because it was removed or modified) {@link
-   * Optional#empty()} is returned.
-   */
-  private Optional<Permission> findDefaultPermission(ProjectConfig config) {
-    AccessSection users =
-        config.getAccessSection(
-            RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", false);
-    if (users == null) {
-      // default permission was removed
-      return Optional.empty();
-    }
-
-    Permission permission = users.getPermission(Permission.LABEL + "Code-Review", false);
-    return isDefaultPermissionUntouched(permission) ? Optional.of(permission) : Optional.empty();
-  }
-
-  /**
-   * Checks whether the given permission matches the default "Label-Code-Review" permission on the
-   * user branch that was initially setup by {@link AllUsersCreator}.
-   */
-  private boolean isDefaultPermissionUntouched(Permission permission) {
-    if (permission == null) {
-      // default permission was removed
-      return false;
-    } else if (permission.getExclusiveGroup()) {
-      // default permission was modified
-      return false;
-    }
-
-    if (permission.getRules().size() != 1) {
-      // default permission was modified
-      return false;
-    }
-
-    PermissionRule rule = permission.getRule(systemGroupBackend.getGroup(REGISTERED_USERS));
-    if (rule == null) {
-      // default permission was removed
-      return false;
-    }
-
-    if (rule.getAction() != Action.ALLOW
-        || rule.getForce()
-        || rule.getMin() != -2
-        || rule.getMax() != 2) {
-      // default permission was modified
-      return false;
-    }
-
-    return true;
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_166.java b/java/com/google/gerrit/server/schema/Schema_166.java
deleted file mode 100644
index 901df56..0000000
--- a/java/com/google/gerrit/server/schema/Schema_166.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-
-/** Set HEAD for All-Users to refs/meta/config. */
-public class Schema_166 extends ReviewDbSchemaVersion {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-
-  @Inject
-  Schema_166(
-      Provider<Schema_165> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (Repository git = repoManager.openRepository(allUsersName)) {
-      RefUpdate u = git.updateRef(Constants.HEAD);
-      u.link(RefNames.REFS_CONFIG);
-    } catch (IOException e) {
-      throw new OrmException(String.format("Failed to update HEAD for %s", allUsersName.get()), e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_167.java b/java/com/google/gerrit/server/schema/Schema_167.java
deleted file mode 100644
index 44d89f9..0000000
--- a/java/com/google/gerrit/server/schema/Schema_167.java
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountConfig;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Migrate groups from ReviewDb to NoteDb. */
-public class Schema_167 extends ReviewDbSchemaVersion {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private final GitRepositoryManager repoManager;
-  private final AllUsersName allUsersName;
-  private final Config gerritConfig;
-  private final SitePaths sitePaths;
-  private final PersonIdent serverIdent;
-  private final SystemGroupBackend systemGroupBackend;
-
-  @Inject
-  protected Schema_167(
-      Provider<Schema_166> prior,
-      GitRepositoryManager repoManager,
-      AllUsersName allUsersName,
-      @GerritServerConfig Config gerritConfig,
-      SitePaths sitePaths,
-      @GerritPersonIdent PersonIdent serverIdent,
-      SystemGroupBackend systemGroupBackend) {
-    super(prior);
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
-    this.gerritConfig = gerritConfig;
-    this.sitePaths = sitePaths;
-    this.serverIdent = serverIdent;
-    this.systemGroupBackend = systemGroupBackend;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    if (gerritConfig.getBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false)) {
-      // Groups in ReviewDb have already been disabled, nothing to do.
-      return;
-    }
-
-    try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
-      List<GroupReference> allGroupReferences = readGroupReferencesFromReviewDb(db);
-
-      BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
-      writeAllGroupNamesToNoteDb(allUsersRepo, allGroupReferences, batchRefUpdate);
-
-      GroupRebuilder groupRebuilder = createGroupRebuilder(db, allUsersRepo);
-      for (GroupReference groupReference : allGroupReferences) {
-        migrateOneGroupToNoteDb(
-            db, allUsersRepo, groupRebuilder, groupReference.getUUID(), batchRefUpdate);
-      }
-
-      RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException(
-          String.format("Failed to migrate groups to NoteDb for %s", allUsersName.get()), e);
-    }
-  }
-
-  private List<GroupReference> readGroupReferencesFromReviewDb(ReviewDb db) throws SQLException {
-    try (Statement stmt = ReviewDbWrapper.unwrapJbdcSchema(db).getConnection().createStatement();
-        ResultSet rs = stmt.executeQuery("SELECT group_uuid, name FROM account_groups")) {
-      List<GroupReference> allGroupReferences = new ArrayList<>();
-      while (rs.next()) {
-        AccountGroup.UUID groupUuid = new AccountGroup.UUID(rs.getString(1));
-        String groupName = rs.getString(2);
-        allGroupReferences.add(new GroupReference(groupUuid, groupName));
-      }
-      return allGroupReferences;
-    }
-  }
-
-  private void writeAllGroupNamesToNoteDb(
-      Repository allUsersRepo,
-      List<GroupReference> allGroupReferences,
-      BatchRefUpdate batchRefUpdate)
-      throws IOException {
-    try (ObjectInserter inserter = allUsersRepo.newObjectInserter()) {
-      GroupNameNotes.updateAllGroups(
-          allUsersRepo, inserter, batchRefUpdate, allGroupReferences, serverIdent);
-      inserter.flush();
-    }
-  }
-
-  private GroupRebuilder createGroupRebuilder(ReviewDb db, Repository allUsersRepo)
-      throws IOException, ConfigInvalidException {
-    AuditLogFormatter auditLogFormatter =
-        createAuditLogFormatter(db, allUsersRepo, gerritConfig, sitePaths);
-    return new GroupRebuilder(serverIdent, allUsersName, auditLogFormatter);
-  }
-
-  private AuditLogFormatter createAuditLogFormatter(
-      ReviewDb db, Repository allUsersRepo, Config gerritConfig, SitePaths sitePaths)
-      throws IOException, ConfigInvalidException {
-    String serverId = new GerritServerIdProvider(gerritConfig, sitePaths).get();
-    SimpleInMemoryAccountCache accountCache =
-        new SimpleInMemoryAccountCache(allUsersName, allUsersRepo);
-    SimpleInMemoryGroupCache groupCache = new SimpleInMemoryGroupCache(db);
-    return AuditLogFormatter.create(
-        accountCache::get,
-        uuid -> {
-          if (systemGroupBackend.handles(uuid)) {
-            return Optional.ofNullable(systemGroupBackend.get(uuid));
-          }
-          return groupCache.get(uuid);
-        },
-        serverId);
-  }
-
-  private static void migrateOneGroupToNoteDb(
-      ReviewDb db,
-      Repository allUsersRepo,
-      GroupRebuilder rebuilder,
-      AccountGroup.UUID uuid,
-      BatchRefUpdate batchRefUpdate)
-      throws ConfigInvalidException, IOException, OrmException {
-    GroupBundle reviewDbBundle = GroupBundle.Factory.fromReviewDb(db, uuid);
-    RefUpdateUtil.deleteChecked(allUsersRepo, RefNames.refsGroups(uuid));
-    rebuilder.rebuild(allUsersRepo, reviewDbBundle, batchRefUpdate);
-  }
-
-  // The regular account cache isn't available during init. -> Use a simple replacement which tries
-  // to load every account only once from disk.
-  private static class SimpleInMemoryAccountCache {
-    private final AllUsersName allUsersName;
-    private final Repository allUsersRepo;
-    private Map<Account.Id, Optional<Account>> accounts = new HashMap<>();
-
-    public SimpleInMemoryAccountCache(AllUsersName allUsersName, Repository allUsersRepo) {
-      this.allUsersName = allUsersName;
-      this.allUsersRepo = allUsersRepo;
-    }
-
-    public Optional<Account> get(Account.Id accountId) {
-      accounts.computeIfAbsent(accountId, this::load);
-      return accounts.get(accountId);
-    }
-
-    private Optional<Account> load(Account.Id accountId) {
-      try {
-        AccountConfig accountConfig =
-            new AccountConfig(accountId, allUsersName, allUsersRepo).load();
-        return accountConfig.getLoadedAccount();
-      } catch (IOException | ConfigInvalidException ignored) {
-        logger.atWarning().withCause(ignored).log(
-            "Failed to load account %s."
-                + " Cannot get account name for group audit log commit messages.",
-            accountId.get());
-        return Optional.empty();
-      }
-    }
-  }
-
-  // The regular GroupBackends (especially external GroupBackends) and our internal group cache
-  // aren't available during init. -> Use a simple replacement which tries to look up only internal
-  // groups and which loads every internal group only once from disc. (There's no way we can look up
-  // external groups during init. As we need those groups only for cosmetic aspects in
-  // AuditLogFormatter, it's safe to exclude them.)
-  private static class SimpleInMemoryGroupCache {
-    private final ReviewDb db;
-    private Map<AccountGroup.UUID, Optional<GroupDescription.Basic>> groups = new HashMap<>();
-
-    public SimpleInMemoryGroupCache(ReviewDb db) {
-      this.db = db;
-    }
-
-    public Optional<GroupDescription.Basic> get(AccountGroup.UUID groupUuid) {
-      groups.computeIfAbsent(groupUuid, this::load);
-      return groups.get(groupUuid);
-    }
-
-    private Optional<GroupDescription.Basic> load(AccountGroup.UUID groupUuid) {
-      if (!AccountGroup.isInternalGroup(groupUuid)) {
-        return Optional.empty();
-      }
-
-      List<GroupDescription.Basic> groupDescriptions = getGroupDescriptions(groupUuid);
-      if (groupDescriptions.size() == 1) {
-        return Optional.of(Iterables.getOnlyElement(groupDescriptions));
-      }
-      return Optional.empty();
-    }
-
-    private List<GroupDescription.Basic> getGroupDescriptions(AccountGroup.UUID groupUuid) {
-      try (Statement stmt = ReviewDbWrapper.unwrapJbdcSchema(db).getConnection().createStatement();
-          ResultSet rs =
-              stmt.executeQuery(
-                  "SELECT name FROM account_groups where group_uuid = '" + groupUuid + "'")) {
-        List<GroupDescription.Basic> groupDescriptions = new ArrayList<>();
-        while (rs.next()) {
-          String groupName = rs.getString(1);
-          groupDescriptions.add(toGroupDescription(groupUuid, groupName));
-        }
-        return groupDescriptions;
-      } catch (SQLException ignored) {
-        logger.atWarning().withCause(ignored).log(
-            "Failed to load group %s."
-                + " Cannot get group name for group audit log commit messages.",
-            groupUuid.get());
-        return ImmutableList.of();
-      }
-    }
-
-    private static GroupDescription.Basic toGroupDescription(
-        AccountGroup.UUID groupUuid, String groupName) {
-      return new GroupDescription.Basic() {
-        @Override
-        public AccountGroup.UUID getGroupUUID() {
-          return groupUuid;
-        }
-
-        @Override
-        public String getName() {
-          return groupName;
-        }
-
-        @Nullable
-        @Override
-        public String getEmailAddress() {
-          return null;
-        }
-
-        @Nullable
-        @Override
-        public String getUrl() {
-          return null;
-        }
-      };
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_169.java b/java/com/google/gerrit/server/schema/Schema_169.java
deleted file mode 100644
index 11601e4..0000000
--- a/java/com/google/gerrit/server/schema/Schema_169.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.CommentJsonMigrator;
-import com.google.gerrit.server.notedb.CommentJsonMigrator.ProjectMigrationResult;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.SortedSet;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-
-/** Migrate NoteDb inline comments to JSON format. */
-public class Schema_169 extends ReviewDbSchemaVersion {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-  private final CommentJsonMigrator migrator;
-  private final GitRepositoryManager repoManager;
-  private final NotesMigration notesMigration;
-
-  @Inject
-  Schema_169(
-      Provider<Schema_168> prior,
-      CommentJsonMigrator migrator,
-      GitRepositoryManager repoManager,
-      @GerritServerConfig Config config) {
-    super(prior);
-    this.migrator = migrator;
-    this.repoManager = repoManager;
-    this.notesMigration = MutableNotesMigration.fromConfig(config);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
-    migrateData(ui);
-  }
-
-  @VisibleForTesting
-  protected void migrateData(UpdateUI ui) throws OrmException {
-    //  If the migration hasn't started, no need to look for non-JSON
-    if (!notesMigration.commitChangeWrites()) {
-      return;
-    }
-
-    boolean ok = true;
-    ProgressMonitor pm = new TextProgressMonitor();
-    SortedSet<Project.NameKey> projects = repoManager.list();
-    pm.beginTask("Migrating projects", projects.size());
-    int skipped = 0;
-    for (Project.NameKey project : projects) {
-      try (Repository repo = repoManager.openRepository(project)) {
-        ProjectMigrationResult progress = migrator.migrateProject(project, repo, false);
-        skipped += progress.skipped;
-      } catch (IOException e) {
-        ok = false;
-        logger.atWarning().log("Error migrating project " + project, e);
-      }
-      pm.update(1);
-    }
-
-    pm.endTask();
-    ui.message(
-        "Skipped " + skipped + " project" + (skipped == 1 ? "" : "s") + " with no legacy comments");
-
-    if (!ok) {
-      throw new OrmException("Migration failed");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_170.java b/java/com/google/gerrit/server/schema/Schema_170.java
deleted file mode 100644
index 6a86494..0000000
--- a/java/com/google/gerrit/server/schema/Schema_170.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_170 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_170(Provider<Schema_169> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_83.java b/java/com/google/gerrit/server/schema/Schema_83.java
deleted file mode 100644
index 95b7e6f..0000000
--- a/java/com/google/gerrit/server/schema/Schema_83.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-public class Schema_83 extends ReviewDbSchemaVersion {
-
-  @Inject
-  Schema_83() {
-    super(
-        new Provider<ReviewDbSchemaVersion>() {
-          @Override
-          public ReviewDbSchemaVersion get() {
-            throw new ProvisionException("Upgrade first to 2.8 or 2.9");
-          }
-        });
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_84.java b/java/com/google/gerrit/server/schema/Schema_84.java
deleted file mode 100644
index 415b2e3..0000000
--- a/java/com/google/gerrit/server/schema/Schema_84.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_84 extends ReviewDbSchemaVersion {
-
-  @Inject
-  Schema_84(Provider<Schema_83> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_85.java b/java/com/google/gerrit/server/schema/Schema_85.java
deleted file mode 100644
index ee8fbdb..0000000
--- a/java/com/google/gerrit/server/schema/Schema_85.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_85 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_85(Provider<Schema_84> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_86.java b/java/com/google/gerrit/server/schema/Schema_86.java
deleted file mode 100644
index e468345..0000000
--- a/java/com/google/gerrit/server/schema/Schema_86.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_86 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_86(Provider<Schema_85> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_87.java b/java/com/google/gerrit/server/schema/Schema_87.java
deleted file mode 100644
index 79884ba..0000000
--- a/java/com/google/gerrit/server/schema/Schema_87.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-
-public class Schema_87 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_87(Provider<Schema_86> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try (PreparedStatement uuidRetrieval =
-            prepareStatement(db, "SELECT group_uuid FROM account_groups WHERE group_id = ?");
-        PreparedStatement groupDeletion =
-            prepareStatement(db, "DELETE FROM account_groups WHERE group_id = ?");
-        PreparedStatement groupNameDeletion =
-            prepareStatement(db, "DELETE FROM account_group_names WHERE group_id = ?")) {
-      for (AccountGroup.Id id : scanSystemGroups(db)) {
-        Optional<AccountGroup.UUID> groupUuid = getUuid(uuidRetrieval, id);
-        if (groupUuid.filter(SystemGroupBackend::isSystemGroup).isPresent()) {
-          groupDeletion.setInt(1, id.get());
-          groupDeletion.executeUpdate();
-
-          groupNameDeletion.setInt(1, id.get());
-          groupNameDeletion.executeUpdate();
-        }
-      }
-    }
-  }
-
-  private static Optional<AccountGroup.UUID> getUuid(
-      PreparedStatement uuidRetrieval, AccountGroup.Id id) throws SQLException {
-    uuidRetrieval.setInt(1, id.get());
-    try (ResultSet uuidResults = uuidRetrieval.executeQuery()) {
-      if (uuidResults.next()) {
-        Optional.of(new AccountGroup.UUID(uuidResults.getString(1)));
-      }
-    }
-    return Optional.empty();
-  }
-
-  private static Set<AccountGroup.Id> scanSystemGroups(ReviewDb db) throws SQLException {
-    try (Statement stmt = newStatement(db);
-        ResultSet rs =
-            stmt.executeQuery("SELECT group_id FROM account_groups WHERE group_type = 'SYSTEM'")) {
-      Set<AccountGroup.Id> ids = new HashSet<>();
-      while (rs.next()) {
-        ids.add(new AccountGroup.Id(rs.getInt(1)));
-      }
-      return ids;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_88.java b/java/com/google/gerrit/server/schema/Schema_88.java
deleted file mode 100644
index d5e9994..0000000
--- a/java/com/google/gerrit/server/schema/Schema_88.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_88 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_88(Provider<Schema_87> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_89.java b/java/com/google/gerrit/server/schema/Schema_89.java
deleted file mode 100644
index 3d352da..0000000
--- a/java/com/google/gerrit/server/schema/Schema_89.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-public class Schema_89 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_89(Provider<Schema_88> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    SqlDialect dialect = ((JdbcSchema) db).getDialect();
-    try (StatementExecutor e = newExecutor(db)) {
-      dialect.dropIndex(e, "patch_set_approvals", "patch_set_approvals_openByUser");
-      dialect.dropIndex(e, "patch_set_approvals", "patch_set_approvals_closedByU");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_90.java b/java/com/google/gerrit/server/schema/Schema_90.java
deleted file mode 100644
index 3831f33..0000000
--- a/java/com/google/gerrit/server/schema/Schema_90.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_90 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_90(Provider<Schema_89> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
-    try (Statement stmt = newStatement(db)) {
-      stmt.executeUpdate("UPDATE accounts set size_bar_in_change_table = 'Y'");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_91.java b/java/com/google/gerrit/server/schema/Schema_91.java
deleted file mode 100644
index 6dd2d58..0000000
--- a/java/com/google/gerrit/server/schema/Schema_91.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_91 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_91(Provider<Schema_90> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_92.java b/java/com/google/gerrit/server/schema/Schema_92.java
deleted file mode 100644
index 9af33c0..0000000
--- a/java/com/google/gerrit/server/schema/Schema_92.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_92 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_92(Provider<Schema_91> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_93.java b/java/com/google/gerrit/server/schema/Schema_93.java
deleted file mode 100644
index e9a6691..0000000
--- a/java/com/google/gerrit/server/schema/Schema_93.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_93 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_93(Provider<Schema_92> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_94.java b/java/com/google/gerrit/server/schema/Schema_94.java
deleted file mode 100644
index 1551650..0000000
--- a/java/com/google/gerrit/server/schema/Schema_94.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_94 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_94(Provider<Schema_93> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
-    try (Statement stmt = newStatement(db)) {
-      stmt.execute("CREATE INDEX patch_sets_byRevision ON patch_sets (revision)");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_95.java b/java/com/google/gerrit/server/schema/Schema_95.java
deleted file mode 100644
index 19dfa97..0000000
--- a/java/com/google/gerrit/server/schema/Schema_95.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-public class Schema_95 extends ReviewDbSchemaVersion {
-  private final AllUsersCreator allUsersCreator;
-
-  @Inject
-  Schema_95(Provider<Schema_94> prior, AllUsersCreator allUsersCreator) {
-    super(prior);
-    this.allUsersCreator = allUsersCreator;
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
-    try {
-      allUsersCreator.create();
-    } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException(e);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_96.java b/java/com/google/gerrit/server/schema/Schema_96.java
deleted file mode 100644
index 2eb9a87..0000000
--- a/java/com/google/gerrit/server/schema/Schema_96.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_96 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_96(Provider<Schema_95> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_97.java b/java/com/google/gerrit/server/schema/Schema_97.java
deleted file mode 100644
index 98f548e..0000000
--- a/java/com/google/gerrit/server/schema/Schema_97.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_97 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_97(Provider<Schema_96> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_98.java b/java/com/google/gerrit/server/schema/Schema_98.java
deleted file mode 100644
index 8a7498f..0000000
--- a/java/com/google/gerrit/server/schema/Schema_98.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_98 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_98(Provider<Schema_97> prior) {
-    super(prior);
-  }
-
-  @Override
-  protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
-    ui.message("Migrate user preference showUserInReview to reviewCategoryStrategy");
-    try (Statement stmt = newStatement(db)) {
-      stmt.executeUpdate(
-          "UPDATE accounts SET "
-              + "REVIEW_CATEGORY_STRATEGY='NAME' "
-              + "WHERE (SHOW_USER_IN_REVIEW='Y')");
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_99.java b/java/com/google/gerrit/server/schema/Schema_99.java
deleted file mode 100644
index ca3a959..0000000
--- a/java/com/google/gerrit/server/schema/Schema_99.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_99 extends ReviewDbSchemaVersion {
-  @Inject
-  Schema_99(Provider<Schema_98> prior) {
-    super(prior);
-  }
-}
diff --git a/java/com/google/gerrit/server/schema/ScriptRunner.java b/java/com/google/gerrit/server/schema/ScriptRunner.java
deleted file mode 100644
index f4cba98..0000000
--- a/java/com/google/gerrit/server/schema/ScriptRunner.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.base.CharMatcher;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Parses an SQL script from a resource file and later runs it. */
-class ScriptRunner {
-  private final String name;
-  private final List<String> commands;
-
-  static final ScriptRunner NOOP =
-      new ScriptRunner(null, null) {
-        @Override
-        void run(ReviewDb db) {}
-      };
-
-  ScriptRunner(String scriptName, InputStream script) {
-    this.name = scriptName;
-    try {
-      this.commands = script != null ? parse(script) : null;
-    } catch (IOException e) {
-      throw new IllegalStateException("Cannot parse " + name, e);
-    }
-  }
-
-  void run(ReviewDb db) throws OrmException {
-    try {
-      final JdbcSchema schema = (JdbcSchema) db;
-      final Connection c = schema.getConnection();
-      final SqlDialect dialect = schema.getDialect();
-      try (Statement stmt = c.createStatement()) {
-        for (String sql : commands) {
-          try {
-            if (!dialect.isStatementDelimiterSupported()) {
-              sql = CharMatcher.is(';').trimTrailingFrom(sql);
-            }
-            stmt.execute(sql);
-          } catch (SQLException e) {
-            throw new OrmException("Error in " + name + ":\n" + sql, e);
-          }
-        }
-      }
-    } catch (SQLException e) {
-      throw new OrmException("Cannot run statements for " + name, e);
-    }
-  }
-
-  private List<String> parse(InputStream in) throws IOException {
-    try (BufferedReader br = new BufferedReader(new InputStreamReader(in, UTF_8))) {
-      String delimiter = ";";
-      List<String> commands = new ArrayList<>();
-      StringBuilder buffer = new StringBuilder();
-      String line;
-      while ((line = br.readLine()) != null) {
-        if (line.isEmpty()) {
-          continue;
-        }
-        if (line.startsWith("--")) {
-          continue;
-        }
-
-        if (buffer.length() == 0 && line.toLowerCase().startsWith("delimiter ")) {
-          delimiter = line.substring("delimiter ".length()).trim();
-          continue;
-        }
-
-        if (buffer.length() > 0) {
-          buffer.append('\n');
-        }
-        buffer.append(line);
-
-        if (isDone(delimiter, line, buffer)) {
-          String cmd = buffer.toString();
-          commands.add(cmd);
-          buffer = new StringBuilder();
-        }
-      }
-      if (buffer.length() > 0) {
-        commands.add(buffer.toString());
-      }
-      return commands;
-    }
-  }
-
-  private boolean isDone(String delimiter, String line, StringBuilder buffer) {
-    if (";".equals(delimiter)) {
-      return buffer.charAt(buffer.length() - 1) == ';';
-
-    } else if (line.equals(delimiter)) {
-      buffer.setLength(buffer.length() - delimiter.length());
-      return true;
-
-    } else {
-      return false;
-    }
-  }
-}
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 8e018a5..81e2661 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -911,7 +911,7 @@
               cd.getId(),
               new BatchUpdateOp() {
                 @Override
-                public boolean updateChange(ChangeContext ctx) throws OrmException {
+                public boolean updateChange(ChangeContext ctx) {
                   Change change = ctx.getChange();
                   if (!change.getStatus().isOpen()) {
                     return false;
@@ -926,8 +926,7 @@
                           change.getLastUpdatedOn(),
                           ChangeMessagesUtil.TAG_MERGED,
                           "Project was deleted.");
-                  cmUtil.addChangeMessage(
-                      ctx.getDb(), ctx.getUpdate(change.currentPatchSetId()), msg);
+                  cmUtil.addChangeMessage(ctx.getUpdate(change.currentPatchSetId()), msg);
 
                   return true;
                 }
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 3be4c31..fd49042 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeMessagesUtil;
@@ -483,9 +482,8 @@
         psId, ctx.getUser(), ctx.getWhen(), body, ChangeMessagesUtil.TAG_MERGED);
   }
 
-  private void setMerged(ChangeContext ctx, ChangeMessage msg) throws OrmException {
+  private void setMerged(ChangeContext ctx, ChangeMessage msg) {
     Change c = ctx.getChange();
-    ReviewDb db = ctx.getDb();
     logger.atFine().log("Setting change %s merged", c.getId());
     c.setStatus(Change.Status.MERGED);
     c.setSubmissionId(args.submissionId.toStringForStorage());
@@ -494,7 +492,7 @@
     // which is not the user from the update context. addMergedMessage was able
     // to do this in the past.
     if (msg != null) {
-      args.cmUtil.addChangeMessage(db, ctx.getUpdate(msg.getPatchSetId()), msg);
+      args.cmUtil.addChangeMessage(ctx.getUpdate(msg.getPatchSetId()), msg);
     }
   }
 
diff --git a/java/com/google/gerrit/server/update/BatchUpdate.java b/java/com/google/gerrit/server/update/BatchUpdate.java
index b3472d2..a768888 100644
--- a/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.validators.OnSubmitValidators;
 import com.google.gerrit.server.logging.RequestId;
-import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.NoSuchProjectException;
@@ -93,7 +92,6 @@
     return new FactoryModule() {
       @Override
       public void configure() {
-        factory(ReviewDbBatchUpdate.AssistedFactory.class);
         factory(NoteDbBatchUpdate.AssistedFactory.class);
       }
     };
@@ -101,27 +99,17 @@
 
   @Singleton
   public static class Factory {
-    private final NotesMigration migration;
-    private final ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory;
     private final NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory;
 
     // TODO(dborowitz): Make this non-injectable to force all callers to use RetryHelper.
     @Inject
-    Factory(
-        NotesMigration migration,
-        ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
-        NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
-      this.migration = migration;
-      this.reviewDbBatchUpdateFactory = reviewDbBatchUpdateFactory;
+    Factory(NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
       this.noteDbBatchUpdateFactory = noteDbBatchUpdateFactory;
     }
 
     public BatchUpdate create(
         ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when) {
-      if (migration.disableChangeReviewDb()) {
-        return noteDbBatchUpdateFactory.create(db, project, user, when);
-      }
-      return reviewDbBatchUpdateFactory.create(db, project, user, when);
+      return noteDbBatchUpdateFactory.create(db, project, user, when);
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
@@ -135,15 +123,9 @@
       // method above, which always returns instances of the type we expect. Just to be safe,
       // copy them into an ImmutableList so there is no chance the callee can pollute the input
       // collection.
-      if (migration.disableChangeReviewDb()) {
-        ImmutableList<NoteDbBatchUpdate> noteDbUpdates =
-            (ImmutableList) ImmutableList.copyOf(updates);
-        NoteDbBatchUpdate.execute(noteDbUpdates, listener, dryRun);
-      } else {
-        ImmutableList<ReviewDbBatchUpdate> reviewDbUpdates =
-            (ImmutableList) ImmutableList.copyOf(updates);
-        ReviewDbBatchUpdate.execute(reviewDbUpdates, listener, dryRun);
-      }
+      ImmutableList<NoteDbBatchUpdate> noteDbUpdates =
+          (ImmutableList) ImmutableList.copyOf(updates);
+      NoteDbBatchUpdate.execute(noteDbUpdates, listener, dryRun);
     }
 
     private static void checkDifferentProject(Collection<BatchUpdate> updates) {
@@ -174,25 +156,6 @@
     return o;
   }
 
-  static boolean getUpdateChangesInParallel(Collection<? extends BatchUpdate> updates) {
-    checkArgument(!updates.isEmpty());
-    Boolean p = null;
-    for (BatchUpdate u : updates) {
-      if (p == null) {
-        p = u.updateChangesInParallel;
-      } else if (u.updateChangesInParallel != p) {
-        throw new IllegalArgumentException("cannot mix parallel and non-parallel operations");
-      }
-    }
-    // Properly implementing this would involve hoisting the parallel loop up
-    // even further. As of this writing, the only user is ReceiveCommits,
-    // which only executes a single BatchUpdate at a time. So bail for now.
-    checkArgument(
-        !p || updates.size() <= 1,
-        "cannot execute ChangeOps in parallel with more than 1 BatchUpdate");
-    return p;
-  }
-
   static void wrapAndThrowException(Exception e) throws UpdateException, RestApiException {
     Throwables.throwIfUnchecked(e);
 
@@ -234,8 +197,6 @@
   protected PushCertificate pushCert;
   protected String refLogMessage;
 
-  private boolean updateChangesInParallel;
-
   protected BatchUpdate(
       GitRepositoryManager repoManager,
       PersonIdent serverIdent,
@@ -296,18 +257,6 @@
     return this;
   }
 
-  /**
-   * Execute {@link BatchUpdateOp#updateChange(ChangeContext)} in parallel for each change.
-   *
-   * <p>This improves performance of writing to multiple changes in separate ReviewDb transactions.
-   * When only NoteDb is used, updates to all changes are written in a single batch ref update, so
-   * parallelization is not used and this option is ignored.
-   */
-  public BatchUpdate updateChangesInParallel() {
-    this.updateChangesInParallel = true;
-    return this;
-  }
-
   protected void initRepository() throws IOException {
     if (repoView == null) {
       repoView = new RepoView(repoManager, project);
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index c8d338b..9bdf293 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -41,7 +41,6 @@
 import com.google.gerrit.metrics.Histogram1;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.time.Duration;
@@ -141,7 +140,6 @@
     return options().build();
   }
 
-  private final NotesMigration migration;
   private final Metrics metrics;
   private final BatchUpdate.Factory updateFactory;
   private final Map<ActionType, Duration> defaultTimeouts;
@@ -152,24 +150,18 @@
   RetryHelper(
       @GerritServerConfig Config cfg,
       Metrics metrics,
-      NotesMigration migration,
-      ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
       NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
-    this(cfg, metrics, migration, reviewDbBatchUpdateFactory, noteDbBatchUpdateFactory, null);
+    this(cfg, metrics, noteDbBatchUpdateFactory, null);
   }
 
   @VisibleForTesting
   public RetryHelper(
       @GerritServerConfig Config cfg,
       Metrics metrics,
-      NotesMigration migration,
-      ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
       NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory,
       @Nullable Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup) {
     this.metrics = metrics;
-    this.migration = migration;
-    this.updateFactory =
-        new BatchUpdate.Factory(migration, reviewDbBatchUpdateFactory, noteDbBatchUpdateFactory);
+    this.updateFactory = new BatchUpdate.Factory(noteDbBatchUpdateFactory);
 
     Duration defaultTimeout =
         Duration.ofMillis(
@@ -229,16 +221,6 @@
   public <T> T execute(ChangeAction<T> changeAction, Options opts)
       throws RestApiException, UpdateException {
     try {
-      if (!migration.disableChangeReviewDb()) {
-        // Either we aren't full-NoteDb, or the underlying ref storage doesn't support atomic
-        // transactions. Either way, retrying a partially-failed operation is not idempotent, so
-        // don't do it automatically. Let the end user decide whether they want to retry.
-        return executeWithTimeoutCount(
-            ActionType.CHANGE_UPDATE,
-            () -> changeAction.call(updateFactory),
-            RetryerBuilder.<T>newBuilder().build());
-      }
-
       return execute(
           ActionType.CHANGE_UPDATE,
           () -> changeAction.call(updateFactory),
diff --git a/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java b/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
deleted file mode 100644
index b859895..0000000
--- a/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
+++ /dev/null
@@ -1,842 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.update;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.Comparator.comparing;
-import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.flogger.FluentLogger;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.git.LockFailureException;
-import com.google.gerrit.metrics.Description;
-import com.google.gerrit.metrics.Description.Units;
-import com.google.gerrit.metrics.Field;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.metrics.Timer1;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.ChangeUpdateExecutor;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.InsertedObject;
-import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.logging.TraceContext;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.MismatchedStateException;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * {@link BatchUpdate} implementation that supports mixed ReviewDb/NoteDb operations, depending on
- * the migration state specified in {@link NotesMigration}.
- *
- * <p>When performing change updates in a mixed ReviewDb/NoteDb environment with ReviewDb primary,
- * the order of operations is very subtle:
- *
- * <ol>
- *   <li>Stage NoteDb updates to get the new NoteDb state, but do not write to the repo.
- *   <li>Write the new state in the Change entity, and commit this to ReviewDb.
- *   <li>Update NoteDb, ignoring any write failures.
- * </ol>
- *
- * The implementation in this class is well-tested, and it is strongly recommended that you not
- * attempt to reimplement this logic. Use {@code BatchUpdate} if at all possible.
- */
-public class ReviewDbBatchUpdate extends BatchUpdate {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  public interface AssistedFactory {
-    ReviewDbBatchUpdate create(
-        ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when);
-  }
-
-  class ContextImpl implements Context {
-    @Override
-    public RepoView getRepoView() throws IOException {
-      return ReviewDbBatchUpdate.this.getRepoView();
-    }
-
-    @Override
-    public RevWalk getRevWalk() throws IOException {
-      return getRepoView().getRevWalk();
-    }
-
-    @Override
-    public Project.NameKey getProject() {
-      return project;
-    }
-
-    @Override
-    public Timestamp getWhen() {
-      return when;
-    }
-
-    @Override
-    public TimeZone getTimeZone() {
-      return tz;
-    }
-
-    @Override
-    public ReviewDb getDb() {
-      return db;
-    }
-
-    @Override
-    public CurrentUser getUser() {
-      return user;
-    }
-
-    @Override
-    public Order getOrder() {
-      return order;
-    }
-  }
-
-  private class RepoContextImpl extends ContextImpl implements RepoContext {
-    @Override
-    public ObjectInserter getInserter() throws IOException {
-      return getRepoView().getInserterWrapper();
-    }
-
-    @Override
-    public void addRefUpdate(ReceiveCommand cmd) throws IOException {
-      initRepository();
-      repoView.getCommands().add(cmd);
-    }
-  }
-
-  private class ChangeContextImpl extends ContextImpl implements ChangeContext {
-    private final ChangeNotes notes;
-    private final Map<PatchSet.Id, ChangeUpdate> updates;
-    private final ReviewDbWrapper dbWrapper;
-    private final Repository threadLocalRepo;
-    private final RevWalk threadLocalRevWalk;
-
-    private boolean deleted;
-    private boolean bumpLastUpdatedOn = true;
-
-    protected ChangeContextImpl(
-        ChangeNotes notes, ReviewDbWrapper dbWrapper, Repository repo, RevWalk rw) {
-      this.notes = requireNonNull(notes);
-      this.dbWrapper = dbWrapper;
-      this.threadLocalRepo = repo;
-      this.threadLocalRevWalk = rw;
-      updates = new TreeMap<>(comparing(PatchSet.Id::get));
-    }
-
-    @Override
-    public ReviewDb getDb() {
-      requireNonNull(dbWrapper);
-      return dbWrapper;
-    }
-
-    @Override
-    public RevWalk getRevWalk() {
-      return threadLocalRevWalk;
-    }
-
-    @Override
-    public ChangeUpdate getUpdate(PatchSet.Id psId) {
-      ChangeUpdate u = updates.get(psId);
-      if (u == null) {
-        u = changeUpdateFactory.create(notes, user, when);
-        if (newChanges.containsKey(notes.getChangeId())) {
-          u.setAllowWriteToNewRef(true);
-        }
-        u.setPatchSetId(psId);
-        updates.put(psId, u);
-      }
-      return u;
-    }
-
-    @Override
-    public ChangeNotes getNotes() {
-      return notes;
-    }
-
-    @Override
-    public void dontBumpLastUpdatedOn() {
-      bumpLastUpdatedOn = false;
-    }
-
-    @Override
-    public void deleteChange() {
-      deleted = true;
-    }
-  }
-
-  @Singleton
-  private static class Metrics {
-    final Timer1<Boolean> executeChangeOpsLatency;
-
-    @Inject
-    Metrics(MetricMaker metricMaker) {
-      executeChangeOpsLatency =
-          metricMaker.newTimer(
-              "batch_update/execute_change_ops",
-              new Description("BatchUpdate change update latency, excluding reindexing")
-                  .setCumulative()
-                  .setUnit(Units.MILLISECONDS),
-              Field.ofBoolean("success"));
-    }
-  }
-
-  static void execute(
-      ImmutableList<ReviewDbBatchUpdate> updates, BatchUpdateListener listener, boolean dryrun)
-      throws UpdateException, RestApiException {
-    if (updates.isEmpty()) {
-      return;
-    }
-    try {
-      Order order = getOrder(updates, listener);
-      boolean updateChangesInParallel = getUpdateChangesInParallel(updates);
-      switch (order) {
-        case REPO_BEFORE_DB:
-          for (ReviewDbBatchUpdate u : updates) {
-            u.executeUpdateRepo();
-          }
-          listener.afterUpdateRepos();
-          for (ReviewDbBatchUpdate u : updates) {
-            u.executeRefUpdates(dryrun);
-          }
-          listener.afterUpdateRefs();
-          for (ReviewDbBatchUpdate u : updates) {
-            u.reindexChanges(u.executeChangeOps(updateChangesInParallel, dryrun));
-          }
-          listener.afterUpdateChanges();
-          break;
-        case DB_BEFORE_REPO:
-          for (ReviewDbBatchUpdate u : updates) {
-            u.reindexChanges(u.executeChangeOps(updateChangesInParallel, dryrun));
-          }
-          for (ReviewDbBatchUpdate u : updates) {
-            u.executeUpdateRepo();
-          }
-          for (ReviewDbBatchUpdate u : updates) {
-            u.executeRefUpdates(dryrun);
-          }
-          break;
-        default:
-          throw new IllegalStateException("invalid execution order: " + order);
-      }
-
-      ChangeIndexer.allAsList(
-              updates.stream().flatMap(u -> u.indexFutures.stream()).collect(toList()))
-          .get();
-
-      // Fire ref update events only after all mutations are finished, since callers may assume a
-      // patch set ref being created means the change was created, or a branch advancing meaning
-      // some changes were closed.
-      updates
-          .stream()
-          .filter(u -> u.batchRefUpdate != null)
-          .forEach(
-              u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
-
-      if (!dryrun) {
-        for (ReviewDbBatchUpdate u : updates) {
-          u.executePostOps();
-        }
-      }
-    } catch (Exception e) {
-      wrapAndThrowException(e);
-    }
-  }
-
-  private final AllUsersName allUsers;
-  private final ChangeIndexer indexer;
-  private final ChangeNotes.Factory changeNotesFactory;
-  private final ChangeUpdate.Factory changeUpdateFactory;
-  private final GitReferenceUpdated gitRefUpdated;
-  private final ListeningExecutorService changeUpdateExector;
-  private final Metrics metrics;
-  private final NoteDbUpdateManager.Factory updateManagerFactory;
-  private final NotesMigration notesMigration;
-  private final ReviewDb db;
-  private final SchemaFactory<ReviewDb> schemaFactory;
-  private final long skewMs;
-
-  @SuppressWarnings("deprecation")
-  private final List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
-      new ArrayList<>();
-
-  @Inject
-  ReviewDbBatchUpdate(
-      @GerritServerConfig Config cfg,
-      AllUsersName allUsers,
-      ChangeIndexer indexer,
-      ChangeNotes.Factory changeNotesFactory,
-      @ChangeUpdateExecutor ListeningExecutorService changeUpdateExector,
-      ChangeUpdate.Factory changeUpdateFactory,
-      @GerritPersonIdent PersonIdent serverIdent,
-      GitReferenceUpdated gitRefUpdated,
-      GitRepositoryManager repoManager,
-      Metrics metrics,
-      NoteDbUpdateManager.Factory updateManagerFactory,
-      NotesMigration notesMigration,
-      SchemaFactory<ReviewDb> schemaFactory,
-      @Assisted ReviewDb db,
-      @Assisted Project.NameKey project,
-      @Assisted CurrentUser user,
-      @Assisted Timestamp when) {
-    super(repoManager, serverIdent, project, user, when);
-    this.allUsers = allUsers;
-    this.changeNotesFactory = changeNotesFactory;
-    this.changeUpdateExector = changeUpdateExector;
-    this.changeUpdateFactory = changeUpdateFactory;
-    this.gitRefUpdated = gitRefUpdated;
-    this.indexer = indexer;
-    this.metrics = metrics;
-    this.notesMigration = notesMigration;
-    this.schemaFactory = schemaFactory;
-    this.updateManagerFactory = updateManagerFactory;
-    this.db = db;
-    skewMs = NoteDbChangeState.getReadOnlySkew(cfg);
-  }
-
-  @Override
-  public void execute(BatchUpdateListener listener) throws UpdateException, RestApiException {
-    execute(ImmutableList.of(this), listener, false);
-  }
-
-  @Override
-  protected Context newContext() {
-    return new ContextImpl();
-  }
-
-  private void executeUpdateRepo() throws UpdateException, RestApiException {
-    try {
-      logDebug("Executing updateRepo on %d ops", ops.size());
-      RepoContextImpl ctx = new RepoContextImpl();
-      for (BatchUpdateOp op : ops.values()) {
-        op.updateRepo(ctx);
-      }
-
-      logDebug("Executing updateRepo on %d RepoOnlyOps", repoOnlyOps.size());
-      for (RepoOnlyOp op : repoOnlyOps) {
-        op.updateRepo(ctx);
-      }
-
-      if (onSubmitValidators != null && !getRefUpdates().isEmpty()) {
-        // Validation of refs has to take place here and not at the beginning of executeRefUpdates.
-        // Otherwise, failing validation in a second BatchUpdate object will happen *after* the
-        // first update's executeRefUpdates has finished, hence after first repo's refs have been
-        // updated, which is too late.
-        onSubmitValidators.validate(
-            project, ctx.getRevWalk().getObjectReader(), repoView.getCommands());
-      }
-
-      if (repoView != null) {
-        logDebug("Flushing inserter");
-        repoView.getInserter().flush();
-      } else {
-        logDebug("No objects to flush");
-      }
-    } catch (Exception e) {
-      Throwables.throwIfInstanceOf(e, RestApiException.class);
-      throw new UpdateException(e);
-    }
-  }
-
-  private void executeRefUpdates(boolean dryrun) throws IOException, RestApiException {
-    if (getRefUpdates().isEmpty()) {
-      logDebug("No ref updates to execute");
-      return;
-    }
-    // May not be opened if the caller added ref updates but no new objects.
-    // TODO(dborowitz): Really?
-    initRepository();
-    batchRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
-    batchRefUpdate.setPushCertificate(pushCert);
-    batchRefUpdate.setRefLogMessage(refLogMessage, true);
-    batchRefUpdate.setAllowNonFastForwards(true);
-    repoView.getCommands().addTo(batchRefUpdate);
-    if (user.isIdentifiedUser()) {
-      batchRefUpdate.setRefLogIdent(user.asIdentifiedUser().newRefLogIdent(when, tz));
-    }
-    logDebug("Executing batch of %d ref updates", batchRefUpdate.getCommands().size());
-    if (dryrun) {
-      return;
-    }
-
-    // Force BatchRefUpdate to read newly referenced objects using a new RevWalk, rather than one
-    // that might have access to unflushed objects.
-    try (RevWalk updateRw = new RevWalk(repoView.getRepository())) {
-      batchRefUpdate.execute(updateRw, NullProgressMonitor.INSTANCE);
-    }
-    boolean ok = true;
-    for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
-      if (cmd.getResult() != ReceiveCommand.Result.OK) {
-        ok = false;
-        break;
-      }
-    }
-    if (!ok) {
-      throw new RestApiException("BatchRefUpdate failed: " + batchRefUpdate);
-    }
-  }
-
-  private List<ChangeTask> executeChangeOps(boolean parallel, boolean dryrun)
-      throws UpdateException, RestApiException {
-    List<ChangeTask> tasks;
-    boolean success = false;
-    Stopwatch sw = Stopwatch.createStarted();
-    try {
-      logDebug("Executing change ops (parallel? %s)", parallel);
-      ListeningExecutorService executor =
-          parallel ? changeUpdateExector : MoreExecutors.newDirectExecutorService();
-
-      tasks = new ArrayList<>(ops.keySet().size());
-      try {
-        if (notesMigration.commitChangeWrites() && repoView != null) {
-          // A NoteDb change may have been rebuilt since the repo was originally
-          // opened, so make sure we see that.
-          logDebug("Preemptively scanning for repo changes");
-          repoView.getRepository().scanForRepoChanges();
-        }
-        if (!ops.isEmpty() && notesMigration.failChangeWrites()) {
-          // Fail fast before attempting any writes if changes are read-only, as
-          // this is a programmer error.
-          logDebug("Failing early due to read-only Changes table");
-          throw new OrmException(NoteDbUpdateManager.CHANGES_READ_ONLY);
-        }
-        List<ListenableFuture<?>> futures = new ArrayList<>(ops.keySet().size());
-        for (Map.Entry<Change.Id, Collection<BatchUpdateOp>> e : ops.asMap().entrySet()) {
-          ChangeTask task =
-              new ChangeTask(e.getKey(), e.getValue(), Thread.currentThread(), dryrun);
-          tasks.add(task);
-          if (!parallel) {
-            logDebug("Direct execution of task for ops: %s", ops);
-          }
-          futures.add(executor.submit(task));
-        }
-        if (parallel) {
-          logDebug(
-              "Waiting on futures for %d ops spanning %d changes", ops.size(), ops.keySet().size());
-        }
-        Futures.allAsList(futures).get();
-
-        if (notesMigration.commitChangeWrites()) {
-          if (!dryrun) {
-            executeNoteDbUpdates(tasks);
-          }
-        }
-        success = true;
-      } catch (ExecutionException | InterruptedException e) {
-        Throwables.throwIfInstanceOf(e.getCause(), UpdateException.class);
-        Throwables.throwIfInstanceOf(e.getCause(), RestApiException.class);
-        throw new UpdateException(e);
-      } catch (OrmException | IOException e) {
-        throw new UpdateException(e);
-      }
-    } finally {
-      metrics.executeChangeOpsLatency.record(success, sw.elapsed(NANOSECONDS), NANOSECONDS);
-    }
-    return tasks;
-  }
-
-  private void reindexChanges(List<ChangeTask> tasks) {
-    // Reindex changes.
-    for (ChangeTask task : tasks) {
-      if (task.deleted) {
-        indexFutures.add(indexer.deleteAsync(task.id));
-      } else if (task.dirty) {
-        indexFutures.add(indexer.indexAsync(project, task.id));
-      }
-    }
-  }
-
-  private void executeNoteDbUpdates(List<ChangeTask> tasks)
-      throws ResourceConflictException, IOException {
-    // Aggregate together all NoteDb ref updates from the ops we executed,
-    // possibly in parallel. Each task had its own NoteDbUpdateManager instance
-    // with its own thread-local copy of the repo(s), but each of those was just
-    // used for staging updates and was never executed.
-    //
-    // Use a new BatchRefUpdate as the original batchRefUpdate field is intended
-    // for use only by the updateRepo phase.
-    //
-    // See the comments in NoteDbUpdateManager#execute() for why we execute the
-    // updates on the change repo first.
-    logDebug("Executing NoteDb updates for %d changes", tasks.size());
-    try {
-      initRepository();
-      BatchRefUpdate changeRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
-      boolean hasAllUsersCommands = false;
-      try (ObjectInserter ins = repoView.getRepository().newObjectInserter()) {
-        int objs = 0;
-        for (ChangeTask task : tasks) {
-          if (task.noteDbResult == null) {
-            logDebug("No-op update to %s", task.id);
-            continue;
-          }
-          for (ReceiveCommand cmd : task.noteDbResult.changeCommands()) {
-            changeRefUpdate.addCommand(cmd);
-          }
-          for (InsertedObject obj : task.noteDbResult.changeObjects()) {
-            objs++;
-            ins.insert(obj.type(), obj.data().toByteArray());
-          }
-          hasAllUsersCommands |= !task.noteDbResult.allUsersCommands().isEmpty();
-        }
-        logDebug(
-            "Collected %d objects and %d ref updates to change repo",
-            objs, changeRefUpdate.getCommands().size());
-        executeNoteDbUpdate(getRevWalk(), ins, changeRefUpdate);
-      }
-
-      if (hasAllUsersCommands) {
-        try (Repository allUsersRepo = repoManager.openRepository(allUsers);
-            RevWalk allUsersRw = new RevWalk(allUsersRepo);
-            ObjectInserter allUsersIns = allUsersRepo.newObjectInserter()) {
-          int objs = 0;
-          BatchRefUpdate allUsersRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
-          for (ChangeTask task : tasks) {
-            for (ReceiveCommand cmd : task.noteDbResult.allUsersCommands()) {
-              allUsersRefUpdate.addCommand(cmd);
-            }
-            for (InsertedObject obj : task.noteDbResult.allUsersObjects()) {
-              allUsersIns.insert(obj.type(), obj.data().toByteArray());
-            }
-          }
-          logDebug(
-              "Collected %d objects and %d ref updates to All-Users",
-              objs, allUsersRefUpdate.getCommands().size());
-          executeNoteDbUpdate(allUsersRw, allUsersIns, allUsersRefUpdate);
-        }
-      } else {
-        logDebug("No All-Users updates");
-      }
-    } catch (IOException e) {
-      if (tasks.stream().allMatch(t -> t.storage == PrimaryStorage.REVIEW_DB)) {
-        // Ignore all errors trying to update NoteDb at this point. We've already written the
-        // NoteDbChangeStates to ReviewDb, which means if any state is out of date it will be
-        // rebuilt the next time it is needed.
-        //
-        // Always log even without RequestId.
-        logger.atFine().withCause(e).log("Ignoring NoteDb update error after ReviewDb write");
-
-        // Otherwise, we can't prove it's safe to ignore the error, either because some change had
-        // NOTE_DB primary, or a task failed before determining the primary storage.
-      } else if (e instanceof LockFailureException) {
-        // LOCK_FAILURE is a special case indicating there was a conflicting write to a meta ref,
-        // although it happened too late for us to produce anything but a generic error message.
-        throw new ResourceConflictException("Updating change failed due to conflicting write", e);
-      }
-      throw e;
-    }
-  }
-
-  private void executeNoteDbUpdate(RevWalk rw, ObjectInserter ins, BatchRefUpdate bru)
-      throws IOException {
-    if (bru.getCommands().isEmpty()) {
-      logDebug("No commands, skipping flush and ref update");
-      return;
-    }
-    ins.flush();
-    bru.setAllowNonFastForwards(true);
-    bru.execute(rw, NullProgressMonitor.INSTANCE);
-    for (ReceiveCommand cmd : bru.getCommands()) {
-      // TODO(dborowitz): LOCK_FAILURE for NoteDb primary should be retried.
-      if (cmd.getResult() != ReceiveCommand.Result.OK) {
-        throw new IOException("Update failed: " + bru);
-      }
-    }
-  }
-
-  private class ChangeTask implements Callable<Void> {
-    final Change.Id id;
-    private final Collection<BatchUpdateOp> changeOps;
-    private final Thread mainThread;
-    private final boolean dryrun;
-
-    PrimaryStorage storage;
-    NoteDbUpdateManager.StagedResult noteDbResult;
-    boolean dirty;
-    boolean deleted;
-
-    private ChangeTask(
-        Change.Id id, Collection<BatchUpdateOp> changeOps, Thread mainThread, boolean dryrun) {
-      this.id = id;
-      this.changeOps = changeOps;
-      this.mainThread = mainThread;
-      this.dryrun = dryrun;
-    }
-
-    @Override
-    public Void call() throws Exception {
-      try (TraceContext traceContext =
-          TraceContext.open()
-              .addTag("TASK_ID", id.toString() + "-" + Thread.currentThread().getId())) {
-        if (Thread.currentThread() == mainThread) {
-          initRepository();
-          Repository repo = repoView.getRepository();
-          try (RevWalk rw = new RevWalk(repo)) {
-            call(ReviewDbBatchUpdate.this.db, repo, rw);
-          }
-        } else {
-          // Possible optimization: allow Ops to declare whether they need to
-          // access the repo from updateChange, and don't open in this thread
-          // unless we need it. However, as of this writing the only operations
-          // that are executed in parallel are during ReceiveCommits, and they
-          // all need the repo open anyway. (The non-parallel case above does not
-          // reopen the repo.)
-          try (ReviewDb threadLocalDb = schemaFactory.open();
-              Repository repo = repoManager.openRepository(project);
-              RevWalk rw = new RevWalk(repo)) {
-            call(threadLocalDb, repo, rw);
-          }
-        }
-        return null;
-      }
-    }
-
-    private void call(ReviewDb db, Repository repo, RevWalk rw) throws Exception {
-      @SuppressWarnings("resource") // Not always opened.
-      NoteDbUpdateManager updateManager = null;
-      try {
-        db.changes().beginTransaction(id);
-        try {
-          ChangeContextImpl ctx = newChangeContext(db, repo, rw, id);
-          NoteDbChangeState oldState = NoteDbChangeState.parse(ctx.getChange());
-          NoteDbChangeState.checkNotReadOnly(oldState, skewMs);
-
-          storage = PrimaryStorage.of(oldState);
-          if (storage == PrimaryStorage.NOTE_DB && !notesMigration.readChanges()) {
-            throw new OrmException("must have NoteDb enabled to update change " + id);
-          }
-
-          // Call updateChange on each op.
-          logDebug("Calling updateChange on %s ops", changeOps.size());
-          for (BatchUpdateOp op : changeOps) {
-            dirty |= op.updateChange(ctx);
-          }
-          if (!dirty) {
-            logDebug("No ops reported dirty, short-circuiting");
-            return;
-          }
-          deleted = ctx.deleted;
-          if (deleted) {
-            logDebug("Change was deleted");
-          }
-
-          // Stage the NoteDb update and store its state in the Change.
-          if (notesMigration.commitChangeWrites()) {
-            updateManager = stageNoteDbUpdate(ctx, deleted);
-          }
-
-          if (storage == PrimaryStorage.REVIEW_DB) {
-            // If primary storage of this change is in ReviewDb, bump
-            // lastUpdatedOn or rowVersion and commit. Otherwise, don't waste
-            // time updating ReviewDb at all.
-            Iterable<Change> cs = changesToUpdate(ctx);
-            if (isNewChange(id)) {
-              // Insert rather than upsert in case of a race on change IDs.
-              logDebug("Inserting change");
-              db.changes().insert(cs);
-            } else if (deleted) {
-              logDebug("Deleting change");
-              db.changes().delete(cs);
-            } else {
-              logDebug("Updating change");
-              db.changes().update(cs);
-            }
-            if (!dryrun) {
-              db.commit();
-            }
-          } else {
-            logDebug("Skipping ReviewDb write since primary storage is %s", storage);
-          }
-        } finally {
-          db.rollback();
-        }
-
-        // Do not execute the NoteDbUpdateManager, as we don't want too much
-        // contention on the underlying repo, and we would rather use a single
-        // ObjectInserter/BatchRefUpdate later.
-        //
-        // TODO(dborowitz): May or may not be worth trying to batch together
-        // flushed inserters as well.
-        if (storage == PrimaryStorage.NOTE_DB) {
-          // Should have failed above if NoteDb is disabled.
-          checkState(notesMigration.commitChangeWrites());
-          noteDbResult = updateManager.stage().get(id);
-        } else if (notesMigration.commitChangeWrites()) {
-          try {
-            noteDbResult = updateManager.stage().get(id);
-          } catch (IOException ex) {
-            // Ignore all errors trying to update NoteDb at this point. We've
-            // already written the NoteDbChangeState to ReviewDb, which means
-            // if the state is out of date it will be rebuilt the next time it
-            // is needed.
-            logger.atFine().withCause(ex).log("Ignoring NoteDb update error after ReviewDb write");
-          }
-        }
-      } catch (Exception e) {
-        logDebug("Error updating change (should be rethrown)", e);
-        Throwables.propagateIfPossible(e, RestApiException.class);
-        throw new UpdateException(e);
-      } finally {
-        if (updateManager != null) {
-          updateManager.close();
-        }
-      }
-    }
-
-    private ChangeContextImpl newChangeContext(
-        ReviewDb db, Repository repo, RevWalk rw, Change.Id id) throws OrmException {
-      Change c = newChanges.get(id);
-      boolean isNew = c != null;
-      if (isNew) {
-        // New change: populate noteDbState.
-        checkState(c.getNoteDbState() == null, "noteDbState should not be filled in by callers");
-        if (notesMigration.changePrimaryStorage() == PrimaryStorage.NOTE_DB) {
-          c.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
-        }
-      } else {
-        // Existing change.
-        c = ChangeNotes.readOneReviewDbChange(db, id);
-        if (c == null) {
-          // Not in ReviewDb, but new changes are created with default primary
-          // storage as NOTE_DB, so we can assume that a missing change is
-          // NoteDb primary. Pass a synthetic change into ChangeNotes.Factory,
-          // which lets ChangeNotes take care of the existence check.
-          //
-          // TODO(dborowitz): This assumption is potentially risky, because
-          // it means once we turn this option on and start creating changes
-          // without writing anything to ReviewDb, we can't turn this option
-          // back off without making those changes inaccessible. The problem
-          // is we have no way of distinguishing a change that only exists in
-          // NoteDb because it only ever existed in NoteDb, from a change that
-          // only exists in NoteDb because it used to exist in ReviewDb and
-          // deleting from ReviewDb succeeded but deleting from NoteDb failed.
-          //
-          // TODO(dborowitz): We actually still have that problem anyway. Maybe
-          // we need a cutoff timestamp? Or maybe we need to start leaving
-          // tombstones in ReviewDb?
-          c = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
-        }
-        NoteDbChangeState.checkNotReadOnly(c, skewMs);
-      }
-      ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
-      return new ChangeContextImpl(notes, new BatchUpdateReviewDb(db), repo, rw);
-    }
-
-    private NoteDbUpdateManager stageNoteDbUpdate(ChangeContextImpl ctx, boolean deleted)
-        throws OrmException, IOException {
-      logDebug("Staging NoteDb update");
-      NoteDbUpdateManager updateManager =
-          updateManagerFactory
-              .create(ctx.getProject())
-              .setChangeRepo(
-                  ctx.threadLocalRepo,
-                  ctx.threadLocalRevWalk,
-                  null,
-                  new ChainedReceiveCommands(ctx.threadLocalRepo));
-      if (ctx.getUser().isIdentifiedUser()) {
-        updateManager.setRefLogIdent(
-            ctx.getUser().asIdentifiedUser().newRefLogIdent(ctx.getWhen(), tz));
-      }
-      for (ChangeUpdate u : ctx.updates.values()) {
-        updateManager.add(u);
-      }
-
-      Change c = ctx.getChange();
-      if (deleted) {
-        updateManager.deleteChange(c.getId());
-      }
-      try {
-        updateManager.stageAndApplyDelta(c);
-      } catch (MismatchedStateException ex) {
-        // Refused to apply update because NoteDb was out of sync, which can
-        // only happen if ReviewDb is the primary storage for this change.
-        //
-        // Go ahead with this ReviewDb update; it's still out of sync, but this
-        // is no worse than before, and it will eventually get rebuilt.
-        logDebug("Ignoring MismatchedStateException while staging");
-      }
-
-      return updateManager;
-    }
-
-    private boolean isNewChange(Change.Id id) {
-      return newChanges.containsKey(id);
-    }
-  }
-
-  private static Iterable<Change> changesToUpdate(ChangeContextImpl ctx) {
-    Change c = ctx.getChange();
-    if (ctx.bumpLastUpdatedOn && c.getLastUpdatedOn().before(ctx.getWhen())) {
-      c.setLastUpdatedOn(ctx.getWhen());
-    }
-    return Collections.singleton(c);
-  }
-
-  private void executePostOps() throws Exception {
-    ContextImpl ctx = new ContextImpl();
-    for (BatchUpdateOp op : ops.values()) {
-      op.postUpdate(ctx);
-    }
-
-    for (RepoOnlyOp op : repoOnlyOps) {
-      op.postUpdate(ctx);
-    }
-  }
-}
diff --git a/java/com/google/gerrit/sshd/commands/AdminQueryShell.java b/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
deleted file mode 100644
index c520e79..0000000
--- a/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.sshd.commands;
-
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.server.permissions.GlobalPermission;
-import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.sshd.AdminHighPriorityCommand;
-import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshCommand;
-import com.google.inject.Inject;
-import org.kohsuke.args4j.Option;
-
-/** Opens a query processor. */
-@AdminHighPriorityCommand
-@RequiresCapability(GlobalCapability.ACCESS_DATABASE)
-@CommandMetaData(name = "gsql", description = "Administrative interface to active database")
-final class AdminQueryShell extends SshCommand {
-  @Inject private PermissionBackend permissionBackend;
-  @Inject private QueryShell.Factory factory;
-
-  @Option(name = "--format", usage = "Set output format")
-  private QueryShell.OutputFormat format = QueryShell.OutputFormat.PRETTY;
-
-  @Option(name = "-c", metaVar = "SQL QUERY", usage = "Query to execute")
-  private String query;
-
-  @Override
-  protected void run() throws Failure {
-    try {
-      permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
-    } catch (AuthException err) {
-      throw die(err.getMessage());
-    } catch (PermissionBackendException e) {
-      throw new Failure(1, "unavailable", e);
-    }
-
-    QueryShell shell = factory.create(in, out);
-    shell.setOutputFormat(format);
-    if (query != null) {
-      shell.execute(query);
-    } else {
-      shell.run();
-    }
-  }
-}
diff --git a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
index 1c857e4..87b6f02 100644
--- a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
+++ b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -90,7 +90,6 @@
     command(gerrit, CreateGroupCommand.class);
     command(gerrit, CreateProjectCommand.class);
     command(gerrit, SetHeadCommand.class);
-    command(gerrit, AdminQueryShell.class);
 
     if (slaveMode) {
       command("git-receive-pack").to(ReceiveSlaveMode.class);
diff --git a/java/com/google/gerrit/testing/DisabledReviewDb.java b/java/com/google/gerrit/testing/DisabledReviewDb.java
index d06beb9..e90474b 100644
--- a/java/com/google/gerrit/testing/DisabledReviewDb.java
+++ b/java/com/google/gerrit/testing/DisabledReviewDb.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.reviewdb.server.PatchSetAccess;
 import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
 import com.google.gwtorm.server.Access;
 import com.google.gwtorm.server.StatementExecutor;
 
@@ -65,11 +64,6 @@
   }
 
   @Override
-  public SchemaVersionAccess schemaVersion() {
-    throw new Disabled();
-  }
-
-  @Override
   public ChangeAccess changes() {
     throw new Disabled();
   }
diff --git a/java/com/google/gerrit/testing/InMemoryDatabase.java b/java/com/google/gerrit/testing/InMemoryDatabase.java
index b489652..1a4b84e 100644
--- a/java/com/google/gerrit/testing/InMemoryDatabase.java
+++ b/java/com/google/gerrit/testing/InMemoryDatabase.java
@@ -14,60 +14,34 @@
 
 package com.google.gerrit.testing;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.pgm.init.index.elasticsearch.ElasticIndexModuleOnInit;
 import com.google.gerrit.pgm.init.index.lucene.LuceneIndexModuleOnInit;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
-import com.google.gerrit.server.schema.ReviewDbSchemaVersion;
-import com.google.gwtorm.jdbc.Database;
-import com.google.gwtorm.jdbc.SimpleDataSource;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
 import java.io.IOException;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Properties;
-import javax.sql.DataSource;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
 
-/**
- * An in-memory test instance of {@link ReviewDb} database.
- *
- * <p>Test classes should create one instance of this class for each unique test database they want
- * to use. When the tests needing this instance are complete, ensure that {@link
- * #drop(InMemoryDatabase)} is called to free the resources so the JVM running the unit tests
- * doesn't run out of heap space.
- */
+/** Husk of an in-memory ReviewDb implementation. */
+// TODO(dborowitz): Inline callers to get their own darn schemaCreator.
 public class InMemoryDatabase implements SchemaFactory<ReviewDb> {
-  public static InMemoryDatabase newDatabase(LifecycleManager lifecycle) {
-    Injector injector = Guice.createInjector(new InMemoryModule());
-    lifecycle.add(injector);
-    return injector.getInstance(InMemoryDatabase.class);
-  }
-
-  /** Drop the database from memory; does nothing if the instance was null. */
-  public static void drop(InMemoryDatabase db) {
-    if (db != null) {
-      db.dbInstance.drop();
-    }
-  }
-
-  private final ReviewDbSchemaCreator schemaCreator;
-  private final Instance dbInstance;
-
-  private boolean created;
+  private final GitRepositoryManager repoManager;
+  private final AllProjectsName allProjectsName;
+  private final SchemaCreator schemaCreator;
+  private final SchemaFactory<ReviewDb> schemaFactory;
 
   @Inject
-  InMemoryDatabase(Injector injector) throws OrmException {
+  InMemoryDatabase(Injector injector) {
     Injector childInjector =
         injector.createChildInjector(
             new AbstractModule() {
@@ -85,110 +59,40 @@
                 }
               }
             });
-    this.schemaCreator = childInjector.getInstance(ReviewDbSchemaCreator.class);
-    Instance dbInstanceFromInjector = childInjector.getInstance(Instance.class);
-    if (dbInstanceFromInjector != null) {
-      this.dbInstance = dbInstanceFromInjector;
-      this.created = true;
-    } else {
-      this.dbInstance = new Instance();
-    }
+    this.repoManager = childInjector.getInstance(GitRepositoryManager.class);
+    this.allProjectsName = childInjector.getInstance(AllProjectsName.class);
+    this.schemaCreator = childInjector.getInstance(SchemaCreator.class);
+    this.schemaFactory =
+        childInjector.getInstance(Key.get(new TypeLiteral<SchemaFactory<ReviewDb>>() {}));
   }
 
-  InMemoryDatabase(ReviewDbSchemaCreator schemaCreator) throws OrmException {
+  InMemoryDatabase(
+      GitRepositoryManager repoManager,
+      AllProjectsName allProjectsName,
+      SchemaCreator schemaCreator,
+      SchemaFactory<ReviewDb> schemaFactory) {
+    this.repoManager = repoManager;
+    this.allProjectsName = allProjectsName;
     this.schemaCreator = schemaCreator;
-    this.dbInstance = new Instance();
-  }
-
-  public Instance getDbInstance() {
-    return dbInstance;
-  }
-
-  public Database<ReviewDb> getDatabase() {
-    return dbInstance.database;
+    this.schemaFactory = schemaFactory;
   }
 
   @Override
   public ReviewDb open() throws OrmException {
-    return getDatabase().open();
+    return schemaFactory.open();
   }
 
   /** Ensure the database schema has been created and initialized. */
   public InMemoryDatabase create() throws OrmException {
-    if (!created) {
-      created = true;
-      try (ReviewDb c = open()) {
-        schemaCreator.create(c);
-      } catch (IOException | ConfigInvalidException e) {
-        throw new OrmException("Cannot create in-memory database", e);
+    try {
+      try {
+        repoManager.openRepository(allProjectsName).close();
+      } catch (RepositoryNotFoundException e) {
+        schemaCreator.create();
       }
+    } catch (IOException | ConfigInvalidException e) {
+      throw new OrmException("Cannot create in-memory database", e);
     }
     return this;
   }
-
-  public CurrentSchemaVersion getSchemaVersion() throws OrmException {
-    try (ReviewDb c = open()) {
-      return c.schemaVersion().get(new CurrentSchemaVersion.Key());
-    }
-  }
-
-  public void assertSchemaVersion() throws OrmException {
-    assertThat(getSchemaVersion().versionNbr).isEqualTo(ReviewDbSchemaVersion.getBinaryVersion());
-  }
-
-  public static class Instance {
-    private static int dbCnt;
-
-    private Connection openHandle;
-    private Database<ReviewDb> database;
-    private boolean keepOpen;
-
-    private static synchronized DataSource newDataSource() throws SQLException {
-      final Properties p = new Properties();
-      p.setProperty("driver", org.h2.Driver.class.getName());
-      p.setProperty("url", "jdbc:h2:mem:Test_" + (++dbCnt));
-      return new SimpleDataSource(p);
-    }
-
-    private Instance() throws OrmException {
-      try {
-        DataSource dataSource = newDataSource();
-
-        // Open one connection. This will peg the database into memory
-        // until someone calls drop on us, allowing subsequent connections
-        // opened against the same URL to go to the same set of tables.
-        //
-        openHandle = dataSource.getConnection();
-
-        // Build the access layer around the connection factory.
-        //
-        database = new Database<>(dataSource, ReviewDb.class);
-
-      } catch (SQLException e) {
-        throw new OrmException(e);
-      }
-    }
-
-    public void setKeepOpen(boolean keepOpen) {
-      this.keepOpen = keepOpen;
-    }
-
-    /** Drop this database from memory so it no longer exists. */
-    public void drop() {
-      if (keepOpen) {
-        return;
-      }
-
-      if (openHandle != null) {
-        try {
-          openHandle.close();
-        } catch (SQLException e) {
-          System.err.println("WARNING: Cannot close database connection");
-          e.printStackTrace(System.err);
-        }
-        openHandle = null;
-        database = null;
-      }
-    }
-  }
 }
diff --git a/java/com/google/gerrit/testing/InMemoryH2Type.java b/java/com/google/gerrit/testing/InMemoryH2Type.java
deleted file mode 100644
index ae3bf36..0000000
--- a/java/com/google/gerrit/testing/InMemoryH2Type.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.testing;
-
-import com.google.gerrit.server.schema.BaseDataSourceType;
-
-public class InMemoryH2Type extends BaseDataSourceType {
-
-  protected InMemoryH2Type() {
-    super(null);
-  }
-
-  @Override
-  public String getUrl() {
-    // not used
-    throw new UnsupportedOperationException();
-  }
-}
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index fc17816..682e8c2 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -70,10 +70,9 @@
 import com.google.gerrit.server.index.change.AllChangesIndexer;
 import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
 import com.google.gerrit.server.index.group.AllGroupsIndexer;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
 import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
 import com.google.gerrit.server.notedb.MutableNotesMigration;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.patch.DiffExecutor;
@@ -81,16 +80,15 @@
 import com.google.gerrit.server.plugins.ServerInformationImpl;
 import com.google.gerrit.server.project.DefaultProjectNameLockManager;
 import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.DataSourceType;
 import com.google.gerrit.server.schema.InMemoryAccountPatchReviewStore;
 import com.google.gerrit.server.schema.NotesMigrationSchemaFactory;
 import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreatorImpl;
 import com.google.gerrit.server.securestore.DefaultSecureStore;
 import com.google.gerrit.server.securestore.SecureStore;
 import com.google.gerrit.server.ssh.NoSshKeyCache;
 import com.google.gerrit.server.submit.LocalMergeSuperSetComputation;
-import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
@@ -189,15 +187,7 @@
     bind(Path.class).annotatedWith(SitePath.class).toInstance(Paths.get("."));
     bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(cfg);
     bind(GerritOptions.class).toInstance(new GerritOptions(false, false, false));
-    bind(PersonIdent.class)
-        .annotatedWith(GerritPersonIdent.class)
-        .toProvider(GerritPersonIdentProvider.class);
-    bind(String.class)
-        .annotatedWith(AnonymousCowardName.class)
-        .toProvider(AnonymousCowardNameProvider.class);
 
-    bind(AllProjectsName.class).toProvider(AllProjectsNameProvider.class);
-    bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
     bind(GitRepositoryManager.class).to(InMemoryRepositoryManager.class);
     bind(InMemoryRepositoryManager.class).in(SINGLETON);
     bind(TrackingFooters.class).toProvider(TrackingFootersProvider.class).in(SINGLETON);
@@ -206,8 +196,6 @@
     bind(ListeningExecutorService.class)
         .annotatedWith(ChangeUpdateExecutor.class)
         .toInstance(MoreExecutors.newDirectExecutorService());
-    bind(DataSourceType.class).to(InMemoryH2Type.class);
-    bind(ChangeBundleReader.class).to(GwtormChangeBundleReader.class);
     bind(SecureStore.class).to(DefaultSecureStore.class);
 
     TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
@@ -215,6 +203,7 @@
     bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
     bind(Key.get(schemaFactory, ReviewDbFactory.class)).to(InMemoryDatabase.class);
 
+    install(new InMemorySchemaModule());
     install(NoSshKeyCache.module());
     install(new GerritInstanceNameModule());
     install(
@@ -274,6 +263,41 @@
     install(new DefaultProjectNameLockManager.Module());
   }
 
+  /** Copy of ReviewDbSchemaModule with a slightly different server ID provider. */
+  // TODO(dborowitz): Better code sharing.
+  private class InMemorySchemaModule extends FactoryModule {
+    @Override
+    public void configure() {
+      bind(PersonIdent.class)
+          .annotatedWith(GerritPersonIdent.class)
+          .toProvider(GerritPersonIdentProvider.class);
+
+      bind(AllProjectsName.class).toProvider(AllProjectsNameProvider.class).in(SINGLETON);
+
+      bind(AllUsersName.class).toProvider(AllUsersNameProvider.class).in(SINGLETON);
+
+      bind(String.class)
+          .annotatedWith(AnonymousCowardName.class)
+          .toProvider(AnonymousCowardNameProvider.class);
+
+      bind(GroupIndexCollection.class);
+      bind(SchemaCreator.class).to(SchemaCreatorImpl.class);
+    }
+
+    @Provides
+    @Singleton
+    @GerritServerId
+    public String createServerId() {
+      String serverId =
+          cfg.getString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY);
+      if (!Strings.isNullOrEmpty(serverId)) {
+        return serverId;
+      }
+
+      return "gerrit";
+    }
+  }
+
   @Provides
   @Singleton
   @SendEmailExecutor
@@ -290,21 +314,12 @@
 
   @Provides
   @Singleton
-  @GerritServerId
-  public String createServerId() {
-    String serverId =
-        cfg.getString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY);
-    if (!Strings.isNullOrEmpty(serverId)) {
-      return serverId;
-    }
-
-    return "gerrit";
-  }
-
-  @Provides
-  @Singleton
-  InMemoryDatabase getInMemoryDatabase(ReviewDbSchemaCreator schemaCreator) throws OrmException {
-    return new InMemoryDatabase(schemaCreator);
+  InMemoryDatabase getInMemoryDatabase(
+      GitRepositoryManager repoManager,
+      AllProjectsName allProjectsName,
+      SchemaCreator schemaCreator,
+      SchemaFactory<ReviewDb> schemaFactory) {
+    return new InMemoryDatabase(repoManager, allProjectsName, schemaCreator, schemaFactory);
   }
 
   private Module luceneIndexModule() {
diff --git a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
index 02be071..c01a44a 100644
--- a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
+++ b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AuthRequest;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gwtorm.server.SchemaFactory;
@@ -50,7 +50,7 @@
   @Inject private AccountManager accountManager;
   @Inject private IdentifiedUser.GenericFactory userFactory;
   @Inject private SchemaFactory<ReviewDb> schemaFactory;
-  @Inject private ReviewDbSchemaCreator schemaCreator;
+  @Inject private SchemaCreator schemaCreator;
   @Inject private ThreadLocalRequestContext requestContext;
   // Only for use in setting up/tearing down injector.
   @Inject private InMemoryDatabase inMemoryDatabase;
@@ -117,9 +117,7 @@
     lifecycle.add(injector);
     lifecycle.start();
 
-    try (ReviewDb underlyingDb = inMemoryDatabase.getDatabase().open()) {
-      schemaCreator.create(underlyingDb);
-    }
+    schemaCreator.create();
     db = schemaFactory.open();
 
     // The first user is added to the "Administrators" group. See AccountManager#create().
@@ -139,6 +137,5 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(inMemoryDatabase);
   }
 }
diff --git a/java/com/google/gerrit/testing/NoteDbChecker.java b/java/com/google/gerrit/testing/NoteDbChecker.java
deleted file mode 100644
index 1dc8ee2..0000000
--- a/java/com/google/gerrit/testing/NoteDbChecker.java
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.testing;
-
-import static com.google.common.truth.Truth.assertThat;
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.runner.Description;
-
-@Singleton
-public class NoteDbChecker {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private final Provider<ReviewDb> dbProvider;
-  private final GitRepositoryManager repoManager;
-  private final MutableNotesMigration notesMigration;
-  private final ChangeBundleReader bundleReader;
-  private final ChangeNotes.Factory notesFactory;
-  private final ChangeRebuilder changeRebuilder;
-  private final CommentsUtil commentsUtil;
-
-  @Inject
-  NoteDbChecker(
-      Provider<ReviewDb> dbProvider,
-      GitRepositoryManager repoManager,
-      MutableNotesMigration notesMigration,
-      ChangeBundleReader bundleReader,
-      ChangeNotes.Factory notesFactory,
-      ChangeRebuilder changeRebuilder,
-      CommentsUtil commentsUtil) {
-    this.dbProvider = dbProvider;
-    this.repoManager = repoManager;
-    this.bundleReader = bundleReader;
-    this.notesMigration = notesMigration;
-    this.notesFactory = notesFactory;
-    this.changeRebuilder = changeRebuilder;
-    this.commentsUtil = commentsUtil;
-  }
-
-  public void rebuildAndCheckAllChanges() throws Exception {
-    rebuildAndCheckChanges(
-        getUnwrappedDb().changes().all().toList().stream().map(Change::getId),
-        ImmutableListMultimap.of());
-  }
-
-  public void rebuildAndCheckChanges(Change.Id... changeIds) throws Exception {
-    rebuildAndCheckChanges(Arrays.stream(changeIds), ImmutableListMultimap.of());
-  }
-
-  private void rebuildAndCheckChanges(
-      Stream<Change.Id> changeIds, ListMultimap<Change.Id, String> expectedDiffs) throws Exception {
-    ReviewDb db = getUnwrappedDb();
-
-    List<ChangeBundle> allExpected = readExpected(changeIds);
-
-    boolean oldWrite = notesMigration.rawWriteChangesSetting();
-    boolean oldRead = notesMigration.readChanges();
-    try {
-      notesMigration.setWriteChanges(true);
-      notesMigration.setReadChanges(true);
-      List<String> msgs = new ArrayList<>();
-      for (ChangeBundle expected : allExpected) {
-        Change c = expected.getChange();
-        try {
-          changeRebuilder.rebuild(db, c.getId());
-        } catch (RepositoryNotFoundException e) {
-          msgs.add("Repository not found for change, cannot convert: " + c);
-        }
-      }
-
-      checkActual(allExpected, expectedDiffs, msgs);
-    } finally {
-      notesMigration.setReadChanges(oldRead);
-      notesMigration.setWriteChanges(oldWrite);
-    }
-  }
-
-  public void checkChanges(Change.Id... changeIds) throws Exception {
-    checkActual(
-        readExpected(Arrays.stream(changeIds)), ImmutableListMultimap.of(), new ArrayList<>());
-  }
-
-  public void rebuildAndCheckChange(Change.Id changeId, String... expectedDiff) throws Exception {
-    ImmutableListMultimap.Builder<Change.Id, String> b = ImmutableListMultimap.builder();
-    b.putAll(changeId, Arrays.asList(expectedDiff));
-    rebuildAndCheckChanges(Stream.of(changeId), b.build());
-  }
-
-  public void assertNoChangeRef(Project.NameKey project, Change.Id changeId) throws Exception {
-    try (Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef(RefNames.changeMetaRef(changeId))).isNull();
-    }
-  }
-
-  public void assertNoReviewDbChanges(Description desc) throws Exception {
-    ReviewDb db = getUnwrappedDb();
-    assertThat(db.changes().all().toList()).named("Changes in " + desc.getTestClass()).isEmpty();
-    assertThat(db.changeMessages().all().toList())
-        .named("ChangeMessages in " + desc.getTestClass())
-        .isEmpty();
-    assertThat(db.patchSets().all().toList())
-        .named("PatchSets in " + desc.getTestClass())
-        .isEmpty();
-    assertThat(db.patchSetApprovals().all().toList())
-        .named("PatchSetApprovals in " + desc.getTestClass())
-        .isEmpty();
-    assertThat(db.patchComments().all().toList())
-        .named("PatchLineComments in " + desc.getTestClass())
-        .isEmpty();
-  }
-
-  private List<ChangeBundle> readExpected(Stream<Change.Id> changeIds) throws Exception {
-    boolean old = notesMigration.readChanges();
-    try {
-      notesMigration.setReadChanges(false);
-      return changeIds
-          .sorted(comparing(IntKey::get))
-          .map(this::readBundleUnchecked)
-          .collect(toList());
-    } finally {
-      notesMigration.setReadChanges(old);
-    }
-  }
-
-  private ChangeBundle readBundleUnchecked(Change.Id id) {
-    try {
-      return bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    } catch (OrmException e) {
-      throw new OrmRuntimeException(e);
-    }
-  }
-
-  private void checkActual(
-      List<ChangeBundle> allExpected,
-      ListMultimap<Change.Id, String> expectedDiffs,
-      List<String> msgs)
-      throws Exception {
-    ReviewDb db = getUnwrappedDb();
-    boolean oldRead = notesMigration.readChanges();
-    boolean oldWrite = notesMigration.rawWriteChangesSetting();
-    try {
-      notesMigration.setWriteChanges(true);
-      notesMigration.setReadChanges(true);
-      for (ChangeBundle expected : allExpected) {
-        Change c = expected.getChange();
-        ChangeBundle actual;
-        try {
-          actual =
-              ChangeBundle.fromNotes(
-                  commentsUtil, notesFactory.create(db, c.getProject(), c.getId()));
-        } catch (Throwable t) {
-          String msg = "Error converting change: " + c;
-          msgs.add(msg);
-          logger.atSevere().withCause(t).log(msg);
-          continue;
-        }
-        List<String> diff = expected.differencesFrom(actual);
-        List<String> expectedDiff = expectedDiffs.get(c.getId());
-        if (!diff.equals(expectedDiff)) {
-          msgs.add("Differences between ReviewDb and NoteDb for " + c + ":");
-          msgs.addAll(diff);
-          if (!expectedDiff.isEmpty()) {
-            msgs.add("Expected differences:");
-            msgs.addAll(expectedDiff);
-          }
-          msgs.add("");
-        } else {
-          System.err.println("NoteDb conversion of change " + c.getId() + " successful");
-        }
-      }
-    } finally {
-      notesMigration.setReadChanges(oldRead);
-      notesMigration.setWriteChanges(oldWrite);
-    }
-    if (!msgs.isEmpty()) {
-      throw new AssertionError(Joiner.on('\n').join(msgs));
-    }
-  }
-
-  private ReviewDb getUnwrappedDb() {
-    ReviewDb db = dbProvider.get();
-    return ReviewDbUtil.unwrapDb(db);
-  }
-}
diff --git a/java/com/google/gerrit/testing/NoteDbMode.java b/java/com/google/gerrit/testing/NoteDbMode.java
index e46acc3..f901cce 100644
--- a/java/com/google/gerrit/testing/NoteDbMode.java
+++ b/java/com/google/gerrit/testing/NoteDbMode.java
@@ -35,13 +35,7 @@
   PRIMARY(NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY),
 
   /** All change tables are entirely disabled, and code/meta ref updates are fused. */
-  ON(NotesMigrationState.NOTE_DB),
-
-  /**
-   * Run tests with NoteDb disabled, then convert ReviewDb to NoteDb and check that the results
-   * match.
-   */
-  CHECK(NotesMigrationState.REVIEW_DB);
+  ON(NotesMigrationState.NOTE_DB);
 
   private static final String ENV_VAR = "GERRIT_NOTEDB";
   private static final String SYS_PROP = "gerrit.notedb";
diff --git a/java/com/google/gerrit/testing/TestChanges.java b/java/com/google/gerrit/testing/TestChanges.java
index 8e752fa..0c87b38 100644
--- a/java/com/google/gerrit/testing/TestChanges.java
+++ b/java/com/google/gerrit/testing/TestChanges.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.notedb.AbstractChangeNotes;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.Injector;
 import java.util.TimeZone;
@@ -98,8 +97,7 @@
 
     ChangeNotes notes = update.getNotes();
     boolean hasPatchSets = notes.getPatchSets() != null && !notes.getPatchSets().isEmpty();
-    NotesMigration migration = injector.getInstance(NotesMigration.class);
-    if (hasPatchSets || !migration.readChanges()) {
+    if (hasPatchSets) {
       return update;
     }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 887035b..66556178 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -123,7 +123,6 @@
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.index.account.AccountIndexer;
 import com.google.gerrit.server.index.account.StalenessChecker;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.project.RefPattern;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
@@ -468,7 +467,7 @@
       RevCommit c = rw.parseCommit(ref.getObjectId());
       long timestampDiffMs =
           Math.abs(c.getCommitTime() * 1000L - getAccount(accountId).getRegisteredOn().getTime());
-      assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
+      assertThat(timestampDiffMs).isAtMost(SECONDS.toMillis(1));
 
       // Check the 'account.config' file.
       try (TreeWalk tw = TreeWalk.forPath(or, AccountProperties.ACCOUNT_CONFIG, c.getTree())) {
@@ -2252,12 +2251,7 @@
             externalIds,
             metaDataUpdateInternalFactory,
             new RetryHelper(
-                cfg,
-                retryMetrics,
-                null,
-                null,
-                null,
-                r -> r.withBlockStrategy(noSleepBlockStrategy)),
+                cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
             extIdNotesFactory,
             ident,
             ident,
@@ -2310,8 +2304,6 @@
                 cfg,
                 retryMetrics,
                 null,
-                null,
-                null,
                 r ->
                     r.withStopStrategy(StopStrategies.stopAfterAttempt(status.size()))
                         .withBlockStrategy(noSleepBlockStrategy)),
@@ -2369,12 +2361,7 @@
             externalIds,
             metaDataUpdateInternalFactory,
             new RetryHelper(
-                cfg,
-                retryMetrics,
-                null,
-                null,
-                null,
-                r -> r.withBlockStrategy(noSleepBlockStrategy)),
+                cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
             extIdNotesFactory,
             ident,
             ident,
@@ -2440,12 +2427,7 @@
             externalIds,
             metaDataUpdateInternalFactory,
             new RetryHelper(
-                cfg,
-                retryMetrics,
-                null,
-                null,
-                null,
-                r -> r.withBlockStrategy(noSleepBlockStrategy)),
+                cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
             extIdNotesFactory,
             ident,
             ident,
@@ -2910,7 +2892,7 @@
         TreeWalk.forPath(
             allUsersRepo.getRepository(),
             AccountProperties.ACCOUNT_CONFIG,
-            getHead(allUsersRepo.getRepository()).getTree())) {
+            getHead(allUsersRepo.getRepository(), "HEAD").getTree())) {
       assertThat(tw).isNotNull();
       ac.fromText(
           new String(
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index bb4b728..324f33b 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 import static com.google.gerrit.acceptance.PushOneCommit.FILE_CONTENT;
@@ -40,7 +39,6 @@
 import static com.google.gerrit.extensions.client.ReviewerState.REMOVED;
 import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
 import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
 import static com.google.gerrit.server.StarredChangesUtil.DEFAULT_LABEL;
 import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
 import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
@@ -133,9 +131,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.LabelId;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.ChangeMessagesUtil;
@@ -146,7 +142,6 @@
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.project.testing.Util;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.restapi.change.PostReview;
@@ -162,7 +157,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -513,8 +507,6 @@
 
   @Test
   public void pendingReviewersInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     ConfigInput conf = new ConfigInput();
     conf.enableReviewerByEmail = InheritableBoolean.TRUE;
     gApi.projects().name(project.get()).config(conf);
@@ -854,16 +846,11 @@
 
     List<Integer> reviewers =
         result.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
-    if (notesMigration.readChanges()) {
-      assertThat(result).containsKey(ReviewerState.CC);
-      List<Integer> ccs =
-          result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
-      assertThat(ccs).containsExactly(accountCreator.user2().id.get());
-      assertThat(reviewers).containsExactly(user.id.get(), admin.id.get());
-    } else {
-      assertThat(reviewers)
-          .containsExactly(user.id.get(), admin.id.get(), accountCreator.user2().id.get());
-    }
+    assertThat(result).containsKey(ReviewerState.CC);
+    List<Integer> ccs =
+        result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
+    assertThat(ccs).containsExactly(accountCreator.user2().id.get());
+    assertThat(reviewers).containsExactly(user.id.get(), admin.id.get());
   }
 
   @Test
@@ -929,17 +916,6 @@
     assertThat(cr.all).hasSize(1);
     assertThat(cr.all.get(0).value).isEqualTo(1);
 
-    if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
-      // Ensure record was actually copied under ReviewDb
-      List<PatchSetApproval> psas =
-          unwrapDb(db)
-              .patchSetApprovals()
-              .byPatchSet(new PatchSet.Id(new Change.Id(c2._number), 2))
-              .toList();
-      assertThat(psas).hasSize(1);
-      assertThat(psas.get(0).getValue()).isEqualTo((short) 1);
-    }
-
     // Rebasing the second change again should fail
     exception.expect(ResourceConflictException.class);
     exception.expectMessage("Change is already up to date");
@@ -967,7 +943,8 @@
     ri2 = ci2.revisions.get(ci2.currentRevision);
     assertThat(ri2.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
 
-    List<RelatedChangeAndCommitInfo> related = getRelated(id2, ri2._number);
+    List<RelatedChangeAndCommitInfo> related =
+        gApi.changes().id(id2.get()).revision(ri2._number).related().changes;
     assertThat(related).hasSize(2);
     assertThat(related.get(0)._changeNumber).isEqualTo(id2.get());
     assertThat(related.get(0)._revisionNumber).isEqualTo(2);
@@ -1001,7 +978,7 @@
     ri2 = ci2.revisions.get(ci2.currentRevision);
     assertThat(ri2.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
 
-    assertThat(getRelated(id2, ri2._number)).isEmpty();
+    assertThat(gApi.changes().id(id2.get()).revision(ri2._number).related().changes).isEmpty();
   }
 
   @Test
@@ -1027,7 +1004,7 @@
     RevisionInfo ri3 = ci3.revisions.get(ci3.currentRevision);
     assertThat(ri3.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
 
-    assertThat(getRelated(id3, ri3._number)).isEmpty();
+    assertThat(gApi.changes().id(id3.get()).revision(ri3._number).related().changes).isEmpty();
   }
 
   @Test
@@ -1710,8 +1687,6 @@
 
   @Test
   public void addReviewerThatIsInactiveEmailFallback() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     ConfigInput conf = new ConfigInput();
     conf.enableReviewerByEmail = InheritableBoolean.TRUE;
     gApi.projects().name(project.get()).config(conf);
@@ -1927,26 +1902,6 @@
   }
 
   @Test
-  public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-    assume().that(notesMigration.changePrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-
-    PushOneCommit.Result r = createChange();
-
-    // insert dummy approval in ReviewDb
-    PatchSetApproval psa =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(r.getPatchSetId(), user.id, new LabelId("Code-Review")),
-            (short) 0,
-            TimeUtil.nowTs());
-    db.patchSetApprovals().insert(Collections.singleton(psa));
-
-    AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
-    gApi.changes().id(r.getChangeId()).addReviewer(in);
-  }
-
-  @Test
   public void addSelfAsReviewer() throws Exception {
     TestTimeUtil.resetWithClockStep(1, SECONDS);
     PushOneCommit.Result r = createChange();
@@ -2004,9 +1959,7 @@
     in.reviewers = ImmutableList.of();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
 
-    // If we're not reading from NoteDb, then the CCed user will be returned in the REVIEWER state.
-    assertThat(getReviewerState(r.getChangeId(), testAccount.id))
-        .hasValue(notesMigration.readChanges() ? CC : REVIEWER);
+    assertThat(getReviewerState(r.getChangeId(), testAccount.id)).hasValue(CC);
   }
 
   @Test
@@ -2035,8 +1988,7 @@
         .revision(r.getCommit().name())
         .review(new ReviewInput().message("hi"));
     c = gApi.changes().id(r.getChangeId()).get();
-    ReviewerState state = notesMigration.readChanges() ? CC : REVIEWER;
-    assertThat(c.reviewers.get(state).stream().map(ai -> ai._accountId).collect(toList()))
+    assertThat(c.reviewers.get(CC).stream().map(ai -> ai._accountId).collect(toList()))
         .containsExactly(user.id.get());
   }
 
@@ -2885,8 +2837,6 @@
 
   @Test
   public void noteDbCommitsOnPatchSetCreation() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     PushOneCommit.Result r = createChange();
     pushFactory
         .create(
@@ -3438,12 +3388,7 @@
     gApi.changes().id(changeId).current().review(input);
 
     Map<String, Short> votes = gApi.changes().id(changeId).current().reviewer(admin.email).votes();
-    if (!notesMigration.readChanges()) {
-      assertThat(votes.keySet()).containsExactly("Code-Review");
-      assertThat(votes.values()).containsExactly((short) 0);
-    } else {
-      assertThat(votes).isEmpty();
-    }
+    assertThat(votes).isEmpty();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 18eb37a..85fbfa6 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -27,6 +27,7 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.truth.Correspondence;
 import com.google.common.util.concurrent.AtomicLongMap;
@@ -145,25 +146,6 @@
     }
   }
 
-  // Creates a group, but with uniquified name.
-  protected String createUniqueGroup() throws Exception {
-    // TODO(hanwen): rewrite this test in terms of UUID. This requires redoing the assertion helpers
-    // too.
-    AccountGroup.UUID g = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
-    return groupRef(g).getName();
-  }
-
-  protected String createGroup(String name, String owner) throws Exception {
-    // TODO(hanwen): rewrite to use groupOperations. This requires passing the owner
-    // group's UUID rathen than its name.
-    name = name(name);
-    GroupInput in = new GroupInput();
-    in.name = name;
-    in.ownerId = owner;
-    gApi.groups().create(in);
-    return name;
-  }
-
   @Override
   protected ProjectResetter.Config resetProjects() {
     // Don't reset All-Users since deleting users makes groups inconsistent (e.g. groups would
@@ -192,12 +174,14 @@
 
   @Test
   public void addRemoveMember() throws Exception {
-    String g = createUniqueGroup();
-    gApi.groups().id(g).addMembers("user");
-    assertMembers(g, user);
+    AccountGroup.UUID group = groupOperations.newGroup().create();
 
-    gApi.groups().id(g).removeMembers("user");
-    assertNoMembers(g);
+    gApi.groups().id(group.get()).addMembers("user");
+    assertMembers(group.get(), user);
+
+    gApi.groups().id(group.get()).removeMembers("user");
+    ImmutableSet<Account.Id> members = groupOperations.group(group).get().members();
+    assertThat(members).isEmpty();
   }
 
   @Test
@@ -207,16 +191,15 @@
 
     // Fill the cache for the observed account.
     groupIncludeCache.getGroupsWithMember(accountId);
-    String groupName = createUniqueGroup();
-    AccountGroup.UUID groupUuid = new AccountGroup.UUID(gApi.groups().id(groupName).get().id);
+    AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
 
-    gApi.groups().id(groupName).addMembers(username);
+    gApi.groups().id(groupUuid.get()).addMembers(username);
 
     Collection<AccountGroup.UUID> groupsWithMemberAfterAddition =
         groupIncludeCache.getGroupsWithMember(accountId);
     assertThat(groupsWithMemberAfterAddition).contains(groupUuid);
 
-    gApi.groups().id(groupName).removeMembers(username);
+    gApi.groups().id(groupUuid.get()).removeMembers(username);
 
     Collection<AccountGroup.UUID> groupsWithMemberAfterRemoval =
         groupIncludeCache.getGroupsWithMember(accountId);
@@ -239,16 +222,16 @@
 
   @Test
   public void addMultipleMembers() throws Exception {
-    String g = createUniqueGroup();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
 
     String u1 = name("u1");
     accountOperations.newAccount().username(u1).create();
     String u2 = name("u2");
     accountOperations.newAccount().username(u2).create();
 
-    gApi.groups().id(g).addMembers(u1, u2);
+    gApi.groups().id(group.get()).addMembers(u1, u2);
 
-    List<AccountInfo> members = gApi.groups().id(g).members();
+    List<AccountInfo> members = gApi.groups().id(group.get()).members();
     assertThat(members)
         .comparingElementsUsing(getAccountToUsernameCorrespondence())
         .containsExactly(u1, u2);
@@ -256,13 +239,13 @@
 
   @Test
   public void membersWithAtSignInUsernameCanBeAdded() throws Exception {
-    String g = createUniqueGroup();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
     String usernameWithAt = name("u1@something");
     accountOperations.newAccount().username(usernameWithAt).create();
 
-    gApi.groups().id(g).addMembers(usernameWithAt);
+    gApi.groups().id(group.get()).addMembers(usernameWithAt);
 
-    List<AccountInfo> members = gApi.groups().id(g).members();
+    List<AccountInfo> members = gApi.groups().id(group.get()).members();
     assertThat(members)
         .comparingElementsUsing(getAccountToUsernameCorrespondence())
         .containsExactly(usernameWithAt);
@@ -270,7 +253,7 @@
 
   @Test
   public void membersWithAtSignInUsernameAreNotConfusedWithSimilarUsernames() throws Exception {
-    String g = createUniqueGroup();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
     String usernameWithAt = name("u1@something");
     accountOperations.newAccount().username(usernameWithAt).create();
     String usernameWithoutAt = name("u1something");
@@ -281,10 +264,10 @@
     accountOperations.newAccount().username(usernameOnlySuffix).create();
 
     gApi.groups()
-        .id(g)
+        .id(group.get())
         .addMembers(usernameWithAt, usernameWithoutAt, usernameOnlyPrefix, usernameOnlySuffix);
 
-    List<AccountInfo> members = gApi.groups().id(g).members();
+    List<AccountInfo> members = gApi.groups().id(group.get()).members();
     assertThat(members)
         .comparingElementsUsing(getAccountToUsernameCorrespondence())
         .containsExactly(usernameWithAt, usernameWithoutAt, usernameOnlyPrefix, usernameOnlySuffix);
@@ -292,49 +275,54 @@
 
   @Test
   public void includeRemoveGroup() throws Exception {
-    String p = createUniqueGroup();
-    String g = createUniqueGroup();
-    gApi.groups().id(p).addGroups(g);
-    assertIncludes(p, g);
+    AccountGroup.UUID parent = groupOperations.newGroup().create();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
+    gApi.groups().id(parent.get()).addGroups(group.get());
+    assertThat(groupOperations.group(parent).get().subgroups()).containsExactly(group);
 
-    gApi.groups().id(p).removeGroups(g);
-    assertNoIncludes(p);
+    gApi.groups().id(parent.get()).removeGroups(group.get());
+    assertThat(groupOperations.group(parent).get().subgroups()).isEmpty();
   }
 
   @Test
   public void includeExternalGroup() throws Exception {
-    String g = createUniqueGroup();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
     String subgroupUuid = SystemGroupBackend.REGISTERED_USERS.get();
-    gApi.groups().id(g).addGroups(subgroupUuid);
+    gApi.groups().id(group.get()).addGroups(subgroupUuid);
 
-    List<GroupInfo> subgroups = gApi.groups().id(g).includedGroups();
+    List<GroupInfo> subgroups = gApi.groups().id(group.get()).includedGroups();
     assertThat(subgroups).hasSize(1);
     assertThat(subgroups.get(0).id).isEqualTo(subgroupUuid.replace(":", "%3A"));
     assertThat(subgroups.get(0).name).isEqualTo("Registered Users");
     assertThat(subgroups.get(0).groupId).isNull();
 
-    List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(g).auditLog();
+    List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(group.get()).auditLog();
     assertThat(auditEvents).hasSize(1);
     assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, "Registered Users");
   }
 
   @Test
   public void includeExistingGroup_OK() throws Exception {
-    String p = createUniqueGroup();
-    String g = createUniqueGroup();
-    gApi.groups().id(p).addGroups(g);
-    assertIncludes(p, g);
-    gApi.groups().id(p).addGroups(g);
-    assertIncludes(p, g);
+    AccountGroup.UUID parent = groupOperations.newGroup().create();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
+    groupOperations.group(parent).forUpdate().addSubgroup(group);
+
+    gApi.groups().id(parent.get()).addGroups(group.get());
+
+    ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(parent).get().subgroups();
+    assertThat(subgroups).containsExactly(group);
   }
 
   @Test
   public void addMultipleIncludes() throws Exception {
-    String p = createUniqueGroup();
-    String g1 = createUniqueGroup();
-    String g2 = createUniqueGroup();
-    gApi.groups().id(p).addGroups(g1, g2);
-    assertIncludes(p, g1, g2);
+    AccountGroup.UUID parent = groupOperations.newGroup().create();
+    AccountGroup.UUID group1 = groupOperations.newGroup().create();
+    AccountGroup.UUID group2 = groupOperations.newGroup().create();
+
+    gApi.groups().id(parent.get()).addGroups(group1.get(), group2.get());
+
+    ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(parent).get().subgroups();
+    assertThat(subgroups).containsExactly(group1, group2);
   }
 
   @Test
@@ -635,33 +623,39 @@
 
   @Test
   public void listEmptyGroupIncludes() throws Exception {
-    String gx = createUniqueGroup();
-    assertThat(gApi.groups().id(gx).includedGroups()).isEmpty();
+    AccountGroup.UUID gx = groupOperations.newGroup().create();
+    assertThat(gApi.groups().id(gx.get()).includedGroups()).isEmpty();
   }
 
   @Test
   public void includeNonExistingGroup() throws Exception {
-    String gx = createUniqueGroup();
+    AccountGroup.UUID gx = groupOperations.newGroup().create();
     exception.expect(UnprocessableEntityException.class);
-    gApi.groups().id(gx).addGroups("non-existing");
+    gApi.groups().id(gx.get()).addGroups("non-existing");
   }
 
   @Test
   public void listNonEmptyGroupIncludes() throws Exception {
-    String gx = createUniqueGroup();
-    String gy = createUniqueGroup();
-    String gz = createUniqueGroup();
-    gApi.groups().id(gx).addGroups(gy);
-    gApi.groups().id(gx).addGroups(gz);
-    assertIncludes(gApi.groups().id(gx).includedGroups(), gy, gz);
+    AccountGroup.UUID gz = groupOperations.newGroup().create();
+    AccountGroup.UUID gy = groupOperations.newGroup().create();
+    AccountGroup.UUID gx = groupOperations.newGroup().subgroups(gy, gz).create();
+
+    List<GroupInfo> includes = gApi.groups().id(gx.get()).includedGroups();
+
+    String gyName = groupOperations.group(gy).get().name();
+    String gzName = groupOperations.group(gz).get().name();
+    assertIncludes(includes, gyName, gzName);
   }
 
   @Test
   public void listOneIncludeMember() throws Exception {
-    String gx = createUniqueGroup();
-    String gy = createUniqueGroup();
-    gApi.groups().id(gx).addGroups(gy);
-    assertIncludes(gApi.groups().id(gx).includedGroups(), gy);
+    AccountGroup.UUID gy = groupOperations.newGroup().create();
+    AccountGroup.UUID gx = groupOperations.newGroup().subgroups(gy).create();
+
+    List<GroupInfo> includes = gApi.groups().id(gx.get()).includedGroups();
+
+    String gyName = groupOperations.group(gy).get().name();
+    assertIncludes(includes, gyName);
   }
 
   @Test
@@ -672,101 +666,101 @@
 
   @Test
   public void listEmptyGroupMembers() throws Exception {
-    String group = createUniqueGroup();
-    assertThat(gApi.groups().id(group).members()).isEmpty();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
+    assertThat(gApi.groups().id(group.get()).members()).isEmpty();
   }
 
   @Test
   public void listNonEmptyGroupMembers() throws Exception {
-    String group = createUniqueGroup();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
     String user1 = name("user1");
     accountOperations.newAccount().username(user1).create();
     String user2 = name("user2");
     accountOperations.newAccount().username(user2).create();
-    gApi.groups().id(group).addMembers(user1, user2);
+    gApi.groups().id(group.get()).addMembers(user1, user2);
 
-    assertMembers(gApi.groups().id(group).members(), user1, user2);
+    assertMembers(gApi.groups().id(group.get()).members(), user1, user2);
   }
 
   @Test
   public void listOneGroupMember() throws Exception {
-    String group = createUniqueGroup();
+    AccountGroup.UUID group = groupOperations.newGroup().create();
     String user = name("user1");
     accountOperations.newAccount().username(user).create();
-    gApi.groups().id(group).addMembers(user);
+    gApi.groups().id(group.get()).addMembers(user);
 
-    assertMembers(gApi.groups().id(group).members(), user);
+    assertMembers(gApi.groups().id(group.get()).members(), user);
   }
 
   @Test
   public void listGroupMembersRecursively() throws Exception {
-    String gx = createUniqueGroup();
+    AccountGroup.UUID gx = groupOperations.newGroup().create();
     String ux = name("ux");
     accountOperations.newAccount().username(ux).create();
-    gApi.groups().id(gx).addMembers(ux);
+    gApi.groups().id(gx.get()).addMembers(ux);
 
-    String gy = createUniqueGroup();
+    AccountGroup.UUID gy = groupOperations.newGroup().create();
     String uy = name("uy");
     accountOperations.newAccount().username(uy).create();
-    gApi.groups().id(gy).addMembers(uy);
+    gApi.groups().id(gy.get()).addMembers(uy);
 
-    String gz = createUniqueGroup();
+    AccountGroup.UUID gz = groupOperations.newGroup().create();
     String uz = name("uz");
     accountOperations.newAccount().username(uz).create();
-    gApi.groups().id(gz).addMembers(uz);
+    gApi.groups().id(gz.get()).addMembers(uz);
 
-    gApi.groups().id(gx).addGroups(gy);
-    gApi.groups().id(gy).addGroups(gz);
-    assertMembers(gApi.groups().id(gx).members(), ux);
-    assertMembers(gApi.groups().id(gx).members(true), ux, uy, uz);
+    gApi.groups().id(gx.get()).addGroups(gy.get());
+    gApi.groups().id(gy.get()).addGroups(gz.get());
+    assertMembers(gApi.groups().id(gx.get()).members(), ux);
+    assertMembers(gApi.groups().id(gx.get()).members(true), ux, uy, uz);
   }
 
   @Test
   public void usersSeeTheirDirectMembershipWhenListingMembersRecursively() throws Exception {
-    String group = createUniqueGroup();
-    gApi.groups().id(group).addMembers(user.username);
+    AccountGroup.UUID group = groupOperations.newGroup().create();
+    gApi.groups().id(group.get()).addMembers(user.username);
 
     setApiUser(user);
-    assertMembers(gApi.groups().id(group).members(true), user.fullName);
+    assertMembers(gApi.groups().id(group.get()).members(true), user.fullName);
   }
 
   @Test
   public void usersDoNotSeeTheirIndirectMembershipWhenListingMembersRecursively() throws Exception {
-    String group1 = createUniqueGroup();
-    String group2 = createUniqueGroup();
-    gApi.groups().id(group1).addGroups(group2);
-    gApi.groups().id(group2).addMembers(user.username);
+    AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
+    AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
+    gApi.groups().id(group1.get()).addGroups(group2.get());
+    gApi.groups().id(group2.get()).addMembers(user.username);
 
     setApiUser(user);
-    List<AccountInfo> listedMembers = gApi.groups().id(group1).members(true);
+    List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
 
     assertMembers(listedMembers);
   }
 
   @Test
   public void adminsSeeTheirIndirectMembershipWhenListingMembersRecursively() throws Exception {
-    String ownerGroup = createGroup("ownerGroup", null);
-    String group1 = createGroup("group1", ownerGroup);
-    String group2 = createGroup("group2", ownerGroup);
-    gApi.groups().id(group1).addGroups(group2);
-    gApi.groups().id(group2).addMembers(admin.username);
+    AccountGroup.UUID ownerGroup = groupOperations.newGroup().create();
+    AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+    AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+    gApi.groups().id(group1.get()).addGroups(group2.get());
+    gApi.groups().id(group2.get()).addMembers(admin.username);
 
-    List<AccountInfo> listedMembers = gApi.groups().id(group1).members(true);
+    List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
 
     assertMembers(listedMembers, admin.fullName);
   }
 
   @Test
   public void ownersSeeTheirIndirectMembershipWhenListingMembersRecursively() throws Exception {
-    String ownerGroup = createGroup("ownerGroup", null);
-    String group1 = createGroup("group1", ownerGroup);
-    String group2 = createGroup("group2", ownerGroup);
-    gApi.groups().id(group1).addGroups(group2);
-    gApi.groups().id(ownerGroup).addMembers(user.username);
-    gApi.groups().id(group2).addMembers(user.username);
+    AccountGroup.UUID ownerGroup = groupOperations.newGroup().create();
+    AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+    AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+    gApi.groups().id(group1.get()).addGroups(group2.get());
+    gApi.groups().id(ownerGroup.get()).addMembers(user.username);
+    gApi.groups().id(group2.get()).addMembers(user.username);
 
     setApiUser(user);
-    List<AccountInfo> listedMembers = gApi.groups().id(group1).members(true);
+    List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
 
     assertMembers(listedMembers, user.fullName);
   }
@@ -789,18 +783,21 @@
 
   @Test
   public void getGroupsByOwner() throws Exception {
-    String parent = createUniqueGroup();
-    List<String> children =
-        Arrays.asList(createGroup("test-child1", parent), createGroup("test-child2", parent));
+    AccountGroup.UUID parent = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
+    List<AccountGroup.UUID> children =
+        Arrays.asList(
+            groupOperations.newGroup().ownerGroupUuid(parent).create(),
+            groupOperations.newGroup().ownerGroupUuid(parent).create());
 
     // By UUID
-    List<GroupInfo> owned = gApi.groups().list().withOwnedBy(groupUuid(parent).get()).get();
-    assertThat(owned.stream().map(g -> g.name).collect(toList()))
+    List<GroupInfo> owned = gApi.groups().list().withOwnedBy(parent.get()).get();
+    assertThat(owned.stream().map(g -> new AccountGroup.UUID(g.id)).collect(toList()))
         .containsExactlyElementsIn(children);
 
     // By name
-    owned = gApi.groups().list().withOwnedBy(parent).get();
-    assertThat(owned.stream().map(g -> g.name).collect(toList()))
+    String parentName = groupOperations.group(parent).get().name();
+    owned = gApi.groups().list().withOwnedBy(parentName).get();
+    assertThat(owned.stream().map(g -> new AccountGroup.UUID(g.id)).collect(toList()))
         .containsExactlyElementsIn(children);
 
     // By group that does not own any others
@@ -1495,24 +1492,12 @@
         .inOrder();
   }
 
-  private void assertNoMembers(String group) throws Exception {
-    assertThat(gApi.groups().id(group).members()).isEmpty();
-  }
-
-  private void assertIncludes(String group, String... expectedNames) throws Exception {
-    assertIncludes(gApi.groups().id(group).includedGroups(), expectedNames);
-  }
-
-  private static void assertIncludes(Iterable<GroupInfo> includes, String... expectedNames) {
-    Iterable<String> names = Iterables.transform(includes, i -> i.name);
+  private static void assertIncludes(List<GroupInfo> includes, String... expectedNames) {
+    List<String> names = includes.stream().map(i -> i.name).collect(toImmutableList());
     assertThat(names).containsExactlyElementsIn(Arrays.asList(expectedNames));
     assertThat(names).isOrdered();
   }
 
-  private void assertNoIncludes(String group) throws Exception {
-    assertThat(gApi.groups().id(group).includedGroups()).isEmpty();
-  }
-
   private void assertBadRequest(ListRequest req) throws Exception {
     try {
       req.get();
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index bde042f..20e6d3f 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -795,16 +795,11 @@
     assertThat(result).containsKey(ReviewerState.REVIEWER);
     List<Integer> reviewers =
         result.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
-    if (notesMigration.readChanges()) {
-      assertThat(result).containsKey(ReviewerState.CC);
-      List<Integer> ccs =
-          result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
-      assertThat(ccs).containsExactly(user.id.get());
-      assertThat(reviewers).containsExactly(admin.id.get(), accountCreator.admin2().id.get());
-    } else {
-      assertThat(reviewers)
-          .containsExactly(user.id.get(), admin.id.get(), accountCreator.admin2().id.get());
-    }
+    assertThat(result).containsKey(ReviewerState.CC);
+    List<Integer> ccs =
+        result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
+    assertThat(ccs).containsExactly(user.id.get());
+    assertThat(reviewers).containsExactly(admin.id.get(), accountCreator.admin2().id.get());
   }
 
   @Test
@@ -1491,7 +1486,7 @@
 
   private PushOneCommit.Result createCherryPickableMerge(
       String parent1FileName, String parent2FileName) throws Exception {
-    RevCommit initialCommit = getHead(repo());
+    RevCommit initialCommit = getHead(repo(), "HEAD");
 
     String branchAName = "branchA";
     createBranch(new Branch.NameKey(project, branchAName));
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
index d713db6..c5251f7 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.api.revision;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
 import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
 import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
@@ -38,7 +37,6 @@
 import com.google.gerrit.extensions.common.RobotCommentInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
-import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -85,8 +83,6 @@
 
   @Test
   public void retrievingRobotCommentsBeforeAddingAnyDoesNotRaiseAnException() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     Map<String, List<RobotCommentInfo>> robotComments =
         gApi.changes().id(changeId).current().robotComments();
 
@@ -96,8 +92,6 @@
 
   @Test
   public void addedRobotCommentsCanBeRetrieved() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     RobotCommentInput in = createRobotCommentInput();
     addRobotComment(changeId, in);
 
@@ -110,8 +104,6 @@
 
   @Test
   public void addedRobotCommentsCanBeRetrievedByChange() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     RobotCommentInput in = createRobotCommentInput();
     addRobotComment(changeId, in);
 
@@ -133,8 +125,6 @@
 
   @Test
   public void robotCommentsCanBeRetrievedAsList() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     RobotCommentInput robotCommentInput = createRobotCommentInput();
     addRobotComment(changeId, robotCommentInput);
 
@@ -148,8 +138,6 @@
 
   @Test
   public void specificRobotCommentCanBeRetrieved() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     RobotCommentInput robotCommentInput = createRobotCommentInput();
     addRobotComment(changeId, robotCommentInput);
 
@@ -163,8 +151,6 @@
 
   @Test
   public void robotCommentWithoutOptionalFieldsCanBeAdded() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     RobotCommentInput in = createRobotCommentInputWithMandatoryFields();
     addRobotComment(changeId, in);
 
@@ -176,8 +162,6 @@
 
   @Test
   public void hugeRobotCommentIsRejected() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     int defaultSizeLimit = 1024 * 1024;
     int sizeOfRest = 451;
     fixReplacementInfo.replacement = getStringFor(defaultSizeLimit - sizeOfRest + 1);
@@ -189,8 +173,6 @@
 
   @Test
   public void reasonablyLargeRobotCommentIsAccepted() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     int defaultSizeLimit = 1024 * 1024;
     int sizeOfRest = 451;
     fixReplacementInfo.replacement = getStringFor(defaultSizeLimit - sizeOfRest);
@@ -204,8 +186,6 @@
   @Test
   @GerritConfig(name = "change.robotCommentSizeLimit", value = "10k")
   public void maximumAllowedSizeOfRobotCommentCanBeAdjusted() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     int sizeLimit = 10 * 1024;
     fixReplacementInfo.replacement = getStringFor(sizeLimit);
 
@@ -217,8 +197,6 @@
   @Test
   @GerritConfig(name = "change.robotCommentSizeLimit", value = "0")
   public void zeroForMaximumAllowedSizeOfRobotCommentRemovesRestriction() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     int defaultSizeLimit = 1024 * 1024;
     fixReplacementInfo.replacement = getStringFor(defaultSizeLimit);
 
@@ -232,8 +210,6 @@
   @GerritConfig(name = "change.robotCommentSizeLimit", value = "-1")
   public void negativeValueForMaximumAllowedSizeOfRobotCommentRemovesRestriction()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     int defaultSizeLimit = 1024 * 1024;
     fixReplacementInfo.replacement = getStringFor(defaultSizeLimit);
 
@@ -245,8 +221,6 @@
 
   @Test
   public void addedFixSuggestionCanBeRetrieved() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
 
@@ -255,8 +229,6 @@
 
   @Test
   public void fixIdIsGeneratedForFixSuggestion() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
 
@@ -270,8 +242,6 @@
 
   @Test
   public void descriptionOfFixSuggestionIsAcceptedAsIs() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
 
@@ -284,8 +254,6 @@
 
   @Test
   public void descriptionOfFixSuggestionIsMandatory() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixSuggestionInfo.description = null;
 
     exception.expect(BadRequestException.class);
@@ -298,8 +266,6 @@
 
   @Test
   public void addedFixReplacementCanBeRetrieved() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
 
@@ -312,8 +278,6 @@
 
   @Test
   public void fixReplacementsAreMandatory() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixSuggestionInfo.replacements = Collections.emptyList();
 
     exception.expect(BadRequestException.class);
@@ -327,8 +291,6 @@
 
   @Test
   public void pathOfFixReplacementIsAcceptedAsIs() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
 
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -343,8 +305,6 @@
 
   @Test
   public void pathOfFixReplacementIsMandatory() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = null;
 
     exception.expect(BadRequestException.class);
@@ -357,8 +317,6 @@
 
   @Test
   public void rangeOfFixReplacementIsAcceptedAsIs() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
 
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -373,8 +331,6 @@
 
   @Test
   public void rangeOfFixReplacementIsMandatory() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.range = null;
 
     exception.expect(BadRequestException.class);
@@ -387,8 +343,6 @@
 
   @Test
   public void rangeOfFixReplacementNeedsToBeValid() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.range = createRange(13, 9, 5, 10);
     exception.expect(BadRequestException.class);
     exception.expectMessage("Range (13:9 - 5:10)");
@@ -398,8 +352,6 @@
   @Test
   public void rangesOfFixReplacementsOfSameFixSuggestionForSameFileMayNotOverlap()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -422,8 +374,6 @@
   @Test
   public void rangesOfFixReplacementsOfSameFixSuggestionForDifferentFileMayOverlap()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -447,8 +397,6 @@
   @Test
   public void rangesOfFixReplacementsOfDifferentFixSuggestionsForSameFileMayOverlap()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -472,8 +420,6 @@
 
   @Test
   public void fixReplacementsDoNotNeedToBeOrderedAccordingToRange() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -501,8 +447,6 @@
 
   @Test
   public void replacementStringOfFixReplacementIsAcceptedAsIs() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     addRobotComment(changeId, withFixRobotCommentInput);
 
     List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -517,8 +461,6 @@
 
   @Test
   public void replacementStringOfFixReplacementIsMandatory() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.replacement = null;
 
     exception.expect(BadRequestException.class);
@@ -532,8 +474,6 @@
 
   @Test
   public void fixWithinALineCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME;
     fixReplacementInfo.replacement = "Modified content";
     fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -557,8 +497,6 @@
 
   @Test
   public void fixSpanningMultipleLinesCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME;
     fixReplacementInfo.replacement = "Modified content\n5";
     fixReplacementInfo.range = createRange(3, 2, 5, 3);
@@ -581,8 +519,6 @@
 
   @Test
   public void fixWithTwoCloseReplacementsOnSameFileCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -615,8 +551,6 @@
 
   @Test
   public void twoFixesOnSameFileCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -650,8 +584,6 @@
 
   @Test
   public void twoConflictingFixesOnSameFileCannotBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -679,8 +611,6 @@
 
   @Test
   public void twoFixesOfSameRobotCommentCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -714,8 +644,6 @@
 
   @Test
   public void fixReferringToDifferentFileThanRobotCommentCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME2;
     fixReplacementInfo.range = createRange(2, 0, 3, 0);
     fixReplacementInfo.replacement = "Modified content\n";
@@ -736,8 +664,6 @@
 
   @Test
   public void fixInvolvingTwoFilesCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
     fixReplacementInfo1.path = FILE_NAME;
     fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -775,8 +701,6 @@
 
   @Test
   public void fixReferringToNonExistentFileCannotBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = "a_non_existent_file.txt";
     fixReplacementInfo.range = createRange(1, 0, 2, 0);
     fixReplacementInfo.replacement = "Modified content\n";
@@ -792,8 +716,6 @@
 
   @Test
   public void fixOnPreviousPatchSetWithoutChangeEditCannotBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME;
     fixReplacementInfo.replacement = "Modified content";
     fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -815,8 +737,6 @@
 
   @Test
   public void fixOnPreviousPatchSetWithExistingChangeEditCanBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     // Create an empty change edit.
     gApi.changes().id(changeId).edit().create();
 
@@ -849,8 +769,6 @@
   @Test
   public void fixOnCurrentPatchSetWithChangeEditOnPreviousPatchSetCannotBeApplied()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     // Create an empty change edit.
     gApi.changes().id(changeId).edit().create();
 
@@ -874,8 +792,6 @@
 
   @Test
   public void fixDoesNotModifyCommitMessageOfChangeEdit() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     String changeEditCommitMessage = "This is the commit message of the change edit.\n";
     gApi.changes().id(changeId).edit().modifyCommitMessage(changeEditCommitMessage);
 
@@ -897,8 +813,6 @@
 
   @Test
   public void applyingFixTwiceIsIdempotent() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME;
     fixReplacementInfo.replacement = "Modified content";
     fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -922,8 +836,6 @@
 
   @Test
   public void nonExistentFixCannotBeApplied() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME;
     fixReplacementInfo.replacement = "Modified content";
     fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -941,8 +853,6 @@
 
   @Test
   public void applyingFixReturnsEditInfoForCreatedChangeEdit() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     fixReplacementInfo.path = FILE_NAME;
     fixReplacementInfo.replacement = "Modified content";
     fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -964,8 +874,6 @@
 
   @Test
   public void applyingFixOnTopOfChangeEditReturnsEditInfoForUpdatedChangeEdit() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     gApi.changes().id(changeId).edit().create();
 
     fixReplacementInfo.path = FILE_NAME;
@@ -989,7 +897,6 @@
 
   @Test
   public void createdChangeEditIsBasedOnCurrentPatchSet() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     String currentRevision = gApi.changes().id(changeId).get().currentRevision;
 
     fixReplacementInfo.path = FILE_NAME;
@@ -1008,25 +915,7 @@
   }
 
   @Test
-  public void robotCommentsNotSupportedWithoutNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-
-    RobotCommentInput in = createRobotCommentInput();
-    ReviewInput reviewInput = new ReviewInput();
-    Map<String, List<RobotCommentInput>> robotComments = new HashMap<>();
-    robotComments.put(in.path, ImmutableList.of(in));
-    reviewInput.robotComments = robotComments;
-    reviewInput.message = "comment test";
-
-    exception.expect(MethodNotAllowedException.class);
-    exception.expectMessage("robot comments not supported");
-    gApi.changes().id(changeId).current().review(reviewInput);
-  }
-
-  @Test
   public void queryChangesWithCommentCounts() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     PushOneCommit.Result r1 = createChange();
     PushOneCommit.Result r2 =
         pushFactory
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 69d71dc..5878407 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -18,7 +18,6 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
 import static com.google.gerrit.acceptance.GitUtil.assertPushRejected;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
@@ -543,12 +542,7 @@
     r.assertOkStatus();
     assertThat(sender.getMessages()).hasSize(1);
     Message m = sender.getMessages().get(0);
-    if (notesMigration.readChanges()) {
-      assertThat(m.rcpt()).containsExactly(user.emailAddress);
-    } else {
-      // CCs are considered reviewers in the storage layer and so get notified.
-      assertThat(m.rcpt()).containsExactly(user.emailAddress, user2.emailAddress);
-    }
+    assertThat(m.rcpt()).containsExactly(user.emailAddress);
 
     sender.clear();
     r = pushTo(pushSpec + ",notify=" + NotifyHandling.ALL);
@@ -639,23 +633,19 @@
 
     PushOneCommit.Result r =
         pushTo("refs/for/master%cc=non.existing.1@example.com,cc=non.existing.2@example.com");
-    if (notesMigration.readChanges()) {
-      r.assertOkStatus();
+    r.assertOkStatus();
 
-      ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
-      ImmutableList<AccountInfo> ccs =
-          firstNonNull(ci.reviewers.get(ReviewerState.CC), ImmutableList.<AccountInfo>of())
-              .stream()
-              .sorted(comparing((AccountInfo a) -> a.email))
-              .collect(toImmutableList());
-      assertThat(ccs).hasSize(2);
-      assertThat(ccs.get(0).email).isEqualTo("non.existing.1@example.com");
-      assertThat(ccs.get(0)._accountId).isNull();
-      assertThat(ccs.get(1).email).isEqualTo("non.existing.2@example.com");
-      assertThat(ccs.get(1)._accountId).isNull();
-    } else {
-      r.assertErrorStatus("non.existing.1@example.com does not identify a registered user");
-    }
+    ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
+    ImmutableList<AccountInfo> ccs =
+        firstNonNull(ci.reviewers.get(ReviewerState.CC), ImmutableList.<AccountInfo>of())
+            .stream()
+            .sorted(comparing((AccountInfo a) -> a.email))
+            .collect(toImmutableList());
+    assertThat(ccs).hasSize(2);
+    assertThat(ccs.get(0).email).isEqualTo("non.existing.1@example.com");
+    assertThat(ccs.get(0)._accountId).isNull();
+    assertThat(ccs.get(1).email).isEqualTo("non.existing.2@example.com");
+    assertThat(ccs.get(1)._accountId).isNull();
   }
 
   @Test
@@ -721,23 +711,19 @@
 
     PushOneCommit.Result r =
         pushTo("refs/for/master%r=non.existing.1@example.com,r=non.existing.2@example.com");
-    if (notesMigration.readChanges()) {
-      r.assertOkStatus();
+    r.assertOkStatus();
 
-      ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
-      ImmutableList<AccountInfo> reviewers =
-          firstNonNull(ci.reviewers.get(ReviewerState.REVIEWER), ImmutableList.<AccountInfo>of())
-              .stream()
-              .sorted(comparing((AccountInfo a) -> a.email))
-              .collect(toImmutableList());
-      assertThat(reviewers).hasSize(2);
-      assertThat(reviewers.get(0).email).isEqualTo("non.existing.1@example.com");
-      assertThat(reviewers.get(0)._accountId).isNull();
-      assertThat(reviewers.get(1).email).isEqualTo("non.existing.2@example.com");
-      assertThat(reviewers.get(1)._accountId).isNull();
-    } else {
-      r.assertErrorStatus("non.existing.1@example.com does not identify a registered user");
-    }
+    ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
+    ImmutableList<AccountInfo> reviewers =
+        firstNonNull(ci.reviewers.get(ReviewerState.REVIEWER), ImmutableList.<AccountInfo>of())
+            .stream()
+            .sorted(comparing((AccountInfo a) -> a.email))
+            .collect(toImmutableList());
+    assertThat(reviewers).hasSize(2);
+    assertThat(reviewers.get(0).email).isEqualTo("non.existing.1@example.com");
+    assertThat(reviewers.get(0)._accountId).isNull();
+    assertThat(reviewers.get(1).email).isEqualTo("non.existing.2@example.com");
+    assertThat(reviewers.get(1)._accountId).isNull();
   }
 
   @Test
@@ -1295,9 +1281,6 @@
 
   @Test
   public void pushForMasterWithHashtags() throws Exception {
-    // Hashtags only work when reading from NoteDB is enabled
-    assume().that(notesMigration.readChanges()).isTrue();
-
     // specify a single hashtag as option
     String hashtag1 = "tag1";
     Set<String> expected = ImmutableSet.of(hashtag1);
@@ -1328,9 +1311,6 @@
 
   @Test
   public void pushForMasterWithMultipleHashtags() throws Exception {
-    // Hashtags only work when reading from NoteDB is enabled
-    assume().that(notesMigration.readChanges()).isTrue();
-
     // specify multiple hashtags as options
     String hashtag1 = "tag1";
     String hashtag2 = "tag2";
@@ -1363,14 +1343,6 @@
   }
 
   @Test
-  public void pushForMasterWithHashtagsNoteDbDisabled() throws Exception {
-    // Push with hashtags should fail when reading from NoteDb is disabled.
-    assume().that(notesMigration.readChanges()).isFalse();
-    PushOneCommit.Result r = pushTo("refs/for/master%hashtag=tag1");
-    r.assertErrorStatus("cannot add hashtags; noteDb is disabled");
-  }
-
-  @Test
   public void pushCommitUsingSignedOffBy() throws Exception {
     PushOneCommit push =
         pushFactory.create(
@@ -2350,7 +2322,7 @@
         testRepo
             .commit()
             .message("Foo")
-            .parent(getHead(testRepo.getRepository()))
+            .parent(getHead(testRepo.getRepository(), "HEAD"))
             .insertChangeId()
             .create();
     testRepo.reset(c);
@@ -2378,7 +2350,7 @@
         testRepo
             .commit()
             .message("Foo")
-            .parent(getHead(testRepo.getRepository()))
+            .parent(getHead(testRepo.getRepository(), "HEAD"))
             .insertChangeId()
             .create();
     String id = GitUtil.getChangeId(testRepo, c).get();
@@ -2405,7 +2377,7 @@
         testRepo
             .commit()
             .message("Foo")
-            .parent(getHead(testRepo.getRepository()))
+            .parent(getHead(testRepo.getRepository(), "HEAD"))
             .insertChangeId()
             .create();
     testRepo.reset(c);
@@ -2426,7 +2398,7 @@
 
   @Test
   public void pushCommitsWithSameTreeNoFilesChangedWasRebased() throws Exception {
-    RevCommit head = getHead(testRepo.getRepository());
+    RevCommit head = getHead(testRepo.getRepository(), "HEAD");
     RevCommit c = testRepo.commit().message("Foo").parent(head).insertChangeId().create();
     testRepo.reset(c);
 
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index e4d9f7c..d2fd331 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -49,18 +49,15 @@
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHook;
 import com.google.gerrit.server.notedb.ChangeNoteUtil;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
 import com.google.gerrit.server.project.testing.Util;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.testing.NoteDbMode;
-import com.google.gerrit.testing.TestChanges;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Predicate;
@@ -424,12 +421,6 @@
       PatchSet.Id psId = new PatchSet.Id(c3.getId(), 2);
       c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
 
-      if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
-        PatchSet ps = TestChanges.newPatchSet(psId, rev, admin.getId());
-        db.patchSets().insert(Collections.singleton(ps));
-        db.changes().update(Collections.singleton(c));
-      }
-
       if (notesMigration.commitChangeWrites()) {
         PersonIdent committer = serverIdent.get();
         PersonIdent author =
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java b/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
index 700b18b..cf22a0a 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
@@ -318,7 +318,7 @@
 
   private PatchSetApproval getSubmitter(PatchSet.Id patchSetId) throws Exception {
     ChangeNotes notes = notesFactory.createChecked(db, project, patchSetId.getParentKey()).load();
-    return approvalsUtil.getSubmitter(db, notes, patchSetId);
+    return approvalsUtil.getSubmitter(notes, patchSetId);
   }
 
   private void assertSubmitApproval(PatchSet.Id patchSetId) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index 82b9cd0..f72df99 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.GitUtil.getChangeId;
 
 import com.google.common.collect.ImmutableList;
@@ -800,8 +799,6 @@
 
   @Test
   public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
-
     Project.NameKey subKey1 = createProjectForPush(getSubmitType());
     TestRepository<?> sub1 = cloneProject(subKey1);
     Project.NameKey subKey2 = createProjectForPush(getSubmitType());
diff --git a/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
deleted file mode 100644
index 1bb23fb..0000000
--- a/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
+++ /dev/null
@@ -1,363 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.pgm;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.StandaloneSiteTest;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
-import com.google.gerrit.server.index.GerritIndexStatus;
-import com.google.gerrit.server.index.change.ChangeIndexCollection;
-import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.stream.Stream;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Tests for NoteDb migrations where the entry point is through a program, {@code
- * migrate-to-note-db} or {@code daemon}.
- *
- * <p><strong>Note:</strong> These tests are very slow due to the repeated daemon startup. Prefer
- * adding tests to {@link com.google.gerrit.acceptance.server.notedb.OnlineNoteDbMigrationIT} if
- * possible.
- */
-@NoHttpd
-public class StandaloneNoteDbMigrationIT extends StandaloneSiteTest {
-  private StoredConfig gerritConfig;
-  private StoredConfig noteDbConfig;
-
-  private Project.NameKey project;
-  private Change.Id changeId;
-
-  @Before
-  public void setUp() throws Exception {
-    assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
-    gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
-    // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
-    noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
-
-    // Set gc.pruneExpire=now so GC prunes all unreachable objects from All-Users, which allows us
-    // to reliably test that it behaves as expected.
-    Path cfgPath = sitePaths.site_path.resolve("git").resolve("All-Users.git").resolve("config");
-    assertWithMessage("Expected All-Users config at %s", cfgPath)
-        .that(Files.isRegularFile(cfgPath))
-        .isTrue();
-    FileBasedConfig cfg = new FileBasedConfig(cfgPath.toFile(), FS.detect());
-    cfg.setString("gc", null, "pruneExpire", "now");
-    cfg.save();
-  }
-
-  @Test
-  public void rebuildOneChangeTrialMode() throws Exception {
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoAutoMigrateConfig(noteDbConfig);
-    assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
-    setUpOneChange();
-
-    migrate("--trial");
-    assertNotesMigrationState(NotesMigrationState.READ_WRITE_NO_SEQUENCE);
-
-    try (ServerContext ctx = startServer()) {
-      GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
-      ObjectId metaId;
-      try (Repository repo = repoManager.openRepository(project)) {
-        Ref ref = repo.exactRef(RefNames.changeMetaRef(changeId));
-        assertThat(ref).isNotNull();
-        metaId = ref.getObjectId();
-      }
-
-      try (ReviewDb db = openUnderlyingReviewDb(ctx)) {
-        Change c = db.changes().get(changeId);
-        assertThat(c).isNotNull();
-        NoteDbChangeState state = NoteDbChangeState.parse(c);
-        assertThat(state).isNotNull();
-        assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-        assertThat(state.getRefState()).hasValue(RefState.create(metaId, ImmutableMap.of()));
-      }
-    }
-  }
-
-  @Test
-  public void migrateOneChange() throws Exception {
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoAutoMigrateConfig(noteDbConfig);
-    assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
-    setUpOneChange();
-
-    migrate();
-    assertNotesMigrationState(NotesMigrationState.NOTE_DB);
-
-    File allUsersDir;
-    try (ServerContext ctx = startServer()) {
-      GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
-      try (Repository repo = repoManager.openRepository(project)) {
-        assertThat(repo.exactRef(RefNames.changeMetaRef(changeId))).isNotNull();
-      }
-      assertThat(repoManager).isInstanceOf(LocalDiskRepositoryManager.class);
-      try (Repository repo =
-          repoManager.openRepository(ctx.getInjector().getInstance(AllUsersName.class))) {
-        allUsersDir = repo.getDirectory();
-      }
-
-      try (ReviewDb db = openUnderlyingReviewDb(ctx)) {
-        Change c = db.changes().get(changeId);
-        assertThat(c).isNotNull();
-        NoteDbChangeState state = NoteDbChangeState.parse(c);
-        assertThat(state).isNotNull();
-        assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.NOTE_DB);
-        assertThat(state.getRefState()).isEmpty();
-
-        ChangeInput in = new ChangeInput(project.get(), "master", "NoteDb-only change");
-        in.newBranch = true;
-        GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
-        Change.Id id2 = new Change.Id(gApi.changes().create(in).info()._number);
-        assertThat(db.changes().get(id2)).isNull();
-      }
-    }
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertAutoMigrateConfig(noteDbConfig, false);
-
-    try (FileRepository repo = new FileRepository(allUsersDir)) {
-      try (Stream<Path> paths = Files.walk(repo.getObjectsDirectory().toPath())) {
-        assertThat(paths.filter(p -> !p.toString().contains("pack") && Files.isRegularFile(p)))
-            .named("loose object files in All-Users")
-            .isEmpty();
-      }
-      assertThat(repo.getObjectDatabase().getPacks()).named("packfiles in All-Users").hasSize(1);
-    }
-  }
-
-  @Test
-  public void migrationWithReindex() throws Exception {
-    assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
-    setUpOneChange();
-
-    int version = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
-    GerritIndexStatus status = new GerritIndexStatus(sitePaths);
-    assertThat(status.getReady(ChangeSchemaDefinitions.NAME, version)).isTrue();
-    status.setReady(ChangeSchemaDefinitions.NAME, version, false);
-    status.save();
-    assertServerStartupFails();
-
-    migrate();
-    assertNotesMigrationState(NotesMigrationState.NOTE_DB);
-
-    status = new GerritIndexStatus(sitePaths);
-    assertThat(status.getReady(ChangeSchemaDefinitions.NAME, version)).isTrue();
-  }
-
-  @Test
-  public void onlineMigrationViaDaemon() throws Exception {
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoAutoMigrateConfig(noteDbConfig);
-
-    testOnlineMigration(u -> startServer(u.module(), "--migrate-to-note-db", "true"));
-
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertAutoMigrateConfig(noteDbConfig, false);
-  }
-
-  @Test
-  public void onlineMigrationViaConfig() throws Exception {
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoAutoMigrateConfig(noteDbConfig);
-
-    testOnlineMigration(
-        u -> {
-          gerritConfig.setBoolean("noteDb", "changes", "autoMigrate", true);
-          gerritConfig.save();
-          return startServer(u.module());
-        });
-
-    // Auto-migration is turned off in notedb.config, which takes precedence, but is still on in
-    // gerrit.config. This means Puppet can continue overwriting gerrit.config without turning
-    // auto-migration back on.
-    assertAutoMigrateConfig(gerritConfig, true);
-    assertAutoMigrateConfig(noteDbConfig, false);
-  }
-
-  @Test
-  public void onlineMigrationTrialModeViaFlag() throws Exception {
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoTrialConfig(gerritConfig);
-
-    assertNoAutoMigrateConfig(noteDbConfig);
-    assertNoTrialConfig(noteDbConfig);
-
-    testOnlineMigration(
-        u -> startServer(u.module(), "--migrate-to-note-db", "--trial"),
-        NotesMigrationState.READ_WRITE_NO_SEQUENCE);
-
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoTrialConfig(gerritConfig);
-
-    assertAutoMigrateConfig(noteDbConfig, true);
-    assertTrialConfig(noteDbConfig, true);
-  }
-
-  @Test
-  public void onlineMigrationTrialModeViaConfig() throws Exception {
-    assertNoAutoMigrateConfig(gerritConfig);
-    assertNoTrialConfig(gerritConfig);
-
-    assertNoAutoMigrateConfig(noteDbConfig);
-    assertNoTrialConfig(noteDbConfig);
-
-    testOnlineMigration(
-        u -> {
-          gerritConfig.setBoolean("noteDb", "changes", "autoMigrate", true);
-          gerritConfig.setBoolean("noteDb", "changes", "trial", true);
-          gerritConfig.save();
-          return startServer(u.module());
-        },
-        NotesMigrationState.READ_WRITE_NO_SEQUENCE);
-
-    assertAutoMigrateConfig(gerritConfig, true);
-    assertTrialConfig(gerritConfig, true);
-
-    assertAutoMigrateConfig(noteDbConfig, true);
-    assertTrialConfig(noteDbConfig, true);
-  }
-
-  @FunctionalInterface
-  private interface StartServerWithMigration {
-    ServerContext start(IndexUpgradeController u) throws Exception;
-  }
-
-  private void testOnlineMigration(StartServerWithMigration start) throws Exception {
-    testOnlineMigration(start, NotesMigrationState.NOTE_DB);
-  }
-
-  private void testOnlineMigration(
-      StartServerWithMigration start, NotesMigrationState expectedEndState) throws Exception {
-    assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
-    int prevVersion = ChangeSchemaDefinitions.INSTANCE.getPrevious().getVersion();
-    int currVersion = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
-
-    // Before storing any changes, switch back to the previous version.
-    GerritIndexStatus status = new GerritIndexStatus(sitePaths);
-    status.setReady(ChangeSchemaDefinitions.NAME, currVersion, false);
-    status.setReady(ChangeSchemaDefinitions.NAME, prevVersion, true);
-    status.save();
-
-    setOnlineUpgradeConfig(false);
-    setUpOneChange();
-    setOnlineUpgradeConfig(true);
-
-    IndexUpgradeController u = new IndexUpgradeController(1);
-    try (ServerContext ctx = start.start(u)) {
-      ChangeIndexCollection indexes = ctx.getInjector().getInstance(ChangeIndexCollection.class);
-      assertThat(indexes.getSearchIndex().getSchema().getVersion()).isEqualTo(prevVersion);
-
-      // Index schema upgrades happen after NoteDb migration, so waiting for those to complete
-      // should be sufficient.
-      u.runUpgrades();
-
-      assertThat(indexes.getSearchIndex().getSchema().getVersion()).isEqualTo(currVersion);
-      assertNotesMigrationState(expectedEndState);
-    }
-  }
-
-  private void setUpOneChange() throws Exception {
-    project = new Project.NameKey("project");
-    try (ServerContext ctx = startServer()) {
-      GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
-      gApi.projects().create("project");
-
-      ChangeInput in = new ChangeInput(project.get(), "master", "Test change");
-      in.newBranch = true;
-      changeId = new Change.Id(gApi.changes().create(in).info()._number);
-    }
-  }
-
-  private void migrate(String... additionalArgs) throws Exception {
-    runGerrit(
-        ImmutableList.of(
-            "migrate-to-note-db", "-d", sitePaths.site_path.toString(), "--show-stack-trace"),
-        ImmutableList.copyOf(additionalArgs));
-  }
-
-  private void assertNotesMigrationState(NotesMigrationState expected) throws Exception {
-    noteDbConfig.load();
-    assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected);
-  }
-
-  private ReviewDb openUnderlyingReviewDb(ServerContext ctx) throws Exception {
-    return ctx.getInjector()
-        .getInstance(Key.get(new TypeLiteral<SchemaFactory<ReviewDb>>() {}, ReviewDbFactory.class))
-        .open();
-  }
-
-  private static void assertNoAutoMigrateConfig(StoredConfig cfg) throws Exception {
-    cfg.load();
-    assertThat(cfg.getString("noteDb", "changes", "autoMigrate")).isNull();
-  }
-
-  private static void assertAutoMigrateConfig(StoredConfig cfg, boolean expected) throws Exception {
-    cfg.load();
-    assertThat(cfg.getString("noteDb", "changes", "autoMigrate")).isNotNull();
-    assertThat(cfg.getBoolean("noteDb", "changes", "autoMigrate", false)).isEqualTo(expected);
-  }
-
-  private static void assertNoTrialConfig(StoredConfig cfg) throws Exception {
-    cfg.load();
-    assertThat(cfg.getString("noteDb", "changes", "trial")).isNull();
-  }
-
-  private static void assertTrialConfig(StoredConfig cfg, boolean expected) throws Exception {
-    cfg.load();
-    assertThat(cfg.getString("noteDb", "changes", "trial")).isNotNull();
-    assertThat(cfg.getBoolean("noteDb", "changes", "trial", false)).isEqualTo(expected);
-  }
-
-  private void setOnlineUpgradeConfig(boolean enable) throws Exception {
-    gerritConfig.load();
-    gerritConfig.setBoolean("index", null, "onlineUpgrade", enable);
-    gerritConfig.save();
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index 65c95f8..dd26347 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.account;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
 import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -116,7 +115,7 @@
     assertThat(psa.getRealAccountId()).isEqualTo(admin.id);
 
     ChangeData cd = r.getChange();
-    ChangeMessage m = Iterables.getLast(cmUtil.byChange(db, cd.notes()));
+    ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
     assertThat(m.getMessage()).endsWith(in.message);
     assertThat(m.getAuthor()).isEqualTo(user.id);
     assertThat(m.getRealAuthor()).isEqualTo(admin.id);
@@ -191,7 +190,6 @@
 
   @Test
   public void voteOnBehalfOfWithCommentWritingJson() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     testVoteOnBehalfOfWithComment();
   }
 
@@ -218,7 +216,7 @@
     assertThat(psa.getRealAccountId()).isEqualTo(admin.id);
 
     ChangeData cd = r.getChange();
-    Comment c = Iterables.getOnlyElement(commentsUtil.publishedByChange(db, cd.notes()));
+    Comment c = Iterables.getOnlyElement(commentsUtil.publishedByChange(cd.notes()));
     assertThat(c.message).isEqualTo(ci.message);
     assertThat(c.author.getId()).isEqualTo(user.id);
     assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id);
@@ -226,7 +224,6 @@
 
   @Test
   public void voteOnBehalfOfWithRobotComment() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     allowCodeReviewOnBehalfOf();
     PushOneCommit.Result r = createChange();
 
@@ -342,7 +339,7 @@
     ChangeData cd = r.getChange();
     assertThat(cd.change().getStatus()).isEqualTo(Change.Status.MERGED);
     PatchSetApproval submitter =
-        approvalsUtil.getSubmitter(db, cd.notes(), cd.change().currentPatchSetId());
+        approvalsUtil.getSubmitter(cd.notes(), cd.change().currentPatchSetId());
     assertThat(submitter.getAccountId()).isEqualTo(admin2.id);
     assertThat(submitter.getRealAccountId()).isEqualTo(admin.id);
   }
@@ -520,7 +517,7 @@
     assertThat(psa.getRealAccountId()).isEqualTo(admin.id); // not user2
 
     ChangeData cd = r.getChange();
-    ChangeMessage m = Iterables.getLast(cmUtil.byChange(db, cd.notes()));
+    ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
     assertThat(m.getMessage()).endsWith(in.message);
     assertThat(m.getAuthor()).isEqualTo(user.id);
     assertThat(m.getRealAuthor()).isEqualTo(admin.id); // not user2
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
index 628b63f..db5dfab 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.acceptance.rest.binding;
 
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.rest.util.RestCall.Method.GET;
 import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
 import static java.util.stream.Collectors.toList;
@@ -113,14 +112,6 @@
           RestCall.delete("/changes/%s"));
 
   /**
-   * Change REST endpoints to be tested with NoteDb, each URL contains a placeholder for the change
-   * identifier.
-   */
-  private static final ImmutableList<RestCall> CHANGE_ENDPOINTS_NOTEDB =
-      ImmutableList.of(
-          RestCall.post("/changes/%s/hashtags"), RestCall.post("/changes/%s/rebuild.notedb"));
-
-  /**
    * Reviewer REST endpoints to be tested, each URL contains placeholders for the change identifier
    * and the reviewer identifier.
    */
@@ -285,14 +276,6 @@
   }
 
   @Test
-  public void changeEndpointsNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
-    String changeId = createChange().getChangeId();
-    RestApiCallHelper.execute(adminRestSession, CHANGE_ENDPOINTS_NOTEDB, changeId);
-  }
-
-  @Test
   public void reviewerEndpoints() throws Exception {
     String changeId = createChange().getChangeId();
 
@@ -394,8 +377,6 @@
 
   @Test
   public void robotCommentEndpoints() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     String changeId = createChange().getChangeId();
 
     RobotCommentInput robotCommentInput = new RobotCommentInput();
@@ -421,8 +402,6 @@
 
   @Test
   public void fixEndpoints() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     String changeId = createChange("Subject", FILENAME, "content").getChangeId();
 
     RobotCommentInput robotCommentInput = new RobotCommentInput();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index bab4c1d..a4d9a24 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -905,8 +905,6 @@
 
   @Test
   public void retrySubmitSingleChangeOnLockFailure() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
-
     PushOneCommit.Result change = createChange();
     String id = change.getChangeId();
     approve(id);
@@ -932,7 +930,6 @@
 
   @Test
   public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
     assume().that(isSubmitWholeTopicEnabled()).isTrue();
 
     String topic = "test-topic";
@@ -1281,7 +1278,7 @@
     Change c = getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change();
     ChangeNotes cn = notesFactory.createChecked(db, c);
     PatchSetApproval submitter =
-        approvalsUtil.getSubmitter(db, cn, new PatchSet.Id(cn.getChangeId(), psId));
+        approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
     assertThat(submitter).isNotNull();
     assertThat(submitter.isLegacySubmit()).isTrue();
     assertThat(submitter.getAccountId()).isEqualTo(user.getId());
@@ -1291,7 +1288,7 @@
     Change c = getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change();
     ChangeNotes cn = notesFactory.createChecked(db, c);
     PatchSetApproval submitter =
-        approvalsUtil.getSubmitter(db, cn, new PatchSet.Id(cn.getChangeId(), psId));
+        approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
     assertThat(submitter).isNull();
   }
 
@@ -1305,7 +1302,7 @@
 
   protected void assertRebase(TestRepository<?> testRepo, boolean contentMerge) throws Exception {
     Repository repo = testRepo.getRepository();
-    RevCommit localHead = getHead(repo);
+    RevCommit localHead = getHead(repo, "HEAD");
     RevCommit remoteHead = getRemoteHead();
     assertThat(localHead.getId()).isNotEqualTo(remoteHead.getId());
     assertThat(remoteHead.getParentCount()).isEqualTo(1);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
index 29a81ca..fecb6c5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -17,21 +17,10 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.TruthJUnit.assume;
 
-import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.TestProjectInput;
-import com.google.gerrit.extensions.api.changes.SubmitInput;
-import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.TestSubmitInput;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
 import org.junit.Test;
 
 public abstract class AbstractSubmitByMerge extends AbstractSubmit {
@@ -124,58 +113,4 @@
     assertThat(head.getParent(0)).isEqualTo(change1.getCommit());
     assertThat(head.getParent(1)).isEqualTo(change2.getCommit());
   }
-
-  @Test
-  public void repairChangeStateAfterFailure() throws Exception {
-    // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
-    assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
-    RevCommit initialHead = getRemoteHead();
-    PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
-    submit(change.getChangeId());
-    RevCommit afterChange1Head = getRemoteHead();
-
-    testRepo.reset(initialHead);
-    PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
-    Change.Id id2 = change2.getChange().getId();
-    TestSubmitInput failInput = new TestSubmitInput();
-    failInput.failAfterRefUpdates = true;
-    submit(
-        change2.getChangeId(),
-        failInput,
-        ResourceConflictException.class,
-        "Failing after ref updates");
-
-    // Bad: ref advanced but change wasn't updated.
-    PatchSet.Id psId1 = new PatchSet.Id(id2, 1);
-    ChangeInfo info = gApi.changes().id(id2.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.NEW);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-
-    RevCommit tip;
-    try (Repository repo = repoManager.openRepository(project);
-        RevWalk rw = new RevWalk(repo)) {
-      ObjectId rev1 = repo.exactRef(psId1.toRefName()).getObjectId();
-      assertThat(rev1).isNotNull();
-
-      tip = rw.parseCommit(repo.exactRef("refs/heads/master").getObjectId());
-      assertThat(tip.getParentCount()).isEqualTo(2);
-      assertThat(tip.getParent(0)).isEqualTo(afterChange1Head);
-      assertThat(tip.getParent(1)).isEqualTo(change2.getCommit());
-    }
-
-    submit(change2.getChangeId(), new SubmitInput(), null, null);
-
-    // Change status and patch set entities were updated, and branch tip stayed
-    // the same.
-    info = gApi.changes().id(id2.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-    assertThat(Iterables.getLast(info.messages).message)
-        .isEqualTo("Change has been successfully merged by Administrator");
-
-    try (Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(tip);
-    }
-  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index 0a92cfb..e8e896d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -15,13 +15,11 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.GitUtil.getChangeId;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.acceptance.TestProjectInput;
@@ -30,12 +28,8 @@
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.project.testing.Util;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
@@ -242,80 +236,6 @@
     assertChangeMergedEvents(change.getChangeId(), headAfterFirstSubmit.name());
   }
 
-  @Test
-  public void repairChangeStateAfterFailure() throws Exception {
-    // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
-    assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
-    RevCommit initialHead = getRemoteHead();
-    PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
-    submit(change.getChangeId());
-
-    RevCommit headAfterFirstSubmit = getRemoteHead();
-    testRepo.reset(initialHead);
-    PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
-    Change.Id id2 = change2.getChange().getId();
-    TestSubmitInput failInput = new TestSubmitInput();
-    failInput.failAfterRefUpdates = true;
-    submit(
-        change2.getChangeId(),
-        failInput,
-        ResourceConflictException.class,
-        "Failing after ref updates");
-    RevCommit headAfterFailedSubmit = getRemoteHead();
-
-    // Bad: ref advanced but change wasn't updated.
-    PatchSet.Id psId1 = new PatchSet.Id(id2, 1);
-    PatchSet.Id psId2 = new PatchSet.Id(id2, 2);
-    ChangeInfo info = gApi.changes().id(id2.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.NEW);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-    assertThat(getPatchSet(psId2)).isNull();
-
-    ObjectId rev2;
-    try (Repository repo = repoManager.openRepository(project);
-        RevWalk rw = new RevWalk(repo)) {
-      ObjectId rev1 = repo.exactRef(psId1.toRefName()).getObjectId();
-      assertThat(rev1).isNotNull();
-
-      rev2 = repo.exactRef(psId2.toRefName()).getObjectId();
-      assertThat(rev2).isNotNull();
-      assertThat(rev2).isNotEqualTo(rev1);
-      assertThat(rw.parseCommit(rev2).getParent(0)).isEqualTo(headAfterFirstSubmit);
-
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
-    }
-
-    submit(change2.getChangeId());
-    RevCommit headAfterSecondSubmit = getRemoteHead();
-    assertThat(headAfterSecondSubmit).isEqualTo(headAfterFailedSubmit);
-
-    // Change status and patch set entities were updated, and branch tip stayed
-    // the same.
-    info = gApi.changes().id(id2.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(2);
-    PatchSet ps2 = getPatchSet(psId2);
-    assertThat(ps2).isNotNull();
-    assertThat(ps2.getRevision().get()).isEqualTo(rev2.name());
-    assertThat(Iterables.getLast(info.messages).message)
-        .isEqualTo(
-            "Change has been successfully rebased and submitted as "
-                + rev2.name()
-                + " by Administrator");
-
-    try (Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
-    }
-
-    assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
-    assertChangeMergedEvents(
-        change.getChangeId(),
-        headAfterFirstSubmit.name(),
-        change2.getChangeId(),
-        headAfterSecondSubmit.name());
-  }
-
   protected RevCommit parse(ObjectId id) throws Exception {
     try (Repository repo = repoManager.openRepository(project);
         RevWalk rw = new RevWalk(repo)) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
index 69035f2..c925d88 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -79,7 +78,6 @@
 
   @Test
   public void getPastAssignees() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
     setAssignee(r, user.email);
     setAssignee(r, admin.email);
@@ -92,14 +90,7 @@
 
   @Test
   public void assigneeAddedAsReviewer() throws Exception {
-    ReviewerState state;
-    // Assignee is added as CC, if back-end is reviewDb (that does not support
-    // CC) CC is stored as REVIEWER
-    if (notesMigration.readChanges()) {
-      state = ReviewerState.CC;
-    } else {
-      state = ReviewerState.REVIEWER;
-    }
+    ReviewerState state = ReviewerState.CC;
     PushOneCommit.Result r = createChange();
     Iterable<AccountInfo> reviewers = getReviewers(r, state);
     assertThat(reviewers).isNull();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
index 790b884..48cb050 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -251,10 +251,7 @@
     List<ChangeMessageInfo> messagesBeforeDeletion = gApi.changes().id(changeNum).messages();
 
     List<CommentInfo> commentsBefore = getChangeSortedComments(changeNum);
-    List<RevCommit> commitsBefore = new ArrayList<>();
-    if (notesMigration.readChanges()) {
-      commitsBefore = getChangeMetaCommitsInReverseOrder(new Change.Id(changeNum));
-    }
+    List<RevCommit> commitsBefore = getChangeMetaCommitsInReverseOrder(new Change.Id(changeNum));
 
     String id = messagesBeforeDeletion.get(deletedMessageIndex).id;
     DeleteChangeMessageInput input = new DeleteChangeMessageInput(reason);
@@ -271,11 +268,9 @@
     List<ChangeInfo> changes = gApi.changes().query("message removed").get();
     assertThat(changes.stream().map(c -> c._number).collect(toSet())).contains(changeNum);
 
-    // Verifies states of commits if NoteDb is on.
-    if (notesMigration.readChanges()) {
-      assertMetaCommitsAfterDeletion(
-          commitsBefore, changeNum, deletedMessageIndex, deletedBy, reason);
-    }
+    // Verifies states of commits.
+    assertMetaCommitsAfterDeletion(
+        commitsBefore, changeNum, deletedMessageIndex, deletedBy, reason);
   }
 
   private void assertMessagesAfterDeletion(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index dc71c1f..2a397e4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
 
 import com.google.common.collect.ImmutableList;
@@ -52,7 +51,6 @@
 
   @Test
   public void addByEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -72,7 +70,6 @@
 
   @Test
   public void addByEmailAndById() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo byEmail = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
     AccountInfo byId = new AccountInfo(user.id.get());
 
@@ -98,7 +95,6 @@
 
   @Test
   public void listReviewersByEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -126,7 +122,6 @@
 
   @Test
   public void removeByEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -146,7 +141,6 @@
 
   @Test
   public void convertFromCCToReviewer() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     PushOneCommit.Result r = createChange();
@@ -168,7 +162,6 @@
 
   @Test
   public void addedReviewersGetNotified() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -188,7 +181,6 @@
 
   @Test
   public void removingReviewerTriggersNotification() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -221,7 +213,6 @@
 
   @Test
   public void reviewerAndCCReceiveRegularNotification() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -244,8 +235,6 @@
 
   @Test
   public void reviewerAndCCReceiveSameEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     PushOneCommit.Result r = createChange();
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
       for (int i = 0; i < 10; i++) {
@@ -270,8 +259,6 @@
 
   @Test
   public void addingMultipleReviewersAndCCsAtOnceSendsOnlyOneEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     PushOneCommit.Result r = createChange();
     ReviewInput reviewInput = new ReviewInput();
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -288,7 +275,6 @@
 
   @Test
   public void rejectMissingEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
 
     AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer("");
@@ -298,7 +284,6 @@
 
   @Test
   public void rejectMalformedEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
 
     AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@");
@@ -308,8 +293,6 @@
 
   @Test
   public void rejectWhenFeatureIsDisabled() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     ConfigInput conf = new ConfigInput();
     conf.enableReviewerByEmail = InheritableBoolean.FALSE;
     gApi.projects().name(project.get()).config(conf);
@@ -326,7 +309,6 @@
 
   @Test
   public void reviewersByEmailAreServedFromIndex() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -351,7 +333,6 @@
 
   @Test
   public void addExistingReviewerByEmailShortCircuits() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput input = new AddReviewerInput();
@@ -369,7 +350,6 @@
 
   @Test
   public void addExistingCcByEmailShortCircuits() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput input = new AddReviewerInput();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index 6a9a27c..069607a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
 import static com.google.gerrit.extensions.client.ReviewerState.CC;
 import static com.google.gerrit.extensions.client.ReviewerState.REMOVED;
@@ -134,31 +133,18 @@
     assertThat(result.confirm).isNull();
     assertThat(result.error).isNull();
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertThat(result.reviewers).isNull();
-      assertThat(result.ccs).hasSize(1);
-      AccountInfo ai = result.ccs.get(0);
-      assertThat(ai._accountId).isEqualTo(user.id.get());
-      assertReviewers(c, CC, user);
-    } else {
-      assertThat(result.ccs).isNull();
-      assertThat(result.reviewers).hasSize(1);
-      AccountInfo ai = result.reviewers.get(0);
-      assertThat(ai._accountId).isEqualTo(user.id.get());
-      assertReviewers(c, REVIEWER, user);
-    }
+    assertThat(result.reviewers).isNull();
+    assertThat(result.ccs).hasSize(1);
+    AccountInfo ai = result.ccs.get(0);
+    assertThat(ai._accountId).isEqualTo(user.id.get());
+    assertReviewers(c, CC, user);
 
     // Verify email was sent to CCed account.
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.rcpt()).containsExactly(user.emailAddress);
-    if (notesMigration.readChanges()) {
-      assertThat(m.body()).contains(admin.fullName + " has uploaded this change for review.");
-    } else {
-      assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
-      assertThat(m.body()).contains("I'd like you to do a code review.");
-    }
+    assertThat(m.body()).contains(admin.fullName + " has uploaded this change for review.");
   }
 
   @Test
@@ -185,18 +171,9 @@
     assertThat(result.input).isEqualTo(in.reviewer);
     assertThat(result.confirm).isNull();
     assertThat(result.error).isNull();
-    if (notesMigration.readChanges()) {
-      assertThat(result.reviewers).isNull();
-    } else {
-      assertThat(result.ccs).isNull();
-    }
+    assertThat(result.reviewers).isNull();
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertReviewers(c, CC, firstUsers);
-    } else {
-      assertReviewers(c, REVIEWER, firstUsers);
-      assertReviewers(c, CC);
-    }
+    assertReviewers(c, CC, firstUsers);
 
     // Verify emails were sent to each of the group's accounts.
     List<Message> messages = sender.getMessages();
@@ -222,19 +199,10 @@
     assertThat(result.confirm).isNull();
     assertThat(result.error).isNull();
     c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertThat(result.ccs).hasSize(3);
-      assertThat(result.reviewers).isNull();
-      assertReviewers(c, REVIEWER, reviewer);
-      assertReviewers(c, CC, users);
-    } else {
-      assertThat(result.ccs).isNull();
-      assertThat(result.reviewers).hasSize(3);
-      List<TestAccount> expectedUsers = new ArrayList<>(users.size() + 2);
-      expectedUsers.addAll(users);
-      expectedUsers.add(reviewer);
-      assertReviewers(c, REVIEWER, expectedUsers);
-    }
+    assertThat(result.ccs).hasSize(3);
+    assertThat(result.reviewers).isNull();
+    assertReviewers(c, REVIEWER, reviewer);
+    assertReviewers(c, CC, users);
 
     messages = sender.getMessages();
     assertThat(messages).hasSize(1);
@@ -243,11 +211,6 @@
     for (int i = 0; i < 3; i++) {
       expectedAddresses.add(users.get(users.size() - i - 1).emailAddress);
     }
-    if (!notesMigration.readChanges()) {
-      for (int i = 0; i < 3; i++) {
-        expectedAddresses.add(users.get(i).emailAddress);
-      }
-    }
     expectedAddresses.add(reviewer.emailAddress);
     assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
   }
@@ -261,13 +224,8 @@
     in.state = CC;
     addReviewer(changeId, in);
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertReviewers(c, REVIEWER);
-      assertReviewers(c, CC, user);
-    } else {
-      assertReviewers(c, REVIEWER, user);
-      assertReviewers(c, CC);
-    }
+    assertReviewers(c, REVIEWER);
+    assertReviewers(c, CC, user);
 
     in.state = REVIEWER;
     addReviewer(changeId, in);
@@ -293,15 +251,8 @@
 
     // Verify user is added to CC list.
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertReviewers(c, REVIEWER);
-      assertReviewers(c, CC, user);
-    } else {
-      // If we aren't reading from NoteDb, the user will appear as a
-      // reviewer.
-      assertReviewers(c, REVIEWER, user);
-      assertReviewers(c, CC);
-    }
+    assertReviewers(c, REVIEWER);
+    assertReviewers(c, CC, user);
   }
 
   @Test
@@ -350,26 +301,13 @@
 
     // Verify reviewer state.
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertReviewers(c, REVIEWER);
-      assertReviewers(c, CC, user);
-      // Verify no approvals were added.
-      assertThat(c.labels).isNotNull();
-      LabelInfo label = c.labels.get("Code-Review");
-      assertThat(label).isNotNull();
-      assertThat(label.all).isNull();
-    } else {
-      // When approvals are stored in ReviewDb, we still create a label for
-      // the reviewing user, and force them into the REVIEWER state.
-      assertReviewers(c, REVIEWER, user);
-      assertReviewers(c, CC);
-      LabelInfo label = c.labels.get("Code-Review");
-      assertThat(label).isNotNull();
-      assertThat(label.all).isNotNull();
-      assertThat(label.all).hasSize(1);
-      ApprovalInfo approval = label.all.get(0);
-      assertThat(approval._accountId).isEqualTo(user.getId().get());
-    }
+    assertReviewers(c, REVIEWER);
+    assertReviewers(c, CC, user);
+    // Verify no approvals were added.
+    assertThat(c.labels).isNotNull();
+    LabelInfo label = c.labels.get("Code-Review");
+    assertThat(label).isNotNull();
+    assertThat(label.all).isNull();
   }
 
   @Test
@@ -449,14 +387,8 @@
     // Verify reviewer and CC were added. If not in NoteDb read mode, both
     // parties will be returned as CCed.
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
-    if (notesMigration.readChanges()) {
-      assertReviewers(c, REVIEWER, admin, user);
-      assertReviewers(c, CC, observer);
-    } else {
-      // In legacy mode, everyone should be a reviewer.
-      assertReviewers(c, REVIEWER, admin, user, observer);
-      assertReviewers(c, CC);
-    }
+    assertReviewers(c, REVIEWER, admin, user);
+    assertReviewers(c, CC, observer);
 
     // Verify emails were sent to added reviewers.
     List<Message> messages = sender.getMessages();
@@ -549,23 +481,12 @@
     c = gApi.changes().id(r.getChangeId()).get();
     assertThat(c.messages).hasSize(2);
 
-    if (notesMigration.readChanges()) {
-      assertReviewers(c, REVIEWER, admin, user);
-      assertReviewers(c, CC, users.subList(0, mediumGroupSize));
-    } else {
-      // If not in NoteDb mode, then everyone is a REVIEWER.
-      List<TestAccount> expected = users.subList(0, mediumGroupSize);
-      expected.add(admin);
-      expected.add(user);
-      assertReviewers(c, REVIEWER, expected);
-      assertReviewers(c, CC);
-    }
+    assertReviewers(c, REVIEWER, admin, user);
+    assertReviewers(c, CC, users.subList(0, mediumGroupSize));
   }
 
   @Test
   public void noteDbAddReviewerToReviewerChangeInfo() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
     AddReviewerInput in = new AddReviewerInput();
@@ -645,9 +566,6 @@
     assertThat(reviewerResult.reviewers).hasSize(1);
 
     // Repeat the above for CCs
-    if (!notesMigration.readChanges()) {
-      return;
-    }
     r = createChange();
     input = ReviewInput.approve().reviewer(group1, CC, false).reviewer(group2, CC, false);
     result = review(r.getChangeId(), r.getCommit().name(), input);
@@ -792,7 +710,6 @@
 
   @Test
   public void addExistingReviewerShortCircuits() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput input = new AddReviewerInput();
@@ -809,7 +726,6 @@
 
   @Test
   public void addExistingCcShortCircuits() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput input = new AddReviewerInput();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 9218336..28252e5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.common.data.Permission.READ;
 import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -276,8 +275,6 @@
 
   @Test
   public void noteDbCommit() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     ChangeInfo c = assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
     try (Repository repo = repoManager.openRepository(project);
         RevWalk rw = new RevWalk(repo)) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
index 864f08d..47ec0d2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 import static java.util.Objects.requireNonNull;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -33,17 +32,11 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.testing.TestTimeUtil;
 import org.junit.AfterClass;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 @NoHttpd
 public class HashtagsIT extends AbstractDaemonTest {
-  @Before
-  public void before() {
-    assume().that(notesMigration.readChanges()).isTrue();
-  }
-
   @BeforeClass
   public static void setTimeForTesting() {
     TestTimeUtil.resetWithClockStep(1, SECONDS);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
index 936feed..c2d3817 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
@@ -130,7 +130,7 @@
   public void pushDraftsWithPrivateByDefaultAndDisablePrivateChangesTrue() throws Exception {
     setPrivateByDefault(project2, InheritableBoolean.TRUE);
 
-    RevCommit initialHead = getRemoteHead();
+    RevCommit initialHead = getRemoteHead(project2, "master");
     TestRepository<InMemoryRepository> testRepo = cloneProject(project2);
     PushOneCommit.Result result =
         pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%draft");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index 8160d9a..8a81d6d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
 import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
 
@@ -31,19 +30,13 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.TestSubmitInput;
 import com.google.gerrit.server.git.ChangeMessageModifier;
 import com.google.gerrit.server.submit.CommitMergeStatus;
 import com.google.inject.Inject;
 import java.util.List;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
 import org.junit.Test;
 
 public class SubmitByCherryPickIT extends AbstractSubmit {
@@ -389,76 +382,4 @@
         change2.getChangeId(),
         headAfterFirstSubmit.name());
   }
-
-  @Test
-  public void repairChangeStateAfterFailure() throws Exception {
-    // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
-    assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
-    RevCommit initialHead = getRemoteHead();
-    PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
-    submit(change.getChangeId());
-
-    RevCommit headAfterFirstSubmit = getRemoteHead();
-    testRepo.reset(initialHead);
-    PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
-    Change.Id id2 = change2.getChange().getId();
-    TestSubmitInput failInput = new TestSubmitInput();
-    failInput.failAfterRefUpdates = true;
-    submit(
-        change2.getChangeId(),
-        failInput,
-        ResourceConflictException.class,
-        "Failing after ref updates");
-    RevCommit headAfterFailedSubmit = getRemoteHead();
-
-    // Bad: ref advanced but change wasn't updated.
-    PatchSet.Id psId1 = new PatchSet.Id(id2, 1);
-    PatchSet.Id psId2 = new PatchSet.Id(id2, 2);
-    ChangeInfo info = gApi.changes().id(id2.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.NEW);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-    assertThat(getPatchSet(psId2)).isNull();
-
-    ObjectId rev2;
-    try (Repository repo = repoManager.openRepository(project);
-        RevWalk rw = new RevWalk(repo)) {
-      ObjectId rev1 = repo.exactRef(psId1.toRefName()).getObjectId();
-      assertThat(rev1).isNotNull();
-
-      rev2 = repo.exactRef(psId2.toRefName()).getObjectId();
-      assertThat(rev2).isNotNull();
-      assertThat(rev2).isNotEqualTo(rev1);
-      assertThat(rw.parseCommit(rev2).getParent(0)).isEqualTo(headAfterFirstSubmit);
-
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
-    }
-
-    submit(change2.getChangeId());
-
-    // Change status and patch set entities were updated, and branch tip stayed
-    // the same.
-    RevCommit headAfterSecondSubmit = getRemoteHead();
-    assertThat(headAfterSecondSubmit).isEqualTo(headAfterFailedSubmit);
-    info = gApi.changes().id(id2.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(2);
-    PatchSet ps2 = getPatchSet(psId2);
-    assertThat(ps2).isNotNull();
-    assertThat(ps2.getRevision().get()).isEqualTo(rev2.name());
-    assertThat(Iterables.getLast(info.messages).message)
-        .isEqualTo(
-            "Change has been successfully cherry-picked as " + rev2.name() + " by Administrator");
-
-    try (Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
-    }
-
-    assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
-    assertChangeMergedEvents(
-        change.getChangeId(),
-        headAfterFirstSubmit.name(),
-        change2.getChangeId(),
-        headAfterSecondSubmit.name());
-  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
index ea8b98a..ccb684c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -15,26 +15,16 @@
 package com.google.gerrit.acceptance.rest.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 
-import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.GitUtil;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.extensions.client.ChangeStatus;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.ActionInfo;
-import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.TestSubmitInput;
 import java.util.Map;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.transport.PushResult;
 import org.junit.Test;
 
@@ -146,52 +136,6 @@
   }
 
   @Test
-  public void repairChangeStateAfterFailure() throws Exception {
-    // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
-    assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
-    PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
-    Change.Id id = change.getChange().getId();
-    TestSubmitInput failInput = new TestSubmitInput();
-    failInput.failAfterRefUpdates = true;
-    submit(
-        change.getChangeId(),
-        failInput,
-        ResourceConflictException.class,
-        "Failing after ref updates");
-
-    // Bad: ref advanced but change wasn't updated.
-    PatchSet.Id psId = new PatchSet.Id(id, 1);
-    ChangeInfo info = gApi.changes().id(id.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.NEW);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-
-    ObjectId rev;
-    try (Repository repo = repoManager.openRepository(project);
-        RevWalk rw = new RevWalk(repo)) {
-      rev = repo.exactRef(psId.toRefName()).getObjectId();
-      assertThat(rev).isNotNull();
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev);
-    }
-
-    submit(change.getChangeId());
-
-    // Change status was updated, and branch tip stayed the same.
-    info = gApi.changes().id(id.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
-    assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-    assertThat(Iterables.getLast(info.messages).message)
-        .isEqualTo("Change has been successfully merged by Administrator");
-
-    try (Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev);
-    }
-
-    assertRefUpdatedEvents();
-    assertChangeMergedEvents(change.getChangeId(), getRemoteHead().name());
-  }
-
-  @Test
   public void submitSameCommitsAsInExperimentalBranch() throws Exception {
     RevCommit initialHead = getRemoteHead();
 
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index 0d40a1c..a352baa 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
 import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
 import static java.util.stream.Collectors.groupingBy;
@@ -946,7 +945,6 @@
 
   @Test
   public void jsonCommentHasLegacyFormatFalse() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     PushOneCommit.Result result = createChange();
     Change.Id changeId = result.getChange().getId();
     addComment(result.getChangeId(), "comment");
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index 2af90a8..e377a86 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.server.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.common.ProblemInfo.Status.FIXED;
 import static com.google.gerrit.extensions.common.ProblemInfo.Status.FIX_FAILED;
 import static com.google.gerrit.testing.TestChanges.newPatchSet;
@@ -35,7 +34,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
@@ -52,8 +50,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.NoteDbMode;
 import com.google.gerrit.testing.TestChanges;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -97,11 +93,6 @@
   private ConsistencyChecker checker;
   private TestRepository<InMemoryRepository> serverSideTestRepo;
 
-  private void assumeNoteDbDisabled() {
-    assume().that(notesMigration.readChanges()).isFalse();
-    assume().that(NoteDbMode.get()).isNotEqualTo(NoteDbMode.CHECK);
-  }
-
   @Before
   public void setUp() throws Exception {
     serverSideTestRepo =
@@ -134,38 +125,6 @@
     assertProblems(notes, null, problem("Missing change owner: " + owner.getId()));
   }
 
-  @Test
-  public void missingRepo() throws Exception {
-    // NoteDb can't have a change without a repo.
-    assumeNoteDbDisabled();
-
-    ChangeNotes notes = insertChange();
-    Project.NameKey name = notes.getProjectName();
-    ((InMemoryRepositoryManager) repoManager).deleteRepository(name);
-    assertThat(checker.check(notes, null).problems())
-        .containsExactly(problem("Destination repository not found: " + name));
-  }
-
-  @Test
-  public void invalidRevision() throws Exception {
-    // NoteDb always parses the revision when inserting a patch set, so we can't
-    // create an invalid patch set.
-    assumeNoteDbDisabled();
-
-    ChangeNotes notes = insertChange();
-    PatchSet ps =
-        newPatchSet(
-            notes.getChange().currentPatchSetId(),
-            "fooooooooooooooooooooooooooooooooooooooo",
-            adminId);
-    db.patchSets().update(singleton(ps));
-
-    assertProblems(
-        notes,
-        null,
-        problem("Invalid revision on patch set 1: fooooooooooooooooooooooooooooooooooooooo"));
-  }
-
   // No test for ref existing but object missing; InMemoryRepository won't let
   // us do such a thing.
 
@@ -282,17 +241,10 @@
   public void onlyPatchSetObjectMissingWithFix() throws Exception {
     Change c = TestChanges.newChange(project, admin.getId(), sequences.nextChangeId());
 
-    // Set review started, mimicking Schema_153, so tests pass with NoteDbMode.CHECK.
-    c.setReviewStarted(true);
-
     PatchSet.Id psId = c.currentPatchSetId();
     String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
     PatchSet ps = newPatchSet(psId, rev, adminId);
 
-    if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
-      db.changes().insert(singleton(c));
-      db.patchSets().insert(singleton(ps));
-    }
     addNoteDbCommit(
         c.getId(),
         "Create change\n"
@@ -331,16 +283,6 @@
   }
 
   @Test
-  public void currentPatchSetMissing() throws Exception {
-    // NoteDb can't create a change without a patch set.
-    assumeNoteDbDisabled();
-
-    ChangeNotes notes = insertChange();
-    db.patchSets().deleteKeys(singleton(notes.getChange().currentPatchSetId()));
-    assertProblems(notes, null, problem("Current patch set 1 not found"));
-  }
-
-  @Test
   public void duplicatePatchSetRevisions() throws Exception {
     ChangeNotes notes = insertChange();
     PatchSet ps1 = psUtil.current(db, notes);
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index 8e8aeac..fd5afef 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -622,7 +622,8 @@
 
   private void assertRelated(PatchSet.Id psId, RelatedChangeAndCommitInfo... expected)
       throws Exception {
-    List<RelatedChangeAndCommitInfo> actual = getRelated(psId);
+    List<RelatedChangeAndCommitInfo> actual =
+        gApi.changes().id(psId.getParentKey().get()).revision(psId.get()).related().changes;
     assertThat(actual).named("related to " + psId).hasSize(expected.length);
     for (int i = 0; i < actual.size(); i++) {
       String name = "index " + i + " related to " + psId;
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index 209d0a2..35d9e88 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.acceptance.server.mail;
 
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.ALL;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.NONE;
 import static com.google.gerrit.extensions.api.changes.NotifyHandling.OWNER;
@@ -255,31 +254,7 @@
    * AddReviewerSender tests.
    */
 
-  private void addReviewerToReviewableChangeInReviewDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(sc.reviewer, sc.ccer)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .noOneElse();
-  }
-
-  @Test
-  public void addReviewerToReviewableChangeInReviewDbSingly() throws Exception {
-    addReviewerToReviewableChangeInReviewDb(singly());
-  }
-
-  @Test
-  public void addReviewerToReviewableChangeInReviewDbBatch() throws Exception {
-    addReviewerToReviewableChangeInReviewDb(batch());
-  }
-
-  private void addReviewerToReviewableChangeInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerToReviewableChange(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
@@ -293,17 +268,16 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbSingly() throws Exception {
-    addReviewerToReviewableChangeInNoteDb(singly());
+  public void addReviewerToReviewableChangeSingly() throws Exception {
+    addReviewerToReviewableChange(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbBatch() throws Exception {
-    addReviewerToReviewableChangeInNoteDb(batch());
+  public void addReviewerToReviewableChangeBatch() throws Exception {
+    addReviewerToReviewableChange(batch());
   }
 
-  private void addReviewerToReviewableChangeByOwnerCcingSelfInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerToReviewableChangeByOwnerCcingSelf(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, null);
@@ -317,17 +291,16 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeByOwnerCcingSelfInNoteDbSingly() throws Exception {
-    addReviewerToReviewableChangeByOwnerCcingSelfInNoteDb(singly());
+  public void addReviewerToReviewableChangeByOwnerCcingSelfSingly() throws Exception {
+    addReviewerToReviewableChangeByOwnerCcingSelf(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeByOwnerCcingSelfInNoteDbBatch() throws Exception {
-    addReviewerToReviewableChangeByOwnerCcingSelfInNoteDb(batch());
+  public void addReviewerToReviewableChangeByOwnerCcingSelfBatch() throws Exception {
+    addReviewerToReviewableChangeByOwnerCcingSelf(batch());
   }
 
-  private void addReviewerToReviewableChangeByOtherInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerToReviewableChangeByOther(Adder adder) throws Exception {
     TestAccount other = accountCreator.create("other", "other@example.com", "other");
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
@@ -342,17 +315,16 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeByOtherInNoteDbSingly() throws Exception {
-    addReviewerToReviewableChangeByOtherInNoteDb(singly());
+  public void addReviewerToReviewableChangeByOtherSingly() throws Exception {
+    addReviewerToReviewableChangeByOther(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeByOtherInNoteDbBatch() throws Exception {
-    addReviewerToReviewableChangeByOtherInNoteDb(batch());
+  public void addReviewerToReviewableChangeByOtherBatch() throws Exception {
+    addReviewerToReviewableChangeByOther(batch());
   }
 
-  private void addReviewerToReviewableChangeByOtherCcingSelfInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerToReviewableChangeByOtherCcingSelf(Adder adder) throws Exception {
     TestAccount other = accountCreator.create("other", "other@example.com", "other");
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
@@ -367,35 +339,16 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeByOtherCcingSelfInNoteDbSingly() throws Exception {
-    addReviewerToReviewableChangeByOtherCcingSelfInNoteDb(singly());
+  public void addReviewerToReviewableChangeByOtherCcingSelfSingly() throws Exception {
+    addReviewerToReviewableChangeByOtherCcingSelf(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeByOtherCcingSelfInNoteDbBatch() throws Exception {
-    addReviewerToReviewableChangeByOtherCcingSelfInNoteDb(batch());
+  public void addReviewerToReviewableChangeByOtherCcingSelfBatch() throws Exception {
+    addReviewerToReviewableChangeByOtherCcingSelf(batch());
   }
 
-  private void addReviewerByEmailToReviewableChangeInReviewDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    String email = "addedbyemail@example.com";
-    StagedChange sc = stageReviewableChange();
-    addReviewer(adder, sc.changeId, sc.owner, email);
-    assertThat(sender).notSent();
-  }
-
-  @Test
-  public void addReviewerByEmailToReviewableChangeInReviewDbSingly() throws Exception {
-    addReviewerByEmailToReviewableChangeInReviewDb(singly());
-  }
-
-  @Test
-  public void addReviewerByEmailToReviewableChangeInReviewDbBatch() throws Exception {
-    addReviewerByEmailToReviewableChangeInReviewDb(batch());
-  }
-
-  private void addReviewerByEmailToReviewableChangeInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerByEmailToReviewableChange(Adder adder) throws Exception {
     String email = "addedbyemail@example.com";
     StagedChange sc = stageReviewableChange();
     addReviewer(adder, sc.changeId, sc.owner, email);
@@ -409,13 +362,13 @@
   }
 
   @Test
-  public void addReviewerByEmailToReviewableChangeInNoteDbSingly() throws Exception {
-    addReviewerByEmailToReviewableChangeInNoteDb(singly());
+  public void addReviewerByEmailToReviewableChangeSingly() throws Exception {
+    addReviewerByEmailToReviewableChange(singly());
   }
 
   @Test
-  public void addReviewerByEmailToReviewableChangeInNoteDbBatch() throws Exception {
-    addReviewerByEmailToReviewableChangeInNoteDb(batch());
+  public void addReviewerByEmailToReviewableChangeBatch() throws Exception {
+    addReviewerByEmailToReviewableChange(batch());
   }
 
   private void addReviewerToWipChange(Adder adder) throws Exception {
@@ -452,8 +405,7 @@
     addReviewerToReviewableWipChange(batch());
   }
 
-  private void addReviewerToWipChangeInNoteDbNotifyAll(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerToWipChangeNotifyAll(Adder adder) throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email, NotifyHandling.ALL);
@@ -467,41 +419,16 @@
   }
 
   @Test
-  public void addReviewerToWipChangeInNoteDbNotifyAllSingly() throws Exception {
-    addReviewerToWipChangeInNoteDbNotifyAll(singly());
+  public void addReviewerToWipChangeNotifyAllSingly() throws Exception {
+    addReviewerToWipChangeNotifyAll(singly());
   }
 
   @Test
-  public void addReviewerToWipChangeInNoteDbNotifyAllBatch() throws Exception {
-    addReviewerToWipChangeInNoteDbNotifyAll(batch());
+  public void addReviewerToWipChangeNotifyAllBatch() throws Exception {
+    addReviewerToWipChangeNotifyAll(batch());
   }
 
-  private void addReviewerToWipChangeInReviewDbNotifyAll(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email, NotifyHandling.ALL);
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(reviewer)
-        .cc(sc.reviewer, sc.ccer)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .noOneElse();
-  }
-
-  @Test
-  public void addReviewerToWipChangeInReviewDbNotifyAllSingly() throws Exception {
-    addReviewerToWipChangeInReviewDbNotifyAll(singly());
-  }
-
-  @Test
-  public void addReviewerToWipChangeInReviewDbNotifyAllBatch() throws Exception {
-    addReviewerToWipChangeInReviewDbNotifyAll(batch());
-  }
-
-  private void addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewers(Adder adder)
-      throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addReviewerToReviewableChangeNotifyOwnerReviewers(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email, OWNER_REVIEWERS);
@@ -515,18 +442,17 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewersSingly() throws Exception {
-    addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewers(singly());
+  public void addReviewerToReviewableChangeNotifyOwnerReviewersSingly() throws Exception {
+    addReviewerToReviewableChangeNotifyOwnerReviewers(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewersBatch() throws Exception {
-    addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewers(batch());
+  public void addReviewerToReviewableChangeNotifyOwnerReviewersBatch() throws Exception {
+    addReviewerToReviewableChangeNotifyOwnerReviewers(batch());
   }
 
-  private void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwner(Adder adder)
+  private void addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwner(Adder adder)
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, OWNER);
@@ -534,20 +460,17 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwnerSingly()
-      throws Exception {
-    addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwner(singly());
+  public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwnerSingly() throws Exception {
+    addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwner(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwnerBatch()
-      throws Exception {
-    addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwner(batch());
+  public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwnerBatch() throws Exception {
+    addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwner(batch());
   }
 
-  private void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNone(Adder adder)
+  private void addReviewerToReviewableChangeByOwnerCcingSelfNotifyNone(Adder adder)
       throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
     addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, NONE);
@@ -555,19 +478,16 @@
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNoneSingly()
-      throws Exception {
-    addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNone(singly());
+  public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyNoneSingly() throws Exception {
+    addReviewerToReviewableChangeByOwnerCcingSelfNotifyNone(singly());
   }
 
   @Test
-  public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNoneBatch()
-      throws Exception {
-    addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNone(batch());
+  public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyNoneBatch() throws Exception {
+    addReviewerToReviewableChangeByOwnerCcingSelfNotifyNone(batch());
   }
 
-  private void addNonUserReviewerByEmailInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addNonUserReviewerByEmail(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     addReviewer(adder, sc.changeId, sc.owner, "nonexistent@example.com");
     assertThat(sender)
@@ -579,17 +499,16 @@
   }
 
   @Test
-  public void addNonUserReviewerByEmailInNoteDbSingly() throws Exception {
-    addNonUserReviewerByEmailInNoteDb(singly(ReviewerState.REVIEWER));
+  public void addNonUserReviewerByEmailSingly() throws Exception {
+    addNonUserReviewerByEmail(singly(ReviewerState.REVIEWER));
   }
 
   @Test
-  public void addNonUserReviewerByEmailInNoteDbBatch() throws Exception {
-    addNonUserReviewerByEmailInNoteDb(batch(ReviewerState.REVIEWER));
+  public void addNonUserReviewerByEmailBatch() throws Exception {
+    addNonUserReviewerByEmail(batch(ReviewerState.REVIEWER));
   }
 
-  private void addNonUserCcByEmailInNoteDb(Adder adder) throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  private void addNonUserCcByEmail(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     addReviewer(adder, sc.changeId, sc.owner, "nonexistent@example.com");
     assertThat(sender)
@@ -601,13 +520,13 @@
   }
 
   @Test
-  public void addNonUserCcByEmailInNoteDbSingly() throws Exception {
-    addNonUserCcByEmailInNoteDb(singly(ReviewerState.CC));
+  public void addNonUserCcByEmailSingly() throws Exception {
+    addNonUserCcByEmail(singly(ReviewerState.CC));
   }
 
   @Test
-  public void addNonUserCcByEmailInNoteDbBatch() throws Exception {
-    addNonUserCcByEmailInNoteDb(batch(ReviewerState.CC));
+  public void addNonUserCcByEmailBatch() throws Exception {
+    addNonUserCcByEmail(batch(ReviewerState.CC));
   }
 
   private interface Adder {
@@ -923,8 +842,7 @@
   }
 
   @Test
-  public void addReviewerOnWipChangeAndStartReviewInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void addReviewerOnWipChangeAndStartReview() throws Exception {
     StagedChange sc = stageWipChange();
     ReviewInput in = ReviewInput.noScore().reviewer(other.email).setWorkInProgress(false);
     gApi.changes().id(sc.changeId).revision("current").review(in);
@@ -946,28 +864,6 @@
   }
 
   @Test
-  public void addReviewerOnWipChangeAndStartReviewInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    ReviewInput in = ReviewInput.noScore().reviewer(other.email).setWorkInProgress(false);
-    gApi.changes().id(sc.changeId).revision("current").review(in);
-    assertThat(sender)
-        .sent("comment", sc)
-        .cc(sc.reviewer, sc.ccer, other)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .bcc(sc.starrer)
-        .bcc(ALL_COMMENTS)
-        .noOneElse();
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(other)
-        .cc(sc.reviewer, sc.ccer)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .noOneElse();
-    assertThat(sender).notSent();
-  }
-
-  @Test
   public void startReviewMessageNotRepeated() throws Exception {
     // TODO(logan): Remove this test check once PolyGerrit workaround is rolled back.
     StagedChange sc = stageWipChange();
@@ -1094,18 +990,12 @@
             users -> ImmutableList.of("r=" + users.reviewer.username, "cc=" + users.ccer.username));
     FakeEmailSenderSubject subject =
         assertThat(sender).sent("newchange", spc).to(spc.reviewer, spc.watchingProjectOwner);
-    if (notesMigration.readChanges()) {
-      subject.cc(spc.ccer);
-    } else {
-      // CCs are considered reviewers in the storage layer.
-      subject.to(spc.ccer);
-    }
+    subject.cc(spc.ccer);
     subject.bcc(NEW_CHANGES, NEW_PATCHSETS).noOneElse();
   }
 
   @Test
-  public void createReviewableChangeWithReviewersAndCcsByEmailInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void createReviewableChangeWithReviewersAndCcsByEmail() throws Exception {
     StagedPreChange spc =
         stagePreChange(
             "refs/for/master",
@@ -1290,8 +1180,7 @@
   }
 
   @Test
-  public void deleteReviewerByEmailFromWipChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void deleteReviewerByEmailFromWipChange() throws Exception {
     StagedChange sc = stageWipChangeWithExtraReviewer();
     gApi.changes().id(sc.changeId).reviewer(sc.reviewerByEmail).remove();
     assertThat(sender).notSent();
@@ -1650,8 +1539,7 @@
    */
 
   @Test
-  public void newPatchSetByOwnerOnReviewableChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetByOwnerOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     pushTo(sc, "refs/for/master", sc.owner);
     assertThat(sender)
@@ -1665,21 +1553,7 @@
   }
 
   @Test
-  public void newPatchSetByOwnerOnReviewableChangeInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    pushTo(sc, "refs/for/master", sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void newPatchSetByOtherOnReviewableChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetByOtherOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     pushTo(sc, "refs/for/master", other);
     assertThat(sender)
@@ -1694,22 +1568,7 @@
   }
 
   @Test
-  public void newPatchSetByOtherOnReviewableChangeInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    pushTo(sc, "refs/for/master", other);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .notTo(sc.owner) // TODO(logan): This email shouldn't come from the owner.
-        .to(sc.reviewer, sc.ccer, other)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetByOtherOnReviewableChangeOwnerSelfCc() throws Exception {
     StagedChange sc = stageReviewableChange();
     pushTo(sc, "refs/for/master", other, EmailStrategy.CC_ON_OWN_COMMENTS);
     assertThat(sender)
@@ -1724,22 +1583,7 @@
   }
 
   @Test
-  public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    pushTo(sc, "refs/for/master", other, EmailStrategy.CC_ON_OWN_COMMENTS);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
-        .to(sc.reviewer, sc.ccer, other)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void newPatchSetByOtherOnReviewableChangeNotifyOwnerReviewersInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetByOtherOnReviewableChangeNotifyOwnerReviewers() throws Exception {
     StagedChange sc = stageReviewableChange();
     pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other);
     assertThat(sender)
@@ -1753,23 +1597,8 @@
   }
 
   @Test
-  public void newPatchSetByOtherOnReviewableChangeNotifyOwnerReviewersInReviewDb()
+  public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewers()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
-        .to(sc.reviewer, sc.ccer)
-        .to(other)
-        .noOneElse();
-  }
-
-  @Test
-  public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInNoteDb()
-      throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     StagedChange sc = stageReviewableChange();
     pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other, EmailStrategy.CC_ON_OWN_COMMENTS);
     assertThat(sender)
@@ -1783,20 +1612,6 @@
   }
 
   @Test
-  public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInReviewDb()
-      throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other, EmailStrategy.CC_ON_OWN_COMMENTS);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .to(other)
-        .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
-        .noOneElse();
-  }
-
-  @Test
   public void newPatchSetByOtherOnReviewableChangeNotifyOwner() throws Exception {
     StagedChange sc = stageReviewableChange();
     pushTo(sc, "refs/for/master%notify=OWNER", other);
@@ -1843,8 +1658,7 @@
   }
 
   @Test
-  public void newPatchSetOnWipChangeNotifyAllInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetOnWipChangeNotifyAll() throws Exception {
     StagedChange sc = stageWipChange();
     pushTo(sc, "refs/for/master%wip,notify=ALL", sc.owner);
     assertThat(sender)
@@ -1858,21 +1672,7 @@
   }
 
   @Test
-  public void newPatchSetOnWipChangeNotifyAllInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    pushTo(sc, "refs/for/master%wip,notify=ALL", sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void newPatchSetOnWipChangeToReadyInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetOnWipChangeToReady() throws Exception {
     StagedChange sc = stageWipChange();
     pushTo(sc, "refs/for/master%ready", sc.owner);
     assertThat(sender)
@@ -1886,19 +1686,6 @@
   }
 
   @Test
-  public void newPatchSetOnWipChangeToReadyInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    pushTo(sc, "refs/for/master%ready", sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
   public void newPatchSetOnReviewableWipChange() throws Exception {
     StagedChange sc = stageReviewableWipChange();
     pushTo(sc, "refs/for/master%wip", sc.owner);
@@ -1906,8 +1693,7 @@
   }
 
   @Test
-  public void newPatchSetOnReviewableChangeAddingReviewerInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetOnReviewableChangeAddingReviewer() throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount newReviewer = sc.testAccount("newReviewer");
     pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
@@ -1923,22 +1709,6 @@
   }
 
   @Test
-  public void newPatchSetOnReviewableChangeAddingReviewerInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    TestAccount newReviewer = sc.testAccount("newReviewer");
-    pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer, newReviewer)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-    assertThat(sender).notSent();
-  }
-
-  @Test
   public void newPatchSetOnWipChangeAddingReviewer() throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount newReviewer = sc.testAccount("newReviewer");
@@ -1947,8 +1717,7 @@
   }
 
   @Test
-  public void newPatchSetOnWipChangeAddingReviewerNotifyAllInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetOnWipChangeAddingReviewerNotifyAll() throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount newReviewer = sc.testAccount("newReviewer");
     pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username, sc.owner);
@@ -1964,24 +1733,7 @@
   }
 
   @Test
-  public void newPatchSetOnWipChangeAddingReviewerNotifyAllInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    TestAccount newReviewer = sc.testAccount("newReviewer");
-    pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username, sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer, newReviewer)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-    assertThat(sender).notSent();
-  }
-
-  @Test
-  public void newPatchSetOnWipChangeSettingReadyInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void newPatchSetOnWipChangeSettingReady() throws Exception {
     StagedChange sc = stageWipChange();
     pushTo(sc, "refs/for/master%ready", sc.owner);
     assertThat(sender)
@@ -1995,21 +1747,6 @@
     assertThat(sender).notSent();
   }
 
-  @Test
-  public void newPatchSetOnWipChangeSettingReadyInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    pushTo(sc, "refs/for/master%ready", sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .cc(sc.reviewerByEmail, sc.ccerByEmail)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-    assertThat(sender).notSent();
-  }
-
   private void pushTo(StagedChange sc, String ref, TestAccount by) throws Exception {
     pushTo(sc, ref, by, ENABLED);
   }
@@ -2021,8 +1758,7 @@
   }
 
   @Test
-  public void editCommitMessageEditByOwnerOnReviewableChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void editCommitMessageEditByOwnerOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, sc.owner);
     assertThat(sender)
@@ -2036,21 +1772,7 @@
   }
 
   @Test
-  public void editCommitMessageEditByOwnerOnReviewableChangeInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    editCommitMessage(sc, sc.owner);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void editCommitMessageEditByOtherOnReviewableChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void editCommitMessageEditByOtherOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, other);
     assertThat(sender)
@@ -2064,21 +1786,7 @@
   }
 
   @Test
-  public void editCommitMessageEditByOtherOnReviewableChangeInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    editCommitMessage(sc, other);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.owner, sc.reviewer, sc.ccer)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCc() throws Exception {
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, other, CC_ON_OWN_COMMENTS);
     assertThat(sender)
@@ -2092,22 +1800,7 @@
   }
 
   @Test
-  public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    editCommitMessage(sc, other, CC_ON_OWN_COMMENTS);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.owner, sc.reviewer, sc.ccer, other)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
-  @Test
-  public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewersInNoteDb()
-      throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewers() throws Exception {
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, other, OWNER_REVIEWERS);
     assertThat(sender)
@@ -2119,18 +1812,8 @@
   }
 
   @Test
-  public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewersInReviewDb()
+  public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewers()
       throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    editCommitMessage(sc, other, OWNER_REVIEWERS);
-    assertThat(sender).sent("newpatchset", sc).to(sc.owner, sc.reviewer, sc.ccer).noOneElse();
-  }
-
-  @Test
-  public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInNoteDb()
-      throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, other, OWNER_REVIEWERS, CC_ON_OWN_COMMENTS);
     assertThat(sender)
@@ -2142,19 +1825,6 @@
   }
 
   @Test
-  public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInReviewDb()
-      throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    editCommitMessage(sc, other, OWNER_REVIEWERS, CC_ON_OWN_COMMENTS);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.owner, sc.reviewer, sc.ccer)
-        .cc(other)
-        .noOneElse();
-  }
-
-  @Test
   public void editCommitMessageByOtherOnReviewableChangeNotifyOwner() throws Exception {
     StagedChange sc = stageReviewableChange();
     editCommitMessage(sc, other, OWNER);
@@ -2204,8 +1874,7 @@
   }
 
   @Test
-  public void editCommitMessageOnWipChangeNotifyAllInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void editCommitMessageOnWipChangeNotifyAll() throws Exception {
     StagedChange sc = stageWipChange();
     editCommitMessage(sc, sc.owner, ALL);
     assertThat(sender)
@@ -2218,19 +1887,6 @@
         .noOneElse();
   }
 
-  @Test
-  public void editCommitMessageOnWipChangeNotifyAllInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageWipChange();
-    editCommitMessage(sc, sc.owner, ALL);
-    assertThat(sender)
-        .sent("newpatchset", sc)
-        .to(sc.reviewer, sc.ccer)
-        .bcc(sc.starrer)
-        .bcc(NEW_PATCHSETS)
-        .noOneElse();
-  }
-
   private void editCommitMessage(StagedChange sc, TestAccount by) throws Exception {
     editCommitMessage(sc, by, null, ENABLED);
   }
@@ -2357,31 +2013,7 @@
    */
 
   @Test
-  public void revertChangeByOwnerInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageChange();
-    revert(sc, sc.owner);
-
-    // email for the newly created revert change
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
-        .bcc(NEW_CHANGES, NEW_PATCHSETS)
-        .noOneElse();
-
-    // email for the change that is reverted
-    assertThat(sender)
-        .sent("revert", sc)
-        .cc(sc.reviewer, sc.ccer, admin)
-        .cc(sc.reviewerByEmail)
-        .bcc(sc.starrer)
-        .bcc(ALL_COMMENTS)
-        .noOneElse();
-  }
-
-  @Test
-  public void revertChangeByOwnerInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void revertChangeByOwner() throws Exception {
     StagedChange sc = stageChange();
     revert(sc, sc.owner);
 
@@ -2404,33 +2036,7 @@
   }
 
   @Test
-  public void revertChangeByOwnerCcingSelfInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageChange();
-    revert(sc, sc.owner, CC_ON_OWN_COMMENTS);
-
-    // email for the newly created revert change
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
-        .cc(sc.owner)
-        .bcc(NEW_CHANGES, NEW_PATCHSETS)
-        .noOneElse();
-
-    // email for the change that is reverted
-    assertThat(sender)
-        .sent("revert", sc)
-        .to(sc.owner)
-        .cc(sc.reviewer, sc.ccer, admin)
-        .cc(sc.reviewerByEmail)
-        .bcc(sc.starrer)
-        .bcc(ALL_COMMENTS)
-        .noOneElse();
-  }
-
-  @Test
-  public void revertChangeByOwnerCcingSelfInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void revertChangeByOwnerCcingSelf() throws Exception {
     StagedChange sc = stageChange();
     revert(sc, sc.owner, CC_ON_OWN_COMMENTS);
 
@@ -2454,32 +2060,7 @@
   }
 
   @Test
-  public void revertChangeByOtherInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageChange();
-    revert(sc, other);
-
-    // email for the newly created revert change
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(sc.owner, sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
-        .bcc(NEW_CHANGES, NEW_PATCHSETS)
-        .noOneElse();
-
-    // email for the change that is reverted
-    assertThat(sender)
-        .sent("revert", sc)
-        .to(sc.owner)
-        .cc(sc.reviewer, sc.ccer, admin)
-        .cc(sc.reviewerByEmail)
-        .bcc(sc.starrer)
-        .bcc(ALL_COMMENTS)
-        .noOneElse();
-  }
-
-  @Test
-  public void revertChangeByOtherInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void revertChangeByOther() throws Exception {
     StagedChange sc = stageChange();
     revert(sc, other);
 
@@ -2503,33 +2084,7 @@
   }
 
   @Test
-  public void revertChangeByOtherCcingSelfInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageChange();
-    revert(sc, other, CC_ON_OWN_COMMENTS);
-
-    // email for the newly created revert change
-    assertThat(sender)
-        .sent("newchange", sc)
-        .to(sc.owner, sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
-        .cc(other)
-        .bcc(NEW_CHANGES, NEW_PATCHSETS)
-        .noOneElse();
-
-    // email for the change that is reverted
-    assertThat(sender)
-        .sent("revert", sc)
-        .to(sc.owner)
-        .cc(other, sc.reviewer, sc.ccer, admin)
-        .cc(sc.reviewerByEmail)
-        .bcc(sc.starrer)
-        .bcc(ALL_COMMENTS)
-        .noOneElse();
-  }
-
-  @Test
-  public void revertChangeByOtherCcingSelfInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void revertChangeByOtherCcingSelf() throws Exception {
     StagedChange sc = stageChange();
     revert(sc, other, CC_ON_OWN_COMMENTS);
 
@@ -2623,8 +2178,7 @@
   }
 
   @Test
-  public void setAssigneeToSelfOnReviewableChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void setAssigneeToSelfOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     assign(sc, sc.owner, sc.owner);
     assertThat(sender)
@@ -2634,14 +2188,6 @@
   }
 
   @Test
-  public void setAssigneeToSelfOnReviewableChangeInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    assign(sc, sc.owner, sc.owner);
-    assertThat(sender).notSent();
-  }
-
-  @Test
   public void changeAssigneeOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount other = accountCreator.create("other", "other@example.com", "other");
@@ -2656,8 +2202,7 @@
   }
 
   @Test
-  public void changeAssigneeToSelfOnReviewableChangeInNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void changeAssigneeToSelfOnReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
     assign(sc, sc.owner, sc.assignee);
     sender.clear();
@@ -2669,16 +2214,6 @@
   }
 
   @Test
-  public void changeAssigneeToSelfOnReviewableChangeInReviewDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    StagedChange sc = stageReviewableChange();
-    assign(sc, sc.owner, sc.assignee);
-    sender.clear();
-    assign(sc, sc.owner, sc.owner);
-    assertThat(sender).notSent();
-  }
-
-  @Test
   public void setAssigneeOnReviewableWipChange() throws Exception {
     StagedChange sc = stageReviewableWipChange();
     assign(sc, sc.owner, sc.assignee);
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
deleted file mode 100644
index 29f1b7d..0000000
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ /dev/null
@@ -1,1598 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.server.notedb;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.reviewdb.client.RefNames.refsDraftComments;
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.concurrent.TimeUnit.DAYS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Ordering;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.acceptance.TestAccount;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.api.changes.DraftInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
-import com.google.gerrit.extensions.client.Side;
-import com.google.gerrit.extensions.common.CommentInfo;
-import com.google.gerrit.extensions.common.Input;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.testing.Util;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.restapi.change.PostReview;
-import com.google.gerrit.server.restapi.change.Rebuild;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gerrit.server.update.BatchUpdateOp;
-import com.google.gerrit.server.update.ChangeContext;
-import com.google.gerrit.server.update.UpdateException;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.NoteDbChecker;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gerrit.testing.TestChanges;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Timestamp;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
-import org.apache.http.Header;
-import org.apache.http.message.BasicHeader;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ChangeRebuilderIT extends AbstractDaemonTest {
-  @ConfigSuite.Default
-  public static Config defaultConfig() {
-    Config cfg = new Config();
-    cfg.setBoolean("noteDb", null, "testRebuilderWrapper", true);
-
-    // Disable async reindex-if-stale check after index update. This avoids
-    // unintentional auto-rebuilding of the change in NoteDb during the read
-    // path of the reindex-if-stale check. For the purposes of this test, we
-    // want precise control over when auto-rebuilding happens.
-    cfg.setBoolean("index", null, "autoReindexIfStale", false);
-
-    // setNotesMigration tries to keep IDs in sync between ReviewDb and NoteDb, which is behavior
-    // unique to this test. This gets prohibitively slow if we use the default sequence gap.
-    cfg.setInt("noteDb", "changes", "initialSequenceGap", 0);
-
-    return cfg;
-  }
-
-  @Inject private NoteDbChecker checker;
-
-  @Inject private Rebuild rebuildHandler;
-
-  @Inject private Provider<ReviewDb> dbProvider;
-
-  @Inject private CommentsUtil commentsUtil;
-
-  @Inject private Provider<PostReview> postReview;
-
-  @Inject private TestChangeRebuilderWrapper rebuilderWrapper;
-
-  @Inject private Sequences seq;
-
-  @Inject private ChangeBundleReader bundleReader;
-
-  @Inject private PatchSetInfoFactory patchSetInfoFactory;
-
-  @Inject private PatchListCache patchListCache;
-
-  @Before
-  public void setUp() throws Exception {
-    assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
-    TestTimeUtil.resetWithClockStep(1, SECONDS);
-    setNotesMigration(false, false);
-  }
-
-  @After
-  public void tearDown() {
-    TestTimeUtil.useSystemTime();
-  }
-
-  @SuppressWarnings("deprecation")
-  private void setNotesMigration(boolean writeChanges, boolean readChanges) throws Exception {
-    notesMigration.setWriteChanges(writeChanges);
-    notesMigration.setReadChanges(readChanges);
-    db = atrScope.reopenDb().getReviewDbProvider().get();
-
-    if (notesMigration.readChangeSequence()) {
-      // Copy next ReviewDb ID to NoteDb.
-      seq.getChangeIdRepoSequence().set(db.nextChangeId());
-    } else {
-      // Copy next NoteDb ID to ReviewDb.
-      while (db.nextChangeId() < seq.getChangeIdRepoSequence().next()) {}
-    }
-  }
-
-  @Test
-  public void changeFields() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    gApi.changes().id(id.get()).topic(name("a-topic"));
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void patchSets() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    amendChange(r.getChangeId());
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void publishedComment() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putComment(user, id, 1, "comment", null);
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void publishedCommentAndReply() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putComment(user, id, 1, "comment", null);
-    Map<String, List<CommentInfo>> comments = getPublishedComments(id);
-    String parentUuid = comments.get("a.txt").get(0).id;
-    putComment(user, id, 1, "comment", parentUuid);
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void patchSetWithNullGroups() throws Exception {
-    Timestamp ts = TimeUtil.nowTs();
-    Change c = TestChanges.newChange(project, user.getId(), seq.nextChangeId());
-    c.setCreatedOn(ts);
-    c.setLastUpdatedOn(ts);
-    c.setReviewStarted(true);
-    PatchSet ps =
-        TestChanges.newPatchSet(
-            c.currentPatchSetId(), "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", user.getId());
-    ps.setCreatedOn(ts);
-    db.changes().insert(Collections.singleton(c));
-    db.patchSets().insert(Collections.singleton(ps));
-
-    assertThat(ps.getGroups()).isEmpty();
-    checker.rebuildAndCheckChanges(c.getId());
-  }
-
-  @Test
-  public void draftComment() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "comment", null);
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void draftAndPublishedComment() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "draft comment", null);
-    putComment(user, id, 1, "published comment", null);
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void publishDraftComment() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "draft comment", null);
-    publishDrafts(user, id);
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void nullAccountId() throws Exception {
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId = r.getPatchSetId();
-    Change.Id id = psId.getParentKey();
-
-    // Events need to be otherwise identical for the account ID to be compared.
-    ChangeMessage msg1 = insertMessage(id, psId, user.getId(), TimeUtil.nowTs(), "message 1");
-    insertMessage(id, psId, null, msg1.getWrittenOn(), "message 2");
-
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void nullPatchSetId() throws Exception {
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId1 = r.getPatchSetId();
-    Change.Id id = psId1.getParentKey();
-
-    // Events need to be otherwise identical for the PatchSet.ID to be compared.
-    ChangeMessage msg1 = insertMessage(id, null, user.getId(), TimeUtil.nowTs(), "message 1");
-    insertMessage(id, null, user.getId(), msg1.getWrittenOn(), "message 2");
-
-    PatchSet.Id psId2 = amendChange(r.getChangeId()).getPatchSetId();
-
-    ChangeMessage msg3 = insertMessage(id, null, user.getId(), TimeUtil.nowTs(), "message 3");
-    insertMessage(id, null, user.getId(), msg3.getWrittenOn(), "message 4");
-
-    checker.rebuildAndCheckChanges(id);
-
-    setNotesMigration(true, true);
-
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    Map<String, PatchSet.Id> psIds = new HashMap<>();
-    for (ChangeMessage msg : notes.getChangeMessages()) {
-      PatchSet.Id psId = msg.getPatchSetId();
-      assertThat(psId).named("patchset for " + msg).isNotNull();
-      psIds.put(msg.getMessage(), psId);
-    }
-    // Patch set IDs were replaced during conversion process.
-    assertThat(psIds).containsEntry("message 1", psId1);
-    assertThat(psIds).containsEntry("message 2", psId1);
-    assertThat(psIds).containsEntry("message 3", psId2);
-    assertThat(psIds).containsEntry("message 4", psId2);
-  }
-
-  @Test
-  public void noWriteToNewRef() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    checker.assertNoChangeRef(project, id);
-
-    setNotesMigration(true, false);
-    gApi.changes().id(id.get()).topic(name("a-topic"));
-
-    // First write doesn't create the ref, but rebuilding works.
-    checker.assertNoChangeRef(project, id);
-    assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isNull();
-    checker.rebuildAndCheckChanges(id);
-
-    // Now that there is a ref, writes are "turned on" for this change, and
-    // NoteDb stays up to date without explicit rebuilding.
-    gApi.changes().id(id.get()).topic(name("new-topic"));
-    assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isNotNull();
-    checker.checkChanges(id);
-  }
-
-  @Test
-  public void restApiNotFoundWhenNoteDbDisabled() throws Exception {
-    PushOneCommit.Result r = createChange();
-    exception.expect(ResourceNotFoundException.class);
-    rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Input());
-  }
-
-  @Test
-  public void rebuildViaRestApi() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    setNotesMigration(true, false);
-
-    checker.assertNoChangeRef(project, id);
-    rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Input());
-    checker.checkChanges(id);
-  }
-
-  @Test
-  public void writeToNewRefForNewChange() throws Exception {
-    PushOneCommit.Result r1 = createChange();
-    Change.Id id1 = r1.getPatchSetId().getParentKey();
-
-    setNotesMigration(true, false);
-    gApi.changes().id(id1.get()).topic(name("a-topic"));
-    PushOneCommit.Result r2 = createChange();
-    Change.Id id2 = r2.getPatchSetId().getParentKey();
-
-    // Second change was created after NoteDb writes were turned on, so it was
-    // allowed to write to a new ref.
-    checker.checkChanges(id2);
-
-    // First change was created before NoteDb writes were turned on, so its meta
-    // ref doesn't exist until a manual rebuild.
-    checker.assertNoChangeRef(project, id1);
-    checker.rebuildAndCheckChanges(id1);
-  }
-
-  @Test
-  public void noteDbChangeState() throws Exception {
-    setNotesMigration(true, true);
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-
-    ObjectId changeMetaId = getMetaRef(project, changeMetaRef(id));
-    assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isEqualTo(changeMetaId.name());
-
-    putDraft(user, id, 1, "comment by user", null);
-    ObjectId userDraftsId = getMetaRef(allUsers, refsDraftComments(id, user.getId()));
-    assertThat(getUnwrappedDb().changes().get(id).getNoteDbState())
-        .isEqualTo(changeMetaId.name() + "," + user.getId() + "=" + userDraftsId.name());
-
-    putDraft(admin, id, 2, "comment by admin", null);
-    ObjectId adminDraftsId = getMetaRef(allUsers, refsDraftComments(id, admin.getId()));
-    assertThat(admin.getId().get()).isLessThan(user.getId().get());
-    assertThat(getUnwrappedDb().changes().get(id).getNoteDbState())
-        .isEqualTo(
-            changeMetaId.name()
-                + ","
-                + admin.getId()
-                + "="
-                + adminDraftsId.name()
-                + ","
-                + user.getId()
-                + "="
-                + userDraftsId.name());
-
-    putDraft(admin, id, 2, "revised comment by admin", null);
-    adminDraftsId = getMetaRef(allUsers, refsDraftComments(id, admin.getId()));
-    assertThat(getUnwrappedDb().changes().get(id).getNoteDbState())
-        .isEqualTo(
-            changeMetaId.name()
-                + ","
-                + admin.getId()
-                + "="
-                + adminDraftsId.name()
-                + ","
-                + user.getId()
-                + "="
-                + userDraftsId.name());
-  }
-
-  @Test
-  public void rebuildAutomaticallyWhenChangeOutOfDate() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    assertChangeUpToDate(true, id);
-
-    // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
-    setNotesMigration(false, false);
-    gApi.changes().id(id.get()).topic(name("a-topic"));
-    setInvalidNoteDbState(id);
-    assertChangeUpToDate(false, id);
-
-    // On next NoteDb read, the change is transparently rebuilt.
-    setNotesMigration(true, true);
-    assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
-    assertChangeUpToDate(true, id);
-
-    // Check that the bundles are equal.
-    ChangeBundle actual =
-        ChangeBundle.fromNotes(commentsUtil, notesFactory.create(dbProvider.get(), project, id));
-    ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    assertThat(actual.differencesFrom(expected)).isEmpty();
-  }
-
-  @Test
-  public void rebuildAutomaticallyWithinBatchUpdate() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    final Change.Id id = r.getPatchSetId().getParentKey();
-    assertChangeUpToDate(true, id);
-
-    // Update ReviewDb and NoteDb, then revert the corresponding NoteDb change
-    // to simulate it failing.
-    NoteDbChangeState oldState = NoteDbChangeState.parse(getUnwrappedDb().changes().get(id));
-    String topic = name("a-topic");
-    gApi.changes().id(id.get()).topic(topic);
-    try (Repository repo = repoManager.openRepository(project)) {
-      new TestRepository<>(repo).update(RefNames.changeMetaRef(id), oldState.getChangeMetaId());
-    }
-    assertChangeUpToDate(false, id);
-
-    // Next NoteDb read comes inside the transaction started by BatchUpdate. In
-    // reality this could be caused by a failed update happening between when
-    // the change is parsed by ChangesCollection and when the BatchUpdate
-    // executes. We simulate it here by using BatchUpdate directly and not going
-    // through an API handler.
-    final String msg = "message from BatchUpdate";
-    try (BatchUpdate bu =
-        batchUpdateFactory.create(
-            db, project, identifiedUserFactory.create(user.getId()), TimeUtil.nowTs())) {
-      bu.addOp(
-          id,
-          new BatchUpdateOp() {
-            @Override
-            public boolean updateChange(ChangeContext ctx) throws OrmException {
-              PatchSet.Id psId = ctx.getChange().currentPatchSetId();
-              ChangeMessage cm =
-                  new ChangeMessage(
-                      new ChangeMessage.Key(id, ChangeUtil.messageUuid()),
-                      ctx.getAccountId(),
-                      ctx.getWhen(),
-                      psId);
-              cm.setMessage(msg);
-              ctx.getDb().changeMessages().insert(Collections.singleton(cm));
-              ctx.getUpdate(psId).setChangeMessage(msg);
-              return true;
-            }
-          });
-      try {
-        bu.execute();
-        fail("expected update to fail");
-      } catch (UpdateException e) {
-        assertThat(e.getMessage()).contains("cannot copy ChangeNotesState");
-      }
-    }
-
-    // TODO(dborowitz): Re-enable these assertions once we fix auto-rebuilding
-    // in the BatchUpdate path.
-    // As an implementation detail, change wasn't actually rebuilt inside the
-    // BatchUpdate transaction, but it was rebuilt during read for the
-    // subsequent reindex. Thus it's impossible to actually observe an
-    // out-of-date state in the caller.
-    // assertChangeUpToDate(true, id);
-
-    // Check that the bundles are equal.
-    // ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-    // ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
-    // ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    // assertThat(actual.differencesFrom(expected)).isEmpty();
-    // assertThat(
-    //        Iterables.transform(
-    //            notes.getChangeMessages(),
-    //            ChangeMessage::getMessage))
-    //    .contains(msg);
-    // assertThat(actual.getChange().getTopic()).isEqualTo(topic);
-  }
-
-  @Test
-  public void rebuildIgnoresErrorIfChangeIsUpToDateAfter() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    assertChangeUpToDate(true, id);
-
-    // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
-    setNotesMigration(false, false);
-    gApi.changes().id(id.get()).topic(name("a-topic"));
-    setInvalidNoteDbState(id);
-    assertChangeUpToDate(false, id);
-
-    // Force the next rebuild attempt to fail but also rebuild the change in the
-    // background.
-    rebuilderWrapper.stealNextUpdate();
-    setNotesMigration(true, true);
-    assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
-    assertChangeUpToDate(true, id);
-
-    // Check that the bundles are equal.
-    ChangeBundle actual =
-        ChangeBundle.fromNotes(commentsUtil, notesFactory.create(dbProvider.get(), project, id));
-    ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    assertThat(actual.differencesFrom(expected)).isEmpty();
-  }
-
-  @Test
-  public void rebuildReturnsCorrectResultEvenIfSavingToNoteDbFailed() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    assertChangeUpToDate(true, id);
-    ObjectId oldMetaId = getMetaRef(project, changeMetaRef(id));
-
-    // Make a ReviewDb change behind NoteDb's back.
-    setNotesMigration(false, false);
-    gApi.changes().id(id.get()).topic(name("a-topic"));
-    setInvalidNoteDbState(id);
-    assertChangeUpToDate(false, id);
-    assertThat(getMetaRef(project, changeMetaRef(id))).isEqualTo(oldMetaId);
-
-    // Force the next rebuild attempt to fail.
-    rebuilderWrapper.failNextUpdate();
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-
-    // Not up to date, but the actual returned state matches anyway.
-    assertChangeUpToDate(false, id);
-    assertThat(getMetaRef(project, changeMetaRef(id))).isEqualTo(oldMetaId);
-    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
-    ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    assertThat(actual.differencesFrom(expected)).isEmpty();
-    assertChangeUpToDate(false, id);
-
-    // Another rebuild attempt succeeds
-    notesFactory.create(dbProvider.get(), project, id);
-    assertThat(getMetaRef(project, changeMetaRef(id))).isNotEqualTo(oldMetaId);
-    assertChangeUpToDate(true, id);
-  }
-
-  @Test
-  public void rebuildReturnsDraftResultWhenRebuildingInChangeNotesFails() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "comment by user", null);
-    assertChangeUpToDate(true, id);
-
-    ObjectId oldMetaId = getMetaRef(allUsers, refsDraftComments(id, user.getId()));
-
-    // Add a draft behind NoteDb's back.
-    setNotesMigration(false, false);
-    putDraft(user, id, 1, "second comment by user", null);
-    setInvalidNoteDbState(id);
-    assertDraftsUpToDate(false, id, user);
-    assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
-    // Force the next rebuild attempt to fail (in ChangeNotes).
-    rebuilderWrapper.failNextUpdate();
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-    notes.getDraftComments(user.getId());
-    assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
-    // Not up to date, but the actual returned state matches anyway.
-    assertDraftsUpToDate(false, id, user);
-    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
-    ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    assertThat(actual.differencesFrom(expected)).isEmpty();
-
-    // Another rebuild attempt succeeds
-    notesFactory.create(dbProvider.get(), project, id);
-    assertChangeUpToDate(true, id);
-    assertDraftsUpToDate(true, id, user);
-    assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isNotEqualTo(oldMetaId);
-  }
-
-  @Test
-  public void rebuildReturnsDraftResultWhenRebuildingInDraftCommentNotesFails() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "comment by user", null);
-    assertChangeUpToDate(true, id);
-
-    ObjectId oldMetaId = getMetaRef(allUsers, refsDraftComments(id, user.getId()));
-
-    // Add a draft behind NoteDb's back.
-    setNotesMigration(false, false);
-    putDraft(user, id, 1, "second comment by user", null);
-
-    ReviewDb db = getUnwrappedDb();
-    Change c = db.changes().get(id);
-    // Leave change meta ID alone so DraftCommentNotes does the rebuild.
-    ObjectId badSha = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
-    NoteDbChangeState bogusState =
-        new NoteDbChangeState(
-            id,
-            PrimaryStorage.REVIEW_DB,
-            Optional.of(
-                NoteDbChangeState.RefState.create(
-                    NoteDbChangeState.parse(c).getChangeMetaId(),
-                    ImmutableMap.of(user.getId(), badSha))),
-            Optional.empty());
-    c.setNoteDbState(bogusState.toString());
-    db.changes().update(Collections.singleton(c));
-
-    assertDraftsUpToDate(false, id, user);
-    assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
-    // Force the next rebuild attempt to fail (in DraftCommentNotes).
-    rebuilderWrapper.failNextUpdate();
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-    notes.getDraftComments(user.getId());
-    assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
-    // Not up to date, but the actual returned state matches anyway.
-    assertChangeUpToDate(true, id);
-    assertDraftsUpToDate(false, id, user);
-    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
-    ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
-    assertThat(actual.differencesFrom(expected)).isEmpty();
-
-    // Another rebuild attempt succeeds
-    notesFactory.create(dbProvider.get(), project, id).getDraftComments(user.getId());
-    assertChangeUpToDate(true, id);
-    assertDraftsUpToDate(true, id, user);
-    assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isNotEqualTo(oldMetaId);
-  }
-
-  @Test
-  public void rebuildAutomaticallyWhenDraftsOutOfDate() throws Exception {
-    setNotesMigration(true, true);
-    setApiUser(user);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "comment", null);
-    assertDraftsUpToDate(true, id, user);
-
-    // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
-    setNotesMigration(false, false);
-    putDraft(user, id, 1, "comment", null);
-    setInvalidNoteDbState(id);
-    assertDraftsUpToDate(false, id, user);
-
-    // On next NoteDb read, the drafts are transparently rebuilt.
-    setNotesMigration(true, true);
-    assertThat(gApi.changes().id(id.get()).current().drafts()).containsKey(PushOneCommit.FILE_NAME);
-    assertDraftsUpToDate(true, id, user);
-  }
-
-  @Test
-  public void pushCert() throws Exception {
-    // We don't have the code in our test harness to do signed pushes, so just
-    // use a hard-coded cert. This cert was actually generated by C git 2.2.0
-    // (albeit not for sending to Gerrit).
-    String cert =
-        "certificate version 0.1\n"
-            + "pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n"
-            + "pushee git://localhost/repo.git\n"
-            + "nonce 1433954361-bde756572d665bba81d8\n"
-            + "\n"
-            + "0000000000000000000000000000000000000000"
-            + "b981a177396fb47345b7df3e4d3f854c6bea7"
-            + "s/heads/master\n"
-            + "-----BEGIN PGP SIGNATURE-----\n"
-            + "Version: GnuPG v1\n"
-            + "\n"
-            + "iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
-            + "9tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
-            + "htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
-            + "4ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
-            + "IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
-            + "+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
-            + "=XFeC\n"
-            + "-----END PGP SIGNATURE-----\n";
-
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId = r.getPatchSetId();
-    Change.Id id = psId.getParentKey();
-
-    PatchSet ps = db.patchSets().get(psId);
-    ps.setPushCertificate(cert);
-    db.patchSets().update(Collections.singleton(ps));
-    indexer.index(db, project, id);
-
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void emptyTopic() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    Change c = db.changes().get(id);
-    assertThat(c.getTopic()).isNull();
-    c.setTopic("");
-    db.changes().update(Collections.singleton(c));
-
-    checker.rebuildAndCheckChanges(id);
-
-    setNotesMigration(true, true);
-
-    // Rebuild and check was successful, but NoteDb doesn't support storing an
-    // empty topic, so it comes out as null.
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(notes.getChange().getTopic()).isNull();
-  }
-
-  @Test
-  public void commentBeforeFirstPatchSet() throws Exception {
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId = r.getPatchSetId();
-    Change.Id id = psId.getParentKey();
-
-    Change c = db.changes().get(id);
-    c.setCreatedOn(new Timestamp(c.getCreatedOn().getTime() - 5000));
-    db.changes().update(Collections.singleton(c));
-    indexer.index(db, project, id);
-
-    ReviewInput rin = new ReviewInput();
-    rin.message = "comment";
-
-    Timestamp ts = new Timestamp(c.getCreatedOn().getTime() + 2000);
-    assertThat(ts).isGreaterThan(c.getCreatedOn());
-    assertThat(ts).isLessThan(db.patchSets().get(psId).getCreatedOn());
-    RevisionResource revRsrc = parseCurrentRevisionResource(r.getChangeId());
-    postReview.get().apply(batchUpdateFactory, revRsrc, rin, ts);
-
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void commentPredatingChangeBySomeoneOtherThanOwner() throws Exception {
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId = r.getPatchSetId();
-    Change.Id id = psId.getParentKey();
-    Change c = db.changes().get(id);
-
-    ReviewInput rin = new ReviewInput();
-    rin.message = "comment";
-
-    Timestamp ts = new Timestamp(c.getCreatedOn().getTime() - 10000);
-    RevisionResource revRsrc = parseCurrentRevisionResource(r.getChangeId());
-    setApiUser(user);
-    postReview.get().apply(batchUpdateFactory, revRsrc, rin, ts);
-
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void noteDbUsesOriginalSubjectFromPatchSetAndIgnoresChangeField() throws Exception {
-    PushOneCommit.Result r = createChange();
-    String orig = r.getChange().change().getSubject();
-    r =
-        pushFactory
-            .create(
-                db,
-                admin.getIdent(),
-                testRepo,
-                orig + " v2",
-                PushOneCommit.FILE_NAME,
-                "new contents",
-                r.getChangeId())
-            .to("refs/for/master");
-    r.assertOkStatus();
-
-    PatchSet.Id psId = r.getPatchSetId();
-    Change.Id id = psId.getParentKey();
-    Change c = db.changes().get(id);
-
-    c.setCurrentPatchSet(psId, c.getSubject(), "Bogus original subject");
-    db.changes().update(Collections.singleton(c));
-
-    checker.rebuildAndCheckChanges(id);
-
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    Change nc = notes.getChange();
-    assertThat(nc.getSubject()).isEqualTo(c.getSubject());
-    assertThat(nc.getSubject()).isEqualTo(orig + " v2");
-    assertThat(nc.getOriginalSubject()).isNotEqualTo(c.getOriginalSubject());
-    assertThat(nc.getOriginalSubject()).isEqualTo(orig);
-  }
-
-  @Test
-  public void ignorePatchLineCommentsOnPatchSet0() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change change = r.getChange().change();
-    Change.Id id = change.getId();
-
-    PatchLineComment comment =
-        new PatchLineComment(
-            new PatchLineComment.Key(
-                new Patch.Key(new PatchSet.Id(id, 0), PushOneCommit.FILE_NAME), "uuid"),
-            0,
-            user.getId(),
-            null,
-            TimeUtil.nowTs());
-    comment.setSide((short) 1);
-    comment.setMessage("message");
-    comment.setStatus(PatchLineComment.Status.PUBLISHED);
-    db.patchComments().insert(Collections.singleton(comment));
-    indexer.index(db, change.getProject(), id);
-
-    checker.rebuildAndCheckChanges(id);
-
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(notes.getComments()).isEmpty();
-  }
-
-  @Test
-  public void leadingSpacesInSubject() throws Exception {
-    String subj = "   " + PushOneCommit.SUBJECT;
-    PushOneCommit push =
-        pushFactory.create(
-            db,
-            admin.getIdent(),
-            testRepo,
-            subj,
-            PushOneCommit.FILE_NAME,
-            PushOneCommit.FILE_CONTENT);
-    PushOneCommit.Result r = push.to("refs/for/master");
-    r.assertOkStatus();
-    Change change = r.getChange().change();
-    assertThat(change.getSubject()).isEqualTo(subj);
-    Change.Id id = r.getPatchSetId().getParentKey();
-
-    checker.rebuildAndCheckChanges(id);
-
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(notes.getChange().getSubject()).isNotEqualTo(subj);
-    assertThat(notes.getChange().getSubject()).isEqualTo(PushOneCommit.SUBJECT);
-  }
-
-  @Test
-  public void allTimestampsExceptUpdatedAreEqualDueToBadMigration() throws Exception {
-    // https://bugs.chromium.org/p/gerrit/issues/detail?id=7397
-    PushOneCommit.Result r = createChange();
-    Change c = r.getChange().change();
-    Change.Id id = c.getId();
-    Timestamp ts = TimeUtil.nowTs();
-    Timestamp origUpdated = c.getLastUpdatedOn();
-
-    c.setCreatedOn(ts);
-    assertThat(c.getCreatedOn()).isGreaterThan(c.getLastUpdatedOn());
-    db.changes().update(Collections.singleton(c));
-
-    List<ChangeMessage> cm = db.changeMessages().byChange(id).toList();
-    cm.forEach(m -> m.setWrittenOn(ts));
-    db.changeMessages().update(cm);
-
-    List<PatchSet> ps = db.patchSets().byChange(id).toList();
-    ps.forEach(p -> p.setCreatedOn(ts));
-    db.patchSets().update(ps);
-
-    List<PatchSetApproval> psa = db.patchSetApprovals().byChange(id).toList();
-    psa.forEach(p -> p.setGranted(ts));
-    db.patchSetApprovals().update(psa);
-
-    List<PatchLineComment> plc = db.patchComments().byChange(id).toList();
-    plc.forEach(p -> p.setWrittenOn(ts));
-    db.patchComments().update(plc);
-
-    checker.rebuildAndCheckChanges(id);
-
-    setNotesMigration(true, true);
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(notes.getChange().getCreatedOn()).isEqualTo(origUpdated);
-    assertThat(notes.getChange().getLastUpdatedOn()).isAtLeast(origUpdated);
-    assertThat(notes.getPatchSets().get(new PatchSet.Id(id, 1)).getCreatedOn())
-        .isEqualTo(origUpdated);
-  }
-
-  @Test
-  public void createWithAutoRebuildingDisabled() throws Exception {
-    ReviewDb oldDb = db;
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    ChangeNotes oldNotes = notesFactory.create(db, project, id);
-
-    // Make a ReviewDb change behind NoteDb's back.
-    Change c = oldDb.changes().get(id);
-    assertThat(c.getTopic()).isNull();
-    String topic = name("a-topic");
-    c.setTopic(topic);
-    oldDb.changes().update(Collections.singleton(c));
-
-    c = oldDb.changes().get(c.getId());
-    ChangeNotes newNotes = notesFactory.createWithAutoRebuildingDisabled(c, null);
-    assertThat(newNotes.getChange().getTopic()).isNotEqualTo(topic);
-    assertThat(newNotes.getChange().getTopic()).isEqualTo(oldNotes.getChange().getTopic());
-  }
-
-  @Test
-  public void rebuildDeletesOldDraftRefs() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "comment", null);
-
-    Account.Id otherAccountId = new Account.Id(user.getId().get() + 1234);
-    String otherDraftRef = refsDraftComments(id, otherAccountId);
-
-    try (Repository repo = repoManager.openRepository(allUsers);
-        ObjectInserter ins = repo.newObjectInserter()) {
-      ObjectId sha = ins.insert(OBJ_BLOB, "garbage data".getBytes(UTF_8));
-      ins.flush();
-      RefUpdate ru = repo.updateRef(otherDraftRef);
-      ru.setExpectedOldObjectId(ObjectId.zeroId());
-      ru.setNewObjectId(sha);
-      assertThat(ru.update()).isEqualTo(RefUpdate.Result.NEW);
-    }
-
-    checker.rebuildAndCheckChanges(id);
-
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(repo.exactRef(otherDraftRef)).isNull();
-    }
-  }
-
-  @Test
-  public void failWhenWritesDisabled() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    assertChangeUpToDate(true, id);
-    assertThat(gApi.changes().id(id.get()).info().topic).isNull();
-
-    // Turning off writes causes failure.
-    setNotesMigration(false, true);
-    try {
-      gApi.changes().id(id.get()).topic(name("a-topic"));
-      fail("Expected write to fail");
-    } catch (RestApiException e) {
-      assertChangesReadOnly(e);
-    }
-
-    // Update was not written.
-    assertThat(gApi.changes().id(id.get()).info().topic).isNull();
-    assertChangeUpToDate(true, id);
-  }
-
-  @Test
-  public void rebuildWhenWritesDisabledWorksButDoesNotWrite() throws Exception {
-    setNotesMigration(true, true);
-
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    assertChangeUpToDate(true, id);
-
-    // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
-    setNotesMigration(false, false);
-    gApi.changes().id(id.get()).topic(name("a-topic"));
-    setInvalidNoteDbState(id);
-    assertChangeUpToDate(false, id);
-
-    // On next NoteDb read, change is rebuilt in-memory but not stored.
-    setNotesMigration(false, true);
-    assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
-    assertChangeUpToDate(false, id);
-
-    // Attempting to write directly causes failure.
-    try {
-      gApi.changes().id(id.get()).topic(name("other-topic"));
-      fail("Expected write to fail");
-    } catch (RestApiException e) {
-      assertChangesReadOnly(e);
-    }
-
-    // Update was not written.
-    assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
-    assertChangeUpToDate(false, id);
-  }
-
-  @Test
-  public void rebuildChangeWithNoPatchSets() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    db.changes().beginTransaction(id);
-    try {
-      db.patchSets().delete(db.patchSets().byChange(id));
-      db.commit();
-    } finally {
-      db.rollback();
-    }
-
-    try {
-      checker.rebuildAndCheckChanges(id);
-      assert_().fail("expected NoPatchSetsException");
-    } catch (NoPatchSetsException e) {
-      // Expected.
-    }
-
-    Change c = db.changes().get(id);
-    assertThat(c.getNoteDbState()).isNull();
-    checker.assertNoChangeRef(project, id);
-  }
-
-  @Test
-  public void rebuildChangeWithNoEntitiesOtherThanChange() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    db.changes().beginTransaction(id);
-    try {
-      db.changeMessages().delete(db.changeMessages().byChange(id));
-      db.patchSets().delete(db.patchSets().byChange(id));
-      db.patchSetApprovals().delete(db.patchSetApprovals().byChange(id));
-      db.patchComments().delete(db.patchComments().byChange(id));
-      db.commit();
-    } finally {
-      db.rollback();
-    }
-
-    try {
-      checker.rebuildAndCheckChanges(id);
-      assert_().fail("expected NoPatchSetsException");
-    } catch (NoPatchSetsException e) {
-      // Expected.
-    }
-
-    Change c = db.changes().get(id);
-    assertThat(c.getNoteDbState()).isNull();
-    checker.assertNoChangeRef(project, id);
-  }
-
-  @Test
-  public void rebuildEntitiesCreatedByImpersonation() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    PatchSet.Id psId = new PatchSet.Id(id, 1);
-    String prefix = "/changes/" + id + "/revisions/current/";
-
-    // For each of the entities that have a real user field, create one entity
-    // without impersonation and one with.
-    CommentInput ci = new CommentInput();
-    ci.path = Patch.COMMIT_MSG;
-    ci.side = Side.REVISION;
-    ci.line = 1;
-    ci.message = "comment without impersonation";
-    ReviewInput ri = new ReviewInput();
-    ri.label("Code-Review", -1);
-    ri.message = "message without impersonation";
-    ri.drafts = DraftHandling.KEEP;
-    ri.comments = ImmutableMap.of(ci.path, ImmutableList.of(ci));
-    userRestSession.post(prefix + "review", ri).assertOK();
-
-    DraftInput di = new DraftInput();
-    di.path = Patch.COMMIT_MSG;
-    di.side = Side.REVISION;
-    di.line = 1;
-    di.message = "draft without impersonation";
-    userRestSession.put(prefix + "drafts", di).assertCreated();
-
-    allowRunAs();
-    try {
-      Header runAs = new BasicHeader("X-Gerrit-RunAs", user.id.toString());
-      ci.message = "comment with impersonation";
-      ri.message = "message with impersonation";
-      ri.label("Code-Review", 1);
-      adminRestSession.postWithHeader(prefix + "review", runAs, ri).assertOK();
-
-      di.message = "draft with impersonation";
-      adminRestSession.putWithHeader(prefix + "drafts", runAs, di).assertCreated();
-    } finally {
-      removeRunAs();
-    }
-
-    List<ChangeMessage> msgs =
-        Ordering.natural()
-            .onResultOf(ChangeMessage::getWrittenOn)
-            .sortedCopy(db.changeMessages().byChange(id));
-    assertThat(msgs).hasSize(3);
-    assertThat(msgs.get(1).getMessage()).endsWith("message without impersonation");
-    assertThat(msgs.get(1).getAuthor()).isEqualTo(user.id);
-    assertThat(msgs.get(1).getRealAuthor()).isEqualTo(user.id);
-    assertThat(msgs.get(2).getMessage()).endsWith("message with impersonation");
-    assertThat(msgs.get(2).getAuthor()).isEqualTo(user.id);
-    assertThat(msgs.get(2).getRealAuthor()).isEqualTo(admin.id);
-
-    List<PatchSetApproval> psas = db.patchSetApprovals().byChange(id).toList();
-    assertThat(psas).hasSize(1);
-    assertThat(psas.get(0).getLabel()).isEqualTo("Code-Review");
-    assertThat(psas.get(0).getValue()).isEqualTo(1);
-    assertThat(psas.get(0).getAccountId()).isEqualTo(user.id);
-    assertThat(psas.get(0).getRealAccountId()).isEqualTo(admin.id);
-
-    Ordering<PatchLineComment> commentOrder =
-        Ordering.natural().onResultOf(PatchLineComment::getWrittenOn);
-    List<PatchLineComment> drafts =
-        commentOrder.sortedCopy(db.patchComments().draftByPatchSetAuthor(psId, user.id));
-    assertThat(drafts).hasSize(2);
-    assertThat(drafts.get(0).getMessage()).isEqualTo("draft without impersonation");
-    assertThat(drafts.get(0).getAuthor()).isEqualTo(user.id);
-    assertThat(drafts.get(0).getRealAuthor()).isEqualTo(user.id);
-    assertThat(drafts.get(1).getMessage()).isEqualTo("draft with impersonation");
-    assertThat(drafts.get(1).getAuthor()).isEqualTo(user.id);
-    assertThat(drafts.get(1).getRealAuthor()).isEqualTo(admin.id);
-
-    List<PatchLineComment> pub =
-        commentOrder.sortedCopy(db.patchComments().publishedByPatchSet(psId));
-    assertThat(pub).hasSize(2);
-    assertThat(pub.get(0).getMessage()).isEqualTo("comment without impersonation");
-    assertThat(pub.get(0).getAuthor()).isEqualTo(user.id);
-    assertThat(pub.get(0).getRealAuthor()).isEqualTo(user.id);
-    assertThat(pub.get(1).getMessage()).isEqualTo("comment with impersonation");
-    assertThat(pub.get(1).getAuthor()).isEqualTo(user.id);
-    assertThat(pub.get(1).getRealAuthor()).isEqualTo(admin.id);
-  }
-
-  @Test
-  public void laterEventsDependingOnEarlierPatchSetDontIntefereWithOtherPatchSets()
-      throws Exception {
-    PushOneCommit.Result r1 = createChange();
-    ChangeData cd = r1.getChange();
-    Change.Id id = cd.getId();
-    amendChange(cd.change().getKey().get());
-    TestTimeUtil.incrementClock(90, TimeUnit.DAYS);
-
-    ReviewInput rin = ReviewInput.approve();
-    rin.message = "Some very late message on PS1";
-    gApi.changes().id(id.get()).revision(1).review(rin);
-
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void ignoreChangeMessageBeyondCurrentPatchSet() throws Exception {
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId1 = r.getPatchSetId();
-    Change.Id id = psId1.getParentKey();
-    gApi.changes().id(id.get()).current().review(ReviewInput.recommend());
-
-    r = amendChange(r.getChangeId());
-    PatchSet.Id psId2 = r.getPatchSetId();
-
-    assertThat(db.patchSets().byChange(id)).hasSize(2);
-    assertThat(db.changeMessages().byPatchSet(psId2)).hasSize(1);
-    db.patchSets().deleteKeys(Collections.singleton(psId2));
-
-    checker.rebuildAndCheckChanges(psId2.getParentKey());
-    setNotesMigration(true, true);
-
-    ChangeData cd = changeDataFactory.create(db, project, id);
-    assertThat(cd.change().currentPatchSetId()).isEqualTo(psId1);
-    assertThat(cd.patchSets().stream().map(PatchSet::getId).collect(toList()))
-        .containsExactly(psId1);
-    PatchSet ps = cd.currentPatchSet();
-    assertThat(ps).isNotNull();
-    assertThat(ps.getId()).isEqualTo(psId1);
-  }
-
-  @Test
-  public void highestNumberedPatchSetIsNotCurrent() throws Exception {
-    PushOneCommit.Result r1 = createChange();
-    PatchSet.Id psId1 = r1.getPatchSetId();
-    Change.Id id = psId1.getParentKey();
-    PushOneCommit.Result r2 = amendChange(r1.getChangeId());
-    PatchSet.Id psId2 = r2.getPatchSetId();
-
-    try (BatchUpdate bu =
-        batchUpdateFactory.create(
-            db, project, identifiedUserFactory.create(user.getId()), TimeUtil.nowTs())) {
-      bu.addOp(
-          id,
-          new BatchUpdateOp() {
-            @Override
-            public boolean updateChange(ChangeContext ctx)
-                throws PatchSetInfoNotAvailableException {
-              ctx.getChange()
-                  .setCurrentPatchSet(patchSetInfoFactory.get(ctx.getDb(), ctx.getNotes(), psId1));
-              return true;
-            }
-          });
-      bu.execute();
-    }
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(psId1, psId2);
-    assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId1);
-
-    assertThat(db.changes().get(id).currentPatchSetId()).isEqualTo(psId1);
-
-    checker.rebuildAndCheckChanges(id);
-    setNotesMigration(true, true);
-
-    notes = notesFactory.create(db, project, id);
-    assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(psId1, psId2);
-    assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId1);
-  }
-
-  @Test
-  public void resolveCommentsInheritsValueFromParentWhenUnspecified() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getPatchSetId().getParentKey();
-    putDraft(user, id, 1, "comment", true);
-    putDraft(user, id, 1, "newComment", null);
-
-    Map<String, List<CommentInfo>> comments = gApi.changes().id(id.get()).current().drafts();
-    for (List<CommentInfo> cList : comments.values()) {
-      for (CommentInfo ci : cList) {
-        assertThat(ci.unresolved).isTrue();
-      }
-    }
-  }
-
-  @Test
-  public void rebuilderRespectsReadOnlyInNoteDbChangeState() throws Exception {
-    TestTimeUtil.resetWithClockStep(1, SECONDS);
-    PushOneCommit.Result r = createChange();
-    PatchSet.Id psId1 = r.getPatchSetId();
-    Change.Id id = psId1.getParentKey();
-
-    checker.rebuildAndCheckChanges(id);
-    setNotesMigration(true, true);
-
-    ReviewDb db = getUnwrappedDb();
-    Change c = db.changes().get(id);
-    NoteDbChangeState state = NoteDbChangeState.parse(c);
-    Timestamp until = new Timestamp(TimeUtil.nowMs() + MILLISECONDS.convert(1, DAYS));
-    state = state.withReadOnlyUntil(until);
-    c.setNoteDbState(state.toString());
-    db.changes().update(Collections.singleton(c));
-
-    try {
-      rebuilderWrapper.rebuild(db, id);
-      fail("expected rebuild to fail");
-    } catch (OrmRuntimeException e) {
-      assertThat(e.getMessage()).contains("read-only until");
-    }
-
-    TestTimeUtil.setClock(new Timestamp(until.getTime() + MILLISECONDS.convert(1, SECONDS)));
-    rebuilderWrapper.rebuild(db, id);
-  }
-
-  @Test
-  public void commitWithCrLineEndings() throws Exception {
-    PushOneCommit.Result r =
-        createChange("Subject\r\rBody\r", PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT);
-    Change c = r.getChange().change();
-
-    // This assertion demonstrates an arguable bug in JGit's commit subject
-    // parsing, and shows how this kind of data might have gotten into
-    // ReviewDb. If that bug ever gets fixed upstream, this assert may start
-    // failing. If that happens, this test can be rewritten to directly set the
-    // subject field in ReviewDb.
-    assertThat(c.getSubject()).isEqualTo("Subject\r\rBody");
-
-    checker.rebuildAndCheckChanges(c.getId());
-  }
-
-  @Test
-  public void patchSetsOutOfOrder() throws Exception {
-    String id = createChange().getChangeId();
-    amendChange(id);
-    PushOneCommit.Result r = amendChange(id);
-
-    ChangeData cd = r.getChange();
-    PatchSet.Id psId3 = cd.change().currentPatchSetId();
-    assertThat(psId3.get()).isEqualTo(3);
-
-    PatchSet ps1 = db.patchSets().get(new PatchSet.Id(cd.getId(), 1));
-    PatchSet ps3 = db.patchSets().get(psId3);
-    assertThat(ps1.getCreatedOn()).isLessThan(ps3.getCreatedOn());
-
-    // Simulate an old Gerrit bug by setting the created timestamp of the latest
-    // patch set ID to the timestamp of PS1.
-    ps3.setCreatedOn(ps1.getCreatedOn());
-    db.patchSets().update(Collections.singleton(ps3));
-
-    checker.rebuildAndCheckChanges(cd.getId());
-
-    setNotesMigration(true, true);
-    cd = changeDataFactory.create(db, project, cd.getId());
-    assertThat(cd.change().currentPatchSetId()).isEqualTo(psId3);
-
-    List<PatchSet> patchSets = ImmutableList.copyOf(cd.patchSets());
-    assertThat(patchSets).hasSize(3);
-
-    PatchSet newPs1 = patchSets.get(0);
-    assertThat(newPs1.getId()).isEqualTo(ps1.getId());
-    assertThat(newPs1.getCreatedOn()).isEqualTo(ps1.getCreatedOn());
-
-    PatchSet newPs2 = patchSets.get(1);
-    assertThat(newPs2.getCreatedOn()).isGreaterThan(newPs1.getCreatedOn());
-
-    PatchSet newPs3 = patchSets.get(2);
-    assertThat(newPs3.getId()).isEqualTo(ps3.getId());
-    // Migrated with a newer timestamp than the original, to preserve ordering.
-    assertThat(newPs3.getCreatedOn()).isAtLeast(newPs2.getCreatedOn());
-    assertThat(newPs3.getCreatedOn()).isGreaterThan(ps1.getCreatedOn());
-  }
-
-  @Test
-  public void ignoreNoteDbStateWithNoCorrespondingRefWhenWritesAndReadsDisabled() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    ReviewDb db = getUnwrappedDb();
-    Change c = db.changes().get(id);
-    c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
-    db.changes().update(Collections.singleton(c));
-    c = db.changes().get(id);
-
-    String refName = RefNames.changeMetaRef(id);
-    assertThat(getMetaRef(project, refName)).isNull();
-
-    ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-    assertThat(notes.getChange().getRowVersion()).isEqualTo(c.getRowVersion());
-
-    notes = notesFactory.createChecked(dbProvider.get(), project, id);
-    assertThat(notes.getChange().getRowVersion()).isEqualTo(c.getRowVersion());
-
-    assertThat(getMetaRef(project, refName)).isNull();
-  }
-
-  @Test
-  public void autoRebuildMissingRefWriteOnly() throws Exception {
-    setNotesMigration(true, false);
-    testAutoRebuildMissingRef();
-  }
-
-  @Test
-  public void autoRebuildMissingRefReadWrite() throws Exception {
-    setNotesMigration(true, true);
-    testAutoRebuildMissingRef();
-  }
-
-  private void testAutoRebuildMissingRef() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    assertChangeUpToDate(true, id);
-    notesFactory.createChecked(db, project, id);
-
-    try (Repository repo = repoManager.openRepository(project)) {
-      RefUpdate ru = repo.updateRef(RefNames.changeMetaRef(id));
-      ru.setForceUpdate(true);
-      assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
-    }
-    assertChangeUpToDate(false, id);
-
-    notesFactory.createChecked(db, project, id);
-    assertChangeUpToDate(true, id);
-  }
-
-  @Test
-  public void missingPatchSetCommitOkForCommentsNotOnParentSide() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    putDraft(user, id, 1, "draft comment", null, Side.REVISION);
-    putComment(user, id, 1, "published comment", null, Side.REVISION);
-
-    ReviewDb db = getUnwrappedDb();
-    PatchSet ps = db.patchSets().get(new PatchSet.Id(id, 1));
-    ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    db.patchSets().update(Collections.singleton(ps));
-
-    try {
-      patchListCache.getOldId(db.changes().get(id), ps, null);
-      assert_().fail("Expected PatchListNotAvailableException");
-    } catch (PatchListNotAvailableException e) {
-      // Expected.
-    }
-
-    checker.rebuildAndCheckChanges(id);
-  }
-
-  @Test
-  public void missingPatchSetCommitOmitsCommentsOnParentSide() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    CommentInfo draftInfo = putDraft(user, id, 1, "draft comment", null, Side.PARENT);
-    putComment(user, id, 1, "published comment", null, Side.PARENT);
-    CommentInfo commentInfo =
-        gApi.changes()
-            .id(id.get())
-            .comments()
-            .values()
-            .stream()
-            .flatMap(List::stream)
-            .findFirst()
-            .get();
-
-    ReviewDb db = getUnwrappedDb();
-    PatchSet ps = db.patchSets().get(new PatchSet.Id(id, 1));
-    ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    db.patchSets().update(Collections.singleton(ps));
-
-    try {
-      patchListCache.getOldId(db.changes().get(id), ps, null);
-      assert_().fail("Expected PatchListNotAvailableException");
-    } catch (PatchListNotAvailableException e) {
-      // Expected.
-    }
-
-    checker.rebuildAndCheckChange(
-        id,
-        Stream.of(draftInfo.id, commentInfo.id)
-            .sorted()
-            .map(c -> id + ",1," + PushOneCommit.FILE_NAME + "," + c)
-            .collect(
-                joining(", ", "PatchLineComment.Key sets differ: [", "] only in A; [] only in B")));
-  }
-
-  private void assertChangesReadOnly(RestApiException e) throws Exception {
-    Throwable cause = e.getCause();
-    assertThat(cause).isInstanceOf(UpdateException.class);
-    assertThat(cause.getCause()).isInstanceOf(OrmException.class);
-    assertThat(cause.getCause()).hasMessageThat().isEqualTo(NoteDbUpdateManager.CHANGES_READ_ONLY);
-  }
-
-  private void setInvalidNoteDbState(Change.Id id) throws Exception {
-    ReviewDb db = getUnwrappedDb();
-    Change c = db.changes().get(id);
-    // In reality we would have NoteDb writes enabled, which would write a real
-    // state into this field. For tests however, we turn NoteDb writes off, so
-    // just use a dummy state to force ChangeNotes to view the notes as
-    // out-of-date.
-    c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
-    db.changes().update(Collections.singleton(c));
-  }
-
-  private void assertChangeUpToDate(boolean expected, Change.Id id) throws Exception {
-    try (Repository repo = repoManager.openRepository(project)) {
-      Change c = getUnwrappedDb().changes().get(id);
-      assertThat(c).isNotNull();
-      assertThat(c.getNoteDbState()).isNotNull();
-      NoteDbChangeState state = NoteDbChangeState.parse(c);
-      assertThat(state).isNotNull();
-      assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-      assertThat(state.isChangeUpToDate(new RepoRefCache(repo))).isEqualTo(expected);
-    }
-  }
-
-  private void assertDraftsUpToDate(boolean expected, Change.Id changeId, TestAccount account)
-      throws Exception {
-    try (Repository repo = repoManager.openRepository(allUsers)) {
-      Change c = getUnwrappedDb().changes().get(changeId);
-      assertThat(c).isNotNull();
-      assertThat(c.getNoteDbState()).isNotNull();
-      NoteDbChangeState state = NoteDbChangeState.parse(c);
-      assertThat(state.areDraftsUpToDate(new RepoRefCache(repo), account.getId()))
-          .isEqualTo(expected);
-    }
-  }
-
-  private ObjectId getMetaRef(Project.NameKey p, String name) throws Exception {
-    try (Repository repo = repoManager.openRepository(p)) {
-      Ref ref = repo.exactRef(name);
-      return ref != null ? ref.getObjectId() : null;
-    }
-  }
-
-  private CommentInfo putDraft(
-      TestAccount account, Change.Id id, int line, String msg, Boolean unresolved)
-      throws Exception {
-    return putDraft(account, id, line, msg, unresolved, Side.REVISION);
-  }
-
-  private CommentInfo putDraft(
-      TestAccount account, Change.Id id, int line, String msg, Boolean unresolved, Side side)
-      throws Exception {
-    DraftInput in = new DraftInput();
-    in.side = side;
-    in.line = line;
-    in.message = msg;
-    in.path = PushOneCommit.FILE_NAME;
-    in.unresolved = unresolved;
-    AcceptanceTestRequestScope.Context old = setApiUser(account);
-    try {
-      return gApi.changes().id(id.get()).current().createDraft(in).get();
-    } finally {
-      atrScope.set(old);
-    }
-  }
-
-  private void putComment(TestAccount account, Change.Id id, int line, String msg, String inReplyTo)
-      throws Exception {
-    putComment(account, id, line, msg, inReplyTo, Side.REVISION);
-  }
-
-  private void putComment(
-      TestAccount account, Change.Id id, int line, String msg, String inReplyTo, Side side)
-      throws Exception {
-    CommentInput in = new CommentInput();
-    in.side = side;
-    in.line = line;
-    in.message = msg;
-    in.inReplyTo = inReplyTo;
-    ReviewInput rin = new ReviewInput();
-    rin.comments = new HashMap<>();
-    rin.comments.put(PushOneCommit.FILE_NAME, ImmutableList.of(in));
-    rin.drafts = ReviewInput.DraftHandling.KEEP;
-    AcceptanceTestRequestScope.Context old = setApiUser(account);
-    try {
-      gApi.changes().id(id.get()).current().review(rin);
-    } finally {
-      atrScope.set(old);
-    }
-  }
-
-  private void publishDrafts(TestAccount account, Change.Id id) throws Exception {
-    ReviewInput rin = new ReviewInput();
-    rin.drafts = ReviewInput.DraftHandling.PUBLISH_ALL_REVISIONS;
-    AcceptanceTestRequestScope.Context old = setApiUser(account);
-    try {
-      gApi.changes().id(id.get()).current().review(rin);
-    } finally {
-      atrScope.set(old);
-    }
-  }
-
-  private ChangeMessage insertMessage(
-      Change.Id id, PatchSet.Id psId, Account.Id author, Timestamp ts, String message)
-      throws Exception {
-    ChangeMessage msg =
-        new ChangeMessage(new ChangeMessage.Key(id, ChangeUtil.messageUuid()), author, ts, psId);
-    msg.setMessage(message);
-    db.changeMessages().insert(Collections.singleton(msg));
-
-    Change c = db.changes().get(id);
-    if (ts.compareTo(c.getLastUpdatedOn()) > 0) {
-      c.setLastUpdatedOn(ts);
-      db.changes().update(Collections.singleton(c));
-    }
-
-    return msg;
-  }
-
-  private ReviewDb getUnwrappedDb() {
-    ReviewDb db = dbProvider.get();
-    return ReviewDbUtil.unwrapDb(db);
-  }
-
-  private void allowRunAs() throws Exception {
-    try (ProjectConfigUpdate u = updateProject(allProjects)) {
-      Util.allow(
-          u.getConfig(),
-          GlobalCapability.RUN_AS,
-          systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
-      u.save();
-    }
-  }
-
-  private void removeRunAs() throws Exception {
-    try (ProjectConfigUpdate u = updateProject(allProjects)) {
-      Util.remove(
-          u.getConfig(),
-          GlobalCapability.RUN_AS,
-          systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
-      u.save();
-    }
-  }
-
-  private Map<String, List<CommentInfo>> getPublishedComments(Change.Id id) throws Exception {
-    return gApi.changes().id(id.get()).current().comments();
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index 8d6fecd..5047b73 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
 import static java.util.stream.Collectors.toList;
 
@@ -56,7 +55,6 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.Before;
 import org.junit.Test;
 
 public class NoteDbOnlyIT extends AbstractDaemonTest {
@@ -70,14 +68,8 @@
 
   @Inject private RetryHelper retryHelper;
 
-  @Before
-  public void setUp() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
-  }
-
   @Test
   public void updateChangeFailureRollsBackRefUpdate() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
     PushOneCommit.Result r = createChange();
     Change.Id id = r.getChange().getId();
 
@@ -149,7 +141,6 @@
 
   @Test
   public void retryOnLockFailureWithAtomicUpdates() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
     PushOneCommit.Result r = createChange();
     Change.Id id = r.getChange().getId();
     String master = "refs/heads/master";
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java
deleted file mode 100644
index 26d5461..0000000
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java
+++ /dev/null
@@ -1,524 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.server.notedb;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
-import static com.google.gerrit.server.notedb.NoteDbUtil.formatTime;
-import static java.util.concurrent.TimeUnit.DAYS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.toList;
-
-import com.github.rholder.retry.Retryer;
-import com.github.rholder.retry.RetryerBuilder;
-import com.github.rholder.retry.StopStrategies;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.api.changes.DraftInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
-import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gerrit.extensions.common.ApprovalInfo;
-import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.common.CommentInfo;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.ChangeMessagesUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.InternalUser;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.PrimaryStorageMigrator;
-import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
-import com.google.gerrit.server.update.RetryHelper;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.util.Providers;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@NoHttpd
-public class NoteDbPrimaryIT extends AbstractDaemonTest {
-  @ConfigSuite.Default
-  public static Config defaultConfig() {
-    Config cfg = new Config();
-    cfg.setString("notedb", null, "concurrentWriterTimeout", "0s");
-    cfg.setString("notedb", null, "primaryStorageMigrationTimeout", "1d");
-    cfg.setBoolean("noteDb", null, "testRebuilderWrapper", true);
-    return cfg;
-  }
-
-  @Inject private ChangeBundleReader bundleReader;
-  @Inject private CommentsUtil commentsUtil;
-  @Inject private TestChangeRebuilderWrapper rebuilderWrapper;
-  @Inject private ChangeNotes.Factory changeNotesFactory;
-  @Inject private ChangeUpdate.Factory updateFactory;
-  @Inject private InternalUser.Factory internalUserFactory;
-  @Inject private RetryHelper retryHelper;
-
-  private PrimaryStorageMigrator migrator;
-
-  @Before
-  public void setUp() throws Exception {
-    assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.READ_WRITE);
-    db = ReviewDbUtil.unwrapDb(db);
-    TestTimeUtil.resetWithClockStep(1, SECONDS);
-    migrator = newMigrator(null);
-  }
-
-  private PrimaryStorageMigrator newMigrator(
-      @Nullable Retryer<NoteDbChangeState> ensureRebuiltRetryer) {
-    return new PrimaryStorageMigrator(
-        cfg,
-        Providers.of(db),
-        repoManager,
-        allUsers,
-        rebuilderWrapper,
-        ensureRebuiltRetryer,
-        changeNotesFactory,
-        queryProvider,
-        updateFactory,
-        internalUserFactory,
-        retryHelper);
-  }
-
-  @After
-  public void tearDown() {
-    TestTimeUtil.useSystemTime();
-  }
-
-  @Test
-  public void updateChange() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    setNoteDbPrimary(id);
-
-    gApi.changes().id(id.get()).current().review(ReviewInput.approve());
-    gApi.changes().id(id.get()).current().submit();
-
-    ChangeInfo info = gApi.changes().id(id.get()).get();
-    assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
-    ApprovalInfo approval = Iterables.getOnlyElement(info.labels.get("Code-Review").all);
-    assertThat(approval._accountId).isEqualTo(admin.id.get());
-    assertThat(approval.value).isEqualTo(2);
-    assertThat(info.messages).hasSize(3);
-    assertThat(Iterables.getLast(info.messages).message)
-        .isEqualTo("Change has been successfully merged by " + admin.fullName);
-
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
-    assertThat(notes.getChange().getNoteDbState())
-        .isEqualTo(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
-
-    // Writes weren't reflected in ReviewDb.
-    assertThat(db.changes().get(id).getStatus()).isEqualTo(Change.Status.NEW);
-    assertThat(db.patchSetApprovals().byChange(id)).isEmpty();
-    assertThat(db.changeMessages().byChange(id)).hasSize(1);
-  }
-
-  @Test
-  public void deleteDraftComment() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    setNoteDbPrimary(id);
-
-    DraftInput din = new DraftInput();
-    din.path = PushOneCommit.FILE_NAME;
-    din.line = 1;
-    din.message = "A comment";
-    gApi.changes().id(id.get()).current().createDraft(din);
-
-    CommentInfo di =
-        Iterables.getOnlyElement(
-            gApi.changes().id(id.get()).current().drafts().get(PushOneCommit.FILE_NAME));
-    assertThat(di.message).isEqualTo(din.message);
-
-    assertThat(db.patchComments().draftByChangeFileAuthor(id, din.path, admin.id)).isEmpty();
-
-    gApi.changes().id(id.get()).current().draft(di.id).delete();
-    assertThat(gApi.changes().id(id.get()).current().drafts()).isEmpty();
-  }
-
-  @Test
-  public void deleteVote() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    setNoteDbPrimary(id);
-
-    gApi.changes().id(id.get()).current().review(ReviewInput.approve());
-    List<ApprovalInfo> approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
-    assertThat(approvals).hasSize(1);
-    assertThat(approvals.get(0).value).isEqualTo(2);
-
-    gApi.changes().id(id.get()).reviewer(admin.id.toString()).deleteVote("Code-Review");
-
-    approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
-    assertThat(approvals).hasSize(1);
-    assertThat(approvals.get(0).value).isEqualTo(0);
-  }
-
-  @Test
-  public void deleteVoteViaReview() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    setNoteDbPrimary(id);
-
-    gApi.changes().id(id.get()).current().review(ReviewInput.approve());
-    List<ApprovalInfo> approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
-    assertThat(approvals).hasSize(1);
-    assertThat(approvals.get(0).value).isEqualTo(2);
-
-    gApi.changes().id(id.get()).current().review(ReviewInput.noScore());
-
-    approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
-    assertThat(approvals).hasSize(1);
-    assertThat(approvals.get(0).value).isEqualTo(0);
-  }
-
-  @Test
-  public void deleteReviewer() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    setNoteDbPrimary(id);
-
-    gApi.changes().id(id.get()).addReviewer(user.id.toString());
-    assertThat(getReviewers(id)).containsExactly(user.id);
-    gApi.changes().id(id.get()).reviewer(user.id.toString()).remove();
-    assertThat(getReviewers(id)).isEmpty();
-  }
-
-  @Test
-  public void readOnlyReviewDb() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    testReadOnly(id);
-  }
-
-  @Test
-  public void readOnlyNoteDb() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    setNoteDbPrimary(id);
-    testReadOnly(id);
-  }
-
-  private void testReadOnly(Change.Id id) throws Exception {
-    Timestamp before = TimeUtil.nowTs();
-    Timestamp until = new Timestamp(before.getTime() + 1000 * 3600);
-
-    // Set read-only.
-    Change c = db.changes().get(id);
-    assertThat(c).named("change " + id).isNotNull();
-    NoteDbChangeState state = NoteDbChangeState.parse(c);
-    state = state.withReadOnlyUntil(until);
-    c.setNoteDbState(state.toString());
-    db.changes().update(Collections.singleton(c));
-
-    assertThat(gApi.changes().id(id.get()).get().subject).isEqualTo(PushOneCommit.SUBJECT);
-    assertThat(gApi.changes().id(id.get()).get().topic).isNull();
-    try {
-      gApi.changes().id(id.get()).topic("a-topic");
-      fail("expected read-only exception");
-    } catch (RestApiException e) {
-      Optional<Throwable> oe =
-          Throwables.getCausalChain(e)
-              .stream()
-              .filter(x -> x instanceof OrmRuntimeException)
-              .findFirst();
-      assertThat(oe).named("OrmRuntimeException in causal chain of " + e).isPresent();
-      assertThat(oe.get().getMessage()).contains("read-only");
-    }
-    assertThat(gApi.changes().id(id.get()).get().topic).isNull();
-
-    TestTimeUtil.setClock(new Timestamp(until.getTime() + 1000));
-    assertThat(gApi.changes().id(id.get()).get().subject).isEqualTo(PushOneCommit.SUBJECT);
-    gApi.changes().id(id.get()).topic("a-topic");
-    assertThat(gApi.changes().id(id.get()).get().topic).isEqualTo("a-topic");
-  }
-
-  @Test
-  public void migrateToNoteDb() throws Exception {
-    testMigrateToNoteDb(createChange().getChange().getId());
-  }
-
-  @Test
-  public void migrateToNoteDbWithRebuildingFirst() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    Change c = db.changes().get(id);
-    c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
-    db.changes().update(Collections.singleton(c));
-    testMigrateToNoteDb(id);
-  }
-
-  private void testMigrateToNoteDb(Change.Id id) throws Exception {
-    assertThat(PrimaryStorage.of(db.changes().get(id))).isEqualTo(PrimaryStorage.REVIEW_DB);
-    migrator.migrateToNoteDbPrimary(id);
-    assertNoteDbPrimary(id);
-
-    gApi.changes().id(id.get()).topic("a-topic");
-    assertThat(gApi.changes().id(id.get()).get().topic).isEqualTo("a-topic");
-    assertThat(db.changes().get(id).getTopic()).isNull();
-  }
-
-  @Test
-  public void migrateToNoteDbFailsRebuildingOnceAndRetries() throws Exception {
-    Change.Id id = createChange().getChange().getId();
-
-    Change c = db.changes().get(id);
-    c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
-    db.changes().update(Collections.singleton(c));
-    rebuilderWrapper.failNextUpdate();
-
-    migrator =
-        newMigrator(
-            RetryerBuilder.<NoteDbChangeState>newBuilder()
-                .retryIfException()
-                .withStopStrategy(StopStrategies.neverStop())
-                .build());
-    migrator.migrateToNoteDbPrimary(id);
-    assertNoteDbPrimary(id);
-  }
-
-  @Test
-  public void migrateToNoteDbFailsRebuildingAndStops() throws Exception {
-    Change.Id id = createChange().getChange().getId();
-
-    Change c = db.changes().get(id);
-    c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
-    db.changes().update(Collections.singleton(c));
-    rebuilderWrapper.failNextUpdate();
-
-    migrator =
-        newMigrator(
-            RetryerBuilder.<NoteDbChangeState>newBuilder()
-                .retryIfException()
-                .withStopStrategy(StopStrategies.stopAfterAttempt(1))
-                .build());
-    exception.expect(OrmException.class);
-    exception.expectMessage("Retrying failed");
-    migrator.migrateToNoteDbPrimary(id);
-  }
-
-  @Test
-  public void migrateToNoteDbMissingOldState() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    Change c = db.changes().get(id);
-    c.setNoteDbState(null);
-    db.changes().update(Collections.singleton(c));
-
-    exception.expect(PrimaryStorageMigrator.NoNoteDbStateException.class);
-    exception.expectMessage("no note_db_state");
-    migrator.migrateToNoteDbPrimary(id);
-  }
-
-  @Test
-  public void migrateToNoteDbLeaseExpires() throws Exception {
-    TestTimeUtil.resetWithClockStep(2, DAYS);
-    exception.expect(OrmRuntimeException.class);
-    exception.expectMessage("read-only lease");
-    migrator.migrateToNoteDbPrimary(createChange().getChange().getId());
-  }
-
-  @Test
-  public void migrateToNoteDbAlreadyReadOnly() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    Change c = db.changes().get(id);
-    NoteDbChangeState state = NoteDbChangeState.parse(c);
-    Timestamp until = new Timestamp(TimeUtil.nowMs() + MILLISECONDS.convert(1, DAYS));
-    state = state.withReadOnlyUntil(until);
-    c.setNoteDbState(state.toString());
-    db.changes().update(Collections.singleton(c));
-
-    exception.expect(OrmRuntimeException.class);
-    exception.expectMessage("read-only until " + until);
-    migrator.migrateToNoteDbPrimary(id);
-  }
-
-  @Test
-  public void migrateToNoteDbAlreadyMigrated() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    assertThat(PrimaryStorage.of(db.changes().get(id))).isEqualTo(PrimaryStorage.REVIEW_DB);
-    migrator.migrateToNoteDbPrimary(id);
-    assertNoteDbPrimary(id);
-
-    migrator.migrateToNoteDbPrimary(id);
-    assertNoteDbPrimary(id);
-  }
-
-  @Test
-  public void rebuildReviewDb() throws Exception {
-    Change c = createChange().getChange().change();
-    Change.Id id = c.getId();
-
-    CommentInput cin = new CommentInput();
-    cin.line = 1;
-    cin.message = "Published comment";
-    ReviewInput rin = ReviewInput.approve();
-    rin.comments = ImmutableMap.of(PushOneCommit.FILE_NAME, ImmutableList.of(cin));
-    gApi.changes().id(id.get()).current().review(ReviewInput.approve());
-
-    DraftInput din = new DraftInput();
-    din.path = PushOneCommit.FILE_NAME;
-    din.line = 1;
-    din.message = "Draft comment";
-    gApi.changes().id(id.get()).current().createDraft(din);
-    gApi.changes().id(id.get()).current().review(ReviewInput.approve());
-    gApi.changes().id(id.get()).current().createDraft(din);
-
-    assertThat(db.changeMessages().byChange(id)).isNotEmpty();
-    assertThat(db.patchSets().byChange(id)).isNotEmpty();
-    assertThat(db.patchSetApprovals().byChange(id)).isNotEmpty();
-    assertThat(db.patchComments().byChange(id)).isNotEmpty();
-
-    ChangeBundle noteDbBundle =
-        ChangeBundle.fromNotes(commentsUtil, notesFactory.create(db, project, id));
-
-    setNoteDbPrimary(id);
-
-    db.changeMessages().delete(db.changeMessages().byChange(id));
-    db.patchSets().delete(db.patchSets().byChange(id));
-    db.patchSetApprovals().delete(db.patchSetApprovals().byChange(id));
-    db.patchComments().delete(db.patchComments().byChange(id));
-    ChangeMessage bogusMessage =
-        ChangeMessagesUtil.newMessage(
-            c.currentPatchSetId(),
-            identifiedUserFactory.create(admin.getId()),
-            TimeUtil.nowTs(),
-            "some message",
-            null);
-    db.changeMessages().insert(Collections.singleton(bogusMessage));
-
-    rebuilderWrapper.rebuildReviewDb(db, project, id);
-
-    assertThat(db.changeMessages().byChange(id)).isNotEmpty();
-    assertThat(db.patchSets().byChange(id)).isNotEmpty();
-    assertThat(db.patchSetApprovals().byChange(id)).isNotEmpty();
-    assertThat(db.patchComments().byChange(id)).isNotEmpty();
-
-    ChangeBundle reviewDbBundle = bundleReader.fromReviewDb(ReviewDbUtil.unwrapDb(db), id);
-    assertThat(reviewDbBundle.differencesFrom(noteDbBundle)).isEmpty();
-  }
-
-  @Test
-  public void migrateBackToReviewDbPrimary() throws Exception {
-    Change c = createChange().getChange().change();
-    Change.Id id = c.getId();
-
-    migrator.migrateToNoteDbPrimary(id);
-    assertNoteDbPrimary(id);
-
-    gApi.changes().id(id.get()).topic("new-topic");
-    assertThat(gApi.changes().id(id.get()).topic()).isEqualTo("new-topic");
-    assertThat(db.changes().get(id).getTopic()).isNotEqualTo("new-topic");
-
-    migrator.migrateToReviewDbPrimary(id, null);
-    ObjectId metaId;
-    try (Repository repo = repoManager.openRepository(c.getProject());
-        RevWalk rw = new RevWalk(repo)) {
-      metaId = repo.exactRef(RefNames.changeMetaRef(id)).getObjectId();
-      RevCommit commit = rw.parseCommit(metaId);
-      rw.parseBody(commit);
-      assertThat(commit.getFullMessage())
-          .contains("Read-only-until: " + formatTime(serverIdent.get(), new Timestamp(0)));
-    }
-    NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
-    assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-    assertThat(state.getChangeMetaId()).isEqualTo(metaId);
-    assertThat(gApi.changes().id(id.get()).topic()).isEqualTo("new-topic");
-    assertThat(db.changes().get(id).getTopic()).isEqualTo("new-topic");
-
-    ChangeNotes notes = notesFactory.create(db, project, id);
-    assertThat(notes.getRevision()).isEqualTo(metaId); // No rebuilding, change was up to date.
-    assertThat(notes.getReadOnlyUntil()).isNotNull();
-
-    gApi.changes().id(id.get()).topic("reviewdb-topic");
-    assertThat(db.changes().get(id).getTopic()).isEqualTo("reviewdb-topic");
-  }
-
-  private void setNoteDbPrimary(Change.Id id) throws Exception {
-    Change c = db.changes().get(id);
-    assertThat(c).named("change " + id).isNotNull();
-    NoteDbChangeState state = NoteDbChangeState.parse(c);
-    assertThat(state.getPrimaryStorage()).named("storage of " + id).isEqualTo(REVIEW_DB);
-
-    try (Repository changeRepo = repoManager.openRepository(c.getProject());
-        Repository allUsersRepo = repoManager.openRepository(allUsers)) {
-      assertThat(state.isUpToDate(new RepoRefCache(changeRepo), new RepoRefCache(allUsersRepo)))
-          .named("change " + id + " up to date")
-          .isTrue();
-    }
-
-    c.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
-    db.changes().update(Collections.singleton(c));
-  }
-
-  private void assertNoteDbPrimary(Change.Id id) throws Exception {
-    assertThat(PrimaryStorage.of(db.changes().get(id))).isEqualTo(PrimaryStorage.NOTE_DB);
-  }
-
-  private List<Account.Id> getReviewers(Change.Id id) throws Exception {
-    return gApi.changes()
-        .id(id.get())
-        .get()
-        .reviewers
-        .values()
-        .stream()
-        .flatMap(Collection::stream)
-        .map(a -> new Account.Id(a._accountId))
-        .collect(toList());
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
deleted file mode 100644
index 6f226c8..0000000
--- a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ /dev/null
@@ -1,629 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.server.notedb;
-
-import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.NOTE_DB_PRIMARY_STATE;
-import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.WRITE;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Comparator.naturalOrder;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.GerritConfig;
-import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.acceptance.Sandboxed;
-import com.google.gerrit.acceptance.UseLocalDisk;
-import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.gerrit.server.notedb.rebuild.MigrationException;
-import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
-import com.google.gerrit.server.notedb.rebuild.NotesMigrationStateListener;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Stream;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@Sandboxed
-@UseLocalDisk
-@NoHttpd
-public class OnlineNoteDbMigrationIT extends AbstractDaemonTest {
-  private static final String INVALID_STATE = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
-  @Inject private ProjectOperations projectOperations;
-
-  @ConfigSuite.Default
-  public static Config defaultConfig() {
-    Config cfg = new Config();
-    cfg.setInt("noteDb", "changes", "sequenceBatchSize", 10);
-    cfg.setInt("noteDb", "changes", "initialSequenceGap", 500);
-    return cfg;
-  }
-
-  // Tests in this class are generally interested in the actual ReviewDb contents, but the shifting
-  // migration state may result in various kinds of wrappers showing up unexpectedly.
-  @Inject @ReviewDbFactory private SchemaFactory<ReviewDb> schemaFactory;
-
-  @Inject private ChangeBundleReader changeBundleReader;
-  @Inject private CommentsUtil commentsUtil;
-  @Inject private DynamicSet<NotesMigrationStateListener> listeners;
-  @Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
-  @Inject private Sequences sequences;
-  @Inject private SitePaths sitePaths;
-
-  private FileBasedConfig noteDbConfig;
-  private List<RegistrationHandle> addedListeners;
-
-  @Before
-  public void setUp() throws Exception {
-    assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
-    // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
-    noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
-    assertNotesMigrationState(REVIEW_DB, false, false);
-    addedListeners = new ArrayList<>();
-  }
-
-  @After
-  public void tearDown() throws Exception {
-    if (addedListeners != null) {
-      addedListeners.forEach(RegistrationHandle::remove);
-      addedListeners = null;
-    }
-  }
-
-  @Test
-  public void preconditionsFail() throws Exception {
-    List<Change.Id> cs = ImmutableList.of(new Change.Id(1));
-    List<Project.NameKey> ps = ImmutableList.of(new Project.NameKey("p"));
-    assertMigrationException(
-        "Cannot rebuild without noteDb.changes.write=true", b -> b, NoteDbMigrator::rebuild);
-    assertMigrationException(
-        "Cannot set both changes and projects", b -> b.setChanges(cs).setProjects(ps), m -> {});
-    assertMigrationException(
-        "Cannot set changes or projects during full migration",
-        b -> b.setChanges(cs),
-        NoteDbMigrator::migrate);
-    assertMigrationException(
-        "Cannot set changes or projects during full migration",
-        b -> b.setProjects(ps),
-        NoteDbMigrator::migrate);
-
-    setNotesMigrationState(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);
-    assertMigrationException(
-        "Migration has already progressed past the endpoint of the \"trial mode\" state",
-        b -> b.setTrialMode(true),
-        NoteDbMigrator::migrate);
-
-    setNotesMigrationState(READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY);
-    assertMigrationException(
-        "Cannot force rebuild changes; NoteDb is already the primary storage for some changes",
-        b -> b.setForceRebuild(true),
-        NoteDbMigrator::migrate);
-  }
-
-  @Test
-  @GerritConfig(name = "noteDb.changes.initialSequenceGap", value = "-7")
-  public void initialSequenceGapMustBeNonNegative() throws Exception {
-    setNotesMigrationState(READ_WRITE_NO_SEQUENCE);
-    assertMigrationException("Sequence gap must be non-negative: -7", b -> b, m -> {});
-  }
-
-  @Test
-  public void rebuildOneChangeTrialModeAndForceRebuild() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    migrate(b -> b.setTrialMode(true));
-    assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true);
-
-    ObjectId oldMetaId;
-    try (Repository repo = repoManager.openRepository(project);
-        ReviewDb db = schemaFactory.open()) {
-      Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
-      assertThat(ref).isNotNull();
-      oldMetaId = ref.getObjectId();
-
-      Change c = db.changes().get(id);
-      assertThat(c).isNotNull();
-      NoteDbChangeState state = NoteDbChangeState.parse(c);
-      assertThat(state).isNotNull();
-      assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-      assertThat(state.getRefState()).hasValue(RefState.create(oldMetaId, ImmutableMap.of()));
-
-      // Force change to be out of date, and change topic so it will get rebuilt as something other
-      // than oldMetaId.
-      c.setNoteDbState(INVALID_STATE);
-      c.setTopic(name("a-new-topic"));
-      db.changes().update(ImmutableList.of(c));
-    }
-
-    migrate(b -> b.setTrialMode(true));
-    assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true);
-
-    try (Repository repo = repoManager.openRepository(project);
-        ReviewDb db = schemaFactory.open()) {
-      // Change is out of date, but was not rebuilt without forceRebuild.
-      assertThat(repo.exactRef(RefNames.changeMetaRef(id)).getObjectId()).isEqualTo(oldMetaId);
-      Change c = db.changes().get(id);
-      assertThat(c.getNoteDbState()).isEqualTo(INVALID_STATE);
-    }
-
-    migrate(b -> b.setTrialMode(true).setForceRebuild(true));
-    assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true);
-
-    try (Repository repo = repoManager.openRepository(project);
-        ReviewDb db = schemaFactory.open()) {
-      Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
-      assertThat(ref).isNotNull();
-      ObjectId newMetaId = ref.getObjectId();
-      assertThat(newMetaId).isNotEqualTo(oldMetaId);
-
-      NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
-      assertThat(state).isNotNull();
-      assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-      assertThat(state.getRefState()).hasValue(RefState.create(newMetaId, ImmutableMap.of()));
-    }
-  }
-
-  @Test
-  public void autoMigrateTrialMode() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    migrate(b -> b.setAutoMigrate(true).setTrialMode(true).setStopAtStateForTesting(WRITE));
-    assertNotesMigrationState(WRITE, true, true);
-
-    migrate(b -> b);
-    // autoMigrate is still enabled so that we can continue the migration by only unsetting trial.
-    assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, true, true);
-
-    ObjectId metaId;
-    try (Repository repo = repoManager.openRepository(project);
-        ReviewDb db = schemaFactory.open()) {
-      Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
-      assertThat(ref).isNotNull();
-      metaId = ref.getObjectId();
-      NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
-      assertThat(state).isNotNull();
-      assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-      assertThat(state.getRefState()).hasValue(RefState.create(metaId, ImmutableMap.of()));
-    }
-
-    // Unset trial mode and the next migration runs to completion.
-    noteDbConfig.load();
-    NoteDbMigrator.setTrialMode(noteDbConfig, false);
-    noteDbConfig.save();
-
-    migrate(b -> b);
-    assertNotesMigrationState(NOTE_DB, false, false);
-
-    try (Repository repo = repoManager.openRepository(project);
-        ReviewDb db = schemaFactory.open()) {
-      Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
-      assertThat(ref).isNotNull();
-      assertThat(ref.getObjectId()).isEqualTo(metaId);
-      NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
-      assertThat(state).isNotNull();
-      assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.NOTE_DB);
-    }
-  }
-
-  @Test
-  public void rebuildSubsetOfChanges() throws Exception {
-    setNotesMigrationState(WRITE);
-
-    PushOneCommit.Result r1 = createChange();
-    PushOneCommit.Result r2 = createChange();
-    Change.Id id1 = r1.getChange().getId();
-    Change.Id id2 = r2.getChange().getId();
-
-    try (ReviewDb db = schemaFactory.open()) {
-      Change c1 = db.changes().get(id1);
-      c1.setNoteDbState(INVALID_STATE);
-      Change c2 = db.changes().get(id2);
-      c2.setNoteDbState(INVALID_STATE);
-      db.changes().update(ImmutableList.of(c1, c2));
-    }
-
-    migrate(b -> b.setChanges(ImmutableList.of(id2)), NoteDbMigrator::rebuild);
-
-    try (ReviewDb db = schemaFactory.open()) {
-      NoteDbChangeState s1 = NoteDbChangeState.parse(db.changes().get(id1));
-      assertThat(s1.getChangeMetaId().name()).isEqualTo(INVALID_STATE);
-
-      NoteDbChangeState s2 = NoteDbChangeState.parse(db.changes().get(id2));
-      assertThat(s2.getChangeMetaId().name()).isNotEqualTo(INVALID_STATE);
-    }
-  }
-
-  @Test
-  public void rebuildSubsetOfProjects() throws Exception {
-    setNotesMigrationState(WRITE);
-
-    Project.NameKey p2 = projectOperations.newProject().create();
-    TestRepository<?> tr2 = cloneProject(p2, admin);
-
-    PushOneCommit.Result r1 = createChange();
-    PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master");
-    Change.Id id1 = r1.getChange().getId();
-    Change.Id id2 = r2.getChange().getId();
-
-    String invalidState = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
-    try (ReviewDb db = schemaFactory.open()) {
-      Change c1 = db.changes().get(id1);
-      c1.setNoteDbState(invalidState);
-      Change c2 = db.changes().get(id2);
-      c2.setNoteDbState(invalidState);
-      db.changes().update(ImmutableList.of(c1, c2));
-    }
-
-    migrate(b -> b.setProjects(ImmutableList.of(p2)), NoteDbMigrator::rebuild);
-
-    try (ReviewDb db = schemaFactory.open()) {
-      NoteDbChangeState s1 = NoteDbChangeState.parse(db.changes().get(id1));
-      assertThat(s1.getChangeMetaId().name()).isEqualTo(invalidState);
-
-      NoteDbChangeState s2 = NoteDbChangeState.parse(db.changes().get(id2));
-      assertThat(s2.getChangeMetaId().name()).isNotEqualTo(invalidState);
-    }
-  }
-
-  @Test
-  public void enableSequencesNoGap() throws Exception {
-    testEnableSequences(0, 3, "13");
-  }
-
-  @Test
-  public void enableSequencesWithGap() throws Exception {
-    testEnableSequences(-1, 502, "512");
-  }
-
-  private void testEnableSequences(int builderOption, int expectedFirstId, String expectedRefValue)
-      throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-    assertThat(id.get()).isEqualTo(1);
-
-    migrate(
-        b ->
-            b.setSequenceGap(builderOption)
-                .setStopAtStateForTesting(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY));
-
-    assertThat(sequences.nextChangeId()).isEqualTo(expectedFirstId);
-    assertThat(sequences.nextChangeId()).isEqualTo(expectedFirstId + 1);
-
-    try (Repository repo = repoManager.openRepository(allProjects);
-        ObjectReader reader = repo.newObjectReader()) {
-      Ref ref = repo.exactRef("refs/sequences/changes");
-      assertThat(ref).isNotNull();
-      ObjectLoader loader = reader.open(ref.getObjectId());
-      assertThat(loader.getType()).isEqualTo(Constants.OBJ_BLOB);
-      // Acquired a block of 10 to serve the first nextChangeId call after migration.
-      assertThat(new String(loader.getCachedBytes(), UTF_8)).isEqualTo(expectedRefValue);
-    }
-
-    try (ReviewDb db = schemaFactory.open()) {
-      // Underlying, unused ReviewDb is still on its own sequence.
-      @SuppressWarnings("deprecation")
-      int nextFromReviewDb = db.nextChangeId();
-      assertThat(nextFromReviewDb).isEqualTo(3);
-    }
-  }
-
-  @Test
-  public void fullMigrationSameThread() throws Exception {
-    testFullMigration(1);
-  }
-
-  @Test
-  public void fullMigrationMultipleThreads() throws Exception {
-    testFullMigration(2);
-  }
-
-  private void testFullMigration(int threads) throws Exception {
-    PushOneCommit.Result r1 = createChange();
-    PushOneCommit.Result r2 = createChange();
-    Change.Id id1 = r1.getChange().getId();
-    Change.Id id2 = r2.getChange().getId();
-
-    Set<String> objectFiles = getObjectFiles(project);
-    assertThat(objectFiles).isNotEmpty();
-
-    migrate(b -> b.setThreads(threads));
-
-    assertNotesMigrationState(NOTE_DB, false, false);
-    assertThat(sequences.nextChangeId()).isEqualTo(503);
-    assertThat(getObjectFiles(project)).containsExactlyElementsIn(objectFiles);
-
-    ObjectId oldMetaId = null;
-    int rowVersion = 0;
-    try (ReviewDb db = schemaFactory.open();
-        Repository repo = repoManager.openRepository(project)) {
-      for (Change.Id id : ImmutableList.of(id1, id2)) {
-        String refName = RefNames.changeMetaRef(id);
-        Ref ref = repo.exactRef(refName);
-        assertThat(ref).named(refName).isNotNull();
-
-        Change c = db.changes().get(id);
-        assertThat(c.getTopic()).named("topic of change %s", id).isNull();
-        NoteDbChangeState s = NoteDbChangeState.parse(c);
-        assertThat(s.getPrimaryStorage())
-            .named("primary storage of change %s", id)
-            .isEqualTo(PrimaryStorage.NOTE_DB);
-        assertThat(s.getRefState()).named("ref state of change %s").isEmpty();
-
-        if (id.equals(id1)) {
-          oldMetaId = ref.getObjectId();
-          rowVersion = c.getRowVersion();
-        }
-      }
-    }
-
-    // Do not open a new context, to simulate races with other threads that opened a context earlier
-    // in the migration process; this needs to work.
-    gApi.changes().id(id1.get()).topic(name("a-topic"));
-
-    // Of course, it should also work with a new context.
-    resetCurrentApiUser();
-    gApi.changes().id(id1.get()).topic(name("another-topic"));
-
-    try (ReviewDb db = schemaFactory.open();
-        Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef(RefNames.changeMetaRef(id1)).getObjectId()).isNotEqualTo(oldMetaId);
-
-      Change c = db.changes().get(id1);
-      assertThat(c.getTopic()).isNull();
-      assertThat(c.getRowVersion()).isEqualTo(rowVersion);
-    }
-  }
-
-  @Test
-  public void fullMigrationOneChangeWithNoPatchSets() throws Exception {
-    PushOneCommit.Result r1 = createChange();
-    PushOneCommit.Result r2 = createChange();
-    Change.Id id1 = r1.getChange().getId();
-    Change.Id id2 = r2.getChange().getId();
-
-    db.changes().beginTransaction(id2);
-    try {
-      db.patchSets().delete(db.patchSets().byChange(id2));
-      db.commit();
-    } finally {
-      db.rollback();
-    }
-
-    migrate(b -> b);
-    assertNotesMigrationState(NOTE_DB, false, false);
-
-    try (ReviewDb db = schemaFactory.open();
-        Repository repo = repoManager.openRepository(project)) {
-      assertThat(repo.exactRef(RefNames.changeMetaRef(id1))).isNotNull();
-      assertThat(db.changes().get(id1).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE);
-
-      // A change with no patch sets is so corrupt that it is completely skipped by the migration
-      // process.
-      assertThat(repo.exactRef(RefNames.changeMetaRef(id2))).isNull();
-      assertThat(db.changes().get(id2).getNoteDbState()).isNull();
-    }
-  }
-
-  @Test
-  public void fullMigrationMissingPatchSetRefs() throws Exception {
-    PushOneCommit.Result r = createChange();
-    Change.Id id = r.getChange().getId();
-
-    try (Repository repo = repoManager.openRepository(project)) {
-      RefUpdate u = repo.updateRef(new PatchSet.Id(id, 1).toRefName());
-      u.setForceUpdate(true);
-      assertThat(u.delete()).isEqualTo(RefUpdate.Result.FORCED);
-    }
-
-    ChangeBundle reviewDbBundle;
-    try (ReviewDb db = schemaFactory.open()) {
-      reviewDbBundle = changeBundleReader.fromReviewDb(db, id);
-    }
-
-    migrate(b -> b);
-    assertNotesMigrationState(NOTE_DB, false, false);
-
-    try (ReviewDb db = schemaFactory.open();
-        Repository repo = repoManager.openRepository(project)) {
-      // Change migrated successfully even though it was missing patch set refs.
-      assertThat(repo.exactRef(RefNames.changeMetaRef(id))).isNotNull();
-      assertThat(db.changes().get(id).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE);
-
-      ChangeBundle noteDbBundle =
-          ChangeBundle.fromNotes(commentsUtil, notesFactory.createChecked(db, project, id));
-      assertThat(noteDbBundle.differencesFrom(reviewDbBundle)).isEmpty();
-    }
-  }
-
-  @Test
-  public void autoMigrationConfig() throws Exception {
-    createChange();
-
-    migrate(b -> b.setStopAtStateForTesting(WRITE));
-    assertNotesMigrationState(WRITE, false, false);
-
-    migrate(b -> b.setAutoMigrate(true).setStopAtStateForTesting(READ_WRITE_NO_SEQUENCE));
-    assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, true, false);
-
-    migrate(b -> b);
-    assertNotesMigrationState(NOTE_DB, false, false);
-  }
-
-  @Test
-  public void notesMigrationStateListener() throws Exception {
-    NotesMigrationStateListener listener = createStrictMock(NotesMigrationStateListener.class);
-    listener.preStateChange(REVIEW_DB, WRITE);
-    expectLastCall();
-    listener.preStateChange(WRITE, READ_WRITE_NO_SEQUENCE);
-    expectLastCall();
-    listener.preStateChange(READ_WRITE_NO_SEQUENCE, READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);
-    expectLastCall();
-    listener.preStateChange(
-        READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY, READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY);
-    listener.preStateChange(READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY, NOTE_DB);
-    expectLastCall();
-    replay(listener);
-    addListener(listener);
-
-    createChange();
-    migrate(b -> b);
-    assertNotesMigrationState(NOTE_DB, false, false);
-    verify(listener);
-  }
-
-  @Test
-  public void notesMigrationStateListenerFails() throws Exception {
-    NotesMigrationStateListener listener = createStrictMock(NotesMigrationStateListener.class);
-    listener.preStateChange(REVIEW_DB, WRITE);
-    expectLastCall();
-    listener.preStateChange(WRITE, READ_WRITE_NO_SEQUENCE);
-    IOException listenerException = new IOException("Listener failed");
-    expectLastCall().andThrow(listenerException);
-    replay(listener);
-    addListener(listener);
-
-    createChange();
-    try {
-      migrate(b -> b);
-      fail("expected IOException");
-    } catch (IOException e) {
-      assertThat(e).isSameAs(listenerException);
-    }
-    assertNotesMigrationState(WRITE, false, false);
-    verify(listener);
-  }
-
-  private void assertNotesMigrationState(
-      NotesMigrationState expected, boolean autoMigrate, boolean trialMode) throws Exception {
-    assertThat(NotesMigrationState.forNotesMigration(notesMigration)).hasValue(expected);
-    noteDbConfig.load();
-    assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected);
-    assertThat(NoteDbMigrator.getAutoMigrate(noteDbConfig))
-        .named("noteDb.changes.autoMigrate")
-        .isEqualTo(autoMigrate);
-    assertThat(NoteDbMigrator.getTrialMode(noteDbConfig))
-        .named("noteDb.changes.trial")
-        .isEqualTo(trialMode);
-  }
-
-  private void setNotesMigrationState(NotesMigrationState state) throws Exception {
-    noteDbConfig.load();
-    state.setConfigValues(noteDbConfig);
-    noteDbConfig.save();
-    notesMigration.setFrom(state);
-  }
-
-  @FunctionalInterface
-  interface PrepareBuilder {
-    NoteDbMigrator.Builder prepare(NoteDbMigrator.Builder b) throws Exception;
-  }
-
-  @FunctionalInterface
-  interface RunMigration {
-    void run(NoteDbMigrator m) throws Exception;
-  }
-
-  private void migrate(PrepareBuilder b) throws Exception {
-    migrate(b, NoteDbMigrator::migrate);
-  }
-
-  private void migrate(PrepareBuilder b, RunMigration m) throws Exception {
-    try (NoteDbMigrator migrator = b.prepare(migratorBuilderProvider.get()).build()) {
-      m.run(migrator);
-    }
-  }
-
-  private void assertMigrationException(
-      String expectMessageContains, PrepareBuilder b, RunMigration m) throws Exception {
-    try {
-      migrate(b, m);
-      fail("expected MigrationException");
-    } catch (MigrationException e) {
-      assertThat(e).hasMessageThat().contains(expectMessageContains);
-    }
-  }
-
-  private void addListener(NotesMigrationStateListener listener) {
-    addedListeners.add(listeners.add("gerrit", listener));
-  }
-
-  private ImmutableSortedSet<String> getObjectFiles(Project.NameKey project) throws Exception {
-    try (Repository repo = repoManager.openRepository(project);
-        Stream<Path> paths =
-            Files.walk(((FileRepository) repo).getObjectDatabase().getDirectory().toPath())) {
-      return paths
-          .filter(path -> !Files.isDirectory(path))
-          .map(Path::toString)
-          .filter(name -> !name.endsWith(".pack") && !name.endsWith(".idx"))
-          .collect(toImmutableSortedSet(naturalOrder()));
-    }
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
index 8abb59d..04303ea 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.server.project;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -40,7 +39,6 @@
 public class ReflogIT extends AbstractDaemonTest {
   @Test
   public void guessRestApiInReflog() throws Exception {
-    assume().that(notesMigration.disableChangeReviewDb()).isTrue();
     PushOneCommit.Result r = createChange();
     Change.Id id = r.getChange().getId();
 
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
index eb79c18..4e9c4a4 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
@@ -17,7 +17,6 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -27,7 +26,6 @@
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.Sandboxed;
 import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.common.data.GlobalCapability;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -64,7 +62,6 @@
           "create-branch",
           "create-group",
           "create-project",
-          "gsql",
           "index",
           "query",
           "receive-pack",
@@ -113,9 +110,6 @@
   @Test
   @Sandboxed
   public void sshCommandCanBeExecuted() throws Exception {
-    // Access Database capability is required to run the "gerrit gsql" command
-    allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
-
     testCommandExecution(MASTER_COMMANDS);
 
     restartAsSlave();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java b/javatests/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
index 93fc769..03b7143 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/UploadArchiveIT.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.acceptance.ssh;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
 
 import com.google.common.base.Splitter;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -23,7 +22,6 @@
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.testing.NoteDbMode;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
@@ -34,20 +32,12 @@
 import org.eclipse.jgit.transport.PacketLineIn;
 import org.eclipse.jgit.transport.PacketLineOut;
 import org.eclipse.jgit.util.IO;
-import org.junit.Before;
 import org.junit.Test;
 
 @NoHttpd
 @UseSsh
 public class UploadArchiveIT extends AbstractDaemonTest {
 
-  @Before
-  public void setUp() {
-    // There is some Guice request scoping problem preventing this test from
-    // passing in CHECK mode.
-    assume().that(NoteDbMode.get()).isNotEqualTo(NoteDbMode.CHECK);
-  }
-
   @Test
   @GerritConfig(name = "download.archive", value = "off")
   public void archiveFeatureOff() throws Exception {
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index 14fe4de..19ea44b 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -45,7 +45,7 @@
       case V6_4:
         return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3";
       case V6_5:
-        return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.5.1";
+        return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.5.2";
       case V7_0:
         return "docker.elastic.co/elasticsearch/elasticsearch-oss:7.0.0-alpha1";
     }
diff --git a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index b90ccf7c..d5add7d 100644
--- a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -41,7 +41,7 @@
 import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gerrit.testing.GerritBaseTests;
@@ -81,7 +81,7 @@
 
   @Inject private InMemoryDatabase schemaFactory;
 
-  @Inject private ReviewDbSchemaCreator schemaCreator;
+  @Inject private SchemaCreator schemaCreator;
 
   @Inject private ThreadLocalRequestContext requestContext;
 
@@ -112,7 +112,7 @@
     lifecycle.start();
 
     db = schemaFactory.open();
-    schemaCreator.create(db);
+    schemaCreator.create();
     userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
     // Note: does not match any key in TestKeys.
     accountsUpdateProvider
@@ -162,7 +162,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(schemaFactory);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java b/javatests/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java
deleted file mode 100644
index 159ff0d..0000000
--- a/javatests/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.pgm.init;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import com.google.common.io.ByteStreams;
-import com.google.gerrit.common.FileUtil;
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.pgm.init.api.InitFlags;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.securestore.testing.InMemorySecureStore;
-import com.google.gerrit.testing.GerritBaseTests;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collections;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class UpgradeFrom2_0_xTest extends GerritBaseTests {
-  @Rule public TemporaryFolder folder = new TemporaryFolder();
-
-  @Test
-  public void upgrade() throws IOException, ConfigInvalidException {
-    final Path p = folder.newFolder().toPath();
-    final SitePaths site = new SitePaths(p);
-    assertTrue(site.isNew);
-    FileUtil.mkdirsOrDie(site.etc_dir, "Failed to create");
-
-    for (String n : UpgradeFrom2_0_x.etcFiles) {
-      Files.write(p.resolve(n), ("# " + n + "\n").getBytes(UTF_8));
-    }
-
-    FileBasedConfig old = new FileBasedConfig(p.resolve("gerrit.config").toFile(), FS.DETECTED);
-
-    old.setString("ldap", null, "username", "ldap.user");
-    old.setString("ldap", null, "password", "ldap.s3kr3t");
-
-    old.setString("sendemail", null, "smtpUser", "email.user");
-    old.setString("sendemail", null, "smtpPass", "email.s3kr3t");
-    old.save();
-
-    final InMemorySecureStore secureStore = new InMemorySecureStore();
-    final InitFlags flags =
-        new InitFlags(site, secureStore, Collections.<String>emptyList(), false);
-    final ConsoleUI ui = createStrictMock(ConsoleUI.class);
-    Section.Factory sections =
-        new Section.Factory() {
-          @Override
-          public Section get(String name, String subsection) {
-            return new Section(flags, site, secureStore, ui, name, subsection);
-          }
-        };
-
-    expect(ui.yesno(eq(true), eq("Upgrade '%s'"), eq(p.toAbsolutePath().normalize())))
-        .andReturn(true);
-    replay(ui);
-
-    UpgradeFrom2_0_x u = new UpgradeFrom2_0_x(site, flags, ui, sections);
-    assertTrue(u.isNeedUpgrade());
-    u.run();
-    assertFalse(u.isNeedUpgrade());
-    verify(ui);
-
-    for (String n : UpgradeFrom2_0_x.etcFiles) {
-      if ("gerrit.config".equals(n) || "secure.config".equals(n)) {
-        continue;
-      }
-      try (InputStream in = Files.newInputStream(site.etc_dir.resolve(n))) {
-        assertEquals("# " + n + "\n", new String(ByteStreams.toByteArray(in), UTF_8));
-      }
-    }
-
-    FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED);
-    cfg.load();
-
-    assertEquals("email.user", cfg.getString("sendemail", null, "smtpUser"));
-    assertNull(cfg.getString("sendemail", null, "smtpPass"));
-    assertEquals("email.s3kr3t", secureStore.get("sendemail", null, "smtpPass"));
-
-    assertEquals("ldap.user", cfg.getString("ldap", null, "username"));
-    assertNull(cfg.getString("ldap", null, "password"));
-    assertEquals("ldap.s3kr3t", secureStore.get("ldap", null, "password"));
-
-    u.run();
-  }
-}
diff --git a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
index fe980f9..cc15b1b 100644
--- a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
+++ b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
@@ -44,7 +44,7 @@
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gerrit.server.util.time.TimeUtil;
@@ -72,7 +72,7 @@
   @Inject private LabelNormalizer norm;
   @Inject private MetaDataUpdate.User metaDataUpdateFactory;
   @Inject private ProjectCache projectCache;
-  @Inject private ReviewDbSchemaCreator schemaCreator;
+  @Inject private SchemaCreator schemaCreator;
   @Inject protected ThreadLocalRequestContext requestContext;
   @Inject private ChangeNotes.Factory changeNotesFactory;
   @Inject private ProjectConfig.Factory projectConfigFactory;
@@ -94,7 +94,7 @@
     lifecycle.start();
 
     db = schemaFactory.open();
-    schemaCreator.create(db);
+    schemaCreator.create();
     userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
     user = userFactory.create(userId);
 
@@ -148,7 +148,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(schemaFactory);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index d00f96b..ed4aacb 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -137,7 +137,7 @@
                 install(new GitModule());
 
                 install(new DefaultUrlFormatter.Module());
-                install(NoteDbModule.forTest(testConfig));
+                install(NoteDbModule.forTest());
                 bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
                 bind(String.class).annotatedWith(GerritServerId.class).toInstance("gerrit");
                 bind(GitRepositoryManager.class).toInstance(repoManager);
@@ -172,11 +172,6 @@
                         () -> {
                           throw new UnsupportedOperationException();
                         });
-                bind(ChangeBundleReader.class)
-                    .toInstance(
-                        (db, id) -> {
-                          throw new UnsupportedOperationException();
-                        });
               }
             });
 
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java b/javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java
deleted file mode 100644
index fc2a272..0000000
--- a/javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java
+++ /dev/null
@@ -1,1976 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB;
-import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
-import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
-import static com.google.gerrit.server.util.time.TimeUtil.truncateToSecond;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Table;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.LabelId;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TestChanges;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.protobuf.CodecFactory;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import java.sql.Timestamp;
-import java.time.LocalDate;
-import java.time.Month;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.TimeZone;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ChangeBundleTest extends GerritBaseTests {
-  private static final ProtobufCodec<Change> CHANGE_CODEC = CodecFactory.encoder(Change.class);
-  private static final ProtobufCodec<ChangeMessage> CHANGE_MESSAGE_CODEC =
-      CodecFactory.encoder(ChangeMessage.class);
-  private static final ProtobufCodec<PatchSet> PATCH_SET_CODEC =
-      CodecFactory.encoder(PatchSet.class);
-  private static final ProtobufCodec<PatchSetApproval> PATCH_SET_APPROVAL_CODEC =
-      CodecFactory.encoder(PatchSetApproval.class);
-  private static final ProtobufCodec<PatchLineComment> PATCH_LINE_COMMENT_CODEC =
-      CodecFactory.encoder(PatchLineComment.class);
-  private static final String TIMEZONE_ID = "US/Eastern";
-
-  private String systemTimeZoneProperty;
-  private TimeZone systemTimeZone;
-
-  private Project.NameKey project;
-  private Account.Id accountId;
-
-  @Before
-  public void setUp() {
-    systemTimeZoneProperty = System.setProperty("user.timezone", TIMEZONE_ID);
-    systemTimeZone = TimeZone.getDefault();
-    TimeZone.setDefault(TimeZone.getTimeZone(TIMEZONE_ID));
-    long maxMs = ChangeRebuilderImpl.MAX_WINDOW_MS;
-    assertThat(maxMs).isGreaterThan(1000L);
-    TestTimeUtil.resetWithClockStep(maxMs * 2, MILLISECONDS);
-    project = new Project.NameKey("project");
-    accountId = new Account.Id(100);
-  }
-
-  @After
-  public void tearDown() {
-    TestTimeUtil.useSystemTime();
-    System.setProperty("user.timezone", systemTimeZoneProperty);
-    TimeZone.setDefault(systemTimeZone);
-  }
-
-  private void superWindowResolution() {
-    TestTimeUtil.setClockStep(ChangeRebuilderImpl.MAX_WINDOW_MS * 2, MILLISECONDS);
-    TimeUtil.nowTs();
-  }
-
-  private void subWindowResolution() {
-    TestTimeUtil.setClockStep(1, SECONDS);
-    TimeUtil.nowTs();
-  }
-
-  @Test
-  public void diffChangesDifferentIds() throws Exception {
-    Change c1 = TestChanges.newChange(project, accountId);
-    int id1 = c1.getId().get();
-    Change c2 = TestChanges.newChange(project, accountId);
-    int id2 = c2.getId().get();
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    assertDiffs(
-        b1,
-        b2,
-        "changeId differs for Changes: {" + id1 + "} != {" + id2 + "}",
-        "createdOn differs for Changes: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:06.0}",
-        "effective last updated time differs for Changes:"
-            + " {2009-09-30 17:00:00.0} != {2009-09-30 17:00:06.0}");
-  }
-
-  @Test
-  public void diffChangesSameId() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    Change c2 = clone(c1);
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    assertNoDiffs(b1, b2);
-
-    c2.setTopic("topic");
-    assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {null} != {topic}");
-  }
-
-  @Test
-  public void diffChangesMixedSourcesAllowsSlop() throws Exception {
-    subWindowResolution();
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    Change c2 = clone(c1);
-    c2.setCreatedOn(TimeUtil.nowTs());
-    c2.setLastUpdatedOn(TimeUtil.nowTs());
-
-    // Both are ReviewDb, exact timestamp match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:02.0}",
-        "effective last updated time differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:03.0}");
-
-    // One NoteDb, slop is allowed.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    // But not too much slop.
-    superWindowResolution();
-    Change c3 = clone(c1);
-    c3.setLastUpdatedOn(TimeUtil.nowTs());
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    ChangeBundle b3 =
-        new ChangeBundle(
-            c3, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    String msg =
-        "effective last updated time differs for Change.Id "
-            + c1.getId()
-            + " in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:10.0}";
-    assertDiffs(b1, b3, msg);
-    assertDiffs(b3, b1, msg);
-  }
-
-  @Test
-  public void diffChangesIgnoresOriginalSubjectInReviewDb() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    c1.setCurrentPatchSet(c1.currentPatchSetId(), "Subject", "Original A");
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(c2.currentPatchSetId(), c1.getSubject(), "Original B");
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "originalSubject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Original A} != {Original B}");
-
-    // Both NoteDb, exact match required.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "originalSubject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Original A} != {Original B}");
-
-    // One ReviewDb, one NoteDb, original subject is ignored.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangesSanitizesSubjectsBeforeComparison() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    c1.setCurrentPatchSet(c1.currentPatchSetId(), "Subject\r\rbody", "Original");
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(c2.currentPatchSetId(), "Subject  body", "Original");
-
-    // Both ReviewDb, exact match required
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Subject\r\rbody} != {Subject  body}");
-
-    // Both NoteDb, exact match required (although it should be impossible to
-    // create a NoteDb change with '\r' in the subject).
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Subject\r\rbody} != {Subject  body}");
-
-    // One ReviewDb, one NoteDb, '\r' is normalized to ' '.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangesConsidersEmptyReviewDbTopicEquivalentToNullInNoteDb() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    c1.setTopic("");
-    Change c2 = clone(c1);
-    c2.setTopic(null);
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {} != {null}");
-
-    // Topic ignored if ReviewDb is empty and NoteDb is null.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-
-    // Exact match still required if NoteDb has empty value (not realistic).
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {} != {null}");
-
-    // Null is not equal to a non-empty string.
-    Change c3 = clone(c1);
-    c3.setTopic("topic");
-    b1 =
-        new ChangeBundle(
-            c3, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {topic} != {null}");
-
-    // Null is equal to a string that is all whitespace.
-    Change c4 = clone(c1);
-    c4.setTopic("  ");
-    b1 =
-        new ChangeBundle(
-            c4, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangesIgnoresLeadingAndTrailingWhitespaceInReviewDbTopics() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    c1.setTopic(" abc ");
-    Change c2 = clone(c1);
-    c2.setTopic("abc");
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": { abc } != {abc}");
-
-    // Leading whitespace in ReviewDb topic is ignored.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    // Must match except for the leading/trailing whitespace.
-    Change c3 = clone(c1);
-    c3.setTopic("cba");
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c3, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": { abc } != {cba}");
-  }
-
-  @Test
-  public void diffChangesTakesMaxEntityTimestampFromReviewDb() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    PatchSet ps = new PatchSet(c1.currentPatchSetId());
-    ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps.setUploader(accountId);
-    ps.setCreatedOn(TimeUtil.nowTs());
-    PatchSetApproval a =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c1.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            TimeUtil.nowTs());
-
-    Change c2 = clone(c1);
-    c2.setLastUpdatedOn(a.getGranted());
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "effective last updated time differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {2009-09-30 17:00:00.0} != {2009-09-30 17:00:12.0}");
-
-    // NoteDb allows latest timestamp from all entities in bundle.
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-  }
-
-  @Test
-  public void diffChangesIgnoresChangeTimestampIfAnyOtherEntitiesExist() {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    PatchSet ps = new PatchSet(c1.currentPatchSetId());
-    ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps.setUploader(accountId);
-    ps.setCreatedOn(TimeUtil.nowTs());
-    PatchSetApproval a =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c1.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            TimeUtil.nowTs());
-    c1.setLastUpdatedOn(a.getGranted());
-
-    Change c2 = clone(c1);
-    c2.setLastUpdatedOn(TimeUtil.nowTs());
-
-    // ReviewDb has later lastUpdatedOn timestamp than NoteDb, allowed since
-    // NoteDb matches the latest timestamp of a non-Change entity.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c1, messages(), patchSets(ps), approvals(a), comments(), reviewers(), NOTE_DB);
-    assertThat(b1.getChange().getLastUpdatedOn()).isGreaterThan(b2.getChange().getLastUpdatedOn());
-    assertNoDiffs(b1, b2);
-
-    // Timestamps must actually match if Change is the only entity.
-    b1 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "effective last updated time differs for Change.Id "
-            + c1.getId()
-            + " in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:12.0} != {2009-09-30 17:00:18.0}");
-  }
-
-  @Test
-  public void diffChangesAllowsReviewDbSubjectToBePrefixOfNoteDbSubject() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(
-        c1.currentPatchSetId(), c1.getSubject().substring(0, 10), c1.getOriginalSubject());
-    assertThat(c2.getSubject()).isNotEqualTo(c1.getSubject());
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id " + c1.getId() + ": {Change subject} != {Change sub}");
-
-    // ReviewDb has shorter subject, allowed.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-
-    // NoteDb has shorter subject, not allowed.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), latest(c1), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(c2, messages(), latest(c2), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id " + c1.getId() + ": {Change subject} != {Change sub}");
-  }
-
-  @Test
-  public void diffChangesTrimsLeadingSpacesFromReviewDbComparingToNoteDb() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(c1.currentPatchSetId(), "   " + c1.getSubject(), c1.getOriginalSubject());
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Change subject} != {   Change subject}");
-
-    // ReviewDb is missing leading spaces, allowed.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangesDoesntTrimLeadingNonSpaceWhitespaceFromSubject() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(c1.currentPatchSetId(), "\t" + c1.getSubject(), c1.getOriginalSubject());
-
-    // Both ReviewDb.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Change subject} != {\tChange subject}");
-
-    // One NoteDb.
-    b1 =
-        new ChangeBundle(c1, messages(), latest(c1), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), latest(c2), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Change subject} != {\tChange subject}");
-    assertDiffs(
-        b2,
-        b1,
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {\tChange subject} != {Change subject}");
-  }
-
-  @Test
-  public void diffChangesHandlesBuggyJGitSubjectExtraction() throws Exception {
-    Change c1 = TestChanges.newChange(project, accountId);
-    String buggySubject = "Subject\r \r Rest of message.";
-    c1.setCurrentPatchSet(c1.currentPatchSetId(), buggySubject, buggySubject);
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(c2.currentPatchSetId(), "Subject", "Subject");
-
-    // Both ReviewDb.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "originalSubject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Subject\r \r Rest of message.} != {Subject}",
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Subject\r \r Rest of message.} != {Subject}");
-
-    // NoteDb has correct subject without "\r ".
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangesIgnoresInvalidCurrentPatchSetIdInReviewDb() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    Change c2 = clone(c1);
-    c2.setCurrentPatchSet(
-        new PatchSet.Id(c2.getId(), 0), "Unrelated subject", c2.getOriginalSubject());
-
-    // Both ReviewDb.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "currentPatchSetId differs for Change.Id " + c1.getId() + ": {1} != {0}",
-        "subject differs for Change.Id "
-            + c1.getId()
-            + ":"
-            + " {Change subject} != {Unrelated subject}");
-
-    // One NoteDb.
-    //
-    // This is based on a real corrupt change where all patch sets were deleted
-    // but the Change entity stuck around, resulting in a currentPatchSetId of 0
-    // after converting to NoteDb.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangesAllowsCreatedToMatchLastUpdated() throws Exception {
-    Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
-    c1.setCreatedOn(TimeUtil.nowTs());
-    assertThat(c1.getCreatedOn()).isGreaterThan(c1.getLastUpdatedOn());
-    Change c2 = clone(c1);
-    c2.setCreatedOn(c2.getLastUpdatedOn());
-
-    // Both ReviewDb.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for Change.Id "
-            + c1.getId()
-            + ": {2009-09-30 17:00:06.0} != {2009-09-30 17:00:00.0}");
-
-    // One NoteDb.
-    b1 =
-        new ChangeBundle(
-            c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffChangeMessageKeySets() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid1"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    ChangeMessage cm2 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid2"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessage.Key sets differ:"
-            + " ["
-            + id
-            + ",uuid1] only in A; ["
-            + id
-            + ",uuid2] only in B");
-  }
-
-  @Test
-  public void diffChangeMessages() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    cm1.setMessage("message 1");
-    ChangeMessage cm2 = clone(cm1);
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    assertNoDiffs(b1, b2);
-
-    cm2.setMessage("message 2");
-    assertDiffs(
-        b1,
-        b2,
-        "message differs for ChangeMessage.Key "
-            + c.getId()
-            + ",uuid:"
-            + " {message 1} != {message 2}");
-  }
-
-  @Test
-  public void diffChangeMessagesIgnoresUuids() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid1"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    cm1.setMessage("message 1");
-    ChangeMessage cm2 = clone(cm1);
-    cm2.getKey().set("uuid2");
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    // Both are ReviewDb, exact UUID match is required.
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessage.Key sets differ:"
-            + " ["
-            + id
-            + ",uuid1] only in A; ["
-            + id
-            + ",uuid2] only in B");
-
-    // One NoteDb, UUIDs are ignored.
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-  }
-
-  @Test
-  public void diffChangeMessagesWithDifferentCounts() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid1"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    cm1.setMessage("message 1");
-    ChangeMessage cm2 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid2"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    cm1.setMessage("message 2");
-
-    // Both ReviewDb: Uses same keySet diff as other types.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1, cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1, b2, "ChangeMessage.Key sets differ: [" + id + ",uuid2] only in A; [] only in B");
-
-    // One NoteDb: UUIDs in keys can't be used for comparison, just diff counts.
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1, cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(b1, b2, "ChangeMessages differ for Change.Id " + id + "\nOnly in A:\n  " + cm2);
-    assertDiffs(b2, b1, "ChangeMessages differ for Change.Id " + id + "\nOnly in B:\n  " + cm2);
-  }
-
-  @Test
-  public void diffChangeMessagesMixedSourcesWithDifferences() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid1"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    cm1.setMessage("message 1");
-    ChangeMessage cm2 = clone(cm1);
-    cm2.setMessage("message 2");
-    ChangeMessage cm3 = clone(cm1);
-    cm3.getKey().set("uuid2"); // Differs only in UUID.
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1, cm3), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm2, cm3), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    // Implementation happens to pair up cm1 in b1 with cm3 in b2 because it
-    // depends on iteration order and doesn't care about UUIDs. The important
-    // thing is that there's some diff.
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessages differ for Change.Id "
-            + id
-            + "\n"
-            + "Only in A:\n  "
-            + cm3
-            + "\n"
-            + "Only in B:\n  "
-            + cm2);
-    assertDiffs(
-        b2,
-        b1,
-        "ChangeMessages differ for Change.Id "
-            + id
-            + "\n"
-            + "Only in A:\n  "
-            + cm2
-            + "\n"
-            + "Only in B:\n  "
-            + cm3);
-  }
-
-  @Test
-  public void diffChangeMessagesMixedSourcesAllowsSlop() throws Exception {
-    subWindowResolution();
-    Change c = TestChanges.newChange(project, accountId);
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid1"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    ChangeMessage cm2 = clone(cm1);
-    cm2.setWrittenOn(TimeUtil.nowTs());
-
-    // Both are ReviewDb, exact timestamp match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "writtenOn differs for ChangeMessage.Key "
-            + c.getId()
-            + ",uuid1:"
-            + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}");
-
-    // One NoteDb, slop is allowed.
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    // But not too much slop.
-    superWindowResolution();
-    ChangeMessage cm3 = clone(cm1);
-    cm3.setWrittenOn(TimeUtil.nowTs());
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    ChangeBundle b3 =
-        new ChangeBundle(
-            c, messages(cm3), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    int id = c.getId().get();
-    assertDiffs(
-        b1,
-        b3,
-        "ChangeMessages differ for Change.Id "
-            + id
-            + "\n"
-            + "Only in A:\n  "
-            + cm1
-            + "\n"
-            + "Only in B:\n  "
-            + cm3);
-    assertDiffs(
-        b3,
-        b1,
-        "ChangeMessages differ for Change.Id "
-            + id
-            + "\n"
-            + "Only in A:\n  "
-            + cm3
-            + "\n"
-            + "Only in B:\n  "
-            + cm1);
-  }
-
-  @Test
-  public void diffChangeMessagesAllowsNullPatchSetIdFromReviewDb() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    cm1.setMessage("message 1");
-    ChangeMessage cm2 = clone(cm1);
-    cm2.setPatchSetId(null);
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    // Both are ReviewDb, exact patch set ID match is required.
-    assertDiffs(
-        b1,
-        b2,
-        "patchset differs for ChangeMessage.Key "
-            + c.getId()
-            + ",uuid:"
-            + " {"
-            + id
-            + ",1} != {null}");
-
-    // Null patch set ID on ReviewDb is ignored.
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-
-    // Null patch set ID on NoteDb is not ignored (but is not realistic).
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(cm2), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessages differ for Change.Id "
-            + id
-            + "\n"
-            + "Only in A:\n  "
-            + cm1
-            + "\n"
-            + "Only in B:\n  "
-            + cm2);
-    assertDiffs(
-        b2,
-        b1,
-        "ChangeMessages differ for Change.Id "
-            + id
-            + "\n"
-            + "Only in A:\n  "
-            + cm2
-            + "\n"
-            + "Only in B:\n  "
-            + cm1);
-  }
-
-  @Test
-  public void diffPatchSetIdSets() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    TestChanges.incrementPatchSet(c);
-
-    PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
-    ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps1.setUploader(accountId);
-    ps1.setCreatedOn(TimeUtil.nowTs());
-    PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
-    ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
-    ps2.setUploader(accountId);
-    ps2.setCreatedOn(TimeUtil.nowTs());
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1, ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    assertDiffs(b1, b2, "PatchSet.Id sets differ: [] only in A; [" + c.getId() + ",1] only in B");
-  }
-
-  @Test
-  public void diffPatchSets() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    PatchSet ps1 = new PatchSet(c.currentPatchSetId());
-    ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps1.setUploader(accountId);
-    ps1.setCreatedOn(TimeUtil.nowTs());
-    PatchSet ps2 = clone(ps1);
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-
-    assertNoDiffs(b1, b2);
-
-    ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
-    assertDiffs(
-        b1,
-        b2,
-        "revision differs for PatchSet.Id "
-            + c.getId()
-            + ",1:"
-            + " {RevId{deadbeefdeadbeefdeadbeefdeadbeefdeadbeef}}"
-            + " != {RevId{badc0feebadc0feebadc0feebadc0feebadc0fee}}");
-  }
-
-  @Test
-  public void diffPatchSetsMixedSourcesAllowsSlop() throws Exception {
-    subWindowResolution();
-    Change c = TestChanges.newChange(project, accountId);
-    PatchSet ps1 = new PatchSet(c.currentPatchSetId());
-    ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps1.setUploader(accountId);
-    ps1.setCreatedOn(truncateToSecond(TimeUtil.nowTs()));
-    PatchSet ps2 = clone(ps1);
-    ps2.setCreatedOn(TimeUtil.nowTs());
-
-    // Both are ReviewDb, exact timestamp match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for PatchSet.Id "
-            + c.getId()
-            + ",1:"
-            + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}");
-
-    // One NoteDb, slop is allowed.
-    b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-
-    // But not too much slop.
-    superWindowResolution();
-    PatchSet ps3 = clone(ps1);
-    ps3.setCreatedOn(TimeUtil.nowTs());
-    b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB);
-    ChangeBundle b3 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps3), approvals(), comments(), reviewers(), REVIEW_DB);
-    String msg =
-        "createdOn differs for PatchSet.Id "
-            + c.getId()
-            + ",1 in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:10.0}";
-    assertDiffs(b1, b3, msg);
-    assertDiffs(b3, b1, msg);
-  }
-
-  @Test
-  public void diffPatchSetsIgnoresTrailingNewlinesInPushCertificate() throws Exception {
-    subWindowResolution();
-    Change c = TestChanges.newChange(project, accountId);
-    PatchSet ps1 = new PatchSet(c.currentPatchSetId());
-    ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps1.setUploader(accountId);
-    ps1.setCreatedOn(truncateToSecond(TimeUtil.nowTs()));
-    ps1.setPushCertificate("some cert");
-    PatchSet ps2 = clone(ps1);
-    ps2.setPushCertificate(ps2.getPushCertificate() + "\n\n");
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffPatchSetsGreaterThanCurrent() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-
-    PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
-    ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps1.setUploader(accountId);
-    ps1.setCreatedOn(TimeUtil.nowTs());
-    PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
-    ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
-    ps2.setUploader(accountId);
-    ps2.setCreatedOn(TimeUtil.nowTs());
-    assertThat(ps2.getId().get()).isGreaterThan(c.currentPatchSetId().get());
-
-    ChangeMessage cm1 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid1"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-    ChangeMessage cm2 =
-        new ChangeMessage(
-            new ChangeMessage.Key(c.getId(), "uuid2"),
-            accountId,
-            TimeUtil.nowTs(),
-            c.currentPatchSetId());
-
-    PatchSetApproval a1 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(ps1.getId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            TimeUtil.nowTs());
-    PatchSetApproval a2 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(ps2.getId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            TimeUtil.nowTs());
-
-    // Both ReviewDb.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c,
-            messages(cm1, cm2),
-            patchSets(ps1, ps2),
-            approvals(a1, a2),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessage.Key sets differ: [] only in A; [" + cm2.getKey() + "] only in B",
-        "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B",
-        "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B");
-
-    // One NoteDb.
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c,
-            messages(cm1, cm2),
-            patchSets(ps1, ps2),
-            approvals(a1, a2),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessages differ for Change.Id " + c.getId() + "\nOnly in B:\n  " + cm2,
-        "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B",
-        "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B");
-
-    // Both NoteDb.
-    b1 =
-        new ChangeBundle(
-            c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c,
-            messages(cm1, cm2),
-            patchSets(ps1, ps2),
-            approvals(a1, a2),
-            comments(),
-            reviewers(),
-            NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "ChangeMessages differ for Change.Id " + c.getId() + "\nOnly in B:\n  " + cm2,
-        "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B",
-        "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B");
-  }
-
-  @Test
-  public void diffPatchSetsIgnoresLeadingAndTrailingWhitespaceInReviewDbDescriptions()
-      throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-
-    PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
-    ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    ps1.setUploader(accountId);
-    ps1.setCreatedOn(TimeUtil.nowTs());
-    ps1.setDescription(" abc ");
-    PatchSet ps2 = clone(ps1);
-    ps2.setDescription("abc");
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ": { abc } != {abc}");
-
-    // Whitespace in ReviewDb description is ignored.
-    b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    // Must match except for the leading/trailing whitespace.
-    PatchSet ps3 = clone(ps1);
-    ps3.setDescription("cba");
-    b1 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), patchSets(ps3), approvals(), comments(), reviewers(), NOTE_DB);
-    assertDiffs(
-        b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ": { abc } != {cba}");
-  }
-
-  @Test
-  public void diffPatchSetsIgnoresCreatedOnWhenReviewDbIsNonMonotonic() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-
-    Timestamp beforePs1 = TimeUtil.nowTs();
-
-    PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
-    goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    goodPs1.setUploader(accountId);
-    goodPs1.setCreatedOn(TimeUtil.nowTs());
-    PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
-    goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    goodPs2.setUploader(accountId);
-    goodPs2.setCreatedOn(TimeUtil.nowTs());
-    assertThat(goodPs2.getCreatedOn()).isGreaterThan(goodPs1.getCreatedOn());
-
-    PatchSet badPs2 = clone(goodPs2);
-    badPs2.setCreatedOn(beforePs1);
-    assertThat(badPs2.getCreatedOn()).isLessThan(goodPs1.getCreatedOn());
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(goodPs1, goodPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(goodPs1, badPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for PatchSet.Id "
-            + badPs2.getId()
-            + ":"
-            + " {2009-09-30 17:00:18.0} != {2009-09-30 17:00:06.0}");
-
-    // Non-monotonic in ReviewDb but monotonic in NoteDb, timestamps are
-    // ignored, including for ps1.
-    PatchSet badPs1 = clone(goodPs1);
-    badPs1.setCreatedOn(TimeUtil.nowTs());
-    b1 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(badPs1, badPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(goodPs1, goodPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    // Non-monotonic in NoteDb but monotonic in ReviewDb, timestamps are not
-    // ignored.
-    b1 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(goodPs1, goodPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(badPs1, badPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for PatchSet.Id "
-            + badPs1.getId()
-            + " in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:24.0} != {2009-09-30 17:00:12.0}",
-        "createdOn differs for PatchSet.Id "
-            + badPs2.getId()
-            + " in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:06.0} != {2009-09-30 17:00:18.0}");
-  }
-
-  @Test
-  public void diffPatchSetsAllowsFirstPatchSetCreatedOnToMatchChangeCreatedOn() {
-    Change c = TestChanges.newChange(project, accountId);
-    c.setLastUpdatedOn(TimeUtil.nowTs());
-
-    PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
-    goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    goodPs1.setUploader(accountId);
-    goodPs1.setCreatedOn(TimeUtil.nowTs());
-    assertThat(goodPs1.getCreatedOn()).isGreaterThan(c.getCreatedOn());
-
-    PatchSet ps1AtCreatedOn = clone(goodPs1);
-    ps1AtCreatedOn.setCreatedOn(c.getCreatedOn());
-
-    PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
-    goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-    goodPs2.setUploader(accountId);
-    goodPs2.setCreatedOn(TimeUtil.nowTs());
-
-    PatchSet ps2AtCreatedOn = clone(goodPs2);
-    ps2AtCreatedOn.setCreatedOn(c.getCreatedOn());
-
-    // Both ReviewDb, exact match required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(goodPs1, goodPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(ps1AtCreatedOn, ps2AtCreatedOn),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for PatchSet.Id "
-            + c.getId()
-            + ",1: {2009-09-30 17:00:12.0} != {2009-09-30 17:00:00.0}",
-        "createdOn differs for PatchSet.Id "
-            + c.getId()
-            + ",2: {2009-09-30 17:00:18.0} != {2009-09-30 17:00:00.0}");
-
-    // One ReviewDb, PS1 is allowed to match change createdOn, but PS2 isn't.
-    b1 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(goodPs1, goodPs2),
-            approvals(),
-            comments(),
-            reviewers(),
-            REVIEW_DB);
-    b2 =
-        new ChangeBundle(
-            c,
-            messages(),
-            patchSets(ps1AtCreatedOn, ps2AtCreatedOn),
-            approvals(),
-            comments(),
-            reviewers(),
-            NOTE_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "createdOn differs for PatchSet.Id "
-            + c.getId()
-            + ",2 in NoteDb vs. ReviewDb: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:18.0}");
-    assertDiffs(
-        b2,
-        b1,
-        "createdOn differs for PatchSet.Id "
-            + c.getId()
-            + ",2 in NoteDb vs. ReviewDb: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:18.0}");
-  }
-
-  @Test
-  public void diffPatchSetApprovalKeySets() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    PatchSetApproval a1 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            TimeUtil.nowTs());
-    PatchSetApproval a2 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Verified")),
-            (short) 1,
-            TimeUtil.nowTs());
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-
-    assertDiffs(
-        b1,
-        b2,
-        "PatchSetApproval.Key sets differ:"
-            + " ["
-            + id
-            + "%2C1,100,Code-Review] only in A;"
-            + " ["
-            + id
-            + "%2C1,100,Verified] only in B");
-  }
-
-  @Test
-  public void diffPatchSetApprovals() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    PatchSetApproval a1 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            TimeUtil.nowTs());
-    PatchSetApproval a2 = clone(a1);
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-
-    assertNoDiffs(b1, b2);
-
-    a2.setValue((short) -1);
-    assertDiffs(
-        b1,
-        b2,
-        "value differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review: {1} != {-1}");
-  }
-
-  @Test
-  public void diffPatchSetApprovalsMixedSourcesAllowsSlop() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    subWindowResolution();
-    PatchSetApproval a1 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            truncateToSecond(TimeUtil.nowTs()));
-    PatchSetApproval a2 = clone(a1);
-    a2.setGranted(TimeUtil.nowTs());
-
-    // Both are ReviewDb, exact timestamp match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "granted differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review:"
-            + " {2009-09-30 17:00:07.0} != {2009-09-30 17:00:08.0}");
-
-    // One NoteDb, slop is allowed.
-    b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-
-    // But not too much slop.
-    superWindowResolution();
-    PatchSetApproval a3 = clone(a1);
-    a3.setGranted(TimeUtil.nowTs());
-    b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB);
-    ChangeBundle b3 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a3), comments(), reviewers(), REVIEW_DB);
-    String msg =
-        "granted differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:07.0} != {2009-09-30 17:00:15.0}";
-    assertDiffs(b1, b3, msg);
-    assertDiffs(b3, b1, msg);
-  }
-
-  @Test
-  public void diffPatchSetApprovalsAllowsTruncatedTimestampInNoteDb() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    PatchSetApproval a1 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 1,
-            c.getCreatedOn());
-    PatchSetApproval a2 = clone(a1);
-    a2.setGranted(
-        new Timestamp(
-            LocalDate.of(1900, Month.JANUARY, 1)
-                .atStartOfDay()
-                .atZone(ZoneId.of(TIMEZONE_ID))
-                .toInstant()
-                .toEpochMilli()));
-
-    // Both are ReviewDb, exact match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "granted differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review:"
-            + " {2009-09-30 17:00:00.0} != {1900-01-01 00:00:00.0}");
-
-    // Truncating NoteDb timestamp is allowed.
-    b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-  }
-
-  @Test
-  public void diffPatchSetApprovalsIgnoresPostSubmitBitOnZeroVote() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    c.setStatus(Change.Status.MERGED);
-    PatchSetApproval a1 =
-        new PatchSetApproval(
-            new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
-            (short) 0,
-            TimeUtil.nowTs());
-    a1.setPostSubmit(false);
-    PatchSetApproval a2 = clone(a1);
-    a2.setPostSubmit(true);
-
-    // Both are ReviewDb, exact match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "postSubmit differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review:"
-            + " {false} != {true}");
-
-    // One NoteDb, postSubmit is ignored.
-    b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
-    b2 =
-        new ChangeBundle(c, messages(), latest(c), approvals(a2), comments(), reviewers(), NOTE_DB);
-    assertNoDiffs(b1, b2);
-    assertNoDiffs(b2, b1);
-
-    // postSubmit is not ignored if vote isn't 0.
-    a1.setValue((short) 1);
-    a2.setValue((short) 1);
-    assertDiffs(
-        b1,
-        b2,
-        "postSubmit differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review:"
-            + " {false} != {true}");
-    assertDiffs(
-        b2,
-        b1,
-        "postSubmit differs for PatchSetApproval.Key "
-            + c.getId()
-            + "%2C1,100,Code-Review:"
-            + " {true} != {false}");
-  }
-
-  @Test
-  public void diffReviewers() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    Timestamp now = TimeUtil.nowTs();
-    ReviewerSet r1 = reviewers(REVIEWER, new Account.Id(1), now);
-    ReviewerSet r2 = reviewers(REVIEWER, new Account.Id(2), now);
-
-    ChangeBundle b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r1, REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r2, REVIEW_DB);
-    assertNoDiffs(b1, b1);
-    assertNoDiffs(b2, b2);
-    assertDiffs(b1, b2, "reviewer sets differ: [1] only in A; [2] only in B");
-  }
-
-  @Test
-  public void diffReviewersIgnoresStateAndTimestamp() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    ReviewerSet r1 = reviewers(REVIEWER, new Account.Id(1), TimeUtil.nowTs());
-    ReviewerSet r2 = reviewers(CC, new Account.Id(1), TimeUtil.nowTs());
-
-    ChangeBundle b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r1, REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r2, REVIEW_DB);
-    assertNoDiffs(b1, b1);
-    assertNoDiffs(b2, b2);
-  }
-
-  @Test
-  public void diffPatchLineCommentKeySets() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    int id = c.getId().get();
-    PatchLineComment c1 =
-        new PatchLineComment(
-            new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename1"), "uuid1"),
-            5,
-            accountId,
-            null,
-            TimeUtil.nowTs());
-    PatchLineComment c2 =
-        new PatchLineComment(
-            new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename2"), "uuid2"),
-            5,
-            accountId,
-            null,
-            TimeUtil.nowTs());
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
-
-    assertDiffs(
-        b1,
-        b2,
-        "PatchLineComment.Key sets differ:"
-            + " ["
-            + id
-            + ",1,filename1,uuid1] only in A;"
-            + " ["
-            + id
-            + ",1,filename2,uuid2] only in B");
-  }
-
-  @Test
-  public void diffPatchLineComments() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    PatchLineComment c1 =
-        new PatchLineComment(
-            new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename"), "uuid"),
-            5,
-            accountId,
-            null,
-            TimeUtil.nowTs());
-    PatchLineComment c2 = clone(c1);
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
-
-    assertNoDiffs(b1, b2);
-
-    c2.setStatus(PatchLineComment.Status.PUBLISHED);
-    assertDiffs(
-        b1,
-        b2,
-        "status differs for PatchLineComment.Key " + c.getId() + ",1,filename,uuid: {d} != {P}");
-  }
-
-  @Test
-  public void diffPatchLineCommentsMixedSourcesAllowsSlop() throws Exception {
-    subWindowResolution();
-    Change c = TestChanges.newChange(project, accountId);
-    PatchLineComment c1 =
-        new PatchLineComment(
-            new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename"), "uuid"),
-            5,
-            accountId,
-            null,
-            truncateToSecond(TimeUtil.nowTs()));
-    PatchLineComment c2 = clone(c1);
-    c2.setWrittenOn(TimeUtil.nowTs());
-
-    // Both are ReviewDb, exact timestamp match is required.
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
-    assertDiffs(
-        b1,
-        b2,
-        "writtenOn differs for PatchLineComment.Key "
-            + c.getId()
-            + ",1,filename,uuid:"
-            + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}");
-
-    // One NoteDb, slop is allowed.
-    b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(), comments(c1), reviewers(), NOTE_DB);
-    b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-
-    // But not too much slop.
-    superWindowResolution();
-    PatchLineComment c3 = clone(c1);
-    c3.setWrittenOn(TimeUtil.nowTs());
-    b1 =
-        new ChangeBundle(c, messages(), latest(c), approvals(), comments(c1), reviewers(), NOTE_DB);
-    ChangeBundle b3 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c3), reviewers(), REVIEW_DB);
-    String msg =
-        "writtenOn differs for PatchLineComment.Key "
-            + c.getId()
-            + ",1,filename,uuid in NoteDb vs. ReviewDb:"
-            + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:10.0}";
-    assertDiffs(b1, b3, msg);
-    assertDiffs(b3, b1, msg);
-  }
-
-  @Test
-  public void diffPatchLineCommentsIgnoresCommentsOnInvalidPatchSet() throws Exception {
-    Change c = TestChanges.newChange(project, accountId);
-    PatchLineComment c1 =
-        new PatchLineComment(
-            new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename1"), "uuid1"),
-            5,
-            accountId,
-            null,
-            TimeUtil.nowTs());
-    PatchLineComment c2 =
-        new PatchLineComment(
-            new PatchLineComment.Key(
-                new Patch.Key(new PatchSet.Id(c.getId(), 0), "filename2"), "uuid2"),
-            5,
-            accountId,
-            null,
-            TimeUtil.nowTs());
-
-    ChangeBundle b1 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c1, c2), reviewers(), REVIEW_DB);
-    ChangeBundle b2 =
-        new ChangeBundle(
-            c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
-    assertNoDiffs(b1, b2);
-  }
-
-  private static void assertNoDiffs(ChangeBundle a, ChangeBundle b) {
-    assertThat(a.differencesFrom(b)).isEmpty();
-    assertThat(b.differencesFrom(a)).isEmpty();
-  }
-
-  private static void assertDiffs(ChangeBundle a, ChangeBundle b, String first, String... rest) {
-    List<String> actual = a.differencesFrom(b);
-    if (actual.size() == 1 && rest.length == 0) {
-      // This error message is much easier to read.
-      assertThat(actual.get(0)).isEqualTo(first);
-    } else {
-      List<String> expected = new ArrayList<>(1 + rest.length);
-      expected.add(first);
-      Collections.addAll(expected, rest);
-      assertThat(actual).containsExactlyElementsIn(expected).inOrder();
-    }
-    assertThat(a).isNotEqualTo(b);
-  }
-
-  private static List<ChangeMessage> messages(ChangeMessage... ents) {
-    return Arrays.asList(ents);
-  }
-
-  private static List<PatchSet> patchSets(PatchSet... ents) {
-    return Arrays.asList(ents);
-  }
-
-  private static List<PatchSet> latest(Change c) {
-    PatchSet ps = new PatchSet(c.currentPatchSetId());
-    ps.setCreatedOn(c.getLastUpdatedOn());
-    return ImmutableList.of(ps);
-  }
-
-  private static List<PatchSetApproval> approvals(PatchSetApproval... ents) {
-    return Arrays.asList(ents);
-  }
-
-  private static ReviewerSet reviewers(Object... ents) {
-    checkArgument(ents.length % 3 == 0);
-    Table<ReviewerStateInternal, Account.Id, Timestamp> t = HashBasedTable.create();
-    for (int i = 0; i < ents.length; i += 3) {
-      t.put((ReviewerStateInternal) ents[i], (Account.Id) ents[i + 1], (Timestamp) ents[i + 2]);
-    }
-    return ReviewerSet.fromTable(t);
-  }
-
-  private static List<PatchLineComment> comments(PatchLineComment... ents) {
-    return Arrays.asList(ents);
-  }
-
-  private static Change clone(Change ent) {
-    return clone(CHANGE_CODEC, ent);
-  }
-
-  private static ChangeMessage clone(ChangeMessage ent) {
-    return clone(CHANGE_MESSAGE_CODEC, ent);
-  }
-
-  private static PatchSet clone(PatchSet ent) {
-    return clone(PATCH_SET_CODEC, ent);
-  }
-
-  private static PatchSetApproval clone(PatchSetApproval ent) {
-    return clone(PATCH_SET_APPROVAL_CODEC, ent);
-  }
-
-  private static PatchLineComment clone(PatchLineComment ent) {
-    return clone(PATCH_LINE_COMMENT_CODEC, ent);
-  }
-
-  private static <T> T clone(ProtobufCodec<T> codec, T obj) {
-    return codec.decode(codec.encodeToByteArray(obj));
-  }
-}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 549f5db..e274cdf 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -2605,7 +2605,7 @@
     }
 
     // Looking at drafts directly shows the zombie comment.
-    DraftCommentNotes draftNotes = draftNotesFactory.create(c, otherUserId);
+    DraftCommentNotes draftNotes = draftNotesFactory.create(c.getId(), otherUserId);
     assertThat(draftNotes.load().getComments().get(rev1)).containsExactly(comment1, comment2);
 
     // Zombie comment is filtered out of drafts via ChangeNotes.
diff --git a/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java b/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
index b9027bc..fbec5e6 100644
--- a/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
@@ -38,6 +38,7 @@
 import com.google.gerrit.testing.TestChanges;
 import com.google.inject.Inject;
 import java.io.ByteArrayOutputStream;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import org.eclipse.jgit.junit.TestRepository;
@@ -185,7 +186,6 @@
 
     // Comments at each commit all have JSON format.
     ImmutableList<RevCommit> newLog = log(project, RefNames.changeMetaRef(c.getId()));
-    assertLogEqualExceptTrees(newLog, oldLog);
     assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(0))).isEmpty();
     assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(1))).isEmpty();
     assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(2)))
@@ -297,7 +297,6 @@
     // Comments at each commit all have JSON format.
     ImmutableList<RevCommit> newOwnerLog =
         log(allUsers, RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()));
-    assertLogEqualExceptTrees(newOwnerLog, oldOwnerLog);
     assertThat(getLegacyFormatMapForDraftComments(notes, newOwnerLog.get(0)))
         .containsExactly(ownerCommentPs1.key, false);
     assertThat(getLegacyFormatMapForDraftComments(notes, newOwnerLog.get(1)))
@@ -305,7 +304,6 @@
 
     ImmutableList<RevCommit> newOtherLog =
         log(allUsers, RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()));
-    assertLogEqualExceptTrees(newOtherLog, oldOtherLog);
     assertThat(getLegacyFormatMapForDraftComments(notes, newOtherLog.get(0)))
         .containsExactly(otherCommentPs1.key, false);
   }
@@ -392,7 +390,6 @@
 
     // Comments at each commit all have JSON format.
     ImmutableList<RevCommit> newLog = log(project, RefNames.changeMetaRef(c.getId()));
-    assertLogEqualExceptTrees(newLog, oldLog);
     assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(0))).isEmpty();
     assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(1))).isEmpty();
     assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(2))).isEmpty();
@@ -491,17 +488,35 @@
   }
 
   private ImmutableList<RevCommit> log(Project.NameKey project, String refName) throws Exception {
-    try (Repository repo = repoManager.openRepository(project);
-        RevWalk rw = new RevWalk(repo)) {
+    try (Repository repo = repoManager.openRepository(project)) {
+      return log(repo, refName);
+    }
+  }
+
+  private ImmutableList<RevCommit> log(Repository repo, String refName) throws Exception {
+    try (RevWalk rw = new RevWalk(repo)) {
       rw.sort(RevSort.TOPO);
       rw.sort(RevSort.REVERSE);
       Ref ref = repo.exactRef(refName);
-      checkArgument(ref != null, "missing ref: %s", refName);
+      if (ref == null) {
+        return ImmutableList.of();
+      }
       rw.markStart(rw.parseCommit(ref.getObjectId()));
       return ImmutableList.copyOf(rw);
     }
   }
 
+  private ImmutableListMultimap<String, RevCommit> logAll(
+      Project.NameKey project, Collection<Ref> refs) throws Exception {
+    ImmutableListMultimap.Builder<String, RevCommit> logs = ImmutableListMultimap.builder();
+    try (Repository repo = repoManager.openRepository(project)) {
+      for (Ref r : refs) {
+        logs.putAll(r.getName(), log(repo, r.getName()));
+      }
+    }
+    return logs.build();
+  }
+
   private static void assertLogEqualExceptTrees(
       ImmutableList<RevCommit> actualLog, ImmutableList<RevCommit> expectedLog) {
     assertThat(actualLog).hasSize(expectedLog.size());
@@ -522,9 +537,61 @@
   }
 
   private void assertNoDifferences(ChangeNotes actual, ChangeNotes expected) throws Exception {
-    assertThat(
-            ChangeBundle.fromNotes(commentsUtil, actual)
-                .differencesFrom(ChangeBundle.fromNotes(commentsUtil, expected)))
-        .isEmpty();
+    checkArgument(
+        actual.getChangeId().equals(expected.getChangeId()),
+        "must be same change: %s != %s",
+        actual.getChangeId(),
+        expected.getChangeId());
+
+    // Parsed comment representations are equal.
+    // TODO(dborowitz): Comparing collections directly would be much easier, but Comment doesn't
+    // have a proper equals; switch to that when the issues with
+    // https://gerrit-review.googlesource.com/c/gerrit/+/207013 are resolved.
+    assertCommentsEqual(commentsUtil.draftByChange(actual), commentsUtil.draftByChange(expected));
+    assertCommentsEqual(
+        commentsUtil.publishedByChange(actual), commentsUtil.publishedByChange(expected));
+
+    // Change metadata is equal.
+    assertLogEqualExceptTrees(
+        log(project, actual.getRefName()), log(project, expected.getRefName()));
+
+    // Logs of all draft refs are equal.
+    ImmutableListMultimap<String, RevCommit> actualDraftLogs =
+        logAll(allUsersName, commentsUtil.getDraftRefs(actual.getChangeId()));
+    ImmutableListMultimap<String, RevCommit> expectedDraftLogs =
+        logAll(allUsersName, commentsUtil.getDraftRefs(expected.getChangeId()));
+    assertThat(actualDraftLogs.keySet())
+        .named("draft ref names")
+        .containsExactlyElementsIn(expectedDraftLogs.keySet());
+    for (String refName : actualDraftLogs.keySet()) {
+      assertLogEqualExceptTrees(actualDraftLogs.get(refName), actualDraftLogs.get(refName));
+    }
+  }
+
+  private static void assertCommentsEqual(List<Comment> actualList, List<Comment> expectedList) {
+    ImmutableMap<Comment.Key, Comment> actualMap = byKey(actualList);
+    ImmutableMap<Comment.Key, Comment> expectedMap = byKey(expectedList);
+    assertThat(actualMap.keySet()).isEqualTo(expectedMap.keySet());
+    for (Comment.Key key : actualMap.keySet()) {
+      Comment actual = actualMap.get(key);
+      Comment expected = expectedMap.get(key);
+      assertThat(actual.key).isEqualTo(expected.key);
+      assertThat(actual.lineNbr).isEqualTo(expected.lineNbr);
+      assertThat(actual.author).isEqualTo(expected.author);
+      assertThat(actual.getRealAuthor()).isEqualTo(expected.getRealAuthor());
+      assertThat(actual.writtenOn).isEqualTo(expected.writtenOn);
+      assertThat(actual.side).isEqualTo(expected.side);
+      assertThat(actual.message).isEqualTo(expected.message);
+      assertThat(actual.parentUuid).isEqualTo(expected.parentUuid);
+      assertThat(actual.range).isEqualTo(expected.range);
+      assertThat(actual.tag).isEqualTo(expected.tag);
+      assertThat(actual.revId).isEqualTo(expected.revId);
+      assertThat(actual.serverId).isEqualTo(expected.serverId);
+      assertThat(actual.unresolved).isEqualTo(expected.unresolved);
+    }
+  }
+
+  private static ImmutableMap<Comment.Key, Comment> byKey(List<Comment> comments) {
+    return comments.stream().collect(toImmutableMap(c -> c.key, c -> c));
   }
 }
diff --git a/javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java b/javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java
deleted file mode 100644
index 7fb9d82..0000000
--- a/javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.notedb.rebuild;
-
-import static com.google.common.truth.Truth.assertThat;
-import static java.util.stream.Collectors.toList;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TestTimeUtil;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
-import org.junit.Before;
-import org.junit.Test;
-
-public class EventSorterTest extends GerritBaseTests {
-  private class TestEvent extends Event {
-    protected TestEvent(Timestamp when) {
-      super(
-          new PatchSet.Id(new Change.Id(1), 1),
-          new Account.Id(1000),
-          new Account.Id(1000),
-          when,
-          changeCreatedOn,
-          null);
-    }
-
-    @Override
-    boolean uniquePerUpdate() {
-      return false;
-    }
-
-    @Override
-    void apply(ChangeUpdate update) {
-      throw new UnsupportedOperationException();
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public String toString() {
-      return "E{" + when.getSeconds() + '}';
-    }
-  }
-
-  private Timestamp changeCreatedOn;
-
-  @Before
-  public void setUp() {
-    TestTimeUtil.resetWithClockStep(10, TimeUnit.SECONDS);
-    changeCreatedOn = TimeUtil.nowTs();
-  }
-
-  @Test
-  public void naturalSort() {
-    Event e1 = new TestEvent(TimeUtil.nowTs());
-    Event e2 = new TestEvent(TimeUtil.nowTs());
-    Event e3 = new TestEvent(TimeUtil.nowTs());
-
-    for (List<Event> events : Collections2.permutations(events(e1, e2, e3))) {
-      assertSorted(events, events(e1, e2, e3));
-    }
-  }
-
-  @Test
-  public void topoSortOneDep() {
-    List<Event> es;
-
-    // Input list is 0,1,2
-
-    // 0 depends on 1 => 1,0,2
-    es = threeEventsOneDep(0, 1);
-    assertSorted(es, events(es, 1, 0, 2));
-
-    // 1 depends on 0 => 0,1,2
-    es = threeEventsOneDep(1, 0);
-    assertSorted(es, events(es, 0, 1, 2));
-
-    // 0 depends on 2 => 1,2,0
-    es = threeEventsOneDep(0, 2);
-    assertSorted(es, events(es, 1, 2, 0));
-
-    // 2 depends on 0 => 0,1,2
-    es = threeEventsOneDep(2, 0);
-    assertSorted(es, events(es, 0, 1, 2));
-
-    // 1 depends on 2 => 0,2,1
-    es = threeEventsOneDep(1, 2);
-    assertSorted(es, events(es, 0, 2, 1));
-
-    // 2 depends on 1 => 0,1,2
-    es = threeEventsOneDep(2, 1);
-    assertSorted(es, events(es, 0, 1, 2));
-  }
-
-  private List<Event> threeEventsOneDep(int depFromIdx, int depOnIdx) {
-    List<Event> events =
-        Lists.newArrayList(
-            new TestEvent(TimeUtil.nowTs()),
-            new TestEvent(TimeUtil.nowTs()),
-            new TestEvent(TimeUtil.nowTs()));
-    events.get(depFromIdx).addDep(events.get(depOnIdx));
-    return events;
-  }
-
-  @Test
-  public void lastEventDependsOnFirstEvent() {
-    List<Event> events = new ArrayList<>();
-    for (int i = 0; i < 20; i++) {
-      events.add(new TestEvent(TimeUtil.nowTs()));
-    }
-    events.get(events.size() - 1).addDep(events.get(0));
-    assertSorted(events, events);
-  }
-
-  @Test
-  public void firstEventDependsOnLastEvent() {
-    List<Event> events = new ArrayList<>();
-    for (int i = 0; i < 20; i++) {
-      events.add(new TestEvent(TimeUtil.nowTs()));
-    }
-    events.get(0).addDep(events.get(events.size() - 1));
-
-    List<Event> expected = new ArrayList<>();
-    expected.addAll(events.subList(1, events.size()));
-    expected.add(events.get(0));
-    assertSorted(events, expected);
-  }
-
-  @Test
-  public void topoSortChainOfDeps() {
-    Event e1 = new TestEvent(TimeUtil.nowTs());
-    Event e2 = new TestEvent(TimeUtil.nowTs());
-    Event e3 = new TestEvent(TimeUtil.nowTs());
-    Event e4 = new TestEvent(TimeUtil.nowTs());
-    e1.addDep(e2);
-    e2.addDep(e3);
-    e3.addDep(e4);
-
-    assertSorted(events(e1, e2, e3, e4), events(e4, e3, e2, e1));
-  }
-
-  @Test
-  public void topoSortMultipleDeps() {
-    Event e1 = new TestEvent(TimeUtil.nowTs());
-    Event e2 = new TestEvent(TimeUtil.nowTs());
-    Event e3 = new TestEvent(TimeUtil.nowTs());
-    Event e4 = new TestEvent(TimeUtil.nowTs());
-    e1.addDep(e2);
-    e1.addDep(e4);
-    e2.addDep(e3);
-
-    // Processing 3 pops 2, processing 4 pops 1.
-    assertSorted(events(e2, e3, e1, e4), events(e3, e2, e4, e1));
-  }
-
-  @Test
-  public void topoSortMultipleDepsPreservesNaturalOrder() {
-    Event e1 = new TestEvent(TimeUtil.nowTs());
-    Event e2 = new TestEvent(TimeUtil.nowTs());
-    Event e3 = new TestEvent(TimeUtil.nowTs());
-    Event e4 = new TestEvent(TimeUtil.nowTs());
-    e1.addDep(e4);
-    e2.addDep(e4);
-    e3.addDep(e4);
-
-    // Processing 4 pops 1, 2, 3 in natural order.
-    assertSorted(events(e4, e3, e2, e1), events(e4, e1, e2, e3));
-  }
-
-  @Test
-  public void topoSortCycle() {
-    Event e1 = new TestEvent(TimeUtil.nowTs());
-    Event e2 = new TestEvent(TimeUtil.nowTs());
-
-    // Implementation is not really defined, but infinite looping would be bad.
-    // According to current implementation details, 2 pops 1, 1 pops 2 which was
-    // already seen.
-    assertSorted(events(e2, e1), events(e1, e2));
-  }
-
-  @Test
-  public void topoSortDepNotInInputList() {
-    Event e1 = new TestEvent(TimeUtil.nowTs());
-    Event e2 = new TestEvent(TimeUtil.nowTs());
-    Event e3 = new TestEvent(TimeUtil.nowTs());
-    e1.addDep(e3);
-
-    List<Event> events = events(e2, e1);
-    try {
-      new EventSorter(events).sort();
-      fail("expected IllegalArgumentException");
-    } catch (IllegalArgumentException e) {
-      // Expected.
-    }
-  }
-
-  private static List<Event> events(Event... es) {
-    return Lists.newArrayList(es);
-  }
-
-  private static List<Event> events(List<Event> in, Integer... indexes) {
-    return Stream.of(indexes).map(in::get).collect(toList());
-  }
-
-  private static void assertSorted(List<Event> unsorted, List<Event> expected) {
-    List<Event> actual = new ArrayList<>(unsorted);
-    new EventSorter(actual).sort();
-    assertThat(actual).named("sorted" + unsorted).isEqualTo(expected);
-  }
-}
diff --git a/javatests/com/google/gerrit/server/permissions/RefControlTest.java b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
index c467312..bd0316c 100644
--- a/javatests/com/google/gerrit/server/permissions/RefControlTest.java
+++ b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
@@ -63,7 +63,7 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.RefPattern;
 import com.google.gerrit.server.project.testing.Util;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gerrit.testing.GerritBaseTests;
@@ -200,7 +200,7 @@
 
   @Inject private PermissionBackend permissionBackend;
   @Inject private CapabilityCollection.Factory capabilityCollectionFactory;
-  @Inject private ReviewDbSchemaCreator schemaCreator;
+  @Inject private SchemaCreator schemaCreator;
   @Inject private SingleVersionListener singleVersionListener;
   @Inject private InMemoryDatabase schemaFactory;
   @Inject private ThreadLocalRequestContext requestContext;
@@ -289,7 +289,7 @@
     db = schemaFactory.open();
     singleVersionListener.start();
     try {
-      schemaCreator.create(db);
+      schemaCreator.create();
     } finally {
       singleVersionListener.stop();
     }
@@ -329,7 +329,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(schemaFactory);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index 26cc3f8..b0d0849 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -74,7 +74,7 @@
 import com.google.gerrit.server.index.account.AccountIndex;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.index.account.AccountIndexer;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.RequestContext;
@@ -120,7 +120,7 @@
 
   @Inject protected InMemoryDatabase schemaFactory;
 
-  @Inject protected ReviewDbSchemaCreator schemaCreator;
+  @Inject protected SchemaCreator schemaCreator;
 
   @Inject protected ThreadLocalRequestContext requestContext;
 
@@ -165,7 +165,7 @@
 
   protected void setUpDatabase() throws Exception {
     db = schemaFactory.open();
-    schemaCreator.create(db);
+    schemaCreator.create();
 
     Account.Id adminId = createAccount("admin", "Administrator", "admin@example.com", true);
     admin = userFactory.create(adminId);
@@ -214,7 +214,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(schemaFactory);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 0ea5241..e87942c 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -52,7 +52,6 @@
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
-import com.google.gerrit.extensions.api.changes.ReviewInput.RobotCommentInput;
 import com.google.gerrit.extensions.api.changes.StarsInput;
 import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.api.projects.ConfigInput;
@@ -69,7 +68,6 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.reviewdb.client.Account;
@@ -81,7 +79,6 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PatchSetUtil;
@@ -102,21 +99,16 @@
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gerrit.server.index.change.StalenessChecker;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.DisabledReviewDb;
 import com.google.gerrit.testing.GerritServerTests;
 import com.google.gerrit.testing.InMemoryDatabase;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
@@ -131,13 +123,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
@@ -177,7 +167,7 @@
   @Inject protected PatchSetUtil psUtil;
   @Inject protected ChangeNotes.Factory changeNotesFactory;
   @Inject protected Provider<ChangeQueryProcessor> queryProcessorProvider;
-  @Inject protected ReviewDbSchemaCreator schemaCreator;
+  @Inject protected SchemaCreator schemaCreator;
   @Inject protected SchemaFactory<ReviewDb> schemaFactory;
   @Inject protected Sequences seq;
   @Inject protected ThreadLocalRequestContext requestContext;
@@ -185,7 +175,6 @@
   @Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
   @Inject protected IdentifiedUser.GenericFactory identifiedUserFactory;
 
-  // Only for use in setting up/tearing down injector; other users should use schemaFactory.
   @Inject private InMemoryDatabase inMemoryDatabase;
 
   protected Injector injector;
@@ -234,9 +223,7 @@
   protected void initAfterLifecycleStart() throws Exception {}
 
   protected void setUpDatabase() throws Exception {
-    try (ReviewDb underlyingDb = inMemoryDatabase.getDatabase().open()) {
-      schemaCreator.create(underlyingDb);
-    }
+    schemaCreator.create();
     db = schemaFactory.open();
 
     userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
@@ -279,7 +266,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(inMemoryDatabase);
   }
 
   @Before
@@ -577,7 +563,6 @@
   @Test
   public void restorePendingReviewers() throws Exception {
     assume().that(getSchemaVersion()).isAtLeast(44);
-    assume().that(notesMigration.readChanges()).isTrue();
 
     Project.NameKey project = new Project.NameKey("repo");
     TestRepository<Repo> repo = createProject(project.get());
@@ -1604,8 +1589,7 @@
   }
 
   @Test
-  public void byHashtagWithNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
+  public void byHashtag() throws Exception {
     List<Change> changes = setUpHashtagChanges();
     assertQuery("hashtag:foo", changes.get(1), changes.get(0));
     assertQuery("hashtag:bar", changes.get(1));
@@ -1617,35 +1601,6 @@
   }
 
   @Test
-  public void byHashtagWithoutNoteDb() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-
-    notesMigration.setWriteChanges(true);
-    notesMigration.setReadChanges(true);
-    db.close();
-    db = schemaFactory.open();
-    List<Change> changes;
-    try {
-      changes = setUpHashtagChanges();
-      notesMigration.setWriteChanges(false);
-      notesMigration.setReadChanges(false);
-    } finally {
-      db.close();
-    }
-    db = schemaFactory.open();
-    for (Change c : changes) {
-      indexer.index(db, c); // Reindex without hashtag field.
-    }
-    assertQuery("hashtag:foo");
-    assertQuery("hashtag:bar");
-    assertQuery("hashtag:\" bar \"");
-    assertQuery("hashtag:\"a tag\"");
-    assertQuery("hashtag:\" a tag \"");
-    assertQuery("hashtag:#foo");
-    assertQuery("hashtag:\"# #foo\"");
-  }
-
-  @Test
   public void byDefault() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
 
@@ -1776,8 +1731,6 @@
 
   @Test
   public void byDraftByExcludesZombieDrafts() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     Project.NameKey project = new Project.NameKey("repo");
     TestRepository<Repo> repo = createProject(project.get());
     Change change = insert(repo, newChange(repo));
@@ -1809,18 +1762,6 @@
     allUsers.update(draftsRef.getName(), draftsRef.getObjectId());
     assertThat(allUsers.getRepository().exactRef(draftsRef.getName())).isNotNull();
 
-    if (PrimaryStorage.of(change) == PrimaryStorage.REVIEW_DB
-        && !notesMigration.disableChangeReviewDb()) {
-      // Record draft ref in noteDbState as well.
-      ReviewDb db = ReviewDbUtil.unwrapDb(this.db);
-      change = db.changes().get(id);
-      NoteDbChangeState.applyDelta(
-          change,
-          NoteDbChangeState.Delta.create(
-              id, Optional.empty(), ImmutableMap.of(userId, draftsRef.getObjectId())));
-      db.changes().update(Collections.singleton(change));
-    }
-
     indexer.index(db, project, id);
     assertQuery("draftby:" + userId);
   }
@@ -2035,17 +1976,10 @@
     assertQuery("reviewer:self", change3);
 
     requestContext.setContext(newRequestContext(user1));
-    if (notesMigration.readChanges()) {
-      assertQuery("reviewer:" + user1, change1);
-      assertQuery("cc:" + user1, change2);
-      assertQuery("is:cc", change2);
-      assertQuery("cc:self", change2);
-    } else {
-      assertQuery("reviewer:" + user1, change2, change1);
-      assertQuery("cc:" + user1);
-      assertQuery("is:cc");
-      assertQuery("cc:self");
-    }
+    assertQuery("reviewer:" + user1, change1);
+    assertQuery("cc:" + user1, change2);
+    assertQuery("is:cc", change2);
+    assertQuery("cc:self", change2);
   }
 
   @Test
@@ -2108,36 +2042,19 @@
             .collect(toList());
     assertThat(members).contains(user2.toString());
 
-    if (notesMigration.readChanges()) {
-      // CC and REVIEWER are separate in NoteDB
-      assertQuery("reviewerin:\"Registered Users\"", change2, change1);
-      assertQuery("reviewerin:" + group, change2);
-    } else {
-      // CC and REVIEWER are the same in ReviewDb
-      assertQuery("reviewerin:\"Registered Users\"", change3, change2, change1);
-      assertQuery("reviewerin:" + group, change3, change2);
-    }
+    assertQuery("reviewerin:\"Registered Users\"", change2, change1);
+    assertQuery("reviewerin:" + group, change2);
 
     gApi.changes().id(change2.getId().get()).current().review(ReviewInput.approve());
     gApi.changes().id(change2.getId().get()).current().submit();
 
-    if (notesMigration.readChanges()) {
-      // CC and REVIEWER are separate in NoteDB
-      assertQuery("reviewerin:" + group, change2);
-      assertQuery("project:repo reviewerin:" + group, change2);
-      assertQuery("status:merged reviewerin:" + group, change2);
-    } else {
-      // CC and REVIEWER are the same in ReviewDb
-      assertQuery("reviewerin:" + group, change2, change3);
-      assertQuery("project:repo reviewerin:" + group, change2, change3);
-      assertQuery("status:merged reviewerin:" + group, change2);
-    }
+    assertQuery("reviewerin:" + group, change2);
+    assertQuery("project:repo reviewerin:" + group, change2);
+    assertQuery("status:merged reviewerin:" + group, change2);
   }
 
   @Test
   public void reviewerAndCcByEmail() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     Project.NameKey project = new Project.NameKey("repo");
     TestRepository<Repo> repo = createProject(project.get());
     ConfigInput conf = new ConfigInput();
@@ -2184,8 +2101,6 @@
 
   @Test
   public void reviewerAndCcByEmailWithQueryForDifferentUser() throws Exception {
-    assume().that(notesMigration.readChanges()).isTrue();
-
     Project.NameKey project = new Project.NameKey("repo");
     TestRepository<Repo> repo = createProject(project.get());
     ConfigInput conf = new ConfigInput();
@@ -2358,71 +2273,6 @@
   }
 
   @Test
-  public void prepopulatedFields() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    TestRepository<Repo> repo = createProject("repo");
-    Change change = insert(repo, newChange(repo));
-
-    db = new DisabledReviewDb();
-    requestContext.setContext(newRequestContext(userId));
-    // Use QueryProcessor directly instead of API so we get ChangeDatas back.
-    List<ChangeData> cds =
-        queryProcessorProvider
-            .get()
-            .query(queryBuilder.parse(change.getId().toString()))
-            .entities();
-    assertThat(cds).hasSize(1);
-
-    ChangeData cd = cds.get(0);
-    cd.change();
-    cd.patchSets();
-    cd.currentApprovals();
-    cd.changedLines();
-    cd.reviewedBy();
-    cd.reviewers();
-    cd.unresolvedCommentCount();
-
-    if (getSchemaVersion() < 51) {
-      assertMissingField(ChangeField.TOTAL_COMMENT_COUNT);
-    } else {
-      cd.totalCommentCount();
-    }
-
-    // TODO(dborowitz): Swap out GitRepositoryManager somehow? Will probably be
-    // necessary for NoteDb anyway.
-    cd.isMergeable();
-
-    exception.expect(DisabledReviewDb.Disabled.class);
-    cd.messages();
-  }
-
-  @Test
-  public void prepopulateOnlyRequestedFields() throws Exception {
-    assume().that(notesMigration.readChanges()).isFalse();
-    TestRepository<Repo> repo = createProject("repo");
-    Change change = insert(repo, newChange(repo));
-
-    db = new DisabledReviewDb();
-    requestContext.setContext(newRequestContext(userId));
-    // Use QueryProcessor directly instead of API so we get ChangeDatas back.
-    List<ChangeData> cds =
-        queryProcessorProvider
-            .get()
-            .setRequestedFields(
-                ImmutableSet.of(ChangeField.PATCH_SET.getName(), ChangeField.CHANGE.getName()))
-            .query(queryBuilder.parse(change.getId().toString()))
-            .entities();
-    assertThat(cds).hasSize(1);
-
-    ChangeData cd = cds.get(0);
-    cd.change();
-    cd.patchSets();
-
-    exception.expect(DisabledReviewDb.Disabled.class);
-    cd.currentApprovals();
-  }
-
-  @Test
   public void reindexIfStale() throws Exception {
     Account.Id user = createAccount("user");
     Project.NameKey project = new Project.NameKey("repo");
@@ -2450,93 +2300,6 @@
   }
 
   @Test
-  public void refStateFields() throws Exception {
-    // This test method manages primary storage manually.
-    assume().that(notesMigration.changePrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-    Account.Id user = createAccount("user");
-    Project.NameKey project = new Project.NameKey("repo");
-    TestRepository<Repo> repo = createProject(project.get());
-    String path = "file";
-    RevCommit commit = repo.parseBody(repo.commit().message("one").add(path, "contents").create());
-    Change change = insert(repo, newChangeForCommit(repo, commit));
-    Change.Id id = change.getId();
-    int c = id.get();
-    String changeId = change.getKey().get();
-    requestContext.setContext(newRequestContext(user));
-
-    // Ensure one of each type of supported ref is present for the change. If
-    // any more refs are added, update this test to reflect them.
-
-    // Edit
-    gApi.changes().id(changeId).edit().create();
-
-    // Star
-    gApi.accounts().self().starChange(change.getId().toString());
-
-    if (notesMigration.readChanges()) {
-      // Robot comment.
-      ReviewInput rin = new ReviewInput();
-      RobotCommentInput rcin = new RobotCommentInput();
-      rcin.robotId = "happyRobot";
-      rcin.robotRunId = "1";
-      rcin.line = 1;
-      rcin.message = "nit: trailing whitespace";
-      rcin.path = path;
-      rin.robotComments = ImmutableMap.of(path, ImmutableList.of(rcin));
-      gApi.changes().id(c).current().review(rin);
-    }
-
-    // Draft.
-    DraftInput din = new DraftInput();
-    din.path = path;
-    din.line = 1;
-    din.message = "draft";
-    gApi.changes().id(c).current().createDraft(din);
-
-    if (notesMigration.readChanges()) {
-      // Force NoteDb primary.
-      change = ReviewDbUtil.unwrapDb(db).changes().get(id);
-      change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
-      ReviewDbUtil.unwrapDb(db).changes().update(Collections.singleton(change));
-      indexer.index(db, change);
-    }
-
-    QueryOptions opts =
-        IndexedChangeQuery.createOptions(indexConfig, 0, 1, StalenessChecker.FIELDS);
-    ChangeData cd = indexes.getSearchIndex().get(id, opts).get();
-
-    String cs = RefNames.shard(c);
-    int u = user.get();
-    String us = RefNames.shard(u);
-
-    List<String> expectedStates =
-        Lists.newArrayList(
-            "repo:refs/users/" + us + "/edit-" + c + "/1",
-            "All-Users:refs/starred-changes/" + cs + "/" + u);
-    if (notesMigration.readChanges()) {
-      expectedStates.add("repo:refs/changes/" + cs + "/meta");
-      expectedStates.add("repo:refs/changes/" + cs + "/robot-comments");
-      expectedStates.add("All-Users:refs/draft-comments/" + cs + "/" + u);
-    }
-    assertThat(
-            cd.getRefStates()
-                .stream()
-                .map(String::new)
-                // Omit SHA-1, we're just concerned with the project/ref names.
-                .map(s -> s.substring(0, s.lastIndexOf(':')))
-                .collect(toList()))
-        .containsExactlyElementsIn(expectedStates);
-
-    List<String> expectedPatterns = Lists.newArrayList("repo:refs/users/*/edit-" + c + "/*");
-    expectedPatterns.add("All-Users:refs/starred-changes/" + cs + "/*");
-    if (notesMigration.readChanges()) {
-      expectedPatterns.add("All-Users:refs/draft-comments/" + cs + "/*");
-    }
-    assertThat(cd.getRefStatePatterns().stream().map(String::new).collect(toList()))
-        .containsExactlyElementsIn(expectedPatterns);
-  }
-
-  @Test
   public void watched() throws Exception {
     TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChangeWithStatus(repo, Change.Status.NEW);
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index 7e4a0a4..2906409 100644
--- a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -52,7 +52,7 @@
 import com.google.gerrit.server.index.group.GroupField;
 import com.google.gerrit.server.index.group.GroupIndex;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.RequestContext;
@@ -91,7 +91,7 @@
 
   @Inject protected InMemoryDatabase schemaFactory;
 
-  @Inject protected ReviewDbSchemaCreator schemaCreator;
+  @Inject protected SchemaCreator schemaCreator;
 
   @Inject protected ThreadLocalRequestContext requestContext;
 
@@ -134,7 +134,7 @@
 
   protected void setUpDatabase() throws Exception {
     db = schemaFactory.open();
-    schemaCreator.create(db);
+    schemaCreator.create();
 
     Account.Id userId =
         createAccountOutsideRequestContext("user", "User", "user@example.com", true);
@@ -186,7 +186,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(schemaFactory);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
index 3219dfd..4750353 100644
--- a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -50,7 +50,7 @@
 import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.schema.ReviewDbSchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.RequestContext;
@@ -88,7 +88,7 @@
 
   @Inject protected InMemoryDatabase schemaFactory;
 
-  @Inject protected ReviewDbSchemaCreator schemaCreator;
+  @Inject protected SchemaCreator schemaCreator;
 
   @Inject protected ThreadLocalRequestContext requestContext;
 
@@ -125,7 +125,7 @@
 
   protected void setUpDatabase() throws Exception {
     db = schemaFactory.open();
-    schemaCreator.create(db);
+    schemaCreator.create();
 
     Account.Id userId = createAccount("user", "User", "user@example.com", true);
     user = userFactory.create(userId);
@@ -174,7 +174,6 @@
     if (db != null) {
       db.close();
     }
-    InMemoryDatabase.drop(schemaFactory);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/schema/GroupBundleTest.java b/javatests/com/google/gerrit/server/schema/GroupBundleTest.java
deleted file mode 100644
index c1de3a3..0000000
--- a/javatests/com/google/gerrit/server/schema/GroupBundleTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.server.schema.GroupBundle.Source;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TestTimeUtil;
-import java.sql.Timestamp;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GroupBundleTest extends GerritBaseTests {
-  // This class just contains sanity checks that GroupBundle#compare correctly compares all parts of
-  // the bundle. Most other test coverage should come via the slightly more realistic
-  // GroupRebuilderTest.
-
-  private static final String TIMEZONE_ID = "US/Eastern";
-
-  private String systemTimeZoneProperty;
-  private TimeZone systemTimeZone;
-  private Timestamp ts;
-
-  @Before
-  public void setUp() {
-    systemTimeZoneProperty = System.setProperty("user.timezone", TIMEZONE_ID);
-    systemTimeZone = TimeZone.getDefault();
-    TimeZone.setDefault(TimeZone.getTimeZone(TIMEZONE_ID));
-    TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
-    ts = TimeUtil.nowTs();
-  }
-
-  @After
-  public void tearDown() {
-    TestTimeUtil.useSystemTime();
-    System.setProperty("user.timezone", systemTimeZoneProperty);
-    TimeZone.setDefault(systemTimeZone);
-  }
-
-  @Test
-  public void compareNonEqual() throws Exception {
-    GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
-    AccountGroup g2 = new AccountGroup(reviewDbBundle.group());
-    g2.setDescription("Hello!");
-    GroupBundle noteDbBundle = GroupBundle.builder().source(Source.NOTE_DB).group(g2).build();
-    assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle))
-        .containsExactly(
-            "AccountGroups differ\n"
-                + ("ReviewDb: AccountGroup{name=group, groupId=1, description=null,"
-                    + " visibleToAll=false, groupUUID=group-1, ownerGroupUUID=group-1,"
-                    + " createdOn=2009-09-30 17:00:00.0}\n")
-                + ("NoteDb  : AccountGroup{name=group, groupId=1, description=Hello!,"
-                    + " visibleToAll=false, groupUUID=group-1, ownerGroupUUID=group-1,"
-                    + " createdOn=2009-09-30 17:00:00.0}"),
-            "AccountGroupMembers differ\n"
-                + "ReviewDb: [AccountGroupMember{key=1000,1}]\n"
-                + "NoteDb  : []",
-            "AccountGroupMemberAudits differ\n"
-                + ("ReviewDb: [AccountGroupMemberAudit{key=Key{groupId=1, accountId=1000,"
-                    + " addedOn=2009-09-30 17:00:00.0}, addedBy=2000, removedBy=null,"
-                    + " removedOn=null}]\n")
-                + "NoteDb  : []",
-            "AccountGroupByIds differ\n"
-                + "ReviewDb: [AccountGroupById{key=1,subgroup}]\n"
-                + "NoteDb  : []",
-            "AccountGroupByIdAudits differ\n"
-                + ("ReviewDb: [AccountGroupByIdAud{key=Key{groupId=1, includeUUID=subgroup,"
-                    + " addedOn=2009-09-30 17:00:00.0}, addedBy=3000, removedBy=null,"
-                    + " removedOn=null}]\n")
-                + "NoteDb  : []");
-  }
-
-  @Test
-  public void compareIgnoreAudits() throws Exception {
-    GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
-    AccountGroup group = new AccountGroup(reviewDbBundle.group());
-
-    AccountGroupMember member =
-        new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(1), group.getId()));
-    AccountGroupMemberAudit memberAudit =
-        new AccountGroupMemberAudit(member, new Account.Id(2), ts);
-    AccountGroupById byId =
-        new AccountGroupById(
-            new AccountGroupById.Key(group.getId(), new AccountGroup.UUID("subgroup-2")));
-    AccountGroupByIdAud byIdAudit = new AccountGroupByIdAud(byId, new Account.Id(3), ts);
-
-    GroupBundle noteDbBundle =
-        newBundle().source(Source.NOTE_DB).memberAudit(memberAudit).byIdAudit(byIdAudit).build();
-
-    assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle)).isNotEmpty();
-    assertThat(GroupBundle.compareWithoutAudits(reviewDbBundle, noteDbBundle)).isEmpty();
-  }
-
-  @Test
-  public void compareEqual() throws Exception {
-    GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
-    GroupBundle noteDbBundle = newBundle().source(Source.NOTE_DB).build();
-    assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle)).isEmpty();
-  }
-
-  private GroupBundle.Builder newBundle() {
-    AccountGroup group =
-        new AccountGroup(
-            new AccountGroup.NameKey("group"),
-            new AccountGroup.Id(1),
-            new AccountGroup.UUID("group-1"),
-            ts);
-    AccountGroupMember member =
-        new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(1000), group.getId()));
-    AccountGroupMemberAudit memberAudit =
-        new AccountGroupMemberAudit(member, new Account.Id(2000), ts);
-    AccountGroupById byId =
-        new AccountGroupById(
-            new AccountGroupById.Key(group.getId(), new AccountGroup.UUID("subgroup")));
-    AccountGroupByIdAud byIdAudit = new AccountGroupByIdAud(byId, new Account.Id(3000), ts);
-    return GroupBundle.builder()
-        .group(group)
-        .members(member)
-        .memberAudit(memberAudit)
-        .byId(byId)
-        .byIdAudit(byIdAudit);
-  }
-}
diff --git a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
deleted file mode 100644
index 6a8a55a..0000000
--- a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
+++ /dev/null
@@ -1,747 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_GROUPNAMES;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.extensions.common.CommitInfo;
-import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AllUsersNameProvider;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.AuditLogReader;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.GitTestUtil;
-import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import java.sql.Timestamp;
-import java.util.Optional;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.IntStream;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GroupRebuilderTest extends GerritBaseTests {
-  private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
-  private static final String SERVER_ID = "server-id";
-  private static final String SERVER_NAME = "Gerrit Server";
-  private static final String SERVER_EMAIL = "noreply@gerritcodereview.com";
-
-  private AtomicInteger idCounter;
-  private AllUsersName allUsersName;
-  private Repository repo;
-  private GroupRebuilder rebuilder;
-  private GroupBundle.Factory bundleFactory;
-
-  @Before
-  public void setUp() throws Exception {
-    TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
-    idCounter = new AtomicInteger();
-    allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
-    repo = new InMemoryRepositoryManager().createRepository(allUsersName);
-    rebuilder =
-        new GroupRebuilder(
-            GroupRebuilderTest.newPersonIdent(),
-            allUsersName,
-            // Note that the expected name/email values in tests are not necessarily realistic,
-            // since they use these trivial name/email functions.
-            getAuditLogFormatter());
-    bundleFactory = new GroupBundle.Factory(new AuditLogReader(SERVER_ID, allUsersName));
-  }
-
-  @After
-  public void tearDown() {
-    TestTimeUtil.useSystemTime();
-  }
-
-  @Test
-  public void minimalGroupFields() throws Exception {
-    AccountGroup g = newGroup("a");
-    GroupBundle b = builder().group(g).build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(1);
-    assertCommit(log.get(0), "Create group", SERVER_NAME, SERVER_EMAIL);
-    assertThat(logGroupNames()).isEmpty();
-  }
-
-  @Test
-  public void allGroupFields() throws Exception {
-    AccountGroup g = newGroup("a");
-    g.setDescription("Description");
-    g.setOwnerGroupUUID(new AccountGroup.UUID("owner"));
-    g.setVisibleToAll(true);
-    GroupBundle b = builder().group(g).build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(1);
-    assertServerCommit(log.get(0), "Create group");
-  }
-
-  @Test
-  public void emptyGroupName() throws Exception {
-    AccountGroup g = newGroup("");
-    GroupBundle b = builder().group(g).build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    GroupBundle noteDbBundle = reload(g);
-    assertMigratedCleanly(noteDbBundle, b);
-    assertThat(noteDbBundle.group().getName()).isEmpty();
-  }
-
-  @Test
-  public void nullGroupDescription() throws Exception {
-    AccountGroup g = newGroup("a");
-    g.setDescription(null);
-    assertThat(g.getDescription()).isNull();
-    GroupBundle b = builder().group(g).build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    GroupBundle noteDbBundle = reload(g);
-    assertMigratedCleanly(noteDbBundle, b);
-    assertThat(noteDbBundle.group().getDescription()).isNull();
-  }
-
-  @Test
-  public void emptyGroupDescription() throws Exception {
-    AccountGroup g = newGroup("a");
-    g.setDescription("");
-    assertThat(g.getDescription()).isEmpty();
-    GroupBundle b = builder().group(g).build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    GroupBundle noteDbBundle = reload(g);
-    assertMigratedCleanly(noteDbBundle, b);
-    assertThat(noteDbBundle.group().getDescription()).isNull();
-  }
-
-  @Test
-  public void membersAndSubgroups() throws Exception {
-    AccountGroup g = newGroup("a");
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 1), member(g, 2))
-            .byId(byId(g, "x"), byId(g, "y"))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(2);
-    assertServerCommit(log.get(0), "Create group");
-    assertServerCommit(
-        log.get(1),
-        "Update group\n"
-            + "\n"
-            + "Add-group: Group x <x>\n"
-            + "Add-group: Group y <y>\n"
-            + "Add: Account 1 <1@server-id>\n"
-            + "Add: Account 2 <2@server-id>");
-  }
-
-  @Test
-  public void memberAudit() throws Exception {
-    AccountGroup g = newGroup("a");
-    Timestamp t1 = TimeUtil.nowTs();
-    Timestamp t2 = TimeUtil.nowTs();
-    Timestamp t3 = TimeUtil.nowTs();
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 1))
-            .memberAudit(addMember(g, 1, 8, t2), addAndRemoveMember(g, 2, 8, t1, 9, t3))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(4);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(2), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(3), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
-  }
-
-  @Test
-  public void memberAuditLegacyRemoved() throws Exception {
-    AccountGroup g = newGroup("a");
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 2))
-            .memberAudit(
-                addAndLegacyRemoveMember(g, 1, 8, TimeUtil.nowTs()),
-                addMember(g, 2, 8, TimeUtil.nowTs()))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(4);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
-  }
-
-  @Test
-  public void unauditedMembershipsAddedAtEnd() throws Exception {
-    AccountGroup g = newGroup("a");
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 1), member(g, 2), member(g, 3))
-            .memberAudit(addMember(g, 1, 8, TimeUtil.nowTs()))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(3);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
-    assertServerCommit(
-        log.get(2), "Update group\n\nAdd: Account 2 <2@server-id>\nAdd: Account 3 <3@server-id>");
-  }
-
-  @Test
-  public void byIdAudit() throws Exception {
-    AccountGroup g = newGroup("a");
-    Timestamp t1 = TimeUtil.nowTs();
-    Timestamp t2 = TimeUtil.nowTs();
-    Timestamp t3 = TimeUtil.nowTs();
-    GroupBundle b =
-        builder()
-            .group(g)
-            .byId(byId(g, "x"))
-            .byIdAudit(addById(g, "x", 8, t2), addAndRemoveById(g, "y", 8, t1, 9, t3))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(4);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(log.get(1), "Update group\n\nAdd-group: Group y <y>", "Account 8", "8@server-id");
-    assertCommit(log.get(2), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(3), "Update group\n\nRemove-group: Group y <y>", "Account 9", "9@server-id");
-  }
-
-  @Test
-  public void unauditedByIdAddedAtEnd() throws Exception {
-    AccountGroup g = newGroup("a");
-    GroupBundle b =
-        builder()
-            .group(g)
-            .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
-            .byIdAudit(addById(g, "x", 8, TimeUtil.nowTs()))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(3);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
-    assertServerCommit(
-        log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
-  }
-
-  @Test
-  public void auditsAtSameTimestampBrokenDownByType() throws Exception {
-    AccountGroup g = newGroup("a");
-    Timestamp ts = TimeUtil.nowTs();
-    int user = 8;
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 1), member(g, 2))
-            .memberAudit(
-                addMember(g, 1, user, ts),
-                addMember(g, 2, user, ts),
-                addAndRemoveMember(g, 3, user, ts, user, ts))
-            .byId(byId(g, "x"), byId(g, "y"))
-            .byIdAudit(
-                addById(g, "x", user, ts),
-                addById(g, "y", user, ts),
-                addAndRemoveById(g, "z", user, ts, user, ts))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(5);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1),
-        "Update group\n"
-            + "\n"
-            + "Add: Account 1 <1@server-id>\n"
-            + "Add: Account 2 <2@server-id>\n"
-            + "Add: Account 3 <3@server-id>",
-        "Account 8",
-        "8@server-id");
-    assertCommit(
-        log.get(2), "Update group\n\nRemove: Account 3 <3@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(3),
-        "Update group\n"
-            + "\n"
-            + "Add-group: Group x <x>\n"
-            + "Add-group: Group y <y>\n"
-            + "Add-group: Group z <z>",
-        "Account 8",
-        "8@server-id");
-    assertCommit(
-        log.get(4), "Update group\n\nRemove-group: Group z <z>", "Account 8", "8@server-id");
-  }
-
-  @Test
-  public void auditsAtSameTimestampBrokenDownByUserAndType() throws Exception {
-    AccountGroup g = newGroup("a");
-    Timestamp ts = TimeUtil.nowTs();
-    int user1 = 8;
-    int user2 = 9;
-
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 1), member(g, 2), member(g, 3))
-            .memberAudit(
-                addMember(g, 1, user1, ts), addMember(g, 2, user2, ts), addMember(g, 3, user1, ts))
-            .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
-            .byIdAudit(
-                addById(g, "x", user1, ts), addById(g, "y", user2, ts), addById(g, "z", user1, ts))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(5);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1),
-        "Update group\n" + "\n" + "Add: Account 1 <1@server-id>\n" + "Add: Account 3 <3@server-id>",
-        "Account 8",
-        "8@server-id");
-    assertCommit(
-        log.get(2),
-        "Update group\n\nAdd-group: Group x <x>\nAdd-group: Group z <z>",
-        "Account 8",
-        "8@server-id");
-    assertCommit(
-        log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
-    assertCommit(log.get(4), "Update group\n\nAdd-group: Group y <y>", "Account 9", "9@server-id");
-  }
-
-  @Test
-  public void fixupCommitPostDatesAllAuditEventsEvenIfAuditEventsAreInTheFuture() throws Exception {
-    AccountGroup g = newGroup("a");
-    IntStream.range(0, 20).forEach(i -> TimeUtil.nowTs());
-    Timestamp future = TimeUtil.nowTs();
-    TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
-
-    GroupBundle b =
-        builder()
-            .group(g)
-            .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
-            .byIdAudit(addById(g, "x", 8, future))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(3);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
-    assertServerCommit(
-        log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
-
-    assertThat(log.stream().map(c -> c.committer.date).collect(toImmutableList()))
-        .named("%s", log)
-        .isOrdered();
-    assertThat(TimeUtil.nowTs()).isLessThan(future);
-  }
-
-  @Test
-  public void redundantMemberAuditsAreIgnored() throws Exception {
-    AccountGroup g = newGroup("a");
-    Timestamp t1 = TimeUtil.nowTs();
-    Timestamp t2 = TimeUtil.nowTs();
-    Timestamp t3 = TimeUtil.nowTs();
-    Timestamp t4 = TimeUtil.nowTs();
-    Timestamp t5 = TimeUtil.nowTs();
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 2))
-            .memberAudit(
-                addMember(g, 1, 8, t1),
-                addMember(g, 1, 8, t1),
-                addMember(g, 1, 8, t3),
-                addMember(g, 1, 9, t4),
-                addAndRemoveMember(g, 1, 8, t2, 9, t5),
-                addAndLegacyRemoveMember(g, 2, 9, t3),
-                addMember(g, 2, 8, t1),
-                addMember(g, 2, 9, t4),
-                addMember(g, 1, 8, t5))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(5);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1),
-        "Update group\n\nAdd: Account 1 <1@server-id>\nAdd: Account 2 <2@server-id>",
-        "Account 8",
-        "8@server-id");
-    assertCommit(
-        log.get(2), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
-    assertCommit(
-        log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
-    assertCommit(
-        log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
-  }
-
-  @Test
-  public void additionsAndRemovalsWithinSameSecondCanBeMigrated() throws Exception {
-    TestTimeUtil.resetWithClockStep(1, TimeUnit.MILLISECONDS);
-    AccountGroup g = newGroup("a");
-    Timestamp t1 = TimeUtil.nowTs();
-    Timestamp t2 = TimeUtil.nowTs();
-    Timestamp t3 = TimeUtil.nowTs();
-    Timestamp t4 = TimeUtil.nowTs();
-    Timestamp t5 = TimeUtil.nowTs();
-    GroupBundle b =
-        builder()
-            .group(g)
-            .members(member(g, 1))
-            .memberAudit(
-                addAndLegacyRemoveMember(g, 1, 8, t1),
-                addMember(g, 1, 10, t2),
-                addAndRemoveMember(g, 1, 8, t3, 9, t4),
-                addMember(g, 1, 8, t5))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(6);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(
-        log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(3), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 10", "10@server-id");
-    assertCommit(
-        log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
-    assertCommit(
-        log.get(5), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
-  }
-
-  @Test
-  public void redundantByIdAuditsAreIgnored() throws Exception {
-    AccountGroup g = newGroup("a");
-    Timestamp t1 = TimeUtil.nowTs();
-    Timestamp t2 = TimeUtil.nowTs();
-    Timestamp t3 = TimeUtil.nowTs();
-    Timestamp t4 = TimeUtil.nowTs();
-    Timestamp t5 = TimeUtil.nowTs();
-    GroupBundle b =
-        builder()
-            .group(g)
-            .byId()
-            .byIdAudit(
-                addById(g, "x", 8, t1),
-                addById(g, "x", 8, t3),
-                addById(g, "x", 9, t4),
-                addAndRemoveById(g, "x", 8, t2, 9, t5))
-            .build();
-
-    rebuilder.rebuild(repo, b, null);
-
-    assertMigratedCleanly(reload(g), b);
-    ImmutableList<CommitInfo> log = log(g);
-    assertThat(log).hasSize(3);
-    assertServerCommit(log.get(0), "Create group");
-    assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
-    assertCommit(
-        log.get(2), "Update group\n\nRemove-group: Group x <x>", "Account 9", "9@server-id");
-  }
-
-  @Test
-  public void combineWithBatchGroupNameNotes() throws Exception {
-    AccountGroup g1 = newGroup("a");
-    AccountGroup g2 = newGroup("b");
-    GroupReference gr1 = new GroupReference(g1.getGroupUUID(), g1.getName());
-    GroupReference gr2 = new GroupReference(g2.getGroupUUID(), g2.getName());
-
-    GroupBundle b1 = builder().group(g1).build();
-    GroupBundle b2 = builder().group(g2).build();
-
-    BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
-
-    rebuilder.rebuild(repo, b1, bru);
-    rebuilder.rebuild(repo, b2, bru);
-    try (ObjectInserter inserter = repo.newObjectInserter()) {
-      ImmutableList<GroupReference> refs = ImmutableList.of(gr1, gr2);
-      GroupNameNotes.updateAllGroups(repo, inserter, bru, refs, newPersonIdent());
-      inserter.flush();
-    }
-
-    assertThat(log(g1)).isEmpty();
-    assertThat(log(g2)).isEmpty();
-    assertThat(logGroupNames()).isEmpty();
-
-    RefUpdateUtil.executeChecked(bru, repo);
-
-    assertThat(log(g1)).hasSize(1);
-    assertThat(log(g2)).hasSize(1);
-    assertThat(logGroupNames()).hasSize(1);
-    assertMigratedCleanly(reload(g1), b1);
-    assertMigratedCleanly(reload(g2), b2);
-
-    assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(gr1, gr2);
-  }
-
-  @Test
-  public void groupNamesWithLeadingAndTrailingWhitespace() throws Exception {
-    for (String leading : ImmutableList.of("", " ", "  ")) {
-      for (String trailing : ImmutableList.of("", " ", "  ")) {
-        AccountGroup g = newGroup(leading + "a" + trailing);
-        GroupBundle b = builder().group(g).build();
-        rebuilder.rebuild(repo, b, null);
-        assertMigratedCleanly(reload(g), b);
-      }
-    }
-  }
-
-  @Test
-  public void disallowExisting() throws Exception {
-    AccountGroup g = newGroup("a");
-    GroupBundle b = builder().group(g).build();
-
-    rebuilder.rebuild(repo, b, null);
-    assertMigratedCleanly(reload(g), b);
-    String refName = RefNames.refsGroups(g.getGroupUUID());
-    ObjectId oldId = repo.exactRef(refName).getObjectId();
-
-    try {
-      rebuilder.rebuild(repo, b, null);
-      assert_().fail("expected OrmDuplicateKeyException");
-    } catch (OrmDuplicateKeyException e) {
-      // Expected.
-    }
-
-    assertThat(repo.exactRef(refName).getObjectId()).isEqualTo(oldId);
-  }
-
-  private GroupBundle reload(AccountGroup g) throws Exception {
-    return bundleFactory.fromNoteDb(allUsersName, repo, g.getGroupUUID());
-  }
-
-  private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
-    assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
-  }
-
-  private AccountGroup newGroup(String name) {
-    int id = idCounter.incrementAndGet();
-    return new AccountGroup(
-        new AccountGroup.NameKey(name),
-        new AccountGroup.Id(id),
-        new AccountGroup.UUID(name.trim() + "-" + id),
-        TimeUtil.nowTs());
-  }
-
-  private AccountGroupMember member(AccountGroup g, int accountId) {
-    return new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(accountId), g.getId()));
-  }
-
-  private AccountGroupMemberAudit addMember(
-      AccountGroup g, int accountId, int adder, Timestamp addedOn) {
-    return new AccountGroupMemberAudit(member(g, accountId), new Account.Id(adder), addedOn);
-  }
-
-  private AccountGroupMemberAudit addAndLegacyRemoveMember(
-      AccountGroup g, int accountId, int adder, Timestamp addedOn) {
-    AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
-    a.removedLegacy();
-    return a;
-  }
-
-  private AccountGroupMemberAudit addAndRemoveMember(
-      AccountGroup g,
-      int accountId,
-      int adder,
-      Timestamp addedOn,
-      int removedBy,
-      Timestamp removedOn) {
-    AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
-    a.removed(new Account.Id(removedBy), removedOn);
-    return a;
-  }
-
-  private AccountGroupByIdAud addById(
-      AccountGroup g, String subgroupUuid, int adder, Timestamp addedOn) {
-    return new AccountGroupByIdAud(byId(g, subgroupUuid), new Account.Id(adder), addedOn);
-  }
-
-  private AccountGroupByIdAud addAndRemoveById(
-      AccountGroup g,
-      String subgroupUuid,
-      int adder,
-      Timestamp addedOn,
-      int removedBy,
-      Timestamp removedOn) {
-    AccountGroupByIdAud a = addById(g, subgroupUuid, adder, addedOn);
-    a.removed(new Account.Id(removedBy), removedOn);
-    return a;
-  }
-
-  private AccountGroupById byId(AccountGroup g, String subgroupUuid) {
-    return new AccountGroupById(
-        new AccountGroupById.Key(g.getId(), new AccountGroup.UUID(subgroupUuid)));
-  }
-
-  private ImmutableList<CommitInfo> log(AccountGroup g) throws Exception {
-    return GitTestUtil.log(repo, RefNames.refsGroups(g.getGroupUUID()));
-  }
-
-  private ImmutableList<CommitInfo> logGroupNames() throws Exception {
-    return GitTestUtil.log(repo, REFS_GROUPNAMES);
-  }
-
-  private static GroupBundle.Builder builder() {
-    return GroupBundle.builder().source(GroupBundle.Source.REVIEW_DB);
-  }
-
-  private static PersonIdent newPersonIdent() {
-    return new PersonIdent(SERVER_NAME, SERVER_EMAIL, TimeUtil.nowTs(), TZ);
-  }
-
-  private static void assertServerCommit(CommitInfo commitInfo, String expectedMessage) {
-    assertCommit(commitInfo, expectedMessage, SERVER_NAME, SERVER_EMAIL);
-  }
-
-  private static void assertCommit(
-      CommitInfo commitInfo, String expectedMessage, String expectedName, String expectedEmail) {
-    assertThat(commitInfo).message().isEqualTo(expectedMessage);
-    assertThat(commitInfo).author().name().isEqualTo(expectedName);
-    assertThat(commitInfo).author().email().isEqualTo(expectedEmail);
-
-    // Committer should always be the server, regardless of author.
-    assertThat(commitInfo).committer().name().isEqualTo(SERVER_NAME);
-    assertThat(commitInfo).committer().email().isEqualTo(SERVER_EMAIL);
-    assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
-    assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
-  }
-
-  private static AuditLogFormatter getAuditLogFormatter() {
-    return AuditLogFormatter.create(
-        GroupRebuilderTest::getAccount, GroupRebuilderTest::getGroup, SERVER_ID);
-  }
-
-  private static Optional<Account> getAccount(Account.Id id) {
-    Account account = new Account(id, TimeUtil.nowTs());
-    account.setFullName("Account " + id);
-    return Optional.of(account);
-  }
-
-  private static Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
-    GroupDescription.Basic group =
-        new GroupDescription.Basic() {
-          @Override
-          public AccountGroup.UUID getGroupUUID() {
-            return uuid;
-          }
-
-          @Override
-          public String getName() {
-            return "Group " + uuid;
-          }
-
-          @Nullable
-          @Override
-          public String getEmailAddress() {
-            return null;
-          }
-
-          @Nullable
-          @Override
-          public String getUrl() {
-            return null;
-          }
-        };
-    return Optional.of(group);
-  }
-}
diff --git a/javatests/com/google/gerrit/server/schema/HANATest.java b/javatests/com/google/gerrit/server/schema/HANATest.java
deleted file mode 100644
index ccf747f..0000000
--- a/javatests/com/google/gerrit/server/schema/HANATest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.testing.GerritBaseTests;
-import org.eclipse.jgit.lib.Config;
-import org.junit.Before;
-import org.junit.Test;
-
-public class HANATest extends GerritBaseTests {
-
-  private HANA hana;
-  private Config config;
-
-  @Before
-  public void setup() {
-    config = new Config();
-    config.setString("database", null, "hostname", "my.host");
-    hana = new HANA(config);
-  }
-
-  @Test
-  public void getUrl() throws Exception {
-    config.setString("database", null, "port", "4242");
-    assertThat(hana.getUrl()).isEqualTo("jdbc:sap://my.host:4242");
-  }
-
-  @Test
-  public void getIndexScript() throws Exception {
-    assertThat(hana.getIndexScript()).isSameAs(ScriptRunner.NOOP);
-  }
-}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
index 9af7b1b..6018d85 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
@@ -94,20 +94,25 @@
     protected final NoteDbSchemaVersion.Arguments args;
     private final List<String> messages;
 
-    TestUpdate(Optional<Integer> initialVersion) throws Exception {
+    TestUpdate(Optional<Integer> initialVersion) {
       cfg = new Config();
       allProjectsName = new AllProjectsName("The-Projects");
       allUsersName = new AllUsersName("The-Users");
       repoManager = new InMemoryRepositoryManager();
-      try (Repository repo = repoManager.createRepository(allProjectsName)) {
-        if (initialVersion.isPresent()) {
-          TestRepository<?> tr = new TestRepository<>(repo);
-          tr.update(RefNames.REFS_VERSION, tr.blob(initialVersion.get().toString()));
-        }
-      }
-      repoManager.createRepository(allUsersName).close();
 
-      setUp();
+      SchemaCreator schemaCreator =
+          () -> {
+            try (Repository repo = repoManager.createRepository(allProjectsName)) {
+              if (initialVersion.isPresent()) {
+                TestRepository<?> tr = new TestRepository<>(repo);
+                tr.update(RefNames.REFS_VERSION, tr.blob(initialVersion.get().toString()));
+              }
+            } catch (Exception e) {
+              throw new OrmException(e);
+            }
+            repoManager.createRepository(allUsersName).close();
+            setUp();
+          };
 
       args = new NoteDbSchemaVersion.Arguments(repoManager, allProjectsName);
       NoteDbSchemaVersionManager versionManager =
@@ -117,8 +122,10 @@
       updater =
           new NoteDbSchemaUpdater(
               cfg,
+              allProjectsName,
               allUsersName,
               repoManager,
+              schemaCreator,
               notesMigration,
               versionManager,
               args,
@@ -133,7 +140,7 @@
       cfg.setString("noteDb", "changes", "disableReviewDb", "true");
     }
 
-    protected void seedGroupSequenceRef() throws Exception {
+    protected void seedGroupSequenceRef() throws OrmException {
       new RepoSequence(
               repoManager,
               GitReferenceUpdated.DISABLED,
@@ -144,7 +151,12 @@
           .next();
     }
 
-    protected void setUp() throws Exception {}
+    /**
+     * Test-specific setup.
+     *
+     * @throws OrmException if an error occurs.
+     */
+    protected void setUp() throws OrmException {}
 
     ImmutableList<String> update() throws Exception {
       updater.update(
@@ -197,7 +209,7 @@
     TestUpdate u =
         new TestUpdate(Optional.empty()) {
           @Override
-          public void setUp() throws Exception {
+          public void setUp() throws OrmException {
             setNotesMigrationConfig();
             seedGroupSequenceRef();
           }
@@ -217,7 +229,7 @@
     TestUpdate u =
         new TestUpdate(Optional.empty()) {
           @Override
-          public void setUp() throws Exception {
+          public void setUp() throws OrmException {
             seedGroupSequenceRef();
           }
         };
@@ -236,7 +248,7 @@
     TestUpdate u =
         new TestUpdate(Optional.empty()) {
           @Override
-          public void setUp() throws Exception {
+          public void setUp() {
             setNotesMigrationConfig();
           }
         };
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
index 02388ba..042ac30 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
@@ -45,9 +45,7 @@
 
   @Test
   public void exceedsReviewDbVersion() {
-    assertThat(NoteDbSchemaVersions.ALL.firstKey())
-        // TODO(dborowitz): Replace with hard-coded max number once ReviewDb code is deleted.
-        .isGreaterThan(ReviewDbSchemaVersion.guessVersion(ReviewDbSchemaVersion.C));
+    assertThat(NoteDbSchemaVersions.ALL.firstKey()).isGreaterThan(170);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/schema/ReviewDbSchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/ReviewDbSchemaUpdaterTest.java
deleted file mode 100644
index 55bd5f9..0000000
--- a/javatests/com/google/gerrit/server/schema/ReviewDbSchemaUpdaterTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.metrics.DisabledMetricMaker;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.GerritPersonIdentProvider;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AnonymousCowardName;
-import com.google.gerrit.server.config.AnonymousCowardNameProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.InMemoryDatabase;
-import com.google.gerrit.testing.InMemoryH2Type;
-import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Guice;
-import com.google.inject.Key;
-import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.UUID;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ReviewDbSchemaUpdaterTest extends GerritBaseTests {
-  private LifecycleManager lifecycle;
-  private InMemoryDatabase db;
-
-  @Before
-  public void setUp() throws Exception {
-    lifecycle = new LifecycleManager();
-    db = InMemoryDatabase.newDatabase(lifecycle);
-    lifecycle.start();
-  }
-
-  @After
-  public void tearDown() throws Exception {
-    if (lifecycle != null) {
-      lifecycle.stop();
-    }
-    InMemoryDatabase.drop(db);
-  }
-
-  @Test
-  public void update() throws OrmException, FileNotFoundException, IOException {
-    db.create();
-
-    final Path site = Paths.get(UUID.randomUUID().toString());
-    final SitePaths paths = new SitePaths(site);
-    ReviewDbSchemaUpdater u =
-        Guice.createInjector(
-                new FactoryModule() {
-                  @Override
-                  protected void configure() {
-                    TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
-                        new TypeLiteral<SchemaFactory<ReviewDb>>() {};
-                    bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
-                    bind(Key.get(schemaFactory, ReviewDbFactory.class)).toInstance(db);
-                    bind(SitePaths.class).toInstance(paths);
-
-                    Config cfg = new Config();
-                    cfg.setString("user", null, "name", "Gerrit Code Review");
-                    cfg.setString("user", null, "email", "gerrit@localhost");
-                    cfg.setString(
-                        GerritServerIdProvider.SECTION,
-                        null,
-                        GerritServerIdProvider.KEY,
-                        "1234567");
-                    bind(Config.class) //
-                        .annotatedWith(GerritServerConfig.class) //
-                        .toInstance(cfg);
-
-                    bind(PersonIdent.class) //
-                        .annotatedWith(GerritPersonIdent.class) //
-                        .toProvider(GerritPersonIdentProvider.class);
-
-                    bind(String.class).annotatedWith(GerritServerId.class).toInstance("gerrit");
-
-                    bind(AllProjectsName.class).toInstance(new AllProjectsName("All-Projects"));
-                    bind(AllUsersName.class).toInstance(new AllUsersName("All-Users"));
-
-                    bind(GitRepositoryManager.class).toInstance(new InMemoryRepositoryManager());
-
-                    bind(String.class) //
-                        .annotatedWith(AnonymousCowardName.class) //
-                        .toProvider(AnonymousCowardNameProvider.class);
-
-                    bind(DataSourceType.class).to(InMemoryH2Type.class);
-
-                    bind(SystemGroupBackend.class);
-                    install(new NotesMigration.Module());
-                    bind(MetricMaker.class).to(DisabledMetricMaker.class);
-                  }
-                })
-            .getInstance(ReviewDbSchemaUpdater.class);
-
-    for (ReviewDbSchemaVersion s = u.getLatestSchemaVersion();
-        s.getVersionNbr() > 1;
-        s = s.getPrior()) {
-      try {
-        assertThat(s.getPrior().getVersionNbr())
-            .named(
-                "schema %s has prior version %s. Not true that",
-                s.getVersionNbr(), s.getPrior().getVersionNbr())
-            .isEqualTo(s.getVersionNbr() - 1);
-      } catch (ProvisionException e) {
-        // Ignored
-        // The oldest supported schema version doesn't have a prior schema
-        // version.
-        break;
-      }
-    }
-
-    u.update(new TestUpdateUI());
-
-    db.assertSchemaVersion();
-  }
-}
diff --git a/javatests/com/google/gerrit/server/schema/ReviewDbSchemaCreatorTest.java b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
similarity index 76%
rename from javatests/com/google/gerrit/server/schema/ReviewDbSchemaCreatorTest.java
rename to javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
index fa0d8b3..ad2d184 100644
--- a/javatests/com/google/gerrit/server/schema/ReviewDbSchemaCreatorTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
@@ -28,27 +28,21 @@
 import com.google.gerrit.testing.GerritBaseTests;
 import com.google.gerrit.testing.InMemoryDatabase;
 import com.google.gerrit.testing.InMemoryModule;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import java.io.File;
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-public class ReviewDbSchemaCreatorTest extends GerritBaseTests {
+public class SchemaCreatorImplTest extends GerritBaseTests {
   @Inject private AllProjectsName allProjects;
 
   @Inject private GitRepositoryManager repoManager;
 
-  @Inject private InMemoryDatabase db;
+  @Inject private InMemoryDatabase inMemoryDatabase;
 
   @Inject private ProjectConfig.Factory projectConfigFactory;
 
@@ -57,35 +51,8 @@
     new InMemoryModule().inject(this);
   }
 
-  @After
-  public void tearDown() throws Exception {
-    InMemoryDatabase.drop(db);
-  }
-
-  @Test
-  public void getCauses_CreateSchema() throws OrmException, SQLException {
-    // Initially the schema should be empty.
-    String[] types = {"TABLE", "VIEW"};
-    try (JdbcSchema d = (JdbcSchema) db.open();
-        ResultSet rs = d.getConnection().getMetaData().getTables(null, null, null, types)) {
-      assertThat(rs.next()).isFalse();
-    }
-
-    // Create the schema using the current schema version.
-    //
-    db.create();
-    db.assertSchemaVersion();
-
-    // By default sitePath is set to the current working directory.
-    //
-    File sitePath = new File(".").getAbsoluteFile();
-    if (sitePath.getName().equals(".")) {
-      sitePath = sitePath.getParentFile();
-    }
-  }
-
   private LabelTypes getLabelTypes() throws Exception {
-    db.create();
+    inMemoryDatabase.create();
     ProjectConfig c = projectConfigFactory.create(allProjects);
     try (Repository repo = repoManager.openRepository(allProjects)) {
       c.load(repo);
diff --git a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java b/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
deleted file mode 100644
index 4d5db6d..0000000
--- a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroup.Id;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.GroupUUID;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.Month;
-import java.time.ZoneOffset;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_150_to_151_Test {
-
-  @Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
-
-  @Inject private Schema_151 schema151;
-  @Inject private ReviewDb db;
-  @Inject private IdentifiedUser currentUser;
-  @Inject private @GerritPersonIdent Provider<PersonIdent> serverIdent;
-  @Inject private Sequences seq;
-
-  private Connection connection;
-  private PreparedStatement createdOnRetrieval;
-  private PreparedStatement createdOnUpdate;
-  private PreparedStatement auditEntryDeletion;
-  private JdbcSchema jdbcSchema;
-
-  @Before
-  public void unwrapDb() {
-    jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
-  }
-
-  @Before
-  public void setUp() throws Exception {
-    assume().that(db instanceof JdbcSchema).isTrue();
-
-    connection = ((JdbcSchema) db).getConnection();
-
-    try (Statement stmt = connection.createStatement()) {
-      stmt.execute(
-          "CREATE TABLE account_groups ("
-              + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
-              + " group_id INTEGER DEFAULT 0 NOT NULL,"
-              + " name varchar(255) DEFAULT '' NOT NULL,"
-              + " created_on TIMESTAMP,"
-              + " description CLOB,"
-              + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
-              + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
-              + ")");
-
-      stmt.execute(
-          "CREATE TABLE account_group_members ("
-              + " group_id INTEGER DEFAULT 0 NOT NULL,"
-              + " account_id INTEGER DEFAULT 0 NOT NULL"
-              + ")");
-
-      stmt.execute(
-          "CREATE TABLE account_group_members_audit ("
-              + " group_id INTEGER DEFAULT 0 NOT NULL,"
-              + " account_id INTEGER DEFAULT 0 NOT NULL,"
-              + " added_by INTEGER DEFAULT 0 NOT NULL,"
-              + " added_on TIMESTAMP,"
-              + " removed_by INTEGER,"
-              + " removed_on TIMESTAMP"
-              + ")");
-    }
-
-    createdOnRetrieval =
-        connection.prepareStatement("SELECT created_on FROM account_groups WHERE group_id = ?");
-    createdOnUpdate =
-        connection.prepareStatement("UPDATE account_groups SET created_on = ? WHERE group_id = ?");
-    auditEntryDeletion =
-        connection.prepareStatement("DELETE FROM account_group_members_audit WHERE group_id = ?");
-  }
-
-  @After
-  public void tearDown() throws Exception {
-    if (auditEntryDeletion != null) {
-      auditEntryDeletion.close();
-    }
-    if (createdOnUpdate != null) {
-      createdOnUpdate.close();
-    }
-    if (createdOnRetrieval != null) {
-      createdOnRetrieval.close();
-    }
-    if (connection != null) {
-      connection.close();
-    }
-  }
-
-  @Test
-  public void createdOnIsPopulatedForGroupsCreatedAfterAudit() throws Exception {
-    Timestamp testStartTime = TimeUtil.nowTs();
-    AccountGroup.Id groupId = createGroupInReviewDb("Group for schema migration");
-    setCreatedOnToVeryOldTimestamp(groupId);
-
-    schema151.migrateData(db, new TestUpdateUI());
-
-    Timestamp createdOn = getCreatedOn(groupId);
-    assertThat(createdOn).isAtLeast(testStartTime);
-  }
-
-  @Test
-  public void createdOnIsPopulatedForGroupsCreatedBeforeAudit() throws Exception {
-    AccountGroup.Id groupId = createGroupInReviewDb("Ancient group for schema migration");
-    setCreatedOnToVeryOldTimestamp(groupId);
-    removeAuditEntriesFor(groupId);
-
-    schema151.migrateData(db, new TestUpdateUI());
-
-    Timestamp createdOn = getCreatedOn(groupId);
-    assertThat(createdOn).isEqualTo(AccountGroup.auditCreationInstantTs());
-  }
-
-  private AccountGroup.Id createGroupInReviewDb(String name) throws Exception {
-    AccountGroup group =
-        new AccountGroup(
-            new AccountGroup.NameKey(name),
-            new AccountGroup.Id(seq.nextGroupId()),
-            GroupUUID.make(name, serverIdent.get()),
-            TimeUtil.nowTs());
-    storeInReviewDb(group);
-    addMembersInReviewDb(group.getId(), currentUser.getAccountId());
-    return group.getId();
-  }
-
-  private Timestamp getCreatedOn(Id groupId) throws Exception {
-    createdOnRetrieval.setInt(1, groupId.get());
-    try (ResultSet results = createdOnRetrieval.executeQuery()) {
-      if (results.first()) {
-        return results.getTimestamp(1);
-      }
-    }
-    return null;
-  }
-
-  private void setCreatedOnToVeryOldTimestamp(Id groupId) throws Exception {
-    createdOnUpdate.setInt(1, groupId.get());
-    Instant instant = LocalDateTime.of(1800, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC);
-    createdOnUpdate.setTimestamp(1, Timestamp.from(instant));
-    createdOnUpdate.setInt(2, groupId.get());
-    createdOnUpdate.executeUpdate();
-  }
-
-  private void removeAuditEntriesFor(AccountGroup.Id groupId) throws Exception {
-    auditEntryDeletion.setInt(1, groupId.get());
-    auditEntryDeletion.executeUpdate();
-  }
-
-  private void storeInReviewDb(AccountGroup... groups) throws Exception {
-    try (PreparedStatement stmt =
-        jdbcSchema
-            .getConnection()
-            .prepareStatement(
-                "INSERT INTO account_groups"
-                    + " (group_uuid,"
-                    + " group_id,"
-                    + " name,"
-                    + " description,"
-                    + " created_on,"
-                    + " owner_group_uuid,"
-                    + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
-      for (AccountGroup group : groups) {
-        stmt.setString(1, group.getGroupUUID().get());
-        stmt.setInt(2, group.getId().get());
-        stmt.setString(3, group.getName());
-        stmt.setString(4, group.getDescription());
-        stmt.setTimestamp(5, group.getCreatedOn());
-        stmt.setString(6, group.getOwnerGroupUUID().get());
-        stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
-        stmt.addBatch();
-      }
-      stmt.executeBatch();
-    }
-  }
-
-  private void addMembersInReviewDb(AccountGroup.Id groupId, Account.Id... memberIds)
-      throws Exception {
-    try (PreparedStatement addMemberStmt =
-            jdbcSchema
-                .getConnection()
-                .prepareStatement(
-                    "INSERT INTO account_group_members"
-                        + " (group_id,"
-                        + " account_id) VALUES ("
-                        + groupId.get()
-                        + ", ?)");
-        PreparedStatement addMemberAuditStmt =
-            jdbcSchema
-                .getConnection()
-                .prepareStatement(
-                    "INSERT INTO account_group_members_audit"
-                        + " (group_id,"
-                        + " account_id,"
-                        + " added_by,"
-                        + " added_on) VALUES ("
-                        + groupId.get()
-                        + ", ?, "
-                        + currentUser.getAccountId().get()
-                        + ", ?)")) {
-      Timestamp addedOn = TimeUtil.nowTs();
-      for (Account.Id memberId : memberIds) {
-        addMemberStmt.setInt(1, memberId.get());
-        addMemberStmt.addBatch();
-
-        addMemberAuditStmt.setInt(1, memberId.get());
-        addMemberAuditStmt.setTimestamp(2, addedOn);
-        addMemberAuditStmt.addBatch();
-      }
-      addMemberStmt.executeBatch();
-      addMemberAuditStmt.executeBatch();
-    }
-  }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java b/javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java
deleted file mode 100644
index 0080f3f..0000000
--- a/javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS_DEFAULT;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static com.google.gerrit.server.git.UserConfigSections.MY;
-import static com.google.gerrit.server.schema.Schema_160.DEFAULT_DRAFT_ITEMS;
-import static com.google.gerrit.server.schema.VersionedAccountPreferences.PREFERENCES;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.extensions.client.MenuItem;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.index.account.AccountIndexer;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Supplier;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.BlobBasedConfig;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_159_to_160_Test {
-  @Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
-
-  @Inject private AccountCache accountCache;
-  @Inject private AccountIndexer accountIndexer;
-  @Inject private AllUsersName allUsersName;
-  @Inject private GerritApi gApi;
-  @Inject private GitRepositoryManager repoManager;
-  @Inject private Provider<IdentifiedUser> userProvider;
-  @Inject private ReviewDb db;
-  @Inject private Schema_160 schema160;
-
-  private Account.Id accountId;
-
-  @Before
-  public void setUp() throws Exception {
-    accountId = userProvider.get().getAccountId();
-  }
-
-  @Test
-  public void skipUnmodified() throws Exception {
-    ObjectId oldMetaId = metaRef(accountId);
-    ImmutableSet<String> fromNoteDb = myMenusFromNoteDb(accountId);
-    ImmutableSet<String> fromApi = myMenusFromApi(accountId);
-    for (String item : DEFAULT_DRAFT_ITEMS) {
-      assertThat(fromNoteDb).doesNotContain(item);
-      assertThat(fromApi).doesNotContain(item);
-    }
-
-    schema160.migrateData(db, new TestUpdateUI());
-
-    assertThat(metaRef(accountId)).isEqualTo(oldMetaId);
-  }
-
-  @Test
-  public void deleteItems() throws Exception {
-    ObjectId oldMetaId = metaRef(accountId);
-    ImmutableSet<String> defaultNames = myMenusFromApi(accountId);
-
-    GeneralPreferencesInfo prefs = gApi.accounts().id(accountId.get()).getPreferences();
-    prefs.my.add(0, new MenuItem("Something else", DEFAULT_DRAFT_ITEMS.get(0) + "+is:mergeable"));
-    for (int i = 0; i < DEFAULT_DRAFT_ITEMS.size(); i++) {
-      prefs.my.add(new MenuItem("Draft entry " + i, DEFAULT_DRAFT_ITEMS.get(i)));
-    }
-    gApi.accounts().id(accountId.get()).setPreferences(prefs);
-
-    List<String> oldNames =
-        ImmutableList.<String>builder()
-            .add("Something else")
-            .addAll(defaultNames)
-            .add("Draft entry 0")
-            .add("Draft entry 1")
-            .add("Draft entry 2")
-            .add("Draft entry 3")
-            .build();
-    assertThat(myMenusFromApi(accountId)).containsExactlyElementsIn(oldNames).inOrder();
-
-    schema160.migrateData(db, new TestUpdateUI());
-    accountCache.evict(accountId);
-    accountIndexer.index(accountId);
-    testEnv.setApiUser(accountId);
-
-    assertThat(metaRef(accountId)).isNotEqualTo(oldMetaId);
-
-    List<String> newNames =
-        ImmutableList.<String>builder().add("Something else").addAll(defaultNames).build();
-    assertThat(myMenusFromNoteDb(accountId)).containsExactlyElementsIn(newNames).inOrder();
-    assertThat(myMenusFromApi(accountId)).containsExactlyElementsIn(newNames).inOrder();
-  }
-
-  @Test
-  public void skipNonExistentRefsUsersDefault() throws Exception {
-    assertThat(readRef(REFS_USERS_DEFAULT)).isEmpty();
-    schema160.migrateData(db, new TestUpdateUI());
-    assertThat(readRef(REFS_USERS_DEFAULT)).isEmpty();
-  }
-
-  @Test
-  public void deleteDefaultItem() throws Exception {
-    assertThat(readRef(REFS_USERS_DEFAULT)).isEmpty();
-    ImmutableSet<String> defaultNames = defaultMenusFromApi();
-
-    // Setting *any* preference causes preferences.config to contain the full set of "my" sections.
-    // This mimics real-world behavior prior to the 2.15 upgrade; see Issue 8439 for details.
-    GeneralPreferencesInfo prefs = gApi.config().server().getDefaultPreferences();
-    prefs.signedOffBy = !firstNonNull(prefs.signedOffBy, false);
-    gApi.config().server().setDefaultPreferences(prefs);
-
-    try (Repository repo = repoManager.openRepository(allUsersName)) {
-      Config cfg = new BlobBasedConfig(null, repo, readRef(REFS_USERS_DEFAULT).get(), PREFERENCES);
-      assertThat(cfg.getSubsections("my")).containsExactlyElementsIn(defaultNames).inOrder();
-
-      // Add more defaults directly in git, the SetPreferences endpoint doesn't respect the "my"
-      // field in the input in 2.15 and earlier.
-      cfg.setString("my", "Drafts", "url", "#/q/owner:self+is:draft");
-      cfg.setString("my", "Something else", "url", "#/q/owner:self+is:draft+is:mergeable");
-      cfg.setString("my", "Totally not drafts", "url", "#/q/owner:self+is:draft");
-      new TestRepository<>(repo)
-          .branch(REFS_USERS_DEFAULT)
-          .commit()
-          .add(PREFERENCES, cfg.toText())
-          .create();
-    }
-
-    List<String> oldNames =
-        ImmutableList.<String>builder()
-            .addAll(defaultNames)
-            .add("Drafts")
-            .add("Something else")
-            .add("Totally not drafts")
-            .build();
-    assertThat(defaultMenusFromApi()).containsExactlyElementsIn(oldNames).inOrder();
-
-    schema160.migrateData(db, new TestUpdateUI());
-
-    assertThat(readRef(REFS_USERS_DEFAULT)).isPresent();
-
-    List<String> newNames =
-        ImmutableList.<String>builder().addAll(defaultNames).add("Something else").build();
-    assertThat(myMenusFromNoteDb(VersionedAccountPreferences::forDefault).keySet())
-        .containsExactlyElementsIn(newNames)
-        .inOrder();
-    assertThat(defaultMenusFromApi()).containsExactlyElementsIn(newNames).inOrder();
-  }
-
-  private ImmutableSet<String> myMenusFromNoteDb(Account.Id id) throws Exception {
-    return myMenusFromNoteDb(() -> VersionedAccountPreferences.forUser(id)).keySet();
-  }
-
-  // Raw config values, bypassing the defaults set by PreferencesConfig.
-  private ImmutableMap<String, String> myMenusFromNoteDb(
-      Supplier<VersionedAccountPreferences> prefsSupplier) throws Exception {
-    try (Repository repo = repoManager.openRepository(allUsersName)) {
-      VersionedAccountPreferences prefs = prefsSupplier.get();
-      prefs.load(allUsersName, repo);
-      Config cfg = prefs.getConfig();
-      return cfg.getSubsections(MY)
-          .stream()
-          .collect(toImmutableMap(i -> i, i -> cfg.getString(MY, i, KEY_URL)));
-    }
-  }
-
-  private ImmutableSet<String> myMenusFromApi(Account.Id id) throws Exception {
-    return myMenus(gApi.accounts().id(id.get()).getPreferences()).keySet();
-  }
-
-  private ImmutableSet<String> defaultMenusFromApi() throws Exception {
-    return myMenus(gApi.config().server().getDefaultPreferences()).keySet();
-  }
-
-  private static ImmutableMap<String, String> myMenus(GeneralPreferencesInfo prefs) {
-
-    return prefs.my.stream().collect(toImmutableMap(i -> i.name, i -> i.url));
-  }
-
-  private ObjectId metaRef(Account.Id id) throws Exception {
-    return readRef(RefNames.refsUsers(id))
-        .orElseThrow(() -> new AssertionError("missing ref for account " + id));
-  }
-
-  private Optional<ObjectId> readRef(String ref) throws Exception {
-    try (Repository repo = repoManager.openRepository(allUsersName)) {
-      return Optional.ofNullable(repo.exactRef(ref)).map(Ref::getObjectId);
-    }
-  }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java b/javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java
deleted file mode 100644
index 10aabe8..0000000
--- a/javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_161_to_162_Test {
-  @Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
-
-  @Inject private AllProjectsName allProjectsName;
-  @Inject private AllUsersName allUsersName;
-  @Inject private GerritApi gApi;
-  @Inject private GitRepositoryManager repoManager;
-  @Inject private Schema_162 schema162;
-  @Inject private ReviewDb db;
-  @Inject private ProjectConfig.Factory projectConfigFactory;
-  @Inject @GerritPersonIdent private PersonIdent serverUser;
-
-  @Test
-  public void skipCorrectInheritance() throws Exception {
-    assertThatAllUsersInheritsFrom(allProjectsName.get());
-    ObjectId oldHead;
-    try (Repository git = repoManager.openRepository(allUsersName)) {
-      oldHead = git.findRef(RefNames.REFS_CONFIG).getObjectId();
-    }
-
-    schema162.migrateData(db, new TestUpdateUI());
-
-    // Check that the parent remained unchanged and that no commit was made
-    assertThatAllUsersInheritsFrom(allProjectsName.get());
-    try (Repository git = repoManager.openRepository(allUsersName)) {
-      assertThat(oldHead).isEqualTo(git.findRef(RefNames.REFS_CONFIG).getObjectId());
-    }
-  }
-
-  @Test
-  public void fixIncorrectInheritance() throws Exception {
-    String testProject = gApi.projects().create("test").get().name;
-    assertThatAllUsersInheritsFrom(allProjectsName.get());
-
-    try (Repository git = repoManager.openRepository(allUsersName);
-        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
-      ProjectConfig cfg = projectConfigFactory.read(md);
-      cfg.getProject().setParentName(testProject);
-      md.getCommitBuilder().setCommitter(serverUser);
-      md.getCommitBuilder().setAuthor(serverUser);
-      md.setMessage("Test");
-      cfg.commit(md);
-    } catch (ConfigInvalidException | IOException ex) {
-      throw new OrmException(ex);
-    }
-    assertThatAllUsersInheritsFrom(testProject);
-
-    schema162.migrateData(db, new TestUpdateUI());
-
-    assertThatAllUsersInheritsFrom(allProjectsName.get());
-  }
-
-  private void assertThatAllUsersInheritsFrom(String parent) throws Exception {
-    assertThat(gApi.projects().name(allUsersName.get()).access().inheritsFrom.name)
-        .isEqualTo(parent);
-  }
-}
diff --git a/lib/BUILD b/lib/BUILD
index 80da4c0..e5e5765 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -505,10 +505,3 @@
     visibility = ["//visibility:public"],
     exports = ["@icu4j//jar"],
 )
-
-java_library(
-    name = "postgresql",
-    data = ["//lib:LICENSE-postgresql"],
-    visibility = ["//visibility:public"],
-    exports = ["@postgresql//jar"],
-)
diff --git a/lib/LICENSE-postgresql b/lib/LICENSE-postgresql
deleted file mode 100644
index fd416d2..0000000
--- a/lib/LICENSE-postgresql
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright (c) 1997-2011, PostgreSQL Global Development Group
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-3. Neither the name of the PostgreSQL Global Development Group nor the names
-   of its contributors may be used to endorse or promote products derived
-   from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/commons/BUILD b/lib/commons/BUILD
index bb36389..93d3c2f 100644
--- a/lib/commons/BUILD
+++ b/lib/commons/BUILD
@@ -3,21 +3,18 @@
 java_library(
     name = "codec",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-codec//jar"],
 )
 
 java_library(
     name = "compress",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-compress//jar"],
 )
 
 java_library(
     name = "lang",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-lang//jar"],
 )
 
@@ -30,14 +27,12 @@
 java_library(
     name = "net",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-net//jar"],
 )
 
 java_library(
     name = "dbcp",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-dbcp//jar"],
     runtime_deps = [":pool"],
 )
@@ -45,20 +40,17 @@
 java_library(
     name = "pool",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-pool//jar"],
 )
 
 java_library(
     name = "validator",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-validator//jar"],
 )
 
 java_library(
     name = "io",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@commons-io//jar"],
 )
diff --git a/lib/elasticsearch-rest-client/BUILD b/lib/elasticsearch-rest-client/BUILD
index c6357d0..8df3c70 100644
--- a/lib/elasticsearch-rest-client/BUILD
+++ b/lib/elasticsearch-rest-client/BUILD
@@ -3,6 +3,5 @@
 java_library(
     name = "elasticsearch-rest-client",
     data = ["//lib:LICENSE-elasticsearch"],
-    visibility = ["//visibility:public"],
     exports = ["@elasticsearch-rest-client//jar"],
 )
diff --git a/lib/greenmail/BUILD b/lib/greenmail/BUILD
index b09f27b..5d8e1d6 100644
--- a/lib/greenmail/BUILD
+++ b/lib/greenmail/BUILD
@@ -13,7 +13,6 @@
     name = "greenmail",
     testonly = 1,
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@greenmail//jar"],
     runtime_deps = select({
         "//:java9": POST_JDK8_DEPS,
diff --git a/lib/httpcomponents/BUILD b/lib/httpcomponents/BUILD
index 74cca14..03d9b68 100644
--- a/lib/httpcomponents/BUILD
+++ b/lib/httpcomponents/BUILD
@@ -3,7 +3,6 @@
 java_library(
     name = "fluent-hc",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@fluent-hc//jar"],
     runtime_deps = [":httpclient"],
 )
@@ -11,7 +10,6 @@
 java_library(
     name = "httpclient",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@httpclient//jar"],
     runtime_deps = [
         ":httpcore",
@@ -23,7 +21,6 @@
 java_library(
     name = "httpcore",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@httpcore//jar"],
 )
 
diff --git a/lib/jackson/BUILD b/lib/jackson/BUILD
index 3d751ab..0034748 100644
--- a/lib/jackson/BUILD
+++ b/lib/jackson/BUILD
@@ -1,5 +1,3 @@
-package(default_visibility = ["//visibility:public"])
-
 java_library(
     name = "jackson-core",
     data = ["//lib:LICENSE-Apache2.0"],
diff --git a/lib/lucene/BUILD b/lib/lucene/BUILD
index 421caed..eab2ac8 100644
--- a/lib/lucene/BUILD
+++ b/lib/lucene/BUILD
@@ -11,13 +11,11 @@
         "@lucene-core//jar",
     ],
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
 )
 
 java_library(
     name = "lucene-analyzers-common",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@lucene-analyzers-common//jar"],
     runtime_deps = [":lucene-core-and-backward-codecs"],
 )
@@ -25,14 +23,12 @@
 java_library(
     name = "lucene-core",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@lucene-core//jar"],
 )
 
 java_library(
     name = "lucene-misc",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@lucene-misc//jar"],
     runtime_deps = [":lucene-core-and-backward-codecs"],
 )
@@ -40,7 +36,6 @@
 java_library(
     name = "lucene-queryparser",
     data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
     exports = ["@lucene-queryparser//jar"],
     runtime_deps = [":lucene-core-and-backward-codecs"],
 )
diff --git a/lib/polymer_externs/BUILD b/lib/polymer_externs/BUILD
index 2f1bdbd..cd71d64 100644
--- a/lib/polymer_externs/BUILD
+++ b/lib/polymer_externs/BUILD
@@ -12,9 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-package(
-    default_visibility = ["//visibility:public"],
-)
+package(default_visibility = ["//visibility:public"])
 
 load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
 
diff --git a/plugins/delete-project b/plugins/delete-project
index e0618f3..d8fdd55 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit e0618f3dff8fcc1c36861a99b2162ac611e707c6
+Subproject commit d8fdd5596181cc06707665051f0e03a49e5c3a97
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index 863130d..fdbadf3 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit 863130de158605c19809956864870ff05d86b494
+Subproject commit fdbadf312d829990d3a4be3491d13a79d6c0cf5b
diff --git a/polygerrit-ui/BUILD b/polygerrit-ui/BUILD
index 384f835..5889ffd 100644
--- a/polygerrit-ui/BUILD
+++ b/polygerrit-ui/BUILD
@@ -1,6 +1,4 @@
-package(
-    default_visibility = ["//visibility:public"],
-)
+package(default_visibility = ["//visibility:public"])
 
 load("@io_bazel_rules_go//go:def.bzl", "go_binary")
 load("//tools/bzl:js.bzl", "bower_component_bundle")
@@ -51,7 +49,6 @@
         "zip -qr $$ROOT/$@ fonts",
     ]),
     output_to_bindir = 1,
-    visibility = ["//visibility:public"],
 )
 
 go_binary(
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index c735746..0aa70b8 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -1,6 +1,4 @@
-package(
-    default_visibility = ["//visibility:public"],
-)
+package(default_visibility = ["//visibility:public"])
 
 load(":rules.bzl", "polygerrit_bundle")
 load("//tools/bzl:genrule2.bzl", "genrule2")
diff --git a/resources/com/google/gerrit/reviewdb/BUILD b/resources/com/google/gerrit/reviewdb/BUILD
deleted file mode 100644
index 8a1b457..0000000
--- a/resources/com/google/gerrit/reviewdb/BUILD
+++ /dev/null
@@ -1,8 +0,0 @@
-filegroup(
-    name = "reviewdb",
-    srcs = glob(
-        ["**/*"],
-        exclude = ["BUILD"],
-    ),
-    visibility = ["//visibility:public"],
-)
diff --git a/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/resources/com/google/gerrit/reviewdb/server/index_generic.sql
deleted file mode 100644
index c58edb7..0000000
--- a/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ /dev/null
@@ -1,40 +0,0 @@
--- Gerrit 2 : Generic
---
-
--- Indexes to support @Query
---
-
--- *********************************************************************
--- ApprovalCategoryAccess
---    too small to bother indexing
-
-
--- *********************************************************************
--- ApprovalCategoryValueAccess
---     @PrimaryKey covers: byCategory
-
-
--- *********************************************************************
--- BranchAccess
---    @PrimaryKey covers: byProject
-
-
--- *********************************************************************
--- ChangeMessageAccess
---    @PrimaryKey covers: byChange
-
---    covers:             byPatchSet
-CREATE INDEX change_messages_byPatchset
-ON change_messages (patchset_change_id, patchset_patch_set_id);
-
--- *********************************************************************
--- PatchLineCommentAccess
---    @PrimaryKey covers: published, draft
-CREATE INDEX patch_comment_drafts
-ON patch_comments (status, author_id);
-
-
--- *********************************************************************
--- PatchSetAccess
-CREATE INDEX patch_sets_byRevision
-ON patch_sets (revision);
diff --git a/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql b/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
deleted file mode 100644
index 7f0f1bd..0000000
--- a/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
+++ /dev/null
@@ -1,43 +0,0 @@
-delimiter #
--- Gerrit 2 : MaxDB
---
-
--- Indexes to support @Query
---
-
--- *********************************************************************
--- ApprovalCategoryAccess
---    too small to bother indexing
-
-
--- *********************************************************************
--- ApprovalCategoryValueAccess
---     @PrimaryKey covers: byCategory
-
-
--- *********************************************************************
--- BranchAccess
---    @PrimaryKey covers: byProject
-
-
--- *********************************************************************
--- ChangeMessageAccess
---    @PrimaryKey covers: byChange
-
---    covers:             byPatchSet
-CREATE INDEX change_messages_byPatchset
-ON change_messages (patchset_change_id, patchset_patch_set_id)
-#
-
--- *********************************************************************
--- PatchLineCommentAccess
---    @PrimaryKey covers: published, draft
-CREATE INDEX patch_comment_drafts
-ON patch_comments (status, author_id)
-#
-
--- *********************************************************************
--- PatchSetAccess
-CREATE INDEX patch_sets_byRevision
-ON patch_sets (revision)
-#
diff --git a/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
deleted file mode 100644
index 439fed7..0000000
--- a/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ /dev/null
@@ -1,87 +0,0 @@
--- Gerrit 2 : PostgreSQL
---
-
--- Cluster hot tables by their primary method of access
---
-ALTER TABLE patch_sets CLUSTER ON patch_sets_pkey;
-ALTER TABLE change_messages CLUSTER ON change_messages_pkey;
-ALTER TABLE patch_comments CLUSTER ON patch_comments_pkey;
-ALTER TABLE patch_set_approvals CLUSTER ON patch_set_approvals_pkey;
-
-CLUSTER;
-
-
--- Define function for conditional installation of PL/pgSQL.
--- This is required, because starting with PostgreSQL 9.0, PL/pgSQL
--- language is installed by default and database returns error when
--- we try to install it again.
---
--- Source: http://wiki.postgresql.org/wiki/CREATE_OR_REPLACE_LANGUAGE
--- Author: David Fetter
---
-
-delimiter //
-
-CREATE OR REPLACE FUNCTION make_plpgsql()
-RETURNS VOID
-LANGUAGE SQL
-AS $$
-CREATE LANGUAGE plpgsql;
-$$;
-
-//
-
-delimiter ;
-
-SELECT
-    CASE
-    WHEN EXISTS(
-        SELECT 1
-        FROM pg_catalog.pg_language
-        WHERE lanname='plpgsql'
-    )
-    THEN NULL
-    ELSE make_plpgsql() END;
-
-DROP FUNCTION make_plpgsql();
-
-delimiter ;
-
--- Indexes to support @Query
---
-
--- *********************************************************************
--- ApprovalCategoryAccess
---    too small to bother indexing
-
-
--- *********************************************************************
--- ApprovalCategoryValueAccess
---     @PrimaryKey covers: byCategory
-
-
--- *********************************************************************
--- BranchAccess
---    @PrimaryKey covers: byProject
-
-
--- *********************************************************************
--- ChangeMessageAccess
---    @PrimaryKey covers: byChange
-
---    covers:             byPatchSet
-CREATE INDEX change_messages_byPatchset
-ON change_messages (patchset_change_id, patchset_patch_set_id);
-
--- *********************************************************************
--- PatchLineCommentAccess
---    @PrimaryKey covers: published, draft
-CREATE INDEX patch_comment_drafts
-ON patch_comments (author_id)
-WHERE status = 'd';
-
-
--- *********************************************************************
--- PatchSetAccess
-CREATE INDEX patch_sets_byRevision
-ON patch_sets (revision);
diff --git a/tools/bzl/pkg_war.bzl b/tools/bzl/pkg_war.bzl
index 72de14b..1fd1c81 100644
--- a/tools/bzl/pkg_war.bzl
+++ b/tools/bzl/pkg_war.bzl
@@ -19,7 +19,6 @@
 LIBS = [
     "//java/com/google/gerrit/common:version",
     "//java/com/google/gerrit/httpd/init",
-    "//lib:postgresql",
     "//lib/bouncycastle:bcpkix",
     "//lib/bouncycastle:bcprov",
     "//lib/bouncycastle:bcpg",
diff --git a/webapp/WEB-INF/extra/jetty7/gerrit.xml b/webapp/WEB-INF/extra/jetty7/gerrit.xml
index cb0a256..4102f56 100644
--- a/webapp/WEB-INF/extra/jetty7/gerrit.xml
+++ b/webapp/WEB-INF/extra/jetty7/gerrit.xml
@@ -6,11 +6,7 @@
   so it answers to simple URLs like "/$changeid" and "/mine".
 
   * Copy this file to $JETTY_HOME/contexts/gerrit.xml
-  * Edit url, username, password as necessary below for database.
 
-  * Copy commons-dbcp-*.jar     to $JETTY_HOME/lib/ext/
-  * Copy commons-pool-*.jar     to $JETTY_HOME/lib/ext/
-  * Copy JDBC driver            to $JETTY_HOME/lib/ext/
   * Copy www/gerrit-*.war       to $JETTY_HOME/webapps/gerrit.war
 
   * Make sure you remove $JETTY_HOME/context/test.xml
@@ -33,36 +29,4 @@
       <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
     </Array>
   </Set>
-
-  <New id="ReviewDb" class="org.eclipse.jetty.plus.jndi.Resource">
-    <Arg></Arg>
-    <Arg>jdbc/ReviewDb</Arg>
-    <Arg>
-      <New class="org.apache.commons.dbcp.BasicDataSource">
-<!--  PostgreSQL
-        <Set name="driverClassName">org.postgresql.Driver</Set>
-        <Set name="url">jdbc:postgresql:reviewdb</Set>
-        <Set name="username">gerrit</Set>
-        <Set name="password">secretkey</Set>
--->
-<!--  MySQL
-        <Set name="driverClassName">com.mysql.jdbc.Driver</Set>
-        <Set name="url">jdbc:mysql://localhost/reviewdb?user=gerrit&amp;password=secretkey</Set>
--->
-<!--  MariaDB
-        <Set name="driverClassName">org.mariadb.jdbc.Driver</Set>
-        <Set name="url">jdbc:mariadb://localhost/reviewdb?user=gerrit&amp;password=secretkey</Set>
--->
-<!--  H2
-        <Set name="driverClassName">org.h2.Driver</Set>
-        <Set name="url">jdbc:h2:file:ReviewDb</Set>
--->
-        <Set name="initialSize">4</Set>
-        <Set name="maxActive">8</Set>
-        <Set name="minIdle">4</Set>
-        <Set name="maxIdle">4</Set>
-        <Set name="maxWait">30000</Set>
-      </New>
-    </Arg>
-  </New>
 </Configure>