Merge "Update Buck to newest version"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index ff252f7..acd33c0 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -874,7 +874,7 @@
 If it's desired to have the possibility to upload temporarily hidden
 changes there's a specific permission for that.  This enables someone
 to add specific reviewers for early feedback before making the change
-publically visible.  If you want to allow others than the owners to
+publicly visible.  If you want to allow others than the owners to
 publish a draft you also need to grant them `Publish Drafts`.
 
 Optional access rights to grant:
diff --git a/Documentation/cmd-set-account.txt b/Documentation/cmd-set-account.txt
index f31f61b..8fb8e0d 100644
--- a/Documentation/cmd-set-account.txt
+++ b/Documentation/cmd-set-account.txt
@@ -65,7 +65,7 @@
     Delete an email from this user's account if it exists.
     If the email provided is 'ALL', all associated emails are
     deleted from this account.
-    Maybe supplied more than once to remove multiple emails
+    May be supplied more than once to remove multiple emails
     from an account in a single command execution.
 
 --preferred-email::
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 00d923f..b3e3bc0 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -337,26 +337,27 @@
 [[auth.registerUrl]]auth.registerUrl::
 +
 Target for the "Register" link in the upper right corner.  Used only
-when `auth.type` is `LDAP`.
+when `auth.type` is `LDAP`, `LDAP_BIND` or `CUSTOM_EXTENSION`.
 +
 If not set, no "Register" link is displayed.
 
 [[auth.registerText]]auth.registerText::
 +
 Text for the "Register" link in the upper right corner.  Used only
-when `auth.type` is `LDAP`.
+when `auth.type` is `LDAP`, `LDAP_BIND` or `CUSTOM_EXTENSION`.
 +
 If not set, defaults to "Register".
 
 [[auth.editFullNameUrl]]auth.editFullNameUrl::
 +
 Target for the "Edit" button when the user is allowed to edit their
-full name.
+full name.  Used only when `auth.type` is `LDAP`, `LDAP_BIND` or
+`CUSTOM_EXTENSION`.
 
 [[auth.httpPasswordUrl]]auth.httpPasswordUrl::
 +
 Target for the "Obtain Password" link.  Used only when `auth.type` is
-`LDAP`, `LDAP_BIND` or `CUSTOM_EXTENSION`.
+`CUSTOM_EXTENSION`.
 
 [[auth.switchAccountUrl]]auth.switchAccountUrl::
 +
@@ -1204,7 +1205,7 @@
 Size of the buffer to store logging events for asynchronous logging.
 Putting a larger value can protect threads from stalling when the
 AsyncAppender threads are not fast enough to consume the logging events
-from the buffer. It also protects from loosing log entries in this case.
+from the buffer. It also protects from losing log entries in this case.
 +
 Default is 64 entries.
 
@@ -2195,8 +2196,8 @@
 Number of threads to use for indexing in background operations, such as
 online schema upgrades.
 +
-If not set or set to a negative value, defaults to using the same
-thread pool as interactive operations (unless
+If not set or set to a negative value, defaults to logical number of CPU
+cores as returned by the JVM (unless
 link:#changeMerge.threadPoolSize[changeMerge.threadPoolSize] is set).
 
 [[index.onlineUpgrade]]index.onlineUpgrade::
@@ -3406,10 +3407,14 @@
 +
 The maximum number of matches evaluated for change access when using full text search.
 +
-Making this number too high could have a negative impact on performance.
-+
 By default 100.
 
+[[suggest.fullTextSearchRefresh]]suggest.fullTextSearchRefresh::
++
+Refresh interval for the in-memory account search index.
++
+By default 1 hour.
+
 
 [[theme]]
 === Section theme
diff --git a/Documentation/config-reverseproxy.txt b/Documentation/config-reverseproxy.txt
index 4ba2b3d..c3dd12e 100644
--- a/Documentation/config-reverseproxy.txt
+++ b/Documentation/config-reverseproxy.txt
@@ -85,7 +85,7 @@
 If you are encountering 'Page Not Found' errors when opening the change
 screen, your Apache proxy is very likely decoding the passed URL.
 Make sure to either use 'AllowEncodedSlashes On' together with
-'ProxyPass .. nodecode' or alternatively a 'mod_rewrite' configuration with
+'ProxyPass .. nocanon' or alternatively a 'mod_rewrite' configuration with
 'AllowEncodedSlashes NoDecode' set.
 
 
diff --git a/Documentation/dev-inspector.txt b/Documentation/dev-inspector.txt
index 2d56283..7c13a7d 100644
--- a/Documentation/dev-inspector.txt
+++ b/Documentation/dev-inspector.txt
@@ -240,7 +240,7 @@
 
 ----
 [2012-04-17 14:20:30,558] INFO  com.google.gerrit.pgm.shell.JythonShell : Jython shell instance created.
-[2012-04-17 14:20:38,005] ERROR com.google.gerrit.pgm.shell.JythonShell : Exception occured while loading file Startup.py :
+[2012-04-17 14:20:38,005] ERROR com.google.gerrit.pgm.shell.JythonShell : Exception occurred while loading file Startup.py :
 java.lang.reflect.InvocationTargetException
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 0790804..9bb0007 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -404,6 +404,10 @@
 +
 Publication of usage data
 
+* `com.google.gerrit.extensions.events.GarbageCollectorListener`:
++
+Garbage collection ran on a project
+
 [[stream-events]]
 == Sending Events to the Events Stream
 
diff --git a/Documentation/json.txt b/Documentation/json.txt
index feef1a1..8ccd03b 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -156,7 +156,7 @@
 
 newRev:: The new value the ref was updated to.
 
-refName:: Ref name within project.
+refName:: Full ref name within project.
 
 project:: Project path in Gerrit.
 
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index 43caa83..b15c283 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -141,15 +141,14 @@
 
 * `ok(user(ID))` or just `ok(_)` if user info is not important. This status is
   used to tell that this label/category has been met.
-* `need(_)` is used to tell that this label/category is needed for change to
-  become submittable
-* `reject(user(ID))` or just `reject(_)`. This status is used to tell that
-  label/category is blocking change submission
-* `impossible(_)` is used when the logic knows that the change cannot be
-  submitted as-is. Administrative intervention is probably required. This is
-  meant for cases where the logic requires members of "FooEng" to score
-  `Code-Review +2` on a change, but nobody is in group "FooEng". It is to hint
-  at permissions misconfigurations.
+* `need(_)` is used to tell that this label/category is needed for the change to
+   become submittable.
+* `reject(user(ID))` or just `reject(_)`. This status is used to tell that this
+   label/category is blocking submission of the change.
+* `impossible(_)` is used when the logic knows that the change cannot be submitted
+   as-is. This is meant for cases where the logic requires members of a specific
+   group to apply a specific label on a change, but no users are in that group.
+   This is usually caused by misconfiguration of permissions.
 * `may(_)` allows expression of approval categories that are optional, i.e.
   could either be set or unset without ever influencing whether the change
   could be submitted.
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index c6911f1..138f85f 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1687,7 +1687,7 @@
 ----
 
 When change was rebased on top of latest patch set, response
-"`204 No Content`" is returned. When change edit is aready
+"`204 No Content`" is returned. When change edit is already
 based on top of the latest patch set, the response
 "`409 Conflict`" is returned.
 
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index b32f58e..a8bbbfa 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -61,20 +61,16 @@
     },
     "download": {
       "schemes": [
-        {
-          "name": "ssh",
-          "url": "ssh://jdoe@gerrithost:29418/${project}",
-          "is_auth_required": true,
-          "is_auth_supported": true,
+        "anonymous http": {
+          "url": "http://gerrithost:8080/${project}",
           "commands": {
-            "Checkout": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
-            "Format Patch": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
-            "Pull": "git pull ssh://jdoe@gerrithost:29418/${project} ${ref}",
-            "Cherry Pick": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
+            "Checkout": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
+            "Format Patch": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
+            "Pull": "git pull http://gerrithost:8080/${project} ${ref}",
+            "Cherry Pick": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
           }
         },
-        {
-          "name": "http",
+        "http": {
           "url": "http://jdoe@gerrithost:8080/${project}",
           "is_auth_required": true,
           "is_auth_supported": true,
@@ -85,22 +81,23 @@
             "Cherry Pick": "git fetch http://jdoe@gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
           }
         },
-        {
-          "name": "anonymous http",
-          "url": "http://gerrithost:8080/${project}",
+        "ssh": {
+          "url": "ssh://jdoe@gerrithost:29418/${project}",
+          "is_auth_required": true,
+          "is_auth_supported": true,
           "commands": {
-            "Checkout": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
-            "Format Patch": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
-            "Pull": "git pull http://gerrithost:8080/${project} ${ref}",
-            "Cherry Pick": "git fetch http://gerrithost:8080/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
+            "Checkout": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git checkout FETCH_HEAD",
+            "Format Patch": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git format-patch -1 --stdout FETCH_HEAD",
+            "Pull": "git pull ssh://jdoe@gerrithost:29418/${project} ${ref}",
+            "Cherry Pick": "git fetch ssh://jdoe@gerrithost:29418/${project} ${ref} \u0026\u0026 git cherry-pick FETCH_HEAD"
           }
         }
       ],
       "archives": [
-        "TGZ",
-        "TAR",
-        "TBZ2",
-        "TXZ"
+        "tgz",
+        "tar",
+        "tbz2",
+        "txz"
       ]
     },
     "gerrit": {
@@ -1001,11 +998,11 @@
 |=======================
 |Field Name |Description
 |`schemes`  |
-The supported download schemes as list of link:#download-scheme-info[
-DownloadSchemeInfo] entities.
+The supported download schemes as a map which maps the scheme name to a
+of link:#download-scheme-info[DownloadSchemeInfo] entity.
 |`archives` |
-List of supported archive formats. Possible values are `TGZ`, `TAR`,
-`TBZ2` and `TXZ`.
+List of supported archive formats. Possible values are `tgz`, `tar`,
+`tbz2` and `txz`.
 |=======================
 
 [[download-scheme-info]]
@@ -1016,8 +1013,6 @@
 [options="header",cols="1,^1,5"]
 |=================================
 |Field Name          ||Description
-|`name`              ||
-The name of the download scheme.
 |`url`               ||
 The URL of the download scheme, where '${project}' is used as
 placeholder for the project name.
@@ -1026,7 +1021,8 @@
 |`is_auth_supported` |not set if `false`|
 Whether this download scheme supports authentication.
 |`commands`          ||
-List of download commands, where  '${project}' is used as
+Download commands as a map which maps the command name to the download
+command. In the download command '${project}' is used as
 placeholder for the project name, and '${ref}' is used as
 placeholder for the (change) ref.
 
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt
index 5ad6b39..a89d0db 100644
--- a/Documentation/user-inline-edit.txt
+++ b/Documentation/user-inline-edit.txt
@@ -44,10 +44,9 @@
 While in edit mode, it is possible to add new files to the change by clicking
 the 'Add...' button at the top of the file list.
 
-Files can be removed from the change, or restored, by clicking the icon to the
-left of the file name. Reverting a file in the change is also supported and is
-achieved in two steps: remove file from the change and restore the file in the
-change.
+File changes can be reverted or files can be removed from the change or
+deleted files can be restored, by clicking the icons to the left of the file
+name.
 
 To switch from edit mode back to review mode, click the 'Done Editing' button.
 
diff --git a/Documentation/user-named-queries.txt b/Documentation/user-named-queries.txt
new file mode 100644
index 0000000..0ddbbef
--- /dev/null
+++ b/Documentation/user-named-queries.txt
@@ -0,0 +1,32 @@
+= Gerrit Code Review - Named Queries
+
+[[user-named-queries]]
+== User Named Queries
+It is possible to define named queries on a user level. To do
+this, define the named queries in the `queries` file of
+the user's account ref in the `All-Users` project.  The user's
+account ref is based on the user's account id which is an
+integer.  The account refs are sharded by the last two digits
+(`+nn+`) in the refname, leading to refs of the format
+`+refs/users/nn/accountid+`.  The user's queries file is a
+2 column tab delimited file.  The left column represents the
+name of the query, and the right column represents the query
+expression represented by the name.
+
+Example queries file:
+
+----
+# Name         	Query
+#
+selfapproved   	owner:self label:code-review+2,user=self
+blocked        	label:code-review-2 OR label:verified-1
+# Note below how to reference your own named queries in other named queries
+ready          	label:code-review+2 label:verified+1 -query:blocked status:open
+----
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index cac1e08..ca29e44 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -99,6 +99,12 @@
 +
 Changes originally submitted by a user in 'GROUP'.
 
+[[query]]
+query:'NAME'::
++
+Changes which match the current user's query named 'NAME'
+(see link:user-named-queries.html[Named Queries]).
+
 [[reviewer]]
 reviewer:'USER', r:'USER'::
 +
diff --git a/ReleaseNotes/ReleaseNotes-2.10.1.txt b/ReleaseNotes/ReleaseNotes-2.10.1.txt
index 6ef9073..df70b64 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.1.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.1.txt
@@ -23,7 +23,7 @@
 ----
 
 * Several performance improvements when using LDAP, both in the number of LDAP
-requests and in the amount of data transfered.
+requests and in the amount of data transferred.
 
 * Sites using LDAP for authentication but otherwise rely on local Gerrit groups
 should set the new `ldap.fetchMemberOfEagerly` option to `false`.
diff --git a/ReleaseNotes/ReleaseNotes-2.10.3.txt b/ReleaseNotes/ReleaseNotes-2.10.3.txt
index 578a1ae..052840d 100644
--- a/ReleaseNotes/ReleaseNotes-2.10.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.10.3.txt
@@ -33,7 +33,7 @@
 +
 OpenID auth scheme is aware of optional OAuth2 plugin-based authentication.
 This feature is considered to be experimental and hasn't reached full feature set yet.
-Particularly, linking of user identities accross protocol boundaries and even from
+Particularly, linking of user identities across protocol boundaries and even from
 one OAuth2 identity to another OAuth2 identity wasn't implemented yet.
 
 Configuration
diff --git a/ReleaseNotes/ReleaseNotes-2.10.4.txt b/ReleaseNotes/ReleaseNotes-2.10.4.txt
new file mode 100644
index 0000000..e221549
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.10.4.txt
@@ -0,0 +1,49 @@
+Release notes for Gerrit 2.10.4
+===============================
+
+There are no schema changes from link:ReleaseNotes-2.10.3.1.html[2.10.3.1].
+
+Download:
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.4.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.10.4.war]
+
+New Features
+------------
+
+* Support identity linking in hybrid OpenID and OAuth2 authentication.
++
+Linking of user identities across protocol boundaries and from one OAuth2
+identity to another OAuth2 identity is supported.
+
+* Support identity linking in OAuth2 extension point.
++
+Linking of user identities from one OAuth2 identity to another OAuth2
+identity is supported.
+
+Bug Fixes
+---------
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3300[Issue 3300]:
+Fix >10x performance degradation for Git push and replication operations.
++
+A link:https://bugs.eclipse.org/bugs/show_bug.cgi?id=465509[regression in jgit]
+caused a performance degradation.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=3312[Issue 3312]:
+Flush padding on patches downloaded as base64.
++
+The padding was not flushed, which caused the downloaded patch to not be
+valid base64.
+
+OAuth extension point
+~~~~~~~~~~~~~~~~~~~~~
+
+* Check for session validity during logout.
++
+When user was trying to log out, after Gerrit restart, the session was
+invalidated and IllegalStateException was recorded in the error_log.
+
+Updates
+-------
+
+* Update jgit to 4.0.0.201505050340-m2.
diff --git a/ReleaseNotes/ReleaseNotes-2.11.1.txt b/ReleaseNotes/ReleaseNotes-2.11.1.txt
new file mode 100644
index 0000000..eab6d60
--- /dev/null
+++ b/ReleaseNotes/ReleaseNotes-2.11.1.txt
@@ -0,0 +1,136 @@
+Release notes for Gerrit 2.11.1
+===============================
+
+Gerrit 2.11.1 is now available:
+
+link:https://gerrit-releases.storage.googleapis.com/gerrit-2.11.1.war[
+https://gerrit-releases.storage.googleapis.com/gerrit-2.11.1.war]
+
+Gerrit 2.11.1 includes the bug fixes done with
+link:ReleaseNotes-2.10.4.html[Gerrit 2.10.4]. These bug fixes are *not* listed
+in these release notes.
+
+There are no schema changes from link:ReleaseNotes-2.11.html[2.11].
+
+
+New Features
+------------
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=321[Issue 321]:
+Use in-memory Lucene index for a better reviewer suggestion.
++
+Instead of a linear full text search through a list of accounts, use an
+in-memory Lucene index. The index is periodically refreshed. The refresh period
+is configurable via the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-gerrit.html#suggest.fullTextSearchRefresh[
+suggest.fullTextSearchRefresh] parameter.
+
+
+Bug Fixes
+---------
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3363[Issue 3363]:
+Fix performance degrade in background mergeability checks.
++
+When neither `index.batchThreads` nor `changeMerge.threadPoolSize` was defined,
+the background mergeability check fell back to using an interactive executor.
++
+This led to a severe performance degradation during git push operations because
+the `ref-update` listener was reindexing all open changes on the target branch
+interactively. The degradation increased linearly with number of open changes on
+the target branch.
++
+Now, instead of indexing interactively, it falls back to a batch thread pool
+with the logical number of available CPUs.
+
+* Reduce unnecessary database access when queryng changes.
++
+Searching for changes was retrieving more information than necessary from the
+database. This has been optimized to reduce database access and make better use
+of the secondary index.
+
+* Fix `PatchLineCommentsUtil.draftByChangeAuthor`.
++
+There is not a native index for this, and the ReviewDb case was not properly
+filtering a result by change.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3323[Issue 3323]:
+Fix internal server error when cloning from a slave while hiding some refs.
+
+* Require 'View Plugins' capability to list plugins through SSH.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3191[Issue 3191]:
+Always show 'Not Current' as state when looking at old patch set.
++
+For merged changes it was confusing for users to see the status as 'Merged' when
+they look at an old patch set.
+
+* Fix project creation with plugin config if user is not project owner.
++
+On project creation it is possible to specify plugin configuration values that
+should be stored in the `project.config` file. This failed if the calling user
+was not becoming owner of the created project, because only project owners can
+edit the `project.config` file.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3342[Issue 3342]:
+Log `IOException` on failure to update project configuration.
++
+Without logging these exceptions it's hard to guess why the update of the
+project configuration is failing.
+
+* Don't show stack trace when failing to build BloomFilter during reindex.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3337[Issue 3337]:
+Reenable 'Revert' button when revert is cancelled.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3325[Issue 3325]:
+Add missing `--newrev` parameter to the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-hooks.html#_change_merged[
+change-merged hook documentation].
+
+* Fix `gc_log` when running in a web container.
++
+All logs supposed to be in the `gc_log` file were ending up in the main log
+instead when deploying Gerrit in a web container.
+
+* Fix binding of SecureStore modules.
++
+The SecureStore modules were not correctly added when Gerrit was deployed in a
+web container with the site path configured using the `gerrit.site_path`
+property.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3310[Issue 3310]:
+Fix disabling plugins when Gerrit is running on Windows.
++
+When running Gerrit on Windows it was not possible to disable a plugin due to an
+error renaming the plugin's JAR file.
+
+* Remove temporary GitWeb config on Gerrit exit.
++
+A temporary directory was being created but not removed.
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3346[Issue 3346]:
+Fix typo in the
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-reverseproxy.html[
+Apache 2 configuration documentation].
+
+* link:http://code.google.com/p/gerrit/issues/detail?id=3346[Issue 3346]:
+Fix incorrect documentatation of
+link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-gerrit.html#auth.registerUrl[
+auth types].
+
+* Replication plugin
+
+** Fix creation of missing repositories.
++
+Missing projects were not being created on the destination.
+
+** Emit replication status events after initial full sync.
++
+When `replicateOnStartup` is enabled, the plugin was not emitting the status
+events after the initial sync.
+
+Updates
+-------
+
+* Update CodeMirror to 5.0.
diff --git a/ReleaseNotes/ReleaseNotes-2.11.txt b/ReleaseNotes/ReleaseNotes-2.11.txt
index 3bc6c96..90519dc 100644
--- a/ReleaseNotes/ReleaseNotes-2.11.txt
+++ b/ReleaseNotes/ReleaseNotes-2.11.txt
@@ -486,7 +486,7 @@
 * Plugins can provide project-aware top menu extensions
 +
 Plugins can provide sub-menu items within the 'Projects' context. The
-'${projectName}' placeholder is replaced by the project name.
+'$\{projectName\}' placeholder is replaced by the project name.
 
 * Auto register static/init.js as JavaScript plugin.
 +
diff --git a/ReleaseNotes/ReleaseNotes-2.5.txt b/ReleaseNotes/ReleaseNotes-2.5.txt
index cdef554..4abed47 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.txt
@@ -923,11 +923,11 @@
 working tree dirty.  Eclipse 4 (Juno) still overwrites these files but
 doesn't write the timestamp.  This should help to keep the working tree
 clean.  However, since the timestamp is currently present in these
-files, Eclispe 4 would still make them dirty by overwriting and
+files, Eclipse 4 would still make them dirty by overwriting and
 effectively removing the timestamp.
 +
 This change removes the timestamp from these files. This helps those
-using Eclipse 4 and doesn't make it worse for those still using Eclispe
+using Eclipse 4 and doesn't make it worse for those still using Eclipse
 3.
 
 * Add Maven profile to skip build of plugin modules
diff --git a/ReleaseNotes/ReleaseNotes-2.8.txt b/ReleaseNotes/ReleaseNotes-2.8.txt
index bc854a2..92cdda2 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.txt
@@ -625,7 +625,7 @@
 
 * Do not persist default project state in `project.config`.
 
-* Honor the `gerrit.cannonicalWebUrl` setting when opening the browser after init.
+* Honor the `gerrit.canonicalWebUrl` setting when opening the browser after init.
 
 * Fix 'query disabled' error when Query Limit is set.
 
diff --git a/ReleaseNotes/index.txt b/ReleaseNotes/index.txt
index 99db8fb..0ee2a8d 100644
--- a/ReleaseNotes/index.txt
+++ b/ReleaseNotes/index.txt
@@ -4,11 +4,13 @@
 [[2_11]]
 Version 2.11.x
 --------------
+* link:ReleaseNotes-2.11.1.html[2.11.1]
 * link:ReleaseNotes-2.11.html[2.11]
 
 [[2_10]]
 Version 2.10.x
 --------------
+* link:ReleaseNotes-2.10.4.html[2.10.4]
 * link:ReleaseNotes-2.10.3.1.html[2.10.3.1]
 * link:ReleaseNotes-2.10.3.html[2.10.3]
 * link:ReleaseNotes-2.10.2.html[2.10.2]
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 7b4219b..0b40891 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -38,6 +38,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.OutputFormat;
 import com.google.gerrit.server.account.GroupCache;
@@ -143,6 +144,9 @@
   @Inject
   private InProcessProtocol inProcessProtocol;
 
+  @Inject
+  private Provider<AnonymousUser> anonymousUser;
+
   protected TestRepository<InMemoryRepository> testRepo;
   protected GerritServer server;
   protected TestAccount admin;
@@ -427,6 +431,10 @@
     return atrScope.set(newRequestContext(account));
   }
 
+  protected Context setApiUserAnonymous() {
+    return atrScope.newContext(reviewDbProvider, null, anonymousUser.get());
+  }
+
   protected static Gson newGson() {
     return OutputFormat.JSON_COMPACT.newGson();
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
index 61a8191..0ff4709 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
@@ -162,6 +162,21 @@
     return old;
   }
 
+  public Context disableDb() {
+    Context old = current.get();
+    SchemaFactory<ReviewDb> sf = new SchemaFactory<ReviewDb>() {
+      @Override
+      public ReviewDb open() {
+        return new DisabledReviewDb();
+      }
+    };
+    Context ctx = new Context(sf, old.session, old.user, old.created);
+
+    current.set(ctx);
+    local.setContext(ctx);
+    return old;
+  }
+
   /** Returns exactly one instance per command executed. */
   static final Scope REQUEST = new Scope() {
     @Override
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/DisabledReviewDb.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/DisabledReviewDb.java
new file mode 100644
index 0000000..44d3d7f
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/DisabledReviewDb.java
@@ -0,0 +1,206 @@
+// 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.acceptance;
+
+import com.google.gerrit.reviewdb.server.AccountAccess;
+import com.google.gerrit.reviewdb.server.AccountDiffPreferenceAccess;
+import com.google.gerrit.reviewdb.server.AccountExternalIdAccess;
+import com.google.gerrit.reviewdb.server.AccountGroupAccess;
+import com.google.gerrit.reviewdb.server.AccountGroupByIdAccess;
+import com.google.gerrit.reviewdb.server.AccountGroupByIdAudAccess;
+import com.google.gerrit.reviewdb.server.AccountGroupMemberAccess;
+import com.google.gerrit.reviewdb.server.AccountGroupMemberAuditAccess;
+import com.google.gerrit.reviewdb.server.AccountGroupNameAccess;
+import com.google.gerrit.reviewdb.server.AccountPatchReviewAccess;
+import com.google.gerrit.reviewdb.server.AccountProjectWatchAccess;
+import com.google.gerrit.reviewdb.server.AccountSshKeyAccess;
+import com.google.gerrit.reviewdb.server.ChangeAccess;
+import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
+import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
+import com.google.gerrit.reviewdb.server.PatchSetAccess;
+import com.google.gerrit.reviewdb.server.PatchSetAncestorAccess;
+import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
+import com.google.gerrit.reviewdb.server.StarredChangeAccess;
+import com.google.gerrit.reviewdb.server.SubmoduleSubscriptionAccess;
+import com.google.gerrit.reviewdb.server.SystemConfigAccess;
+import com.google.gwtorm.server.Access;
+import com.google.gwtorm.server.StatementExecutor;
+
+/** ReviewDb that is disabled for testing. */
+class DisabledReviewDb implements ReviewDb {
+  private static final String MESSAGE = "ReviewDb is disabled for this test";
+
+  @Override
+  public void close() {
+    // Do nothing.
+  }
+
+  @Override
+  public void commit() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public void rollback() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public void updateSchema(StatementExecutor e) {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public void pruneSchema(StatementExecutor e) {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public Access<?, ?>[] allRelations() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public SchemaVersionAccess schemaVersion() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public SystemConfigAccess systemConfig() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountAccess accounts() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountExternalIdAccess accountExternalIds() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountSshKeyAccess accountSshKeys() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountGroupAccess accountGroups() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountGroupNameAccess accountGroupNames() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountGroupMemberAccess accountGroupMembers() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountGroupMemberAuditAccess accountGroupMembersAudit() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountDiffPreferenceAccess accountDiffPreferences() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public StarredChangeAccess starredChanges() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountProjectWatchAccess accountProjectWatches() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountPatchReviewAccess accountPatchReviews() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public ChangeAccess changes() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public PatchSetApprovalAccess patchSetApprovals() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public ChangeMessageAccess changeMessages() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public PatchSetAccess patchSets() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public PatchSetAncestorAccess patchSetAncestors() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public PatchLineCommentAccess patchComments() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public SubmoduleSubscriptionAccess submoduleSubscriptions() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountGroupByIdAccess accountGroupById() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public AccountGroupByIdAudAccess accountGroupByIdAud() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public int nextAccountId() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public int nextAccountGroupId() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public int nextChangeId() {
+    throw new AssertionError(MESSAGE);
+  }
+
+  @Override
+  public int nextChangeMessageId() {
+    throw new AssertionError(MESSAGE);
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 772c885..9a58702 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -443,4 +443,30 @@
     assertThat(actual.revisions.get(r2.getCommit().getName()).commitWithFooters)
         .isEqualTo(expected);
   }
+
+  @Test
+  public void defaultSearchDoesNotTouchDatabase() throws Exception {
+    PushOneCommit.Result r1 = createChange();
+    gApi.changes()
+        .id(r1.getChangeId())
+        .revision(r1.getCommit().name())
+        .review(ReviewInput.approve());
+    gApi.changes()
+        .id(r1.getChangeId())
+        .revision(r1.getCommit().name())
+        .submit();
+
+    createChange();
+
+    setApiUserAnonymous(); // Identified user may async get stars from DB.
+    atrScope.disableDb();
+    assertThat(gApi.changes().query()
+          .withQuery(
+            "project:{" + project.get() + "} (status:open OR status:closed)")
+          // Options should match defaults in ChangeTable.
+          .withOption(ListChangesOption.LABELS)
+          .withOption(ListChangesOption.DETAILED_ACCOUNTS)
+          .get())
+        .hasSize(2);
+  }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 09e6ffe..0d46ed7 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -180,6 +180,26 @@
   }
 
   @Test
+  public void cherryPickwithNoTopic() throws Exception {
+    PushOneCommit.Result r = pushTo("refs/for/master");
+    CherryPickInput in = new CherryPickInput();
+    in.destination = "foo";
+    in.message = "it goes to stable branch";
+    gApi.projects()
+        .name(project.get())
+        .branch(in.destination)
+        .create(new BranchInput());
+    ChangeApi orig = gApi.changes()
+        .id(project.get() + "~master~" + r.getChangeId());
+
+    ChangeApi cherry = orig.revision(r.getCommit().name())
+        .cherryPick(in);
+    assertThat(cherry.get().topic).isNull();
+    cherry.current().review(ReviewInput.approve());
+    cherry.current().submit();
+  }
+
+  @Test
   public void cherryPickToSameBranch() throws Exception {
     PushOneCommit.Result r = createChange();
     CherryPickInput in = new CherryPickInput();
@@ -505,6 +525,19 @@
     CommentInfo comment = Iterables.getOnlyElement(out.get(FILE_NAME));
     assertThat(comment.message).isEqualTo(in.message);
     assertThat(comment.author.email).isEqualTo(admin.email);
+    assertThat(comment.path).isNull();
+
+    List<CommentInfo> list = gApi.changes()
+        .id(r.getChangeId())
+        .revision(r.getCommit().name())
+        .commentsAsList();
+    assertThat(list).hasSize(1);
+
+    CommentInfo comment2 = list.get(0);
+    assertThat(comment2.path).isEqualTo(FILE_NAME);
+    assertThat(comment2.line).isEqualTo(comment.line);
+    assertThat(comment2.message).isEqualTo(comment.message);
+    assertThat(comment2.author.email).isEqualTo(comment.author.email);
 
     assertThat(gApi.changes()
         .id(r.getChangeId())
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 4007e32..82f8c59 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -456,6 +456,30 @@
   }
 
   @Test
+  public void revertChanges() throws Exception {
+    assertThat(modifier.createEdit(change2, ps2)).isEqualTo(
+        RefUpdate.Result.NEW);
+    Optional<ChangeEdit> edit = editUtil.byChange(change2);
+    assertThat(modifier.restoreFile(edit.get(), FILE_NAME)).isEqualTo(
+        RefUpdate.Result.FORCED);
+    edit = editUtil.byChange(change2);
+    assertByteArray(fileUtil.getContent(projectCache.get(edit.get().getChange().getProject()),
+        ObjectId.fromString(edit.get().getRevision().get()), FILE_NAME), CONTENT_OLD);
+    assertThat(
+        modifier.modifyFile(editUtil.byChange(change2).get(), FILE_NAME,
+            RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED);
+    edit = editUtil.byChange(change2);
+    assertByteArray(fileUtil.getContent(projectCache.get(edit.get().getChange().getProject()),
+        ObjectId.fromString(edit.get().getRevision().get()), FILE_NAME), CONTENT_NEW);
+    assertThat(modifier.restoreFile(edit.get(), FILE_NAME)).isEqualTo(
+        RefUpdate.Result.FORCED);
+    edit = editUtil.byChange(change2);
+    assertByteArray(fileUtil.getContent(projectCache.get(edit.get().getChange().getProject()),
+        ObjectId.fromString(edit.get().getRevision().get()), FILE_NAME), CONTENT_OLD);
+    editUtil.delete(edit.get());
+  }
+
+  @Test
   public void renameFileRest() throws Exception {
     assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
     Post.Input in = new Post.Input();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index 85037f1..7823e7d 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -16,7 +16,6 @@
 
 import static com.google.gerrit.acceptance.GitUtil.getChangeId;
 
-import com.google.gerrit.acceptance.git.AbstractSubmoduleSubscription;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.testutil.ConfigSuite;
@@ -55,7 +54,6 @@
     RevCommit c1 = subRepo.branch("HEAD").commit().insertChangeId()
       .message("first change")
       .add("asdf", "asdf\n")
-      .parent(c)
       .create();
     subRepo.git().push().setRemote("origin")
       .setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo")))
@@ -65,13 +63,11 @@
     RevCommit c2 = subRepo.branch("HEAD").commit().insertChangeId()
       .message("qwerty")
       .add("qwerty", "qwerty")
-      .parent(c)
       .create();
 
     RevCommit c3 = subRepo.branch("HEAD").commit().insertChangeId()
       .message("qwerty followup")
       .add("qwerty", "qwerty\nqwerty\n")
-      .parent(c2)
       .create();
     subRepo.git().push().setRemote("origin")
       .setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo")))
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
index 23fa83f..47d071f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
@@ -71,7 +71,7 @@
     assertThat(c.status).isEqualTo(ChangeStatus.DRAFT);
     RestResponse r = deletePatchSet(changeId, ps, adminSession);
     assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
-    assertThat(getChange(changeId).patches()).hasSize(1);
+    assertThat(getChange(changeId).patchSets()).hasSize(1);
     ps = getCurrentPatchSet(changeId);
     r = deletePatchSet(changeId, ps, adminSession);
     assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index d0627f6..ed6740a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -62,9 +62,9 @@
     group2 = group("users2");
     group3 = group("users3");
 
-    user1 = user("user1", group1);
-    user2 = user("user2", group2);
-    user3 = user("user3", group1, group2);
+    user1 = user("user1", "First1 Last1", group1);
+    user2 = user("user2", "First2 Last2", group2);
+    user3 = user("user3", "First3 Last3", group1, group2);
   }
 
   @Test
@@ -116,7 +116,7 @@
     String changeId = createChange().getChangeId();
     List<SuggestedReviewerInfo> reviewers;
 
-    reviewers = suggestReviewers(changeId, user2.fullName, 2);
+    reviewers = suggestReviewers(changeId, user2.username, 2);
     assertThat(reviewers).hasSize(1);
     assertThat(Iterables.getOnlyElement(reviewers).account.name)
         .isEqualTo(user2.fullName);
@@ -126,13 +126,13 @@
     assertThat(reviewers).isEmpty();
 
     setApiUser(user2);
-    reviewers = suggestReviewers(changeId, user2.fullName, 2);
+    reviewers = suggestReviewers(changeId, user2.username, 2);
     assertThat(reviewers).hasSize(1);
     assertThat(Iterables.getOnlyElement(reviewers).account.name)
         .isEqualTo(user2.fullName);
 
     setApiUser(user3);
-    reviewers = suggestReviewers(changeId, user2.fullName, 2);
+    reviewers = suggestReviewers(changeId, user2.username, 2);
     assertThat(reviewers).hasSize(1);
     assertThat(Iterables.getOnlyElement(reviewers).account.name)
         .isEqualTo(user2.fullName);
@@ -145,13 +145,13 @@
     List<SuggestedReviewerInfo> reviewers;
 
     setApiUser(user1);
-    reviewers = suggestReviewers(changeId, user2.fullName, 2);
+    reviewers = suggestReviewers(changeId, user2.username, 2);
     assertThat(reviewers).isEmpty();
 
     setApiUser(user1); // Clear cached group info.
     allowGlobalCapabilities(group1.getGroupUUID(),
         GlobalCapability.VIEW_ALL_ACCOUNTS);
-    reviewers = suggestReviewers(changeId, user2.fullName, 2);
+    reviewers = suggestReviewers(changeId, user2.username, 2);
     assertThat(reviewers).hasSize(1);
     assertThat(Iterables.getOnlyElement(reviewers).account.name)
         .isEqualTo(user2.fullName);
@@ -170,9 +170,49 @@
   @GerritConfig(name = "suggest.fullTextSearch", value = "true")
   public void suggestReviewersFullTextSearch() throws Exception {
     String changeId = createChange().getChangeId();
-    List<SuggestedReviewerInfo> reviewers =
-        suggestReviewers(changeId, "ser", 5);
-    assertThat(reviewers).hasSize(4);
+    List<SuggestedReviewerInfo> reviewers;
+
+    reviewers = suggestReviewers(changeId, "first", 4);
+    assertThat(reviewers).hasSize(3);
+
+    reviewers = suggestReviewers(changeId, "first1", 2);
+    assertThat(reviewers).hasSize(1);
+
+    reviewers = suggestReviewers(changeId, "last", 4);
+    assertThat(reviewers).hasSize(3);
+
+    reviewers = suggestReviewers(changeId, "last1", 2);
+    assertThat(reviewers).hasSize(1);
+
+    reviewers = suggestReviewers(changeId, "fi la", 4);
+    assertThat(reviewers).hasSize(3);
+
+    reviewers = suggestReviewers(changeId, "la fi", 4);
+    assertThat(reviewers).hasSize(3);
+
+    reviewers = suggestReviewers(changeId, "first1 la", 2);
+    assertThat(reviewers).hasSize(1);
+
+    reviewers = suggestReviewers(changeId, "fi last1", 2);
+    assertThat(reviewers).hasSize(1);
+
+    reviewers = suggestReviewers(changeId, "first1 last2", 1);
+    assertThat(reviewers).hasSize(0);
+
+    reviewers = suggestReviewers(changeId, name("user"), 7);
+    assertThat(reviewers).hasSize(6);
+
+    reviewers = suggestReviewers(changeId, user1.username, 2);
+    assertThat(reviewers).hasSize(1);
+
+    reviewers = suggestReviewers(changeId, "example.com", 6);
+    assertThat(reviewers).hasSize(5);
+
+    reviewers = suggestReviewers(changeId, user1.email, 2);
+    assertThat(reviewers).hasSize(1);
+
+    reviewers = suggestReviewers(changeId, user1.username + " example", 2);
+    assertThat(reviewers).hasSize(1);
   }
 
   @Test
@@ -183,14 +223,14 @@
   public void suggestReviewersFullTextSearchLimitMaxMatches() throws Exception {
     String changeId = createChange().getChangeId();
     List<SuggestedReviewerInfo> reviewers =
-        suggestReviewers(changeId, "ser", 3);
+        suggestReviewers(changeId, name("user"), 2);
     assertThat(reviewers).hasSize(2);
   }
 
   @Test
   public void suggestReviewersWithoutLimitOptionSpecified() throws Exception {
     String changeId = createChange().getChangeId();
-    String query = user3.fullName;
+    String query = user3.username;
     List<SuggestedReviewerInfo> suggestedReviewerInfos = gApi.changes()
         .id(changeId)
         .suggestReviewers(query)
@@ -214,7 +254,8 @@
     return GroupDescriptions.toAccountGroup(d);
   }
 
-  private TestAccount user(String name, AccountGroup... groups) throws Exception {
+  private TestAccount user(String name, String fullName, AccountGroup... groups)
+      throws Exception {
     name = name(name);
     String[] groupNames = FluentIterable.from(Arrays.asList(groups))
         .transform(new Function<AccountGroup, String>() {
@@ -223,6 +264,6 @@
             return in.getName();
           }
         }).toArray(String.class);
-    return accounts.create(name, name + "@example.com", name, groupNames);
+    return accounts.create(name, name + "@example.com", fullName, groupNames);
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index d4268ee..11acf00 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -15,14 +15,15 @@
 package com.google.gerrit.acceptance.server.change;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.acceptance.GitUtil.getChangeId;
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GitUtil;
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.change.GetRelated.ChangeAndCommit;
@@ -50,77 +51,65 @@
   @Test
   public void getRelatedNoResult() throws Exception {
     PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
-    PatchSet.Id ps = push.to("refs/for/master").getPatchSetId();
-    List<ChangeAndCommit> related = getRelated(ps);
-    assertThat(related).isEmpty();
+    assertRelated(push.to("refs/for/master").getPatchSetId());
   }
 
   @Test
   public void getRelatedLinear() throws Exception {
-    RevCommit c1 = commitBuilder()
+    RevCommit c1_1 = commitBuilder()
         .add("a.txt", "1")
         .message("subject: 1")
         .create();
-    String id1 = getChangeId(testRepo, c1).get();
-    RevCommit c2 = commitBuilder()
+    String id1 = getChangeId(c1_1);
+    RevCommit c2_2 = commitBuilder()
         .add("b.txt", "2")
         .message("subject: 2")
         .create();
-    String id2 = getChangeId(testRepo, c2).get();
+    String id2 = getChangeId(c2_2);
     pushHead(testRepo, "refs/for/master", false);
 
-    for (RevCommit c : ImmutableList.of(c2, c1)) {
-      List<ChangeAndCommit> related = getRelated(getPatchSetId(c));
-      String id = getChangeId(testRepo, c).get();
-      assertThat(related).hasSize(2);
-      assertThat(related.get(0).changeId)
-          .named("related to " + id).isEqualTo(id2);
-      assertThat(related.get(1).changeId)
-          .named("related to " + id).isEqualTo(id1);
+    for (RevCommit c : ImmutableList.of(c2_2, c1_1)) {
+      assertRelated(getPatchSetId(c),
+          changeAndCommit(id2, c2_2, 1, 1),
+          changeAndCommit(id1, c1_1, 1, 1));
     }
   }
 
   @Test
   public void getRelatedReorder() throws Exception {
     // Create two commits and push.
-    RevCommit c1 = commitBuilder()
+    RevCommit c1_1 = commitBuilder()
         .add("a.txt", "1")
         .message("subject: 1")
         .create();
-    String id1 = getChangeId(testRepo, c1).get();
-    RevCommit c2 = commitBuilder()
+    String id1 = getChangeId(c1_1);
+    RevCommit c2_1 = commitBuilder()
         .add("b.txt", "2")
         .message("subject: 2")
         .create();
-    String id2 = getChangeId(testRepo, c2).get();
+    String id2 = getChangeId(c2_1);
     pushHead(testRepo, "refs/for/master", false);
-    PatchSet.Id c1ps1 = getPatchSetId(c1);
-    PatchSet.Id c2ps1 = getPatchSetId(c2);
+    PatchSet.Id ps1_1 = getPatchSetId(c1_1);
+    PatchSet.Id ps2_1 = getPatchSetId(c2_1);
 
     // Swap the order of commits and push again.
     testRepo.reset("HEAD~2");
-    testRepo.cherryPick(c2);
-    testRepo.cherryPick(c1);
+    RevCommit c2_2 = testRepo.cherryPick(c2_1);
+    RevCommit c1_2 = testRepo.cherryPick(c1_1);
     pushHead(testRepo, "refs/for/master", false);
-    PatchSet.Id c1ps2 = getPatchSetId(c1);
-    PatchSet.Id c2ps2 = getPatchSetId(c2);
+    PatchSet.Id ps1_2 = getPatchSetId(c1_1);
+    PatchSet.Id ps2_2 = getPatchSetId(c2_1);
 
-    for (PatchSet.Id ps : ImmutableList.of(c2ps2, c1ps2)) {
-      List<ChangeAndCommit> related = getRelated(ps);
-      assertThat(related).hasSize(2);
-      assertThat(related.get(0).changeId).named("related to " + ps)
-          .isEqualTo(id1);
-      assertThat(related.get(1).changeId).named("related to " + ps)
-          .isEqualTo(id2);
+    for (PatchSet.Id ps : ImmutableList.of(ps2_2, ps1_2)) {
+      assertRelated(ps,
+          changeAndCommit(id1, c1_2, 2, 2),
+          changeAndCommit(id2, c2_2, 2, 2));
     }
 
-    for (PatchSet.Id ps : ImmutableList.of(c2ps1, c1ps1)) {
-      List<ChangeAndCommit> related = getRelated(ps);
-      assertThat(related).hasSize(2);
-      assertThat(related.get(0).changeId).named("related to " + ps)
-          .isEqualTo(id2);
-      assertThat(related.get(1).changeId).named("related to " + ps)
-          .isEqualTo(id1);
+    for (PatchSet.Id ps : ImmutableList.of(ps2_1, ps1_1)) {
+      assertRelated(ps,
+          changeAndCommit(id2, c2_1, 1, 2),
+          changeAndCommit(id1, c1_1, 1, 2));
     }
   }
 
@@ -128,95 +117,91 @@
   public void getRelatedReorderAndExtend() throws Exception {
     // Create two commits and push.
     ObjectId initial = repo().getRef("HEAD").getObjectId();
-    RevCommit c1 = commitBuilder()
+    RevCommit c1_1 = commitBuilder()
         .add("a.txt", "1")
         .message("subject: 1")
         .create();
-    String id1 = getChangeId(testRepo, c1).get();
-    RevCommit c2 = commitBuilder()
+    String id1 = getChangeId(c1_1);
+    RevCommit c2_1 = commitBuilder()
         .add("b.txt", "2")
         .message("subject: 2")
         .create();
-    String id2 = getChangeId(testRepo, c2).get();
+    String id2 = getChangeId(c2_1);
     pushHead(testRepo, "refs/for/master", false);
-    PatchSet.Id c1ps1 = getPatchSetId(c1);
-    PatchSet.Id c2ps1 = getPatchSetId(c2);
+    PatchSet.Id ps1_1 = getPatchSetId(c1_1);
+    PatchSet.Id ps2_1 = getPatchSetId(c2_1);
 
     // Swap the order of commits, create a new commit on top, and push again.
     testRepo.reset(initial);
-    testRepo.cherryPick(c2);
-    testRepo.cherryPick(c1);
-    RevCommit c3 = commitBuilder()
+    RevCommit c2_2 = testRepo.cherryPick(c2_1);
+    RevCommit c1_2 = testRepo.cherryPick(c1_1);
+    RevCommit c3_1 = commitBuilder()
         .add("c.txt", "3")
         .message("subject: 3")
         .create();
-    String id3 = getChangeId(testRepo, c3).get();
+    String id3 = getChangeId(c3_1);
     pushHead(testRepo, "refs/for/master", false);
-    PatchSet.Id c1ps2 = getPatchSetId(c1);
-    PatchSet.Id c2ps2 = getPatchSetId(c2);
-    PatchSet.Id c3ps1 = getPatchSetId(c3);
+    PatchSet.Id ps1_2 = getPatchSetId(c1_1);
+    PatchSet.Id ps2_2 = getPatchSetId(c2_1);
+    PatchSet.Id ps3_1 = getPatchSetId(c3_1);
 
-
-    for (PatchSet.Id ps : ImmutableList.of(c3ps1, c2ps2, c1ps2)) {
-      List<ChangeAndCommit> related = getRelated(ps);
-      assertThat(related).hasSize(3);
-      assertThat(related.get(0).changeId).named("related to " + ps)
-          .isEqualTo(id3);
-      assertThat(related.get(1).changeId).named("related to " + ps)
-          .isEqualTo(id1);
-      assertThat(related.get(2).changeId).named("related to " + ps)
-          .isEqualTo(id2);
+    for (PatchSet.Id ps : ImmutableList.of(ps3_1, ps2_2, ps1_2)) {
+      assertRelated(ps,
+          changeAndCommit(id3, c3_1, 1, 1),
+          changeAndCommit(id1, c1_2, 2, 2),
+          changeAndCommit(id2, c2_2, 2, 2));
     }
 
-    for (PatchSet.Id ps : ImmutableList.of(c2ps1, c1ps1)) {
-      List<ChangeAndCommit> related = getRelated(ps);
-      assertThat(related).hasSize(3);
-      assertThat(related.get(0).changeId).named("related to " + ps)
-          .isEqualTo(id3);
-      assertThat(related.get(1).changeId).named("related to " + ps)
-          .isEqualTo(id2);
-      assertThat(related.get(2).changeId).named("related to " + ps)
-          .isEqualTo(id1);
+    for (PatchSet.Id ps : ImmutableList.of(ps2_1, ps1_1)) {
+      assertRelated(ps,
+          changeAndCommit(id3, c3_1, 1, 1),
+          changeAndCommit(id2, c2_1, 1, 2),
+          changeAndCommit(id1, c1_1, 1, 2));
     }
   }
 
   @Test
   public void getRelatedEdit() throws Exception {
-    RevCommit c1 = commitBuilder()
+    RevCommit c1_1 = commitBuilder()
         .add("a.txt", "1")
         .message("subject: 1")
         .create();
-    String id1 = getChangeId(testRepo, c1).get();
-    RevCommit c2 = commitBuilder()
+    String id1 = getChangeId(c1_1);
+    RevCommit c2_1 = commitBuilder()
         .add("b.txt", "2")
         .message("subject: 2")
         .create();
-    String id2 = getChangeId(testRepo, c2).get();
-    RevCommit c3 = commitBuilder()
+    String id2 = getChangeId(c2_1);
+    RevCommit c3_1 = commitBuilder()
         .add("c.txt", "3")
         .message("subject: 3")
         .create();
-    String id3 = getChangeId(testRepo, c3).get();
+    String id3 = getChangeId(c3_1);
     pushHead(testRepo, "refs/for/master", false);
 
-    Change ch2 = getChange(c2).change();
+    Change ch2 = getChange(c2_1).change();
     editModifier.createEdit(ch2, getPatchSet(ch2));
     editModifier.modifyFile(editUtil.byChange(ch2).get(), "a.txt",
         RestSession.newRawInput(new byte[] {'a'}));
-    String editRev = editUtil.byChange(ch2).get().getRevision().get();
+    ObjectId editRev =
+        ObjectId.fromString(editUtil.byChange(ch2).get().getRevision().get());
 
-    List<ChangeAndCommit> related = getRelated(ch2.getId(), 0);
-    assertThat(related).hasSize(3);
-    assertThat(related.get(0).changeId).named("related to " + id2)
-        .isEqualTo(id3);
-    assertThat(related.get(1).changeId).named("related to " + id2)
-        .isEqualTo(id2);
-    assertThat(related.get(1)._revisionNumber).named(
-        "has edit revision number").isEqualTo(0);
-    assertThat(related.get(1).commit.commit).named(
-        "has edit revision " + editRev).isEqualTo(editRev);
-    assertThat(related.get(2).changeId).named("related to " + id2)
-        .isEqualTo(id1);
+    PatchSet.Id ps1_1 = getPatchSetId(c1_1);
+    PatchSet.Id ps2_1 = getPatchSetId(c2_1);
+    PatchSet.Id ps2_edit = new PatchSet.Id(ch2.getId(), 0);
+    PatchSet.Id ps3_1 = getPatchSetId(c3_1);
+
+    for (PatchSet.Id ps : ImmutableList.of(ps1_1, ps2_1, ps3_1)) {
+      assertRelated(ps,
+          changeAndCommit(id3, c3_1, 1, 1),
+          changeAndCommit(id2, c2_1, 1, 1),
+          changeAndCommit(id1, c1_1, 1, 1));
+    }
+
+    assertRelated(ps2_edit,
+        changeAndCommit(id3, c3_1, 1, 1),
+        changeAndCommit(id2, editRev, 0, 1),
+        changeAndCommit(id1, c1_1, 1, 1));
   }
 
   private List<ChangeAndCommit> getRelated(PatchSet.Id ps) throws IOException {
@@ -231,6 +216,10 @@
         RelatedInfo.class).changes;
   }
 
+  private String getChangeId(RevCommit c) throws Exception {
+    return GitUtil.getChangeId(testRepo, c).get();
+  }
+
   private PatchSet.Id getPatchSetId(ObjectId c) throws OrmException {
     return getChange(c).change().currentPatchSetId();
   }
@@ -242,4 +231,35 @@
   private ChangeData getChange(ObjectId c) throws OrmException {
     return Iterables.getOnlyElement(queryProvider.get().byCommit(c));
   }
+
+  private static ChangeAndCommit changeAndCommit(String changeId,
+      ObjectId commitId, int revisionNum, int currentRevisionNum) {
+    ChangeAndCommit result = new ChangeAndCommit();
+    result.changeId = changeId;
+    result.commit = new CommitInfo();
+    result.commit.commit = commitId.name();
+    result._revisionNumber = revisionNum;
+    result._currentRevisionNumber = currentRevisionNum;
+    return result;
+  }
+
+  private void assertRelated(PatchSet.Id psId, ChangeAndCommit... expected)
+      throws Exception {
+    List<ChangeAndCommit> actual = getRelated(psId);
+    assertThat(actual).hasSize(expected.length);
+    for (int i = 0; i < actual.size(); i++) {
+      String name = "index " + i + " related to " + psId;
+      ChangeAndCommit a = actual.get(i);
+      ChangeAndCommit e = expected[i];
+      assertThat(a.changeId).named("Change-Id of " + name)
+          .isEqualTo(e.changeId);
+      assertThat(a.commit.commit).named("commit of " + name)
+          .isEqualTo(e.commit.commit);
+      // Don't bother checking _changeNumber; assume changeId is sufficient.
+      assertThat(a._revisionNumber).named("revision of " + name)
+          .isEqualTo(e._revisionNumber);
+      assertThat(a._currentRevisionNumber).named("current revision of " + name)
+          .isEqualTo(e._currentRevisionNumber);
+    }
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
index 77a338e..045591c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -16,10 +16,7 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Account.FieldName;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme;
 import com.google.gerrit.reviewdb.client.AuthType;
-import com.google.gerrit.reviewdb.client.Project;
 
 import java.util.List;
 import java.util.Set;
@@ -36,16 +33,11 @@
   protected boolean httpPasswordSettingsEnabled = true;
 
   protected GitwebConfig gitweb;
-  protected boolean useContributorAgreements;
-  protected boolean useContactInfo;
   protected AuthType authType;
-  protected Set<DownloadScheme> downloadSchemes;
-  protected Set<DownloadCommand> downloadCommands;
   protected String gitDaemonUrl;
   protected String gitHttpUrl;
   protected String sshdAddress;
   protected String editFullNameUrl;
-  protected Project.NameKey wildProject;
   protected Set<Account.FieldName> editableAccountFields;
   protected boolean documentationAvailable;
   protected String anonymousCowardName;
@@ -137,30 +129,10 @@
     httpPasswordUrl = url;
   }
 
-  public AuthType getAuthType() {
-    return authType;
-  }
-
   public void setAuthType(final AuthType t) {
     authType = t;
   }
 
-  public Set<DownloadScheme> getDownloadSchemes() {
-    return downloadSchemes;
-  }
-
-  public void setDownloadSchemes(final Set<DownloadScheme> s) {
-    downloadSchemes = s;
-  }
-
-  public Set<DownloadCommand> getDownloadCommands() {
-    return downloadCommands;
-  }
-
-  public void setDownloadCommands(final Set<DownloadCommand> downloadCommands) {
-    this.downloadCommands = downloadCommands;
-  }
-
   public GitwebConfig getGitwebLink() {
     return gitweb;
   }
@@ -169,22 +141,6 @@
     gitweb = w;
   }
 
-  public boolean isUseContributorAgreements() {
-    return useContributorAgreements;
-  }
-
-  public void setUseContributorAgreements(final boolean r) {
-    useContributorAgreements = r;
-  }
-
-  public boolean isUseContactInfo() {
-    return useContactInfo;
-  }
-
-  public void setUseContactInfo(final boolean r) {
-    useContactInfo = r;
-  }
-
   public String getGitDaemonUrl() {
     return gitDaemonUrl;
   }
@@ -215,22 +171,6 @@
     sshdAddress = addr;
   }
 
-  public Project.NameKey getWildProject() {
-    return wildProject;
-  }
-
-  public void setWildProject(final Project.NameKey wp) {
-    wildProject = wp;
-  }
-
-  public boolean canEdit(final Account.FieldName f) {
-    return editableAccountFields.contains(f);
-  }
-
-  public Set<Account.FieldName> getEditableAccountFields() {
-    return editableAccountFields;
-  }
-
   public void setEditableAccountFields(final Set<Account.FieldName> af) {
     editableAccountFields = af;
   }
@@ -260,9 +200,9 @@
   }
 
   public boolean siteHasUsernames() {
-    if (getAuthType() == AuthType.CUSTOM_EXTENSION
+    if (authType == AuthType.CUSTOM_EXTENSION
         && getHttpPasswordUrl() != null
-        && !canEdit(FieldName.USER_NAME)) {
+        && !editableAccountFields.contains(FieldName.USER_NAME)) {
       return false;
     }
     return true;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
index dc22fb0..2d2e4e9 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
@@ -71,7 +71,7 @@
     KEEP,
 
     /** Publish pending drafts on all revisions. */
-    PUBLISH_ALL_REVISIONS;
+    PUBLISH_ALL_REVISIONS
   }
 
   public static enum NotifyHandling {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
index 3e7a207..3d1e3bd 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
@@ -50,6 +50,9 @@
   Map<String, List<CommentInfo>> comments() throws RestApiException;
   Map<String, List<CommentInfo>> drafts() throws RestApiException;
 
+  List<CommentInfo> commentsAsList() throws RestApiException;
+  List<CommentInfo> draftsAsList() throws RestApiException;
+
   DraftApi createDraft(DraftInput in) throws RestApiException;
   DraftApi draft(String id) throws RestApiException;
 
@@ -148,6 +151,16 @@
     }
 
     @Override
+    public List<CommentInfo> commentsAsList() throws RestApiException {
+      throw new NotImplementedException();
+    }
+
+    @Override
+    public List<CommentInfo> draftsAsList() throws RestApiException {
+      throw new NotImplementedException();
+    }
+
+    @Override
     public Map<String, List<CommentInfo>> drafts() throws RestApiException {
       throw new NotImplementedException();
     }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
index 7a626f1..0e848b9 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/projects/Projects.java
@@ -62,7 +62,7 @@
 
   public abstract class ListRequest {
     public static enum FilterType {
-      CODE, PARENT_CANDIDATES, PERMISSIONS, ALL;
+      CODE, PARENT_CANDIDATES, PERMISSIONS, ALL
     }
 
     private final List<String> branches = new ArrayList<>();
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java
index 12a741f..5caa903 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.extensions.client;
 
 import java.util.EnumSet;
+import java.util.Set;
 
 /** Output options available for retrieval change details. */
 public enum ListChangesOption {
@@ -90,7 +91,7 @@
     return r;
   }
 
-  public static int toBits(EnumSet<ListChangesOption> set) {
+  public static int toBits(Set<ListChangesOption> set) {
     int r = 0;
     for (ListChangesOption o : set) {
       r |= 1 << o.value;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java
index d55580c..d26ea23 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeType.java
@@ -32,5 +32,5 @@
   COPIED,
 
   /** Sufficient amount of content changed to claim the file was rewritten. */
-  REWRITE;
+  REWRITE
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java
index a117d07..d04b346 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ProblemInfo.java
@@ -16,7 +16,7 @@
 
 public class ProblemInfo {
   public static enum Status {
-    FIXED, FIX_FAILED;
+    FIXED, FIX_FAILED
   }
 
   public String message;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GarbageCollectorListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GarbageCollectorListener.java
new file mode 100644
index 0000000..eb223c6
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GarbageCollectorListener.java
@@ -0,0 +1,40 @@
+// 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.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+import java.util.Properties;
+
+/**
+ * Notified whenever the garbage collector has run successfully on a project.
+ */
+@ExtensionPoint
+public interface GarbageCollectorListener {
+  public interface Event {
+    /** @return The name of the project that has been garbage collected. */
+    String getProjectName();
+
+    /**
+     * Properties describing the result of the garbage collection performed by
+     * JGit
+     *
+     * @see <a href="http://download.eclipse.org/jgit/site/3.7.0.201502260915-r/apidocs/org/eclipse/jgit/api/GarbageCollectCommand.html#call%28%29">GarbageCollectCommand</a>
+     */
+    Properties getStatistics();
+  }
+
+  void onGarbageCollected(Event event);
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java
index 053999a..7f17f4f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java
@@ -72,7 +72,7 @@
     } else if (isGerritServer(account)) {
       setVisible(true);
       setResource(Gerrit.RESOURCES.gerritAvatar26());
-    } else if (account.has_avatar_info()) {
+    } else if (account.hasAvatarInfo()) {
       setVisible(false);
       AvatarInfo info = account.avatar(size);
       if (info != null) {
@@ -121,7 +121,7 @@
   }
 
   private static boolean isGerritServer(AccountInfo account) {
-    return account._account_id() == 0
+    return account._accountId() == 0
         && Util.C.messageNoAuthor().equals(account.name());
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 000f9e6..e43139c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -695,7 +695,7 @@
         }
 
         if (matchExact(SETTINGS_AGREEMENTS, token)
-            && Gerrit.getConfig().isUseContributorAgreements()) {
+            && Gerrit.info().auth().useContributorAgreements()) {
           return new MyAgreementsScreen();
         }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
index 5b51b48..30980e1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -144,8 +144,8 @@
     StringBuilder b = new StringBuilder().append(name);
     if (info.email() != null) {
       b.append(" <").append(info.email()).append(">");
-    } else if (info._account_id() > 0) {
-      b.append(" (").append(info._account_id()).append(")");
+    } else if (info._accountId() > 0) {
+      b.append(" (").append(info._accountId()).append(")");
     }
     return b.toString();
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 65e2858..c176a09 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.client.changes.ChangeConstants;
 import com.google.gerrit.client.changes.ChangeListScreen;
 import com.google.gerrit.client.config.ConfigServerApi;
+import com.google.gerrit.client.config.ServerInfo;
 import com.google.gerrit.client.extensions.TopMenu;
 import com.google.gerrit.client.extensions.TopMenuItem;
 import com.google.gerrit.client.extensions.TopMenuList;
@@ -49,7 +50,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountDiffPreference;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
-import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.aria.client.Roles;
 import com.google.gwt.core.client.EntryPoint;
@@ -106,6 +106,7 @@
 
   private static String myHost;
   private static GerritConfig myConfig;
+  private static ServerInfo myServerInfo;
   private static HostPageData.Theme myTheme;
   private static Account myAccount;
   private static String defaultScreenToken;
@@ -288,6 +289,11 @@
     return myConfig;
   }
 
+  /** Get the public configuration data used by this Gerrit instance. */
+  public static ServerInfo info() {
+    return myServerInfo;
+  }
+
   public static GitwebLink getGitwebLink() {
     GitwebConfig gw = getConfig().getGitwebLink();
     return gw != null && gw.type != null ? new GitwebLink(gw) : null;
@@ -426,8 +432,16 @@
     initHostname();
     Window.setTitle(M.windowTitle1(myHost));
 
-    final HostPageDataService hpd = GWT.create(HostPageDataService.class);
-    hpd.load(new GerritCallback<HostPageData>() {
+    RpcStatus.INSTANCE = new RpcStatus();
+    CallbackGroup cbg = new CallbackGroup();
+    ConfigServerApi.serverInfo(cbg.add(new GerritCallback<ServerInfo>() {
+      @Override
+      public void onSuccess(ServerInfo info) {
+        myServerInfo = info;
+      }
+    }));
+    HostPageDataService hpd = GWT.create(HostPageDataService.class);
+    hpd.load(cbg.addFinal(new GerritCallback<HostPageData>() {
       @Override
       public void onSuccess(final HostPageData result) {
         Document.get().getElementById("gerrit_hostpagedata").removeFromParent();
@@ -444,7 +458,7 @@
         }
         onModuleLoad2(result);
       }
-    });
+    }));
   }
 
   private static void initHostname() {
@@ -538,7 +552,6 @@
     };
     gBody.add(body);
 
-    RpcStatus.INSTANCE = new RpcStatus();
     JsonUtil.addRpcStartHandler(RpcStatus.INSTANCE);
     JsonUtil.addRpcCompleteHandler(RpcStatus.INSTANCE);
     JsonUtil.setDefaultXsrfManager(new XsrfManager() {
@@ -705,9 +718,9 @@
     }
 
     if (signedIn) {
-      whoAmI(cfg.getAuthType() != AuthType.CLIENT_SSL_CERT_LDAP);
+      whoAmI(!info().auth().isClientSslCertLdap());
     } else {
-      switch (cfg.getAuthType()) {
+      switch (info().auth().authType()) {
         case CLIENT_SSL_CERT_LDAP:
           break;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
index 90348db..c8bb731 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.client.account.AccountInfo;
 import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.AnchorElement;
 import com.google.gwt.dom.client.Element;
@@ -53,8 +52,8 @@
     if (showSettingsLink) {
       if (Gerrit.getConfig().getSwitchAccountUrl() != null) {
         switchAccount.setHref(Gerrit.getConfig().getSwitchAccountUrl());
-      } else if (Gerrit.getConfig().getAuthType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT
-          || Gerrit.getConfig().getAuthType() == AuthType.OPENID) {
+      } else if (Gerrit.info().auth().isDev()
+          || Gerrit.info().auth().isOpenId()) {
         switchAccount.setHref(Gerrit.selfRedirect("/login/"));
       } else {
         switchAccount.removeFromParent();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
index 1127374..36fa98d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountInfo.java
@@ -18,7 +18,7 @@
 import com.google.gwt.core.client.JsArray;
 
 public class AccountInfo extends JavaScriptObject {
-  public final native int _account_id() /*-{ return this._account_id || 0; }-*/;
+  public final native int _accountId() /*-{ return this._account_id || 0; }-*/;
   public final native String name() /*-{ return this.name; }-*/;
   public final native String email() /*-{ return this.email; }-*/;
   public final native String username() /*-{ return this.username; }-*/;
@@ -29,7 +29,7 @@
    *         available, such as when no plugin is installed. This method returns
    *         false if the server did not check on avatars for the account.
    */
-  public final native boolean has_avatar_info()
+  public final native boolean hasAvatarInfo()
   /*-{ return this.hasOwnProperty('avatars') }-*/;
 
   public final AvatarInfo avatar(int sz) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
index 4b8f0e2..33d11a8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
@@ -65,7 +65,7 @@
     hasContact.setStyleName(Gerrit.RESOURCES.css().accountContactOnFile());
     hasContact.setVisible(false);
 
-    if (Gerrit.getConfig().isUseContactInfo()) {
+    if (Gerrit.info().hasContactStore()) {
       body.add(privhtml);
       body.add(hasContact);
       body.add(infoSecure);
@@ -116,7 +116,7 @@
   @Override
   ContactInformation toContactInformation() {
     final ContactInformation info;
-    if (Gerrit.getConfig().isUseContactInfo()) {
+    if (Gerrit.info().hasContactStore()) {
       info = new ContactInformation();
       info.setAddress(addressTxt.getText());
       info.setCountry(countryTxt.getText());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
index 405adcf..2d9aa73 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Account.FieldName;
-import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.client.ContactInformation;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.event.dom.client.ChangeEvent;
@@ -101,7 +100,7 @@
     }
 
     int row = 0;
-    if (!Gerrit.getConfig().canEdit(FieldName.USER_NAME)
+    if (!Gerrit.info().auth().canEdit(FieldName.USER_NAME)
         && Gerrit.getConfig().siteHasUsernames()) {
       infoPlainText.resizeRows(infoPlainText.getRowCount() + 1);
       row(infoPlainText, row++, Util.C.userName(), new UsernameField());
@@ -168,11 +167,11 @@
   }
 
   private boolean canEditFullName() {
-    return Gerrit.getConfig().canEdit(Account.FieldName.FULL_NAME);
+    return Gerrit.info().auth().canEdit(Account.FieldName.FULL_NAME);
   }
 
   private boolean canRegisterNewEmail() {
-    return Gerrit.getConfig().canEdit(Account.FieldName.REGISTER_NEW_EMAIL);
+    return Gerrit.info().auth().canEdit(Account.FieldName.REGISTER_NEW_EMAIL);
   }
 
   void hideSaveButton() {
@@ -275,7 +274,7 @@
           @Override
           public void onSuccess(EmailInfo result) {
             box.hide();
-            if (Gerrit.getConfig().getAuthType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT) {
+            if (Gerrit.info().auth().isDev()) {
               currentEmail = addr;
               if (emailPick.getItemCount() == 0) {
                 final Account me = Gerrit.getUserAccount();
@@ -325,7 +324,7 @@
     buttons.add(register);
     buttons.add(cancel);
 
-    if (Gerrit.getConfig().getAuthType() != AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT) {
+    if (!Gerrit.info().auth().isDev()) {
       body.add(new HTML(Util.C.descRegisterNewEmail()));
     }
     body.add(inEmail);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
index af70fac..b638575 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.client.ui.FancyFlexTable;
 import com.google.gerrit.common.auth.openid.OpenIdUrls;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
@@ -59,7 +58,8 @@
     });
     add(deleteIdentity);
 
-    if (Gerrit.getConfig().getAuthType() == AuthType.OPENID) {
+    if (Gerrit.info().auth().isOpenId()
+        || Gerrit.info().auth().isOAuth()) {
       Button linkIdentity = new Button(Util.C.buttonLinkIdentity());
       linkIdentity.addClickHandler(new ClickHandler() {
         @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
index 2810931..5f1e383 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
@@ -70,7 +70,7 @@
     formBody.add(contactGroup);
 
     if (Gerrit.getUserAccount().getUserName() == null
-        && Gerrit.getConfig().canEdit(FieldName.USER_NAME)) {
+        && Gerrit.info().auth().canEdit(FieldName.USER_NAME)) {
       final FlowPanel fp = new FlowPanel();
       fp.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
       fp.add(new SmallHeading(Util.C.welcomeUsernameHeading()));
@@ -116,7 +116,7 @@
 
     final FlowPanel choices = new FlowPanel();
     choices.setStyleName(Gerrit.RESOURCES.css().registerScreenNextLinks());
-    if (Gerrit.getConfig().isUseContributorAgreements()) {
+    if (Gerrit.info().auth().useContributorAgreements()) {
       final FlowPanel agreementGroup = new FlowPanel();
       agreementGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
       agreementGroup.add(new SmallHeading(Util.C.welcomeAgreementHeading()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
index c689b49..ca4ac20 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
@@ -34,7 +34,7 @@
     }
     link(Util.C.tabWebIdentities(), PageLinks.SETTINGS_WEBIDENT);
     link(Util.C.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS);
-    if (Gerrit.getConfig().isUseContributorAgreements()) {
+    if (Gerrit.info().auth().useContributorAgreements()) {
       link(Util.C.tabAgreements(), PageLinks.SETTINGS_AGREEMENTS);
     }
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
index 2077d6b..e440d55 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
@@ -86,7 +86,7 @@
   }
 
   private boolean canEditUserName() {
-    return Gerrit.getConfig().canEdit(Account.FieldName.USER_NAME);
+    return Gerrit.info().auth().canEdit(Account.FieldName.USER_NAME);
   }
 
   private void confirmSetUserName() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
index 157748f..aa72300 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -250,8 +250,7 @@
     if (value.getPermission(permissionName) != null) {
       return;
     }
-    if (Gerrit.getConfig().getWildProject()
-        .equals(projectAccess.getProjectName())
+    if (Gerrit.info().gerrit().isAllProjects(projectAccess.getProjectName())
         && !Permission.canBeOnAllProjects(value.getName(), permissionName)) {
       return;
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
index d3f659e..1e3f918 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
@@ -248,7 +248,7 @@
       for (int row = 1; row < table.getRowCount(); row++) {
         final AccountInfo i = getRowItem(row);
         if (i != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
-          ids.add(i._account_id());
+          ids.add(i._accountId());
         }
       }
       if (!ids.isEmpty()) {
@@ -258,7 +258,7 @@
               public void onSuccess(final VoidResult result) {
                 for (int row = 1; row < table.getRowCount();) {
                   final AccountInfo i = getRowItem(row);
-                  if (i != null && ids.contains(i._account_id())) {
+                  if (i != null && ids.contains(i._accountId())) {
                     table.removeRow(row);
                   } else {
                     row++;
@@ -296,7 +296,7 @@
             return cmp;
           }
 
-          return a._account_id() - b._account_id();
+          return a._accountId() - b._accountId();
         }
 
         public String nullToEmpty(String str) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
index 1ffd6f0..9092508 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
@@ -44,7 +44,7 @@
             public void onSuccess(ChangeInfo result) {
               sent = true;
               hide();
-              Gerrit.display(PageLinks.toChange(result.legacy_id()));
+              Gerrit.display(PageLinks.toChange(result.legacyId()));
             }
 
             @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java
index 86a31ee..0fac957 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java
@@ -32,7 +32,7 @@
           @Override
           public void onSuccess(ChangeInfo result) {
             Gerrit.display(Dispatcher.toEditScreen(
-                new PatchSet.Id(result.legacy_id(), 1), "project.config"));
+                new PatchSet.Id(result.legacyId(), 1), "project.config"));
           }
 
           @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
index a7799ab..a8dd9c5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
@@ -485,8 +485,8 @@
         actionsPanel.add(new Anchor(c.getLinkName(), false,
             c.toBranch(new Branch.NameKey(getProjectKey(), k.ref()))));
       }
-      if (k.web_links() != null) {
-        for (WebLinkInfo webLink : Natives.asList(k.web_links())) {
+      if (k.webLinks() != null) {
+        for (WebLinkInfo webLink : Natives.asList(k.webLinks())) {
           actionsPanel.add(webLink.toAnchor());
         }
       }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
index 4c82ff3..08ab2c0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -38,7 +38,6 @@
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.event.dom.client.ChangeEvent;
 import com.google.gwt.event.dom.client.ChangeHandler;
@@ -265,7 +264,7 @@
     grid.addHeader(new SmallHeading(Util.C.headingAgreements()));
 
     contributorAgreements = newInheritedBooleanBox();
-    if (Gerrit.getConfig().isUseContributorAgreements()) {
+    if (Gerrit.info().auth().useContributorAgreements()) {
       saveEnabler.listenTo(contributorAgreements);
       grid.add(Util.C.useContributorAgreements(), contributorAgreements);
     }
@@ -306,12 +305,12 @@
       if (box.getValue(i).startsWith(InheritableBoolean.INHERIT.name())) {
         inheritedIndex = i;
       }
-      if (box.getValue(i).startsWith(inheritedBoolean.configured_value().name())) {
+      if (box.getValue(i).startsWith(inheritedBoolean.configuredValue().name())) {
         box.setSelectedIndex(i);
       }
     }
     if (inheritedIndex >= 0) {
-      if (getProjectKey().equals(Gerrit.getConfig().getWildProject())) {
+      if (Gerrit.info().gerrit().isAllProjects(getProjectKey())) {
         if (box.getSelectedIndex() == inheritedIndex) {
           for (int i = 0; i < box.getItemCount(); i++) {
             if (box.getValue(i).equals(InheritableBoolean.FALSE.name())) {
@@ -323,7 +322,7 @@
         box.removeItem(inheritedIndex);
       } else {
         box.setItemText(inheritedIndex, InheritableBoolean.INHERIT.name() + " ("
-            + inheritedBoolean.inherited_value() + ")");
+            + inheritedBoolean.inheritedValue() + ")");
       }
     }
   }
@@ -342,20 +341,20 @@
 
   void display(ConfigInfo result) {
     descTxt.setText(result.description());
-    setBool(contributorAgreements, result.use_contributor_agreements());
-    setBool(signedOffBy, result.use_signed_off_by());
-    setBool(contentMerge, result.use_content_merge());
-    setBool(newChangeForAllNotInTarget, result.create_new_change_for_all_not_in_target());
-    setBool(requireChangeID, result.require_change_id());
-    setSubmitType(result.submit_type());
+    setBool(contributorAgreements, result.useContributorAgreements());
+    setBool(signedOffBy, result.useSignedOffBy());
+    setBool(contentMerge, result.useContentMerge());
+    setBool(newChangeForAllNotInTarget, result.createNewChangeForAllNotInTarget());
+    setBool(requireChangeID, result.requireChangeId());
+    setSubmitType(result.submitType());
     setState(result.state());
-    maxObjectSizeLimit.setText(result.max_object_size_limit().configured_value());
-    if (result.max_object_size_limit().inherited_value() != null) {
+    maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue());
+    if (result.maxObjectSizeLimit().inheritedValue() != null) {
       effectiveMaxObjectSizeLimit.setVisible(true);
       effectiveMaxObjectSizeLimit.setText(
-          Util.M.effectiveMaxObjectSizeLimit(result.max_object_size_limit().value()));
+          Util.M.effectiveMaxObjectSizeLimit(result.maxObjectSizeLimit().value()));
       effectiveMaxObjectSizeLimit.setTitle(
-          Util.M.globalMaxObjectSizeLimit(result.max_object_size_limit().inherited_value()));
+          Util.M.globalMaxObjectSizeLimit(result.maxObjectSizeLimit().inheritedValue()));
     } else {
       effectiveMaxObjectSizeLimit.setVisible(false);
     }
@@ -674,19 +673,16 @@
 
   public class ProjectDownloadPanel extends DownloadPanel {
     public ProjectDownloadPanel(String project, boolean isAllowsAnonymous) {
-      super(project, null, isAllowsAnonymous);
+      super(project, isAllowsAnonymous);
     }
 
     @Override
     public void populateDownloadCommandLinks() {
       if (!urls.isEmpty()) {
-        if (allowedCommands.contains(DownloadCommand.CHECKOUT)
-            || allowedCommands.contains(DownloadCommand.DEFAULT_DOWNLOADS)) {
-          commands.add(cmdLinkfactory.new CloneCommandLink());
-          if (Gerrit.getConfig().getSshdAddress() != null && hasUserName()) {
-            commands.add(
-                cmdLinkfactory.new CloneWithCommitMsgHookCommandLink(getProjectKey()));
-          }
+        commands.add(cmdLinkfactory.new CloneCommandLink());
+        if (Gerrit.getConfig().getSshdAddress() != null && hasUserName()) {
+          commands.add(
+              cmdLinkfactory.new CloneWithCommitMsgHookCommandLink(getProjectKey()));
         }
       }
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index 828352c..0dff684 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -186,7 +186,7 @@
 
       private void addWebLinks(int row, ProjectInfo k) {
         GitwebLink gitWebLink = Gerrit.getGitwebLink();
-        List<WebLinkInfo> webLinks = Natives.asList(k.web_links());
+        List<WebLinkInfo> webLinks = Natives.asList(k.webLinks());
         if (gitWebLink != null || (webLinks != null && !webLinks.isEmpty())) {
           FlowPanel p = new FlowPanel();
           table.setWidget(row, ProjectsTable.C_REPO_BROWSER, p);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java
index a5243ae..e831e8b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java
@@ -42,7 +42,7 @@
       ChangeInfo change,
       ActionInfo action,
       ActionButton button) {
-    RestApi api = ChangeApi.change(change.legacy_id().get()).view(action.id());
+    RestApi api = ChangeApi.change(change.legacyId().get()).view(action.id());
     JavaScriptObject f = get(action.id());
     if (f != null) {
       ActionContext c = ActionContext.create(api);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
index 0e4048d..c092375 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
@@ -29,7 +29,7 @@
 
 class DefaultActions {
   static void invoke(ChangeInfo change, ActionInfo action, RestApi api) {
-    invoke(action, api, callback(PageLinks.toChange(change.legacy_id())));
+    invoke(action, api, callback(PageLinks.toChange(change.legacyId())));
   }
 
   static void invoke(Project.NameKey project, ActionInfo action, RestApi api) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
index ebcafb8..f968fd2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
@@ -29,7 +29,7 @@
       ActionInfo action,
       ActionButton button) {
     RestApi api = ChangeApi.edit(
-          change.legacy_id().get())
+          change.legacyId().get())
       .view(action.id());
 
     JavaScriptObject f = get(action.id());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java
index fb489cc..d708e8c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java
@@ -29,7 +29,7 @@
       ActionInfo action,
       ActionButton button) {
     RestApi api = ChangeApi.revision(
-          change.legacy_id().get(),
+          change.legacyId().get(),
           revision.name())
       .view(action.id());
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
index dca7d2f..10d77cb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
@@ -65,6 +65,8 @@
   private String branch;
   private String key;
 
+  private boolean rebaseParentNotCurrent = true;
+
   Actions() {
     initWidget(uiBinder.createAndBindUi(this));
     getElement().setId("change_actions");
@@ -76,17 +78,17 @@
     boolean hasUser = Gerrit.isSignedIn();
     RevisionInfo revInfo = info.revision(revision);
     CommitInfo commit = revInfo.commit();
-    changeId = info.legacy_id();
+    changeId = info.legacyId();
     project = info.project();
     subject = commit.subject();
     message = commit.message();
     branch = info.branch();
-    key = info.change_id();
+    key = info.changeId();
     changeInfo = info;
 
     initChangeActions(info, hasUser);
 
-    NativeMap<ActionInfo> actionMap = revInfo.has_actions()
+    NativeMap<ActionInfo> actionMap = revInfo.hasActions()
         ? revInfo.actions()
         : NativeMap.<ActionInfo> create();
     actionMap.copyKeysIntoChildren("id");
@@ -94,7 +96,7 @@
   }
 
   private void initChangeActions(ChangeInfo info, boolean hasUser) {
-    NativeMap<ActionInfo> actions = info.has_actions()
+    NativeMap<ActionInfo> actions = info.hasActions()
         ? info.actions()
         : NativeMap.<ActionInfo> create();
     actions.copyKeysIntoChildren("id");
@@ -129,8 +131,11 @@
 
     a2b(actions, "cherrypick", cherrypick);
     a2b(actions, "rebase", rebase);
+
+    // The rebase button on change screen is always enabled.
+    // It is the "Rebase" button in the RebaseDialog that might be disabled.
+    rebaseParentNotCurrent = rebase.isEnabled();
     if (rebase.isVisible()) {
-      // it is the rebase button in RebaseDialog that the server wants to disable
       rebase.setEnabled(true);
     }
     RevisionInfo revInfo = changeInfo.revision(revision);
@@ -178,16 +183,8 @@
 
   @UiHandler("rebase")
   void onRebase(@SuppressWarnings("unused") ClickEvent e) {
-    boolean enabled = true;
-    RevisionInfo revInfo = changeInfo.revision(revision);
-    if (revInfo.has_actions()) {
-        NativeMap<ActionInfo> actions = revInfo.actions();
-        if (actions.containsKey("rebase")) {
-          enabled = actions.get("rebase").enabled();
-        }
-    }
     RebaseAction.call(rebase, project, changeInfo.branch(), changeId, revision,
-        enabled);
+        rebaseParentNotCurrent);
   }
 
   @UiHandler("submit")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
index 9545b84..d97634f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
@@ -296,7 +296,7 @@
   }
 
   private void initReplyButton(ChangeInfo info, String revision) {
-    if (!info.revision(revision).is_edit()) {
+    if (!info.revision(revision).isEdit()) {
       reply.setTitle(Gerrit.getConfig().getReplyTitle());
       reply.setHTML(new SafeHtmlBuilder()
         .openDiv()
@@ -310,7 +310,7 @@
   }
 
   private void gotoSibling(final int offset) {
-    if (offset > 0 && changeInfo.current_revision().equals(revision)) {
+    if (offset > 0 && changeInfo.currentRevision().equals(revision)) {
       return;
     }
 
@@ -324,7 +324,7 @@
       if (revision.equals(revisions.get(i).name())) {
         if (0 <= i + offset && i + offset < revisions.length()) {
           Gerrit.display(PageLinks.toChange(
-              new PatchSet.Id(changeInfo.legacy_id(),
+              new PatchSet.Id(changeInfo.legacyId(),
               revisions.get(i + offset)._number())));
           return;
         }
@@ -336,7 +336,7 @@
   private void initIncludedInAction(ChangeInfo info) {
     if (info.status() == Status.MERGED) {
       includedInAction = new IncludedInAction(
-          info.legacy_id(),
+          info.legacyId(),
           style, headerLine, includedIn);
       includedIn.setVisible(true);
     }
@@ -344,7 +344,7 @@
 
   private void initChangeAction(ChangeInfo info) {
     if (info.status() == Status.DRAFT) {
-      NativeMap<ActionInfo> actions = info.has_actions()
+      NativeMap<ActionInfo> actions = info.hasActions()
           ? info.actions()
           : NativeMap.<ActionInfo> create();
       actions.copyKeysIntoChildren("id");
@@ -358,9 +358,9 @@
   private void initRevisionsAction(ChangeInfo info, String revision,
       NativeMap<ActionInfo> actions) {
     int currentPatchSet;
-    if (info.current_revision() != null
-        && info.revisions().containsKey(info.current_revision())) {
-      currentPatchSet = info.revision(info.current_revision())._number();
+    if (info.currentRevision() != null
+        && info.revisions().containsKey(info.currentRevision())) {
+      currentPatchSet = info.revision(info.currentRevision())._number();
     } else {
       JsArray<RevisionInfo> revList = info.revisions().values();
       RevisionInfo.sortRevisionInfoByNumber(revList);
@@ -379,7 +379,7 @@
     patchSetsText.setInnerText(Resources.M.patchSets(
         currentlyViewedPatchSet, currentPatchSet));
     patchSetsAction = new PatchSetsAction(
-        info.legacy_id(), revision,
+        info.legacyId(), revision,
         style, headerLine, patchSets);
 
     RevisionInfo revInfo = info.revision(revision);
@@ -402,20 +402,20 @@
 
   private void initProjectLinks(final ChangeInfo info) {
     projectSettingsLink.setHref(
-        "#" + PageLinks.toProject(info.project_name_key()));
+        "#" + PageLinks.toProject(info.projectNameKey()));
     projectSettings.addDomHandler(new ClickHandler() {
       @Override
       public void onClick(ClickEvent event) {
         if (Hyperlink.impl.handleAsClick((Event) event.getNativeEvent())) {
           event.stopPropagation();
           event.preventDefault();
-          Gerrit.display(PageLinks.toProject(info.project_name_key()));
+          Gerrit.display(PageLinks.toProject(info.projectNameKey()));
         }
       }
     }, ClickEvent.getType());
     projectDashboard.setText(info.project());
     projectDashboard.setTargetHistoryToken(
-        PageLinks.toProjectDefaultDashboard(info.project_name_key()));
+        PageLinks.toProjectDefaultDashboard(info.projectNameKey()));
   }
 
   private void initBranchLink(ChangeInfo info) {
@@ -423,7 +423,7 @@
     branchLink.setTargetHistoryToken(
         PageLinks.toChangeQuery(
             BranchLink.query(
-                info.project_name_key(),
+                info.projectNameKey(),
                 info.status(),
                 info.branch(),
                 null)));
@@ -453,7 +453,7 @@
         reviewMode.setVisible(false);
       }
 
-      if (rev.is_edit()) {
+      if (rev.isEdit()) {
         if (info.hasEditBasedOnCurrentPatchSet()) {
           publishEdit.setVisible(true);
         } else {
@@ -465,11 +465,11 @@
   }
 
   private boolean isEditModeEnabled(ChangeInfo info, RevisionInfo rev) {
-    if (rev.is_edit()) {
+    if (rev.isEdit()) {
       return true;
     }
     if (edit == null) {
-      return revision.equals(info.current_revision());
+      return revision.equals(info.currentRevision());
     }
     return rev._number() == RevisionInfo.findEditParent(
         info.revisions().values());
@@ -781,9 +781,9 @@
   private void loadConfigInfo(final ChangeInfo info, final String base) {
     info.revisions().copyKeysIntoChildren("name");
     if (edit != null) {
-      edit.set_name(edit.commit().commit());
-      info.set_edit(edit);
-      if (edit.has_files()) {
+      edit.setName(edit.commit().commit());
+      info.setEdit(edit);
+      if (edit.hasFiles()) {
         edit.files().copyKeysIntoChildren("path");
       }
       info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
@@ -801,14 +801,14 @@
       if (revision == null) {
         RevisionInfo.sortRevisionInfoByNumber(list);
         RevisionInfo rev = list.get(list.length() - 1);
-        if (rev.is_edit()) {
-          info.set_current_revision(rev.name());
+        if (rev.isEdit()) {
+          info.setCurrentRevision(rev.name());
         }
       } else if (revision.equals("edit") || revision.equals("0")) {
         for (int i = 0; i < list.length(); i++) {
           RevisionInfo r = list.get(i);
-          if (r.is_edit()) {
-            info.set_current_revision(r.name());
+          if (r.isEdit()) {
+            info.setCurrentRevision(r.name());
             break;
           }
         }
@@ -819,7 +819,7 @@
 
     CallbackGroup group = new CallbackGroup();
     Timestamp lastReply = myLastReply(info);
-    if (rev.is_edit()) {
+    if (rev.isEdit()) {
       loadFileList(b, rev, lastReply, group, null, null);
     } else {
       loadDiff(b, rev, lastReply, group);
@@ -833,7 +833,7 @@
 
     RevisionInfoCache.add(changeId, rev);
     ConfigInfoCache.add(info);
-    ConfigInfoCache.get(info.project_name_key(),
+    ConfigInfoCache.get(info.projectNameKey(),
       group.addFinal(new ScreenLoadCallback<ConfigInfoCache.Entry>(this) {
         @Override
         protected void preDisplay(Entry result) {
@@ -848,10 +848,10 @@
 
   static Timestamp myLastReply(ChangeInfo info) {
     if (Gerrit.isSignedIn() && info.messages() != null) {
-      int self = Gerrit.getUserAccountInfo()._account_id();
+      int self = Gerrit.getUserAccountInfo()._accountId();
       for (int i = info.messages().length() - 1; i >= 0; i--) {
         MessageInfo m = info.messages().get(i);
-        if (m.author() != null && m.author()._account_id() == self) {
+        if (m.author() != null && m.author()._accountId() == self) {
           return m.date();
         }
       }
@@ -938,7 +938,7 @@
       JsArray<CommentInfo> thisRevision = JsArray.createArray().cast();
       for (int i = 0; i < allRevisions.length(); i++) {
         CommentInfo c = allRevisions.get(i);
-        if (c.patch_set() == id) {
+        if (c.patchSet() == id) {
           thisRevision.push(c);
         }
       }
@@ -971,7 +971,7 @@
   }
 
   private void loadCommit(final RevisionInfo rev, CallbackGroup group) {
-    if (rev.is_edit()) {
+    if (rev.isEdit()) {
       return;
     }
 
@@ -979,7 +979,7 @@
         group.add(new AsyncCallback<CommitInfo>() {
           @Override
           public void onSuccess(CommitInfo info) {
-            rev.set_commit(info);
+            rev.setCommit(info);
           }
 
           @Override
@@ -1019,7 +1019,7 @@
 
   private RevisionInfo resolveRevisionToDisplay(ChangeInfo info) {
     RevisionInfo rev = resolveRevisionOrPatchSetId(info, revision,
-        info.current_revision());
+        info.currentRevision());
     if (rev != null) {
       revision = rev.name();
       return rev;
@@ -1036,7 +1036,7 @@
       return rev;
     } else {
       new ErrorDialog(
-          Resources.M.changeWithNoRevisions(info.legacy_id().get())).center();
+          Resources.M.changeWithNoRevisions(info.legacyId().get())).center();
       throw new IllegalStateException("no revision, cannot proceed");
     }
   }
@@ -1116,7 +1116,7 @@
 
     star.setValue(info.starred());
     permalink.setHref(ChangeLink.permalink(changeId));
-    permalink.setText(String.valueOf(info.legacy_id()));
+    permalink.setText(String.valueOf(info.legacyId()));
     topic.set(info, revision);
     commit.set(commentLinkProcessor, info, revision);
     related.set(info, revision);
@@ -1128,7 +1128,7 @@
     }
 
     StringBuilder sb = new StringBuilder();
-    sb.append(Util.M.changeScreenTitleId(info.id_abbreviated()));
+    sb.append(Util.M.changeScreenTitleId(info.idAbbreviated()));
     if (info.subject() != null) {
       sb.append(": ");
       sb.append(info.subject());
@@ -1138,8 +1138,8 @@
     // Although this is related to the revision, we can process it early to
     // render it faster.
     if (!info.status().isOpen()
-        || !revision.equals(info.current_revision())
-        || info.revision(revision).is_edit()) {
+        || !revision.equals(info.currentRevision())
+        || info.revision(revision).isEdit()) {
       setVisible(strategy, false);
     }
 
@@ -1151,10 +1151,10 @@
     actions.reloadRevisionActions(emptyMap);
 
     RevisionInfo revisionInfo = info.revision(revision);
-    boolean current = revision.equals(info.current_revision())
-        && !revisionInfo.is_edit();
+    boolean current = revision.equals(info.currentRevision())
+        && !revisionInfo.isEdit();
 
-    if (revisionInfo.is_edit()) {
+    if (revisionInfo.isEdit()) {
       statusText.setInnerText(Util.C.changeEdit());
     } else if (!current) {
       statusText.setInnerText(Util.C.notCurrent());
@@ -1180,6 +1180,8 @@
   private void renderRevisionInfo(ChangeInfo info,
       NativeMap<ActionInfo> actionMap) {
     initRevisionsAction(info, revision, actionMap);
+    commit.setParentNotCurrent(actionMap.containsKey("rebase")
+        && actionMap.get("rebase").enabled());
     actions.reloadRevisionActions(actionMap);
   }
 
@@ -1201,7 +1203,7 @@
         ? info.owner().name()
         : info.owner().email() != null
         ? info.owner().email()
-        : String.valueOf(info.owner()._account_id()), Change.Status.NEW));
+        : String.valueOf(info.owner()._accountId()), Change.Status.NEW));
   }
 
   private void renderSubmitType(String action) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
index bcd8f6b..357f04c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
@@ -47,7 +47,7 @@
 
       @Override
       public void onSend() {
-        ChangeApi.cherrypick(info.legacy_id().get(), revision,
+        ChangeApi.cherrypick(info.legacyId().get(), revision,
             getDestinationBranch(),
             getMessageText(),
             new GerritCallback<ChangeInfo>() {
@@ -55,7 +55,7 @@
               public void onSuccess(ChangeInfo result) {
                 sent = true;
                 hide();
-                Gerrit.display(PageLinks.toChange(result.legacy_id()));
+                Gerrit.display(PageLinks.toChange(result.legacyId()));
               }
 
               @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
index 8cf3a0c..f75f6a1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
@@ -20,12 +20,10 @@
 import com.google.gerrit.client.GitwebLink;
 import com.google.gerrit.client.WebLinkInfo;
 import com.google.gerrit.client.account.AccountInfo;
-import com.google.gerrit.client.actions.ActionInfo;
 import com.google.gerrit.client.changes.ChangeInfo;
 import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
 import com.google.gerrit.client.changes.ChangeInfo.GitPerson;
 import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.NativeMap;
 import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.client.ui.CommentLinkProcessor;
 import com.google.gerrit.client.ui.InlineHyperlink;
@@ -111,8 +109,8 @@
     CommitInfo commit = revInfo.commit();
 
     commitName.setText(revision);
-    idText.setText("Change-Id: " + change.change_id());
-    idText.setPreviewText(change.change_id());
+    idText.setText("Change-Id: " + change.changeId());
+    idText.setPreviewText(change.changeId());
 
     formatLink(commit.author(), authorPanel, authorNameEmail, authorDate,
         change);
@@ -127,15 +125,10 @@
     }
 
     setParents(change.project(), revInfo.commit().parents());
+  }
 
+  void setParentNotCurrent(boolean parentNotCurrent) {
     // display the orange ball if parent has moved on (not current)
-    boolean parentNotCurrent = false;
-    if (revInfo.has_actions()) {
-      NativeMap<ActionInfo> actions = revInfo.actions();
-      if (actions.containsKey("rebase")) {
-        parentNotCurrent = actions.get("rebase").enabled();
-      }
-    }
     UIObject.setVisible(parentNotCurrentText, parentNotCurrent);
     parentNotCurrentText.setInnerText(parentNotCurrent ? "\u25CF" : "");
   }
@@ -148,7 +141,7 @@
           gw.getLinkName());
     }
 
-    JsArray<WebLinkInfo> links = revInfo.commit().web_links();
+    JsArray<WebLinkInfo> links = revInfo.commit().webLinks();
     if (links != null) {
       for (WebLinkInfo link : Natives.asList(links)) {
         webLinkPanel.add(link.toAnchor());
@@ -198,7 +191,7 @@
       a.setStyleName(style.parentWebLink());
       panel.add(a);
     }
-    JsArray<WebLinkInfo> links = c.web_links();
+    JsArray<WebLinkInfo> links = c.webLinks();
     if (links != null) {
       for (WebLinkInfo link : Natives.asList(links)) {
         panel.add(link.toAnchor());
@@ -219,7 +212,7 @@
     // only try to fetch the avatar image for author and committer if an avatar
     // plugin is installed, if the change owner has no avatar info assume that
     // no avatar plugin is installed
-    if (change.owner().has_avatar_info()) {
+    if (change.owner().hasAvatarInfo()) {
       AvatarImage avatar;
       if (change.owner().email().equals(person.email())) {
         avatar = new AvatarImage(change.owner());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java
index b1bb4e0..34e67dd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java
@@ -30,7 +30,7 @@
       Widget downloadButton) {
     super(style, relativeTo, downloadButton);
     this.downloadBox = new DownloadBox(info, revision,
-        new PatchSet.Id(info.legacy_id(),
+        new PatchSet.Id(info.legacyId(),
             info.revision(revision)._number()));
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
index 49389f3..0303a88 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
@@ -80,7 +80,7 @@
   protected void onLoad() {
     if (fetch == null) {
       if (psId.get() == 0) {
-        ChangeApi.editWithCommands(change.legacy_id().get()).get(
+        ChangeApi.editWithCommands(change.legacyId().get()).get(
             new AsyncCallback<EditInfo>() {
           @Override
           public void onSuccess(EditInfo result) {
@@ -93,9 +93,9 @@
           }
         });
       } else {
-        RestApi call = ChangeApi.detail(change.legacy_id().get());
+        RestApi call = ChangeApi.detail(change.legacyId().get());
         ChangeList.addOptions(call, EnumSet.of(
-            revision.equals(change.current_revision())
+            revision.equals(change.currentRevision())
                ? ListChangesOption.CURRENT_REVISION
                : ListChangesOption.ALL_REVISIONS,
             ListChangesOption.DOWNLOAD_COMMANDS));
@@ -268,7 +268,7 @@
     if (scheme != null && scheme != pref.getDownloadUrl()) {
       pref.setDownloadUrl(scheme);
       PreferenceInput in = PreferenceInput.create();
-      in.download_scheme(scheme);
+      in.downloadScheme(scheme);
       AccountApi.self().view("preferences")
           .put(in, new AsyncCallback<JavaScriptObject>() {
             @Override
@@ -303,11 +303,11 @@
       return createObject().cast();
     }
 
-    final void download_scheme(DownloadScheme s) {
-      download_scheme0(s.name());
+    final void downloadScheme(DownloadScheme s) {
+      downloadScheme0(s.name());
     }
 
-    private final native void download_scheme0(String n) /*-{
+    private final native void downloadScheme0(String n) /*-{
       this.download_scheme = n;
     }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
index 2947be8..dd8df36 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
@@ -87,7 +87,7 @@
     String deltaColumn2();
     String inserted();
     String deleted();
-    String removeButton();
+    String restoreDelete();
   }
 
   public static enum Mode {
@@ -516,8 +516,8 @@
       for (int i = 0; i < list.length(); i++) {
         FileInfo info = list.get(i);
         if (!Patch.COMMIT_MSG.equals(info.path()) && !info.binary()) {
-          inserted += info.lines_inserted();
-          deleted += info.lines_deleted();
+          inserted += info.linesInserted();
+          deleted += info.linesDeleted();
         }
       }
     }
@@ -548,7 +548,7 @@
       if (mode == Mode.REVIEW) {
         sb.openTh().setStyleName(R.css().reviewed()).closeTh();
       } else {
-        sb.openTh().setStyleName(R.css().removeButton()).closeTh();
+        sb.openTh().setStyleName(R.css().restoreDelete()).closeTh();
       }
       sb.openTh().setStyleName(R.css().status()).closeTh();
       sb.openTh().append(Util.C.patchTableColumnName()).closeTh();
@@ -592,20 +592,26 @@
     }
 
     private void columnDeleteRestore(SafeHtmlBuilder sb, FileInfo info) {
-      sb.openTd().setStyleName(R.css().removeButton());
+      sb.openTd().setStyleName(R.css().restoreDelete());
       if (hasUser) {
         if (!Patch.COMMIT_MSG.equals(info.path())) {
           boolean editable = isEditable(info);
-          sb.openElement("button")
-            .setAttribute("title", editable
-                ? Resources.C.removeFileInline()
-                : Resources.C.restoreFileInline())
-            .setAttribute("onclick", (editable ? DELETE : RESTORE)
-                + "(event," + info._row() + ")")
-            .append(new ImageResourceRenderer().render(editable
-                ? Gerrit.RESOURCES.redNot()
-                : Gerrit.RESOURCES.editUndo()))
+          sb.openDiv()
+            .openElement("button")
+            .setAttribute("title", Resources.C.restoreFileInline())
+            .setAttribute("onclick", RESTORE + "(event," + info._row() + ")")
+            .append(new ImageResourceRenderer().render(
+                Gerrit.RESOURCES.editUndo()))
             .closeElement("button");
+          if (editable) {
+            sb.openElement("button")
+              .setAttribute("title", Resources.C.removeFileInline())
+              .setAttribute("onclick", DELETE + "(event," + info._row() + ")")
+              .append(new ImageResourceRenderer().render(
+                  Gerrit.RESOURCES.redNot()))
+              .closeElement("button");
+          }
+          sb.closeDiv();
         }
       }
       sb.closeTd();
@@ -657,10 +663,10 @@
       }
 
       sb.closeAnchor();
-      if (info.old_path() != null) {
+      if (info.oldPath() != null) {
         sb.br();
         sb.openSpan().setStyleName(R.css().renameCopySource())
-          .append(info.old_path())
+          .append(info.oldPath())
           .closeSpan();
       }
       sb.closeTd();
@@ -733,16 +739,16 @@
       sb.openTd().setStyleName(R.css().deltaColumn1());
       if (!Patch.COMMIT_MSG.equals(info.path()) && !info.binary()) {
         if (showChangeSizeBars) {
-          sb.append(info.lines_inserted() + info.lines_deleted());
+          sb.append(info.linesInserted() + info.linesDeleted());
         } else if (!ChangeType.DELETED.matches(info.status())) {
           if (ChangeType.ADDED.matches(info.status())) {
-            sb.append(info.lines_inserted())
+            sb.append(info.linesInserted())
               .append(" lines");
           } else {
             sb.append("+")
-              .append(info.lines_inserted())
+              .append(info.linesInserted())
               .append(", -")
-              .append(info.lines_deleted());
+              .append(info.linesDeleted());
           }
         }
       }
@@ -753,24 +759,24 @@
       sb.openTd().setStyleName(R.css().deltaColumn2());
       if (showChangeSizeBars
           && !Patch.COMMIT_MSG.equals(info.path()) && !info.binary()
-          && (info.lines_inserted() != 0 || info.lines_deleted() != 0)) {
+          && (info.linesInserted() != 0 || info.linesDeleted() != 0)) {
         int w = 80;
         int t = inserted + deleted;
-        int i = Math.max(5, (int) (((double) w) * info.lines_inserted() / t));
-        int d = Math.max(5, (int) (((double) w) * info.lines_deleted() / t));
+        int i = Math.max(5, (int) (((double) w) * info.linesInserted() / t));
+        int d = Math.max(5, (int) (((double) w) * info.linesDeleted() / t));
 
         sb.setAttribute(
             "title",
-            Util.M.patchTableSize_LongModify(info.lines_inserted(),
-                info.lines_deleted()));
+            Util.M.patchTableSize_LongModify(info.linesInserted(),
+                info.linesDeleted()));
 
-        if (0 < info.lines_inserted()) {
+        if (0 < info.linesInserted()) {
           sb.openDiv()
             .setStyleName(R.css().inserted())
             .setAttribute("style", "width:" + i + "px")
             .closeDiv();
         }
-        if (0 < info.lines_deleted()) {
+        if (0 < info.linesDeleted()) {
           sb.openDiv()
             .setStyleName(R.css().deleted())
             .setAttribute("style", "width:" + d + "px")
@@ -786,7 +792,7 @@
       if (mode == Mode.REVIEW) {
         sb.openTh().setStyleName(R.css().reviewed()).closeTh();
       } else {
-        sb.openTh().setStyleName(R.css().removeButton()).closeTh();
+        sb.openTh().setStyleName(R.css().restoreDelete()).closeTh();
       }
       sb.openTh().setStyleName(R.css().status()).closeTh();
       sb.openTd().closeTd(); // path
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
index 5a7df72..30394d6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
@@ -39,7 +39,7 @@
         new GerritCallback<ChangeInfo>() {
           @Override
           public void onSuccess(ChangeInfo result) {
-            Gerrit.display(PageLinks.toChange(result.legacy_id()));
+            Gerrit.display(PageLinks.toChange(result.legacyId()));
             hide();
           }
         });
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java
index df9cc5e..d2afbcf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java
@@ -123,8 +123,8 @@
   }
 
   void set(ChangeInfo info) {
-    canEdit = info.has_actions() && info.actions().containsKey("hashtags");
-    this.changeId = info.legacy_id();
+    canEdit = info.hasActions() && info.actions().containsKey("hashtags");
+    this.changeId = info.legacyId();
     display(info);
     openForm.setVisible(canEdit);
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
index d1f6f2e..47a870b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
@@ -91,7 +91,7 @@
       for (CommentInfo c : Natives.asList(map.get(path))) {
         c.path(path);
         if (c.author() != null) {
-          int authorId = c.author()._account_id();
+          int authorId = c.author()._accountId();
           List<CommentInfo> l = byAuthor.get(authorId);
           if (l == null) {
             l = new ArrayList<>();
@@ -108,7 +108,7 @@
       return Collections.emptyList();
     }
 
-    int authorId = msg.author()._account_id();
+    int authorId = msg.author()._accountId();
     List<CommentInfo> list = byAuthor.get(authorId);
     if (list == null) {
       return Collections.emptyList();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
index 0a02b48..f192a71 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
@@ -143,7 +143,7 @@
 
       String val = LabelValue.formatValue(v.shortValue());
       html.openSpan();
-      html.setAttribute("title", label.value_text(val));
+      html.setAttribute("title", label.valueText(val));
       if (v.intValue() == approved) {
         html.setStyleName(style.label_ok());
       } else if (v.intValue() == rejected) {
@@ -172,12 +172,12 @@
 
   private static boolean isApproved(LabelInfo label, ApprovalInfo ai) {
     return label.approved() != null
-        && label.approved()._account_id() == ai._account_id();
+        && label.approved()._accountId() == ai._accountId();
   }
 
   private static boolean isRejected(LabelInfo label, ApprovalInfo ai) {
     return label.rejected() != null
-        && label.rejected()._account_id() == ai._account_id();
+        && label.rejected()._accountId() == ai._accountId();
   }
 
   private String getStyleForLabel(LabelInfo label) {
@@ -234,12 +234,12 @@
       } else if (ai.email() != null) {
         name = ai.email();
       } else {
-        name = Integer.toString(ai._account_id());
+        name = Integer.toString(ai._accountId());
       }
 
       String votableCategories = "";
       if (votable != null) {
-        Set<String> s = votable.get(ai._account_id()).votableLabels();
+        Set<String> s = votable.get(ai._accountId()).votableLabels();
         if (!s.isEmpty()) {
           StringBuilder sb = new StringBuilder(Util.C.votable());
           sb.append(" ");
@@ -254,7 +254,7 @@
       }
       html.openSpan()
           .setAttribute("role", "listitem")
-          .setAttribute(DATA_ID, ai._account_id())
+          .setAttribute(DATA_ID, ai._accountId())
           .setAttribute("title", getTitle(ai, votableCategories))
           .setStyleName(style.label_user());
       if (img != null) {
@@ -270,7 +270,7 @@
         html.closeSelf();
       }
       html.append(name);
-      if (removable.contains(ai._account_id())) {
+      if (removable.contains(ai._accountId())) {
         html.openElement("button")
             .setAttribute("title", Util.M.removeReviewer(name))
             .setAttribute("onclick", REMOVE + "(event)")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java
index 7b3c310..2d5dce0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java
@@ -46,9 +46,9 @@
     initWidget(uiBinder.createAndBindUi(this));
 
     PatchSet.Id ps;
-    if (info.patch_set() != defaultPs.get()) {
-      ps = new PatchSet.Id(defaultPs.getParentKey(), info.patch_set());
-      psNum.setInnerText(Integer.toString(info.patch_set()));
+    if (info.patchSet() != defaultPs.get()) {
+      ps = new PatchSet.Id(defaultPs.getParentKey(), info.patchSet());
+      psNum.setInnerText(Integer.toString(info.patchSet()));
     } else {
       ps = defaultPs;
       psLoc.removeFromParent();
@@ -56,7 +56,7 @@
       psNum= null;
     }
 
-    if (info.has_line()) {
+    if (info.hasLine()) {
       fileLoc.removeFromParent();
       fileLoc = null;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
index 92f6956..f513356 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
@@ -142,7 +142,7 @@
         @Override
         public void onSuccess(ChangeInfo result) {
           if (edit != null) {
-            edit.set_name(edit.commit().commit());
+            edit.setName(edit.commit().commit());
             result.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
           }
           render(result.revisions());
@@ -201,7 +201,7 @@
     if (r.draft()) {
       sb.append(Resources.C.draft()).append(' ');
     }
-    if (r.has_draft_comments()) {
+    if (r.hasDraftComments()) {
       sb.openSpan()
         .addStyleName(style.draft_comment())
         .setAttribute("title", Resources.C.draftCommentsTooltip())
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
index 8758474..105a3c8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
@@ -42,12 +42,12 @@
   }
 
   void set(ChangeInfo info, String commit, ReplyAction action) {
-    if (!info.has_permitted_labels() || !info.status().isOpen()) {
+    if (!info.hasPermittedLabels() || !info.status().isOpen()) {
       // Quick approve needs at least one label on an open change.
       setVisible(false);
       return;
     }
-    if (info.revision(commit).is_edit() || info.revision(commit).draft()) {
+    if (info.revision(commit).isEdit() || info.revision(commit).draft()) {
       setVisible(false);
       return;
     }
@@ -58,11 +58,11 @@
 
     int index = info.getMissingLabelIndex();
     if (index != -1) {
-      LabelInfo label = Natives.asList(info.all_labels().values()).get(index);
-      JsArrayString values = info.permitted_values(label.name());
+      LabelInfo label = Natives.asList(info.allLabels().values()).get(index);
+      JsArrayString values = info.permittedValues(label.name());
       String s = values.get(values.length() - 1);
       short v = LabelInfo.parseValue(s);
-      if (v > 0 && s.equals(label.max_value())) {
+      if (v > 0 && s.equals(label.maxValue())) {
         qName = label.name();
         qValueStr = s;
         qValue = v;
@@ -70,7 +70,7 @@
     }
 
     if (qName != null) {
-      changeId = info.legacy_id();
+      changeId = info.legacyId();
       revision = commit;
       input = ReviewInput.create();
       input.drafts(DraftHandling.PUBLISH_ALL_REVISIONS);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
index 84936ec..0020c50 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
@@ -180,7 +180,7 @@
       setForOpenChange(info, revision);
     }
 
-    ChangeApi.revision(info.legacy_id().get(), revision).view("related")
+    ChangeApi.revision(info.legacyId().get(), revision).view("related")
         .get(new TabCallback<RelatedInfo>(Tab.RELATED_CHANGES, info.project(), revision) {
               @Override
               public JsArray<ChangeAndCommit> convert(RelatedInfo result) {
@@ -190,8 +190,8 @@
 
     StringBuilder cherryPicksQuery = new StringBuilder();
     cherryPicksQuery.append(op("project", info.project()));
-    cherryPicksQuery.append(" ").append(op("change", info.change_id()));
-    cherryPicksQuery.append(" ").append(op("-change", info.legacy_id().get()));
+    cherryPicksQuery.append(" ").append(op("change", info.changeId()));
+    cherryPicksQuery.append(" ").append(op("-change", info.legacyId().get()));
     cherryPicksQuery.append(" -is:abandoned");
     ChangeList.query(cherryPicksQuery.toString(),
         EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT),
@@ -215,7 +215,7 @@
       StringBuilder conflictsQuery = new StringBuilder();
       conflictsQuery.append("status:open");
       conflictsQuery.append(" is:mergeable");
-      conflictsQuery.append(" ").append(op("conflicts", info.legacy_id().get()));
+      conflictsQuery.append(" ").append(op("conflicts", info.legacyId().get()));
       ChangeList.query(conflictsQuery.toString(),
           EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT),
           new TabChangeListCallback(Tab.CONFLICTING_CHANGES, info.project(), revision));
@@ -323,16 +323,16 @@
     protected JsArray<ChangeAndCommit> convert(ChangeList l) {
       JsArray<ChangeAndCommit> arr = JavaScriptObject.createArray().cast();
       for (ChangeInfo i : Natives.asList(l)) {
-        if (i.current_revision() != null && i.revisions().containsKey(i.current_revision())) {
-          RevisionInfo currentRevision = i.revision(i.current_revision());
+        if (i.currentRevision() != null && i.revisions().containsKey(i.currentRevision())) {
+          RevisionInfo currentRevision = i.revision(i.currentRevision());
           ChangeAndCommit c = ChangeAndCommit.create();
-          c.set_id(i.id());
-          c.set_commit(currentRevision.commit());
-          c.set_change_number(i.legacy_id().get());
-          c.set_revision_number(currentRevision._number());
-          c.set_branch(i.branch());
-          c.set_project(i.project());
-          c.set_submittable(i.submittable() && i.mergeable());
+          c.setId(i.id());
+          c.setCommit(currentRevision.commit());
+          c.setChangeNumber(i.legacyId().get());
+          c.setRevisionNumber(currentRevision._number());
+          c.setBranch(i.branch());
+          c.setProject(i.project());
+          c.setSubmittable(i.submittable() && i.mergeable());
           arr.push(c);
         }
       }
@@ -357,56 +357,56 @@
     final native String project() /*-{ return this.project }-*/;
     final native boolean submittable() /*-{ return this._submittable ? true : false; }-*/;
 
-    final native void set_id(String i)
+    final native void setId(String i)
     /*-{ if(i)this.change_id=i; }-*/;
 
-    final native void set_commit(CommitInfo c)
+    final native void setCommit(CommitInfo c)
     /*-{ if(c)this.commit=c; }-*/;
 
-    final native void set_branch(String b)
+    final native void setBranch(String b)
     /*-{ if(b)this.branch=b; }-*/;
 
-    final native void set_project(String b)
+    final native void setProject(String b)
     /*-{ if(b)this.project=b; }-*/;
 
-    public final Change.Id legacy_id() {
-      return has_change_number() ? new Change.Id(_change_number()) : null;
+    public final Change.Id legacyId() {
+      return hasChangeNumber() ? new Change.Id(_changeNumber()) : null;
     }
 
-    public final PatchSet.Id patch_set_id() {
-      return has_change_number() && has_revision_number()
-          ? new PatchSet.Id(legacy_id(), _revision_number())
+    public final PatchSet.Id patchSetId() {
+      return hasChangeNumber() && hasRevisionNumber()
+          ? new PatchSet.Id(legacyId(), _revisionNumber())
           : null;
     }
 
-    public final native boolean has_change_number()
+    public final native boolean hasChangeNumber()
     /*-{ return this.hasOwnProperty('_change_number') }-*/;
 
-    final native boolean has_revision_number()
+    final native boolean hasRevisionNumber()
     /*-{ return this.hasOwnProperty('_revision_number') }-*/;
 
-    final native boolean has_current_revision_number()
+    final native boolean hasCurrentRevisionNumber()
     /*-{ return this.hasOwnProperty('_current_revision_number') }-*/;
 
-    final native int _change_number()
+    final native int _changeNumber()
     /*-{ return this._change_number }-*/;
 
-    final native int _revision_number()
+    final native int _revisionNumber()
     /*-{ return this._revision_number }-*/;
 
-    final native int _current_revision_number()
+    final native int _currentRevisionNumber()
     /*-{ return this._current_revision_number }-*/;
 
-    final native void set_change_number(int n)
+    final native void setChangeNumber(int n)
     /*-{ this._change_number=n; }-*/;
 
-    final native void set_revision_number(int n)
+    final native void setRevisionNumber(int n)
     /*-{ this._revision_number=n; }-*/;
 
-    final native void set_current_revision_number(int n)
+    final native void setCurrentRevisionNumber(int n)
     /*-{ this._current_revision_number=n; }-*/;
 
-    final native void set_submittable(boolean s)
+    final native void setSubmittable(boolean s)
     /*-{ this._submittable=s; }-*/;
 
     protected ChangeAndCommit() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
index c77ca56..1a09fc5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
@@ -302,7 +302,7 @@
 
       sb.openSpan();
       GitwebLink gw = Gerrit.getGitwebLink();
-      if (gw != null && (!info.has_change_number() || !info.has_revision_number())) {
+      if (gw != null && (!info.hasChangeNumber() || !info.hasRevisionNumber())) {
         sb.setStyleName(RelatedChanges.R.css().gitweb());
         sb.setAttribute("title", gw.getLinkName());
         sb.append('\u25CF'); // Unicode 'BLACK CIRCLE'
@@ -310,8 +310,8 @@
         sb.setStyleName(RelatedChanges.R.css().indirect());
         sb.setAttribute("title", Resources.C.indirectAncestor());
         sb.append('~');
-      } else if (info.has_current_revision_number() && info.has_revision_number()
-          && info._current_revision_number() != info._revision_number()) {
+      } else if (info.hasCurrentRevisionNumber() && info.hasRevisionNumber()
+          && info._currentRevisionNumber() != info._revisionNumber()) {
         sb.setStyleName(RelatedChanges.R.css().notCurrent());
         sb.setAttribute("title", Util.C.notCurrent());
         sb.append('\u25CF'); // Unicode 'BLACK CIRCLE'
@@ -328,8 +328,8 @@
     }
 
     private String url() {
-      if (info.has_change_number() && info.has_revision_number()) {
-        PatchSet.Id id = info.patch_set_id();
+      if (info.hasChangeNumber() && info.hasRevisionNumber()) {
+        PatchSet.Id id = info.patchSetId();
         return "#" + PageLinks.toChange(
             id.getParentKey(),
             id.getId());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java
index 326ed37..cccab34 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java
@@ -54,7 +54,7 @@
       Widget replyButton,
       Widget quickApproveButton) {
     this.psId = new PatchSet.Id(
-        info.legacy_id(),
+        info.legacyId(),
         info.revisions().get(revision)._number());
     this.revision = revision;
     this.hasDraftComments = hasDraftComments;
@@ -63,10 +63,10 @@
     this.replyButton = replyButton;
     this.quickApproveButton = quickApproveButton;
 
-    boolean current = revision.equals(info.current_revision());
-    allLabels = info.all_labels();
-    permittedLabels = current && info.has_permitted_labels()
-        ? info.permitted_labels()
+    boolean current = revision.equals(info.currentRevision());
+    allLabels = info.allLabels();
+    permittedLabels = current && info.hasPermittedLabels()
+        ? info.permittedLabels()
         : NativeMap.<JsArrayString> create();
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index d6e7159..aec617a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -291,7 +291,7 @@
     List<LabelAndValues> checkboxes = new ArrayList<>(labels.size());
     int row = 1;
     for (LabelAndValues lv : labels) {
-      if (isCheckBox(lv.info.value_set())) {
+      if (isCheckBox(lv.info.valueSet())) {
         checkboxes.add(lv);
       } else {
         renderRadio(row++, columns, lv);
@@ -328,7 +328,7 @@
     fmt.setStyleName(row, labelHelpColumn, style.label_help());
 
     ApprovalInfo self = Gerrit.isSignedIn()
-        ? lv.info.for_user(Gerrit.getUserAccount().getId().get())
+        ? lv.info.forUser(Gerrit.getUserAccount().getId().get())
         : null;
 
     final LabelRadioGroup group =
@@ -336,7 +336,7 @@
     for (int i = 0; i < columns.size(); i++) {
       Short v = columns.get(i);
       if (lv.permitted.contains(v)) {
-        String text = lv.info.value_text(LabelValue.formatValue(v));
+        String text = lv.info.valueText(LabelValue.formatValue(v));
         LabelRadioButton b = new LabelRadioButton(group, text, v);
         if ((self != null && v == self.value()) || (self == null && v.equals(dv))) {
           b.setValue(true);
@@ -352,7 +352,7 @@
 
   private void renderCheckBox(int row, LabelAndValues lv) {
     ApprovalInfo self = Gerrit.isSignedIn()
-        ? lv.info.for_user(Gerrit.getUserAccount().getId().get())
+        ? lv.info.forUser(Gerrit.getUserAccount().getId().get())
         : null;
 
     final String id = lv.info.name();
@@ -373,7 +373,7 @@
 
     CellFormatter fmt = labelsTable.getCellFormatter();
     fmt.setStyleName(row, labelHelpColumn, style.label_help());
-    labelsTable.setText(row, labelHelpColumn, lv.info.value_text("+1"));
+    labelsTable.setText(row, labelHelpColumn, lv.info.valueText("+1"));
   }
 
   private static boolean isCheckBox(Set<Short> values) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
index 47d6cad..fe0e6d0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
@@ -48,7 +48,7 @@
               public void onSuccess(ChangeInfo result) {
                 sent = true;
                 hide();
-                Gerrit.display(PageLinks.toChange(result.legacy_id()));
+                Gerrit.display(PageLinks.toChange(result.legacyId()));
               }
 
               @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
index 18e5e87..ce35747 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
@@ -100,7 +100,7 @@
   }
 
   void set(ChangeInfo info) {
-    this.changeId = info.legacy_id();
+    this.changeId = info.legacyId();
     display(info);
     reviewerSuggestOracle.setChange(changeId);
     openForm.setVisible(Gerrit.isSignedIn());
@@ -125,7 +125,7 @@
 
   @UiHandler("addMe")
   void onAddMe(@SuppressWarnings("unused") ClickEvent e) {
-    String accountId = String.valueOf(Gerrit.getUserAccountInfo()._account_id());
+    String accountId = String.valueOf(Gerrit.getUserAccountInfo()._accountId());
     addReviewer(accountId, false);
   }
 
@@ -198,22 +198,22 @@
   private void display(ChangeInfo info) {
     Map<Integer, AccountInfo> r = new HashMap<>();
     Map<Integer, AccountInfo> cc = new HashMap<>();
-    for (LabelInfo label : Natives.asList(info.all_labels().values())) {
+    for (LabelInfo label : Natives.asList(info.allLabels().values())) {
       if (label.all() != null) {
         for (ApprovalInfo ai : Natives.asList(label.all())) {
-          (ai.value() != 0 ? r : cc).put(ai._account_id(), ai);
+          (ai.value() != 0 ? r : cc).put(ai._accountId(), ai);
         }
       }
     }
     for (Integer i : r.keySet()) {
       cc.remove(i);
     }
-    cc.remove(info.owner()._account_id());
+    cc.remove(info.owner()._accountId());
 
     Set<Integer> removable = new HashSet<>();
-    if (info.removable_reviewers() != null) {
-      for (AccountInfo a : Natives.asList(info.removable_reviewers())) {
-        removable.add(a._account_id());
+    if (info.removableReviewers() != null) {
+      for (AccountInfo a : Natives.asList(info.removableReviewers())) {
+        removable.add(a._accountId());
       }
     }
 
@@ -227,8 +227,8 @@
     reviewersText.setInnerSafeHtml(rHtml);
     ccText.setInnerSafeHtml(ccHtml);
     if (Gerrit.isSignedIn()) {
-      int currentUser = Gerrit.getUserAccountInfo()._account_id();
-      boolean showAddMeButton = info.owner()._account_id() != currentUser
+      int currentUser = Gerrit.getUserAccountInfo()._accountId();
+      boolean showAddMeButton = info.owner()._accountId() != currentUser
           && !cc.containsKey(currentUser)
           && !r.containsKey(currentUser);
       addMe.setVisible(showAddMeButton);
@@ -241,13 +241,13 @@
       LabelInfo label = change.label(name);
       if (label.all() != null) {
         for (ApprovalInfo ai : Natives.asList(label.all())) {
-          int id = ai._account_id();
+          int id = ai._accountId();
           VotableInfo ad = d.get(id);
           if (ad == null) {
             ad = new VotableInfo();
             d.put(id, ad);
           }
-          if (ai.has_value()) {
+          if (ai.hasValue()) {
             ad.votable(name);
           }
         }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
index 09d3476..f45b983 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
@@ -27,7 +27,7 @@
 class SubmitAction {
   static void call(ChangeInfo changeInfo, RevisionInfo revisionInfo) {
     if (ChangeGlue.onSubmitChange(changeInfo, revisionInfo)) {
-      final Change.Id changeId = changeInfo.legacy_id();
+      final Change.Id changeId = changeInfo.legacyId();
       ChangeApi.submit(
         changeId.get(), revisionInfo.name(),
         new GerritCallback<SubmitInfo>() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
index 9f45678..b3eff93 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
@@ -68,12 +68,10 @@
   }
 
   void set(ChangeInfo info, String revision) {
-    canEdit = info.has_actions()
-        && info.actions().containsKey("topic")
-        && info.actions().get("topic").enabled();
+    canEdit = info.hasActions() && info.actions().containsKey("topic");
 
     psId = new PatchSet.Id(
-        info.legacy_id(),
+        info.legacyId(),
         info.revisions().get(revision)._number());
 
     initTopicLink(info);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
index cdbc693..2daa9ea 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
@@ -48,7 +48,7 @@
     HashSet<Integer> seen = new HashSet<>();
     StringBuilder r = new StringBuilder();
     for (MessageInfo m : newMessages) {
-      int a = m.author() != null ? m.author()._account_id() : 0;
+      int a = m.author() != null ? m.author()._accountId() : 0;
       if (seen.add(a)) {
         if (r.length() > 0) {
           r.append(", ");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
index 2803db3..f0101cb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
@@ -13,14 +13,14 @@
  * limitations under the License.
  */
 
-.pointer, .reviewed, .removeButton {
+.pointer, .reviewed, .restoreDelete {
   padding: 0px;
   vertical-align: top;
 }
 .pointer {
   width: 12px;
 }
-.reviewed, .removeButton {
+.reviewed {
   height: 19px;
   width: 20px;
 }
@@ -96,7 +96,11 @@
   background-color: #d44;
 }
 
-.removeButton button {
+.restoreDelete div {
+  white-space: nowrap;
+}
+
+.restoreDelete button {
   cursor: pointer;
   padding: 0;
   margin: 0 0 0 5px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
index dd6c41c..9974532 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -30,8 +30,16 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.EnumSet;
+import java.util.Set;
 
 public class AccountDashboardScreen extends Screen implements ChangeListScreen {
+  private static final Set<ListChangesOption> MY_DASHBOARD_OPTIONS;
+  static {
+    EnumSet<ListChangesOption> options = EnumSet.copyOf(ChangeTable.OPTIONS);
+    options.add(ListChangesOption.REVIEWED);
+    MY_DASHBOARD_OPTIONS = Collections.unmodifiableSet(options);
+  }
+
   private final Account.Id ownerId;
   private final boolean mine;
   private ChangeTable table;
@@ -96,16 +104,14 @@
     super.onLoad();
 
     String who = mine ? "self" : ownerId.toString();
-    ChangeList.query(
+    ChangeList.queryMultiple(
         new ScreenLoadCallback<JsArray<ChangeList>>(this) {
           @Override
           protected void preDisplay(JsArray<ChangeList> result) {
             display(result);
           }
         },
-        mine
-          ? EnumSet.of(ListChangesOption.REVIEWED)
-          : EnumSet.noneOf(ListChangesOption.class),
+        mine ? MY_DASHBOARD_OPTIONS : DashboardTable.OPTIONS,
         queryOutgoing(who),
         queryIncoming(who),
         queryClosed(who) + " -age:4w limit:10");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index 652e4bd..7a5e239 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -51,7 +51,7 @@
     input.project(emptyToNull(project));
     input.branch(emptyToNull(branch));
     input.subject(emptyToNull(subject));
-    input.base_change(emptyToNull(base));
+    input.baseChange(emptyToNull(base));
 
     if (Gerrit.getConfig().isAllowDraftChanges()) {
       input.status(Change.Status.DRAFT.toString());
@@ -187,7 +187,7 @@
   /** Submit a specific revision of a change. */
   public static void submit(int id, String commit, AsyncCallback<SubmitInfo> cb) {
     SubmitInput in = SubmitInput.create();
-    in.wait_for_merge(true);
+    in.waitForMerge(true);
     call(id, commit, "submit").post(in, cb);
   }
 
@@ -251,7 +251,7 @@
     public final native void branch(String b) /*-{ if(b)this.branch=b; }-*/;
     public final native void project(String p) /*-{ if(p)this.project=p; }-*/;
     public final native void subject(String s) /*-{ if(s)this.subject=s; }-*/;
-    public final native void base_change(String b) /*-{ if(b)this.base_change=b; }-*/;
+    public final native void baseChange(String b) /*-{ if(b)this.base_change=b; }-*/;
     public final native void status(String s)  /*-{ if(s)this.status=s; }-*/;
 
     protected CreateChangeInput() {
@@ -281,7 +281,7 @@
   }
 
   private static class SubmitInput extends JavaScriptObject {
-    final native void wait_for_merge(boolean b) /*-{ this.wait_for_merge=b; }-*/;
+    final native void waitForMerge(boolean b) /*-{ this.wait_for_merge=b; }-*/;
 
     static SubmitInput create() {
       return (SubmitInput) createObject();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
index a00e329..0928cd8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
@@ -76,15 +76,15 @@
   public static void rename(int id, String path, String newPath,
       AsyncCallback<VoidResult> cb) {
     Input in = Input.create();
-    in.old_path(path);
-    in.new_path(newPath);
+    in.oldPath(path);
+    in.newPath(newPath);
     ChangeApi.edit(id).post(in, cb);
   }
 
   /** Restore (undo delete/modify) a file in the pending edit. */
   public static void restore(int id, String path, AsyncCallback<VoidResult> cb) {
     Input in = Input.create();
-    in.restore_path(path);
+    in.restorePath(path);
     ChangeApi.edit(id).post(in, cb);
   }
 
@@ -101,9 +101,9 @@
       return createObject().cast();
     }
 
-    final native void restore_path(String p) /*-{ this.restore_path=p }-*/;
-    final native void old_path(String p) /*-{ this.old_path=p }-*/;
-    final native void new_path(String p) /*-{ this.new_path=p }-*/;
+    final native void restorePath(String p) /*-{ this.restore_path=p }-*/;
+    final native void oldPath(String p) /*-{ this.old_path=p }-*/;
+    final native void newPath(String p) /*-{ this.new_path=p }-*/;
 
     protected Input() {
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
index a4f2e3f..9052fef 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
@@ -41,24 +41,24 @@
 
 public class ChangeInfo extends JavaScriptObject {
   public final void init() {
-    if (all_labels() != null) {
-      all_labels().copyKeysIntoChildren("_name");
+    if (allLabels() != null) {
+      allLabels().copyKeysIntoChildren("_name");
     }
   }
 
-  public final Project.NameKey project_name_key() {
+  public final Project.NameKey projectNameKey() {
     return new Project.NameKey(project());
   }
 
-  public final Change.Id legacy_id() {
+  public final Change.Id legacyId() {
     return new Change.Id(_number());
   }
 
   public final Timestamp created() {
-    Timestamp ts = _get_cts();
+    Timestamp ts = _getCts();
     if (ts == null) {
       ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(createdRaw());
-      _set_cts(ts);
+      _setCts(ts);
     }
     return ts;
   }
@@ -66,18 +66,18 @@
   public final boolean hasEditBasedOnCurrentPatchSet() {
     JsArray<RevisionInfo> revList = revisions().values();
     RevisionInfo.sortRevisionInfoByNumber(revList);
-    return revList.get(revList.length() - 1).is_edit();
+    return revList.get(revList.length() - 1).isEdit();
   }
 
-  private final native Timestamp _get_cts() /*-{ return this._cts; }-*/;
-  private final native void _set_cts(Timestamp ts) /*-{ this._cts = ts; }-*/;
+  private final native Timestamp _getCts() /*-{ return this._cts; }-*/;
+  private final native void _setCts(Timestamp ts) /*-{ this._cts = ts; }-*/;
 
   public final Timestamp updated() {
     return JavaSqlTimestamp_JsonSerializer.parseTimestamp(updatedRaw());
   }
 
-  public final String id_abbreviated() {
-    return new Change.Key(change_id()).abbreviate();
+  public final String idAbbreviated() {
+    return new Change.Key(changeId()).abbreviate();
   }
 
   public final Change.Status status() {
@@ -85,14 +85,14 @@
   }
 
   public final Set<String> labels() {
-    return all_labels().keySet();
+    return allLabels().keySet();
   }
 
   public final native String id() /*-{ return this.id; }-*/;
   public final native String project() /*-{ return this.project; }-*/;
   public final native String branch() /*-{ return this.branch; }-*/;
   public final native String topic() /*-{ return this.topic; }-*/;
-  public final native String change_id() /*-{ return this.change_id; }-*/;
+  public final native String changeId() /*-{ return this.change_id; }-*/;
   public final native boolean mergeable() /*-{ return this.mergeable || false; }-*/;
   public final native int insertions() /*-{ return this.insertions; }-*/;
   public final native int deletions() /*-{ return this.deletions; }-*/;
@@ -103,30 +103,30 @@
   private final native String updatedRaw() /*-{ return this.updated; }-*/;
   public final native boolean starred() /*-{ return this.starred ? true : false; }-*/;
   public final native boolean reviewed() /*-{ return this.reviewed ? true : false; }-*/;
-  public final native NativeMap<LabelInfo> all_labels() /*-{ return this.labels; }-*/;
+  public final native NativeMap<LabelInfo> allLabels() /*-{ return this.labels; }-*/;
   public final native LabelInfo label(String n) /*-{ return this.labels[n]; }-*/;
-  public final native String current_revision() /*-{ return this.current_revision; }-*/;
-  public final native void set_current_revision(String r) /*-{ this.current_revision = r; }-*/;
-  private final native void set_submittable(boolean x) /*-{ this.submittable = x; }-*/;
+  public final native String currentRevision() /*-{ return this.current_revision; }-*/;
+  public final native void setCurrentRevision(String r) /*-{ this.current_revision = r; }-*/;
+  private final native void setSubmittable(boolean x) /*-{ this.submittable = x; }-*/;
   public final native NativeMap<RevisionInfo> revisions() /*-{ return this.revisions; }-*/;
   public final native RevisionInfo revision(String n) /*-{ return this.revisions[n]; }-*/;
   public final native JsArray<MessageInfo> messages() /*-{ return this.messages; }-*/;
-  public final native void set_edit(EditInfo edit) /*-{ this.edit = edit; }-*/;
+  public final native void setEdit(EditInfo edit) /*-{ this.edit = edit; }-*/;
   public final native EditInfo edit() /*-{ return this.edit; }-*/;
-  public final native boolean has_edit() /*-{ return this.hasOwnProperty('edit') }-*/;
+  public final native boolean hasEdit() /*-{ return this.hasOwnProperty('edit') }-*/;
   public final native JsArrayString hashtags() /*-{ return this.hashtags; }-*/;
 
-  public final native boolean has_permitted_labels()
+  public final native boolean hasPermittedLabels()
   /*-{ return this.hasOwnProperty('permitted_labels') }-*/;
-  public final native NativeMap<JsArrayString> permitted_labels()
+  public final native NativeMap<JsArrayString> permittedLabels()
   /*-{ return this.permitted_labels; }-*/;
-  public final native JsArrayString permitted_values(String n)
+  public final native JsArrayString permittedValues(String n)
   /*-{ return this.permitted_labels[n]; }-*/;
 
-  public final native JsArray<AccountInfo> removable_reviewers()
+  public final native JsArray<AccountInfo> removableReviewers()
   /*-{ return this.removable_reviewers; }-*/;
 
-  public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/;
+  public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/;
   public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
 
   final native int _number() /*-{ return this._number; }-*/;
@@ -151,14 +151,14 @@
   public final int getMissingLabelIndex() {
     int i = -1;
     int ret = -1;
-    List<LabelInfo> labels = Natives.asList(all_labels().values());
+    List<LabelInfo> labels = Natives.asList(allLabels().values());
     for (LabelInfo label : labels) {
       i++;
-      if (!permitted_labels().containsKey(label.name())) {
+      if (!permittedLabels().containsKey(label.name())) {
         continue;
       }
 
-      JsArrayString values = permitted_values(label.name());
+      JsArrayString values = permittedValues(label.name());
       if (values.length() == 0) {
         continue;
       }
@@ -168,7 +168,7 @@
           if (ret != -1) {
             // more than one label is missing, so it's unclear which to quick
             // approve, return -1
-            set_submittable(false);
+            setSubmittable(false);
             return -1;
           } else {
             ret = i;
@@ -181,11 +181,11 @@
 
         case REJECT: // Submit cannot happen, do not quick approve.
         case IMPOSSIBLE:
-          set_submittable(false);
+          setSubmittable(false);
           return -1;
       }
     }
-    set_submittable(ret == -1);
+    setSubmittable(ret == -1);
     return ret;
   }
 
@@ -213,10 +213,10 @@
     public final native AccountInfo disliked() /*-{ return this.disliked; }-*/;
 
     public final native JsArray<ApprovalInfo> all() /*-{ return this.all; }-*/;
-    public final ApprovalInfo for_user(int user) {
+    public final ApprovalInfo forUser(int user) {
       JsArray<ApprovalInfo> all = all();
       for (int i = 0; all != null && i < all.length(); i++) {
-        if (all.get(i)._account_id() == user) {
+        if (all.get(i)._accountId() == user) {
           return all.get(i);
         }
       }
@@ -227,7 +227,7 @@
     public final Set<String> values() {
       return Natives.keys(_values());
     }
-    public final native String value_text(String n) /*-{ return this.values[n]; }-*/;
+    public final native String valueText(String n) /*-{ return this.values[n]; }-*/;
 
     public final native boolean optional() /*-{ return this.optional ? true : false; }-*/;
     public final native boolean blocking() /*-{ return this.blocking ? true : false; }-*/;
@@ -240,11 +240,11 @@
       return 0;
     }-*/;
 
-    public final String max_value() {
-      return LabelValue.formatValue(value_set().last());
+    public final String maxValue() {
+      return LabelValue.formatValue(valueSet().last());
     }
 
-    public final SortedSet<Short> value_set() {
+    public final SortedSet<Short> valueSet() {
       SortedSet<Short> values = new TreeSet<>();
       for (String v : values()) {
         values.add(parseValue(v));
@@ -266,7 +266,7 @@
   }
 
   public static class ApprovalInfo extends AccountInfo {
-    public final native boolean has_value() /*-{ return this.hasOwnProperty('value'); }-*/;
+    public final native boolean hasValue() /*-{ return this.hasOwnProperty('value'); }-*/;
     public final native short value() /*-{ return this.value || 0; }-*/;
 
     protected ApprovalInfo() {
@@ -275,17 +275,17 @@
 
   public static class EditInfo extends JavaScriptObject {
     public final native String name() /*-{ return this.name; }-*/;
-    public final native String set_name(String n) /*-{ this.name = n; }-*/;
-    public final native String base_revision() /*-{ return this.base_revision; }-*/;
+    public final native String setName(String n) /*-{ this.name = n; }-*/;
+    public final native String baseRevision() /*-{ return this.base_revision; }-*/;
     public final native CommitInfo commit() /*-{ return this.commit; }-*/;
 
-    public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/;
+    public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/;
     public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
 
-    public final native boolean has_fetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
+    public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
     public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/;
 
-    public final native boolean has_files() /*-{ return this.hasOwnProperty('files') }-*/;
+    public final native boolean hasFiles() /*-{ return this.hasOwnProperty('files') }-*/;
     public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/;
 
     protected EditInfo() {
@@ -307,19 +307,19 @@
     public final native int _number() /*-{ return this._number; }-*/;
     public final native String name() /*-{ return this.name; }-*/;
     public final native boolean draft() /*-{ return this.draft || false; }-*/;
-    public final native boolean has_draft_comments() /*-{ return this.has_draft_comments || false; }-*/;
-    public final native boolean is_edit() /*-{ return this._number == 0; }-*/;
+    public final native boolean hasDraftComments() /*-{ return this.has_draft_comments || false; }-*/;
+    public final native boolean isEdit() /*-{ return this._number == 0; }-*/;
     public final native CommitInfo commit() /*-{ return this.commit; }-*/;
-    public final native void set_commit(CommitInfo c) /*-{ this.commit = c; }-*/;
-    public final native String edit_base() /*-{ return this.edit_base; }-*/;
+    public final native void setCommit(CommitInfo c) /*-{ this.commit = c; }-*/;
+    public final native String editBase() /*-{ return this.edit_base; }-*/;
 
-    public final native boolean has_files() /*-{ return this.hasOwnProperty('files') }-*/;
+    public final native boolean hasFiles() /*-{ return this.hasOwnProperty('files') }-*/;
     public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/;
 
-    public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/;
+    public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/;
     public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
 
-    public final native boolean has_fetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
+    public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
     public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/;
 
     public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
@@ -331,7 +331,7 @@
         }
 
         private int num(RevisionInfo r) {
-          return !r.is_edit() ? 2 * (r._number() - 1) + 1 : 2 * editParent;
+          return !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent;
         }
       });
     }
@@ -340,8 +340,8 @@
       for (int i = 0; i < list.length(); i++) {
         // edit under revisions?
         RevisionInfo editInfo = list.get(i);
-        if (editInfo.is_edit()) {
-          String parentRevision = editInfo.edit_base();
+        if (editInfo.isEdit()) {
+          String parentRevision = editInfo.editBase();
           // find parent
           for (int j = 0; j < list.length(); j++) {
             RevisionInfo parentInfo = list.get(j);
@@ -381,7 +381,7 @@
     public final native GitPerson committer() /*-{ return this.committer; }-*/;
     public final native String subject() /*-{ return this.subject; }-*/;
     public final native String message() /*-{ return this.message; }-*/;
-    public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+    public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
 
     protected CommitInfo() {
     }
@@ -415,7 +415,7 @@
   }
 
   public static class MergeableInfo extends JavaScriptObject {
-    public final native String submit_type() /*-{ return this.submit_type }-*/;
+    public final native String submitType() /*-{ return this.submit_type }-*/;
     public final native boolean mergeable() /*-{ return this.mergeable }-*/;
 
     protected MergeableInfo() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
index b1866dd..5fcaf24 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
@@ -20,52 +20,68 @@
 import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwtorm.client.KeyUtil;
 
-import java.util.EnumSet;
+import java.util.Set;
 
 /** List of changes available from {@code /changes/}. */
 public class ChangeList extends JsArray<ChangeInfo> {
   private static final String URI = "/changes/";
-  private static final EnumSet<ListChangesOption> OPTIONS = EnumSet.of(
-      ListChangesOption.LABELS, ListChangesOption.DETAILED_ACCOUNTS);
 
-  /** Run 2 or more queries in a single remote invocation. */
-  public static void query(
-      AsyncCallback<JsArray<ChangeList>> callback,
-      EnumSet<ListChangesOption> options,
+  /** Run multiple queries in a single remote invocation. */
+  public static void queryMultiple(
+      final AsyncCallback<JsArray<ChangeList>> callback,
+      Set<ListChangesOption> options,
       String... queries) {
-    assert queries.length >= 2; // At least 2 is required for correct result.
+    if (queries.length == 0) {
+      return;
+    }
     RestApi call = new RestApi(URI);
     for (String q : queries) {
       call.addParameterRaw("q", KeyUtil.encode(q));
     }
-    OPTIONS.addAll(options);
-    addOptions(call, OPTIONS);
-    call.get(callback);
+    addOptions(call, options);
+    if (queries.length == 1) {
+      // Server unwraps a single query, so wrap it back in an array for the
+      // callback.
+      call.get(new AsyncCallback<ChangeList>() {
+        @Override
+        public void onSuccess(ChangeList result) {
+          JsArray<ChangeList> wrapped = JsArray.createArray(1).cast();
+          wrapped.set(0, result);
+          callback.onSuccess(wrapped);
+        }
+
+        @Override
+        public void onFailure(Throwable caught) {
+          callback.onFailure(caught);
+        }
+      });
+    } else {
+      call.get(callback);
+    }
   }
 
   public static void query(String query,
-      EnumSet<ListChangesOption> options,
+      Set<ListChangesOption> options,
       AsyncCallback<ChangeList> callback) {
-    RestApi call = newQuery(query);
-    addOptions(call, options);
-    call.get(callback);
+    query(query, options, callback, 0, 0);
   }
 
-  public static void next(String query,
-      int start, int limit,
-      AsyncCallback<ChangeList> callback) {
+  public static void query(String query,
+      Set<ListChangesOption> options,
+      AsyncCallback<ChangeList> callback,
+      int start, int limit) {
     RestApi call = newQuery(query);
     if (limit > 0) {
       call.addParameter("n", limit);
     }
-    addOptions(call, OPTIONS);
+    addOptions(call, options);
     if (start != 0) {
       call.addParameter("S", start);
     }
     call.get(callback);
   }
 
-  public static void addOptions(RestApi call, EnumSet<ListChangesOption> s) {
+  public static void addOptions(RestApi call, Set<ListChangesOption> s) {
     call.addParameterRaw("O", Integer.toHexString(ListChangesOption.toBits(s)));
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
index 8b71448..c52ac32 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
 import com.google.gerrit.client.ui.ProjectLink;
 import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.ReviewCategoryStrategy;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gwt.dom.client.Element;
@@ -45,9 +46,17 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Set;
 
 public class ChangeTable extends NavigationTable<ChangeInfo> {
+  // If changing default options, also update in
+  // ChangeIT#defaultSearchDoesNotTouchDatabase().
+  static final Set<ListChangesOption> OPTIONS =
+      Collections.unmodifiableSet(EnumSet.of(
+          ListChangesOption.LABELS, ListChangesOption.DETAILED_ACCOUNTS));
+
   private static final int C_STAR = 1;
   private static final int C_ID = 2;
   private static final int C_SUBJECT = 3;
@@ -118,13 +127,13 @@
 
   @Override
   protected Object getRowItemKey(final ChangeInfo item) {
-    return item.legacy_id();
+    return item.legacyId();
   }
 
   @Override
   protected void onOpenRow(final int row) {
     final ChangeInfo c = getRowItem(row);
-    final Change.Id id = c.legacy_id();
+    final Change.Id id = c.legacyId();
     Gerrit.display(PageLinks.toChange(id));
   }
 
@@ -208,10 +217,10 @@
     CellFormatter fmt = table.getCellFormatter();
     if (Gerrit.isSignedIn()) {
       table.setWidget(row, C_STAR, StarredChanges.createIcon(
-          c.legacy_id(),
+          c.legacyId(),
           c.starred()));
     }
-    table.setWidget(row, C_ID, new TableChangeLink(String.valueOf(c.legacy_id()), c));
+    table.setWidget(row, C_ID, new TableChangeLink(String.valueOf(c.legacyId()), c));
 
     String subject = Util.cropSubject(c.subject());
     table.setWidget(row, C_SUBJECT, new TableChangeLink(subject, c));
@@ -229,8 +238,8 @@
       table.setText(row, C_OWNER, "");
     }
 
-    table.setWidget(row, C_PROJECT, new ProjectLink(c.project_name_key()));
-    table.setWidget(row, C_BRANCH, new BranchLink(c.project_name_key(), c
+    table.setWidget(row, C_PROJECT, new ProjectLink(c.projectNameKey()));
+    table.setWidget(row, C_BRANCH, new BranchLink(c.projectNameKey(), c
         .status(), c.branch(), c.topic()));
     if (Gerrit.isSignedIn()
         && Gerrit.getUserAccount().getGeneralPreferences()
@@ -447,7 +456,7 @@
 
   private final class TableChangeLink extends ChangeLink {
     private TableChangeLink(final String text, final ChangeInfo c) {
-      super(text, c.legacy_id());
+      super(text, c.legacyId());
     }
 
     @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java
index fed041c..c69ee57 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java
@@ -29,7 +29,7 @@
     n.path(path);
     n.side(side);
     if (range != null) {
-      n.line(range.end_line());
+      n.line(range.endLine());
       n.range(range);
     } else if (line > 0) {
       n.line(line);
@@ -41,11 +41,11 @@
     CommentInfo n = createObject().cast();
     n.path(r.path());
     n.side(r.side());
-    n.in_reply_to(r.id());
-    if (r.has_range()) {
-      n.line(r.range().end_line());
+    n.inReplyTo(r.id());
+    if (r.hasRange()) {
+      n.line(r.range().endLine());
       n.range(r.range());
-    } else if (r.has_line()) {
+    } else if (r.hasLine()) {
       n.line(r.line());
     }
     return n;
@@ -56,12 +56,12 @@
     n.path(s.path());
     n.side(s.side());
     n.id(s.id());
-    n.in_reply_to(s.in_reply_to());
+    n.inReplyTo(s.inReplyTo());
     n.message(s.message());
-    if (s.has_range()) {
-      n.line(s.range().end_line());
+    if (s.hasRange()) {
+      n.line(s.range().endLine());
       n.range(s.range());
-    } else if (s.has_line()) {
+    } else if (s.hasLine()) {
       n.line(s.line());
     }
     return n;
@@ -71,7 +71,7 @@
   public final native void id(String i) /*-{ this.id = i }-*/;
   public final native void line(int n) /*-{ this.line = n }-*/;
   public final native void range(CommentRange r) /*-{ this.range = r }-*/;
-  public final native void in_reply_to(String i) /*-{ this.in_reply_to = i }-*/;
+  public final native void inReplyTo(String i) /*-{ this.in_reply_to = i }-*/;
   public final native void message(String m) /*-{ this.message = m }-*/;
 
   public final void side(Side side) {
@@ -81,8 +81,8 @@
 
   public final native String path() /*-{ return this.path }-*/;
   public final native String id() /*-{ return this.id }-*/;
-  public final native String in_reply_to() /*-{ return this.in_reply_to }-*/;
-  public final native int patch_set() /*-{ return this.patch_set }-*/;
+  public final native String inReplyTo() /*-{ return this.in_reply_to }-*/;
+  public final native int patchSet() /*-{ return this.patch_set }-*/;
 
   public final Side side() {
     String s = sideRaw();
@@ -109,8 +109,8 @@
 
   public final native AccountInfo author() /*-{ return this.author }-*/;
   public final native int line() /*-{ return this.line || 0 }-*/;
-  public final native boolean has_line() /*-{ return this.hasOwnProperty('line') }-*/;
-  public final native boolean has_range() /*-{ return this.hasOwnProperty('range') }-*/;
+  public final native boolean hasLine() /*-{ return this.hasOwnProperty('line') }-*/;
+  public final native boolean hasRange() /*-{ return this.hasOwnProperty('range') }-*/;
   public final native CommentRange range() /*-{ return this.range }-*/;
   public final native String message() /*-{ return this.message }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
index 9e97c56..26e11ae 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
@@ -20,14 +20,12 @@
 import com.google.gerrit.client.ui.InlineHyperlink;
 import com.google.gerrit.client.ui.Screen;
 import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.event.dom.client.KeyPressEvent;
 import com.google.gwt.http.client.URL;
 import com.google.gwtexpui.globalkey.client.KeyCommand;
 
 import java.util.ArrayList;
-import java.util.EnumSet;
 import java.util.List;
 import java.util.ListIterator;
 
@@ -104,33 +102,19 @@
   @Override
   protected void onLoad() {
     super.onLoad();
-
-    if (queries.size() == 1) {
-      ChangeList.next(queries.get(0),
-          0, 0,
-          new GerritCallback<ChangeList>() {
-            @Override
-            public void onSuccess(ChangeList result) {
-              updateColumnsForLabels(result);
-              sections.get(0).display(result);
-              finishDisplay();
+    ChangeList.queryMultiple(
+        new GerritCallback<JsArray<ChangeList>>() {
+          @Override
+          public void onSuccess(JsArray<ChangeList> result) {
+            List<ChangeList> cls = Natives.asList(result);
+            updateColumnsForLabels(cls.toArray(new ChangeList[cls.size()]));
+            for (int i = 0; i < cls.size(); i++) {
+              sections.get(i).display(cls.get(i));
             }
-        });
-    } else if (! queries.isEmpty()) {
-      ChangeList.query(
-          new GerritCallback<JsArray<ChangeList>>() {
-            @Override
-            public void onSuccess(JsArray<ChangeList> result) {
-              List<ChangeList> cls = Natives.asList(result);
-              updateColumnsForLabels(cls.toArray(new ChangeList[cls.size()]));
-              for (int i = 0; i < cls.size(); i++) {
-                sections.get(i).display(cls.get(i));
-              }
-              finishDisplay();
-            }
-          },
-          EnumSet.noneOf(ListChangesOption.class),
-          queries.toArray(new String[queries.size()]));
-    }
+            finishDisplay();
+          }
+        },
+        OPTIONS,
+        queries.toArray(new String[queries.size()]));
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
index 488b34b..dddbd61 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
@@ -54,7 +54,7 @@
         if (isAttached()) {
           if (result.length() == 1 && isSingleQuery(query)) {
             ChangeInfo c = result.get(0);
-            Change.Id id = c.legacy_id();
+            Change.Id id = c.legacyId();
             Gerrit.display(PageLinks.toChange(id));
           } else {
             display(result);
@@ -74,7 +74,8 @@
   @Override
   protected void onLoad() {
     super.onLoad();
-    ChangeList.next(query, start, pageSize, loadCallback());
+    ChangeList.query(
+        query, ChangeTable.OPTIONS, loadCallback(), start, pageSize);
   }
 
   private static boolean isSingleQuery(String query) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/AuthInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/AuthInfo.java
new file mode 100644
index 0000000..5e66d83
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/AuthInfo.java
@@ -0,0 +1,82 @@
+// 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.client.config;
+
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AuthInfo extends JavaScriptObject {
+  public final AuthType authType() {
+    return AuthType.valueOf(authTypeRaw());
+  }
+
+  public final boolean isOpenId() {
+    return authType() == AuthType.OPENID;
+  }
+
+  public final boolean isOAuth() {
+    return authType() == AuthType.OAUTH;
+  }
+
+  public final boolean isDev() {
+    return authType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
+  }
+
+  public final boolean isClientSslCertLdap() {
+    return authType() == AuthType.CLIENT_SSL_CERT_LDAP;
+  }
+
+  public final boolean isCustomExtension() {
+    return authType() == AuthType.CUSTOM_EXTENSION;
+  }
+
+  public final boolean canEdit(Account.FieldName f) {
+    return editableAccountFields().contains(f);
+  }
+
+  public final List<Account.FieldName> editableAccountFields() {
+    List<Account.FieldName> fields = new ArrayList<>();
+    for (AccountFieldNameInfo f : Natives.asList(_editableAccountFields())) {
+      fields.add(f.get());
+    }
+    return fields;
+  }
+
+  public final native boolean useContributorAgreements()
+  /*-{ return this.use_contributor_agreements || false; }-*/;
+  private final native String authTypeRaw() /*-{ return this.auth_type; }-*/;
+  private final native JsArray<AccountFieldNameInfo> _editableAccountFields()
+  /*-{ return this.editable_account_fields; }-*/;
+
+  protected AuthInfo() {
+  }
+
+  private static class AccountFieldNameInfo extends JavaScriptObject {
+    final Account.FieldName get() {
+      return Account.FieldName.valueOf(getRaw());
+    }
+
+    private final native String getRaw() /*-{ return this; }-*/;
+
+    protected AccountFieldNameInfo() {
+    }
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
index 5dedaf0..5d79390 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
@@ -37,4 +37,8 @@
   public static void defaultPreferences(AsyncCallback<Preferences> cb) {
     new RestApi("/config/server/preferences").get(cb);
   }
+
+  public static void serverInfo(AsyncCallback<ServerInfo> cb) {
+    new RestApi("/config/server/info").get(cb);
+  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/DownloadInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/DownloadInfo.java
new file mode 100644
index 0000000..e97d472
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/DownloadInfo.java
@@ -0,0 +1,85 @@
+// 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.client.config;
+
+import com.google.gerrit.client.rpc.NativeMap;
+import com.google.gerrit.client.rpc.NativeString;
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gwt.core.client.JavaScriptObject;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DownloadInfo extends JavaScriptObject {
+  public final Set<String> schemes() {
+    return Natives.keys(_schemes());
+  }
+  public final native DownloadSchemeInfo scheme(String n) /*-{ return this.schemes[n]; }-*/;
+  private final native NativeMap<DownloadSchemeInfo> _schemes() /*-{ return this.schemes; }-*/;
+
+  protected DownloadInfo() {
+  }
+
+  public static class DownloadSchemeInfo extends JavaScriptObject {
+    public final Set<String> commandNames() {
+      return Natives.keys(_commands());
+    }
+
+    public final Set<DownloadCommandInfo> commands(String project) {
+      Set<DownloadCommandInfo> commands = new HashSet<>();
+      for (String commandName : commandNames()) {
+        commands.add(new DownloadCommandInfo(commandName, command(commandName,
+            project)));
+      }
+      return commands;
+    }
+
+    public final String command(String commandName, String project) {
+      return command(commandName).replaceAll("\\$\\{project\\}", project);
+    }
+
+    public final String getUrl(String project) {
+      return url().replaceAll("\\$\\{project\\}", project);
+    }
+
+    public final native String name() /*-{ return this.name; }-*/;
+    public final native String url() /*-{ return this.url; }-*/;
+    public final native boolean isAuthRequired() /*-{ return this.is_auth_required || false; }-*/;
+    public final native boolean isAuthSupported() /*-{ return this.is_auth_supported || false; }-*/;
+    public final native String command(String n) /*-{ return this.commands[n]; }-*/;
+    private final native NativeMap<NativeString> _commands() /*-{ return this.commands; }-*/;
+
+    protected DownloadSchemeInfo() {
+    }
+  }
+
+  public static class DownloadCommandInfo {
+    private final String name;
+    private final String command;
+
+    DownloadCommandInfo(String name, String command) {
+      this.name = name;
+      this.command = command;
+    }
+
+    public String name() {
+      return name;
+    }
+
+    public String command() {
+      return command;
+    }
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/GerritInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/GerritInfo.java
new file mode 100644
index 0000000..33036ad
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/GerritInfo.java
@@ -0,0 +1,42 @@
+// 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.client.config;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class GerritInfo extends JavaScriptObject {
+  public final Project.NameKey allProjectsNameKey() {
+    return new Project.NameKey(allProjects());
+  }
+
+  public final boolean isAllProjects(Project.NameKey p) {
+    return allProjectsNameKey().equals(p);
+  }
+
+  public final Project.NameKey allUsersNameKey() {
+    return new Project.NameKey(allUsers());
+  }
+
+  public final boolean isAllUsers(Project.NameKey p) {
+    return allUsersNameKey().equals(p);
+  }
+
+  public final native String allProjects() /*-{ return this.all_projects; }-*/;
+  public final native String allUsers() /*-{ return this.all_users; }-*/;
+
+  protected GerritInfo() {
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java
new file mode 100644
index 0000000..a1e8dbc
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java
@@ -0,0 +1,38 @@
+// 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.client.config;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class ServerInfo extends JavaScriptObject {
+  public final native AuthInfo auth() /*-{ return this.auth; }-*/;
+  public final native ContactStoreInfo contactStore() /*-{ return this.contact_store; }-*/;
+  public final native DownloadInfo download() /*-{ return this.download; }-*/;
+  public final native GerritInfo gerrit() /*-{ return this.gerrit; }-*/;
+
+  public final boolean hasContactStore() {
+    return contactStore() != null;
+  }
+
+  protected ServerInfo() {
+  }
+
+  public static class ContactStoreInfo extends JavaScriptObject {
+    public final native String url() /*-{ return this.url; }-*/;
+
+    protected ContactStoreInfo() {
+    }
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
index 4420940..8ff11e8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
@@ -124,7 +124,7 @@
     padding = new ArrayList<>();
     paddingDivs = new ArrayList<>();
 
-    String diffColor = diff.meta_a() == null || diff.meta_b() == null
+    String diffColor = diff.metaA() == null || diff.metaB() == null
         ? DiffTable.style.intralineBg()
         : DiffTable.style.diff();
 
@@ -175,8 +175,8 @@
 
     colorLines(cmA, color, startA, aLen);
     colorLines(cmB, color, startB, bLen);
-    markEdit(cmA, startA, a, region.edit_a());
-    markEdit(cmB, startB, b, region.edit_b());
+    markEdit(cmA, startA, a, region.editA());
+    markEdit(cmB, startB, b, region.editB());
     addPadding(cmA, startA + aLen - 1, bLen - aLen);
     addPadding(cmB, startB + bLen - 1, aLen - bLen);
     addGutterTag(region, startA, startB);
@@ -317,7 +317,7 @@
 
         DiffChunkInfo target = chunks.get(res);
         CodeMirror targetCm = host.getCmFromSide(target.getSide());
-        targetCm.setCursor(Pos.create(target.getStart()));
+        targetCm.setCursor(Pos.create(target.getStart(), 0));
         targetCm.focus();
         targetCm.scrollToY(
             targetCm.heightAtLine(target.getStart(), "local") -
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
index 7dc6c24b9..4e1a3e1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
@@ -218,8 +218,8 @@
         info,
         expandAll);
 
-    if (info.in_reply_to() != null) {
-      PublishedBox r = published.get(info.in_reply_to());
+    if (info.inReplyTo() != null) {
+      PublishedBox r = published.get(info.inReplyTo());
       if (r != null) {
         r.setReplyBox(box);
       }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
index a38a6ca..9f46e9b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
@@ -38,10 +38,10 @@
         to.line() + 1, to.ch());
   }
 
-  public final native int start_line() /*-{ return this.start_line; }-*/;
-  public final native int start_character() /*-{ return this.start_character; }-*/;
-  public final native int end_line() /*-{ return this.end_line; }-*/;
-  public final native int end_character() /*-{ return this.end_character; }-*/;
+  public final native int startLine() /*-{ return this.start_line; }-*/;
+  public final native int startCharacter() /*-{ return this.start_character; }-*/;
+  public final native int endLine() /*-{ return this.end_line; }-*/;
+  public final native int endCharacter() /*-{ return this.end_character; }-*/;
 
   private final native void set(int sl, int sc, int el, int ec) /*-{
     this.start_line = sl;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
index 7140e07..f7f4528 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
@@ -30,24 +30,24 @@
   public static final String GITLINK = "x-git/gitlink";
   public static final String SYMLINK = "x-git/symlink";
 
-  public final native FileMeta meta_a() /*-{ return this.meta_a; }-*/;
-  public final native FileMeta meta_b() /*-{ return this.meta_b; }-*/;
-  public final native JsArrayString diff_header() /*-{ return this.diff_header; }-*/;
+  public final native FileMeta metaA() /*-{ return this.meta_a; }-*/;
+  public final native FileMeta metaB() /*-{ return this.meta_b; }-*/;
+  public final native JsArrayString diffHeader() /*-{ return this.diff_header; }-*/;
   public final native JsArray<Region> content() /*-{ return this.content; }-*/;
-  public final native JsArray<DiffWebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+  public final native JsArray<DiffWebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
   public final native boolean binary() /*-{ return this.binary || false; }-*/;
 
-  public final List<WebLinkInfo> side_by_side_web_links() {
+  public final List<WebLinkInfo> sideBySideWebLinks() {
     return filterWebLinks(DiffView.SIDE_BY_SIDE);
   }
 
-  public final List<WebLinkInfo> unified_web_links() {
+  public final List<WebLinkInfo> unifiedWebLinks() {
     return filterWebLinks(DiffView.UNIFIED_DIFF);
   }
 
   private final List<WebLinkInfo> filterWebLinks(DiffView diffView) {
     List<WebLinkInfo> filteredDiffWebLinks = new LinkedList<>();
-    List<DiffWebLinkInfo> allDiffWebLinks = Natives.asList(web_links());
+    List<DiffWebLinkInfo> allDiffWebLinks = Natives.asList(webLinks());
     if (allDiffWebLinks != null) {
       for (DiffWebLinkInfo webLink : allDiffWebLinks) {
         if (diffView == DiffView.SIDE_BY_SIDE
@@ -63,22 +63,22 @@
     return filteredDiffWebLinks;
   }
 
-  public final ChangeType change_type() {
-    return ChangeType.valueOf(change_typeRaw());
+  public final ChangeType changeType() {
+    return ChangeType.valueOf(changeTypeRaw());
   }
-  private final native String change_typeRaw()
+  private final native String changeTypeRaw()
   /*-{ return this.change_type }-*/;
 
-  public final IntraLineStatus intraline_status() {
-    String s = intraline_statusRaw();
+  public final IntraLineStatus intralineStatus() {
+    String s = intralineStatusRaw();
     return s != null
         ? IntraLineStatus.valueOf(s)
         : IntraLineStatus.OFF;
   }
-  private final native String intraline_statusRaw()
+  private final native String intralineStatusRaw()
   /*-{ return this.intraline_status }-*/;
 
-  public final boolean has_skip() {
+  public final boolean hasSkip() {
     JsArray<Region> c = content();
     for (int i = 0; i < c.length(); i++) {
       if (c.get(i).skip() != 0) {
@@ -88,7 +88,7 @@
     return false;
   }
 
-  public final String text_a() {
+  public final String textA() {
     StringBuilder s = new StringBuilder();
     JsArray<Region> c = content();
     for (int i = 0; i < c.length(); i++) {
@@ -103,7 +103,7 @@
     return s.toString();
   }
 
-  public final String text_b() {
+  public final String textB() {
     StringBuilder s = new StringBuilder();
     JsArray<Region> c = content();
     for (int i = 0; i < c.length(); i++) {
@@ -133,9 +133,9 @@
 
   public static class FileMeta extends JavaScriptObject {
     public final native String name() /*-{ return this.name; }-*/;
-    public final native String content_type() /*-{ return this.content_type; }-*/;
+    public final native String contentType() /*-{ return this.content_type; }-*/;
     public final native int lines() /*-{ return this.lines || 0 }-*/;
-    public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+    public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
 
     protected FileMeta() {
     }
@@ -148,8 +148,8 @@
     public final native int skip() /*-{ return this.skip || 0; }-*/;
     public final native boolean common() /*-{ return this.common || false; }-*/;
 
-    public final native JsArray<Span> edit_a() /*-{ return this.edit_a }-*/;
-    public final native JsArray<Span> edit_b() /*-{ return this.edit_b }-*/;
+    public final native JsArray<Span> editA() /*-{ return this.edit_a }-*/;
+    public final native JsArray<Span> editB() /*-{ return this.edit_b }-*/;
 
     protected Region() {
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
index 243ce4d..b188c59 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
@@ -151,13 +151,13 @@
 
   void set(DiffPreferences prefs, JsArray<RevisionInfo> list, DiffInfo info,
       boolean editExists, int currentPatchSet, boolean open, boolean binary) {
-    this.changeType = info.change_type();
-    patchSetSelectBoxA.setUpPatchSetNav(list, info.meta_a(), editExists,
+    this.changeType = info.changeType();
+    patchSetSelectBoxA.setUpPatchSetNav(list, info.metaA(), editExists,
         currentPatchSet, open, binary);
-    patchSetSelectBoxB.setUpPatchSetNav(list, info.meta_b(), editExists,
+    patchSetSelectBoxB.setUpPatchSetNav(list, info.metaB(), editExists,
         currentPatchSet, open, binary);
 
-    JsArrayString hdr = info.diff_header();
+    JsArrayString hdr = info.diffHeader();
     if (hdr != null) {
       StringBuilder b = new StringBuilder();
       for (int i = 1; i < hdr.length(); i++) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
index c90db3e..99ecc9b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
@@ -22,7 +22,7 @@
     @external .CodeMirror-linenumber;
     @external .CodeMirror-overlayscroll-vertical, .CodeMirror-scroll;
     @external .CodeMirror-dialog-bottom;
-    @external .cm-animate-fat-cursor, .CodeMirror-cursor;
+    @external .CodeMirror-cursor;
 
     .fullscreen {
       background-color: #f7f7f7;
@@ -116,18 +116,7 @@
       cursor: pointer;
     }
     .difftable .CodeMirror div.CodeMirror-cursor {
-      background: transparent;
-      text-decoration: underline;
-      border: none;
-      z-index: 2;
-    }
-    .difftable .cm-animate-fat-cursor {
-      text-decoration: underline;
-      border: none;
-      animation: none;
-      -webkit-animation: none;
-      -moz-animation: none;
-      -o-animation: none;
+      border-left: 2px solid black;
     }
     .difftable .CodeMirror-dialog-bottom {
       border-top: 0;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
index d7b528c..21b2f50 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
@@ -244,7 +244,7 @@
   }
 
   private void restoreSelection() {
-    if (getFromTo() != null && comment.in_reply_to() == null) {
+    if (getFromTo() != null && comment.inReplyTo() == null) {
       getCm().setSelection(getFromTo().from(), getFromTo().to());
     }
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
index c4459b6..77b28d4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
@@ -25,9 +25,9 @@
 
 public class FileInfo extends JavaScriptObject {
   public final native String path() /*-{ return this.path; }-*/;
-  public final native String old_path() /*-{ return this.old_path; }-*/;
-  public final native int lines_inserted() /*-{ return this.lines_inserted || 0; }-*/;
-  public final native int lines_deleted() /*-{ return this.lines_deleted || 0; }-*/;
+  public final native String oldPath() /*-{ return this.old_path; }-*/;
+  public final native int linesInserted() /*-{ return this.lines_inserted || 0; }-*/;
+  public final native int linesDeleted() /*-{ return this.lines_deleted || 0; }-*/;
   public final native boolean binary() /*-{ return this.binary || false; }-*/;
   public final native String status() /*-{ return this.status; }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
index 2c551c0..42ae61d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
@@ -67,7 +67,7 @@
   }
 
   private static enum ReviewedState {
-    AUTO_REVIEW, LOADED;
+    AUTO_REVIEW, LOADED
   }
 
   @UiField CheckBox reviewed;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
index bbc5830..e597398 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
@@ -116,7 +116,7 @@
         linkPanel.add(createEditIcon());
       }
     }
-    List<WebLinkInfo> webLinks = Natives.asList(meta.web_links());
+    List<WebLinkInfo> webLinks = Natives.asList(meta.webLinks());
     if (webLinks != null) {
       for (WebLinkInfo webLink : webLinks) {
         linkPanel.add(webLink.toAnchor());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
index d06b59e..a8b6065 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
@@ -238,11 +238,11 @@
         changeStatus = info.status();
         info.revisions().copyKeysIntoChildren("name");
         if (edit != null) {
-          edit.set_name(edit.commit().commit());
-          info.set_edit(edit);
+          edit.setName(edit.commit().commit());
+          info.setEdit(edit);
           info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
         }
-        int currentPatchSet = info.revision(info.current_revision())._number();
+        int currentPatchSet = info.revision(info.currentRevision())._number();
         JsArray<RevisionInfo> list = info.revisions().values();
         RevisionInfo.sortRevisionInfoByNumber(list);
         diffTable.set(prefs, list, diff, edit != null, currentPatchSet,
@@ -570,8 +570,8 @@
       diffTable.addStyleName(DiffTable.style.showLineNumbers());
     }
 
-    cmA = newCM(diff.meta_a(), diff.text_a(), diffTable.cmA);
-    cmB = newCM(diff.meta_b(), diff.text_b(), diffTable.cmB);
+    cmA = newCM(diff.metaA(), diff.textA(), diffTable.cmA);
+    cmB = newCM(diff.metaB(), diff.textB(), diffTable.cmB);
 
     cmA.extras().side(DisplaySide.A);
     cmB.extras().side(DisplaySide.B);
@@ -606,7 +606,7 @@
             chunkManager.getLineMapper());
 
     prefsAction = new PreferencesAction(this, prefs);
-    header.init(prefsAction, getLinks(), diff.side_by_side_web_links());
+    header.init(prefsAction, getLinks(), diff.sideBySideWebLinks());
     scrollSynchronizer.setAutoHideDiffTableHeader(prefs.autoHideDiffTableHeader());
 
     if (prefs.syntaxHighlighting() && fileSize.compareTo(FileSize.SMALL) > 0) {
@@ -654,7 +654,7 @@
   }
 
   DiffInfo.IntraLineStatus getIntraLineStatus() {
-    return diff.intraline_status();
+    return diff.intralineStatus();
   }
 
   boolean canEnableRenderEntireFile(DiffPreferences prefs) {
@@ -663,7 +663,7 @@
   }
 
   String getContentType() {
-    return getContentType(diff.meta_b());
+    return getContentType(diff.metaB());
   }
 
   void setThemeStyles(boolean d) {
@@ -716,8 +716,8 @@
         @Override
         public void onSuccess(Void result) {
           if (prefs.syntaxHighlighting()) {
-            cmA.setOption("mode", getContentType(diff.meta_a()));
-            cmB.setOption("mode", getContentType(diff.meta_b()));
+            cmA.setOption("mode", getContentType(diff.metaA()));
+            cmB.setOption("mode", getContentType(diff.metaB()));
           }
         }
 
@@ -923,7 +923,7 @@
     int offset = 6;
 
     // Adjust for merge commits, which have two parent lines
-    if (diff.text_b().startsWith("Merge")) {
+    if (diff.textB().startsWith("Merge")) {
       offset += 1;
     }
 
@@ -983,8 +983,8 @@
 
   private String getContentType(DiffInfo.FileMeta meta) {
     if (prefs.syntaxHighlighting() && meta != null
-        && meta.content_type() != null) {
-     ModeInfo m = ModeInfo.findMode(meta.content_type(), path);
+        && meta.contentType() != null) {
+     ModeInfo m = ModeInfo.findMode(meta.contentType(), path);
      return m != null ? m.mime() : null;
    }
    return null;
@@ -992,8 +992,8 @@
 
   private void injectMode(DiffInfo diffInfo, AsyncCallback<Void> cb) {
     new ModeInjector()
-      .add(getContentType(diffInfo.meta_a()))
-      .add(getContentType(diffInfo.meta_b()))
+      .add(getContentType(diffInfo.metaA()))
+      .add(getContentType(diffInfo.metaB()))
       .inject(cb);
   }
 
@@ -1043,8 +1043,8 @@
           @Override
           public void onSuccess(DiffInfo info) {
             new ModeInjector()
-              .add(getContentType(info.meta_a()))
-              .add(getContentType(info.meta_b()))
+              .add(getContentType(info.metaA()))
+              .add(getContentType(info.metaB()))
               .inject(CallbackGroup.<Void> emptyCallback());
           }
 
@@ -1085,8 +1085,8 @@
   }
 
   private static FileSize bucketFileSize(DiffInfo diff) {
-    FileMeta a = diff.meta_a();
-    FileMeta b = diff.meta_b();
+    FileMeta a = diff.metaA();
+    FileMeta b = diff.metaB();
     FileSize[] sizes = FileSize.values();
     for (int i = sizes.length - 1; 0 <= i; i--) {
       FileSize s = sizes[i];
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
index cea9106..d4237f9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
@@ -15,8 +15,6 @@
 package com.google.gerrit.client.download;
 
 import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gwt.aria.client.Roles;
 import com.google.gwt.event.dom.client.ClickEvent;
@@ -25,89 +23,15 @@
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.VoidResult;
 
 public abstract class DownloadCommandLink extends Anchor implements ClickHandler {
   public static class CopyableCommandLinkFactory {
     protected CopyableLabel copyLabel = null;
     protected Widget widget;
 
-    public class CheckoutCommandLink extends DownloadCommandLink {
-      public CheckoutCommandLink () {
-        super(DownloadCommand.CHECKOUT, "checkout");
-      }
-
-      @Override
-      protected void setCurrentUrl(DownloadUrlLink link) {
-        widget.setVisible(true);
-        copyLabel.setText("git fetch " + link.getUrlData()
-            + " && git checkout FETCH_HEAD");
-      }
-    }
-
-    public class PullCommandLink extends DownloadCommandLink {
-      public PullCommandLink() {
-        super(DownloadCommand.PULL, "pull");
-      }
-
-      @Override
-      protected void setCurrentUrl(DownloadUrlLink link) {
-        widget.setVisible(true);
-        copyLabel.setText("git pull " + link.getUrlData());
-      }
-    }
-
-    public class CherryPickCommandLink extends DownloadCommandLink {
-      public CherryPickCommandLink() {
-        super(DownloadCommand.CHERRY_PICK, "cherry-pick");
-      }
-
-      @Override
-      protected void setCurrentUrl(DownloadUrlLink link) {
-        widget.setVisible(true);
-        copyLabel.setText("git fetch " + link.getUrlData()
-            + " && git cherry-pick FETCH_HEAD");
-      }
-    }
-
-    public class FormatPatchCommandLink extends DownloadCommandLink {
-      public FormatPatchCommandLink() {
-        super(DownloadCommand.FORMAT_PATCH, "patch");
-      }
-
-      @Override
-      protected void setCurrentUrl(DownloadUrlLink link) {
-        widget.setVisible(true);
-        copyLabel.setText("git fetch " + link.getUrlData()
-            + " && git format-patch -1 --stdout FETCH_HEAD");
-      }
-    }
-
-    public class RepoCommandLink extends DownloadCommandLink {
-      String projectName;
-      String ref;
-      public RepoCommandLink(String project, String ref) {
-        super(DownloadCommand.REPO_DOWNLOAD, "repo download");
-        this.projectName = project;
-        this.ref = ref;
-      }
-
-      @Override
-      protected void setCurrentUrl(DownloadUrlLink link) {
-        widget.setVisible(false);
-        final StringBuilder r = new StringBuilder();
-        r.append("repo download ");
-        r.append(projectName);
-        r.append(" ");
-        r.append(ref);
-        copyLabel.setText(r.toString());
-      }
-    }
-
     public class CloneCommandLink extends DownloadCommandLink {
       public CloneCommandLink() {
-        super(DownloadCommand.CHECKOUT, "clone");
+        super("clone");
       }
 
       @Override
@@ -121,7 +45,7 @@
       private final Project.NameKey project;
 
       public CloneWithCommitMsgHookCommandLink(Project.NameKey project) {
-        super(DownloadCommand.CHECKOUT, "clone with commit-msg hook");
+        super("clone with commit-msg hook");
         this.project = project;
       }
 
@@ -175,12 +99,8 @@
     }
   }
 
-  final DownloadCommand cmdType;
-
-  public DownloadCommandLink(DownloadCommand cmdType,
-      String text) {
+  public DownloadCommandLink(String text) {
     super(text);
-    this.cmdType = cmdType;
     setStyleName(Gerrit.RESOURCES.css().downloadLink());
     Roles.getTabRole().set(getElement());
     addClickHandler(this);
@@ -192,28 +112,6 @@
     event.stopPropagation();
 
     select();
-
-    if (Gerrit.isSignedIn()) {
-      // If the user is signed-in, remember this choice for future panels.
-      //
-      AccountGeneralPreferences pref =
-          Gerrit.getUserAccount().getGeneralPreferences();
-      pref.setDownloadCommand(cmdType);
-      com.google.gerrit.client.account.Util.ACCOUNT_SVC.changePreferences(pref,
-          new AsyncCallback<VoidResult>() {
-            @Override
-            public void onFailure(Throwable caught) {
-            }
-
-            @Override
-            public void onSuccess(VoidResult result) {
-            }
-          });
-    }
-  }
-
-  public DownloadCommand getCmdType() {
-    return cmdType;
   }
 
   void select() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java
index d17d6c2..5b7d015 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java
@@ -15,8 +15,6 @@
 package com.google.gerrit.client.download;
 
 import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
 import com.google.gwt.aria.client.Roles;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -34,7 +32,7 @@
     return getWidgetCount() == 0;
   }
 
-  public void select(AccountGeneralPreferences.DownloadCommand cmdType) {
+  public void select() {
     DownloadCommandLink first = null;
 
     for (Widget w : this) {
@@ -43,10 +41,6 @@
         if (first == null) {
           first = d;
         }
-        if (d.cmdType == cmdType) {
-          d.select();
-          return;
-        }
       }
     }
 
@@ -70,9 +64,6 @@
   private void update() {
     if (currentCommand != null && currentUrl != null) {
       currentCommand.setCurrentUrl(currentUrl);
-    } else if (currentCommand != null &&
-        currentCommand.getCmdType().equals(DownloadCommand.REPO_DOWNLOAD)) {
-      currentCommand.setCurrentUrl(null);
     }
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java
index 9fce0bc..19c65a9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java
@@ -16,29 +16,24 @@
 
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
-import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwtexpui.clippy.client.CopyableLabel;
 
-import java.util.Set;
-
 public abstract class DownloadPanel extends FlowPanel {
   protected String projectName;
 
-  protected Set<DownloadCommand> allowedCommands =
-      Gerrit.getConfig().getDownloadCommands();
   protected DownloadCommandLink.CopyableCommandLinkFactory cmdLinkfactory;
 
   protected DownloadCommandPanel commands = new DownloadCommandPanel();
   protected DownloadUrlPanel urls = new DownloadUrlPanel(commands);
   protected CopyableLabel copyLabel = new CopyableLabel("");
 
-  public DownloadPanel(String project, String ref, boolean allowAnonymous) {
+  public DownloadPanel(String project, boolean allowAnonymous) {
     this.projectName = project;
 
     copyLabel.setStyleName(Gerrit.RESOURCES.css().downloadLinkCopyLabel());
-    urls.add(DownloadUrlLink.createDownloadUrlLinks(project, ref, allowAnonymous));
+    urls.add(DownloadUrlLink.createDownloadUrlLinks(project, allowAnonymous));
     cmdLinkfactory = new DownloadCommandLink.CopyableCommandLinkFactory(
         copyLabel, urls);
 
@@ -55,7 +50,7 @@
         pref = new AccountGeneralPreferences();
         pref.resetToDefaults();
       }
-      commands.select(pref.getDownloadCommand());
+      commands.select();
       urls.select(pref.getDownloadUrl());
 
       FlowPanel p = new FlowPanel();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java
index ce5c060..275b918 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme;
-import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gwt.aria.client.Roles;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.dom.client.ClickEvent;
@@ -33,28 +32,9 @@
 import java.util.Set;
 
 public class DownloadUrlLink extends Anchor implements ClickHandler {
-  public static class DownloadRefUrlLink extends DownloadUrlLink {
-    protected String projectName;
-    protected String ref;
-
-    protected DownloadRefUrlLink(DownloadScheme urlType,
-        String text, String project, String ref) {
-      super(urlType, text);
-      this.projectName = project;
-      this.ref = ref;
-    }
-
-    protected void appendRef(StringBuilder r) {
-      if (ref != null) {
-        r.append(" ");
-        r.append(ref);
-      }
-    }
-  }
-
-  public static class AnonGitLink extends DownloadRefUrlLink {
-    public AnonGitLink(String project, String ref) {
-      super(DownloadScheme.ANON_GIT, Util.M.anonymousDownload("Git"), project, ref);
+  public static class AnonGitLink extends DownloadUrlLink {
+    public AnonGitLink(String project) {
+      super(DownloadScheme.ANON_GIT, Util.M.anonymousDownload("Git"), project);
     }
 
     @Override
@@ -62,14 +42,13 @@
       StringBuilder r = new StringBuilder();
       r.append(Gerrit.getConfig().getGitDaemonUrl());
       r.append(projectName);
-      appendRef(r);
       return r.toString();
     }
   }
 
-  public static class AnonHttpLink extends DownloadRefUrlLink {
-    public AnonHttpLink(String project, String ref) {
-      super(DownloadScheme.ANON_HTTP, Util.M.anonymousDownload("HTTP"), project, ref);
+  public static class AnonHttpLink extends DownloadUrlLink {
+    public AnonHttpLink(String project) {
+      super(DownloadScheme.ANON_HTTP, Util.M.anonymousDownload("HTTP"), project);
     }
 
     @Override
@@ -81,14 +60,13 @@
         r.append(hostPageUrl);
       }
       r.append(projectName);
-      appendRef(r);
       return r.toString();
     }
   }
 
-  public static class SshLink extends DownloadRefUrlLink {
-    public SshLink(String project, String ref) {
-      super(DownloadScheme.SSH, "SSH", project, ref);
+  public static class SshLink extends DownloadUrlLink {
+    public SshLink(String project) {
+      super(DownloadScheme.SSH, "SSH", project);
     }
 
     @Override
@@ -107,16 +85,15 @@
       r.append(sshAddr);
       r.append("/");
       r.append(projectName);
-      appendRef(r);
       return r.toString();
     }
   }
 
-  public static class HttpLink extends DownloadRefUrlLink {
+  public static class HttpLink extends DownloadUrlLink {
     protected boolean anonymous;
 
-    public HttpLink(String project, String ref, boolean anonymous) {
-      super(DownloadScheme.HTTP, "HTTP", project, ref);
+    public HttpLink(String project, boolean anonymous) {
+      super(DownloadScheme.HTTP, "HTTP", project);
       this.anonymous = anonymous;
     }
 
@@ -145,46 +122,41 @@
         r.append(base.substring(s));
       }
       r.append(projectName);
-      appendRef(r);
       return r.toString();
     }
   }
 
   public static boolean siteReliesOnHttp() {
     return Gerrit.getConfig().getGitHttpUrl() != null
-        && Gerrit.getConfig().getAuthType() == AuthType.CUSTOM_EXTENSION
+        && Gerrit.info().auth().isCustomExtension()
         && !Gerrit.getConfig().siteHasUsernames();
   }
 
   public static List<DownloadUrlLink> createDownloadUrlLinks(String project,
-      String ref, boolean allowAnonymous) {
+      boolean allowAnonymous) {
     List<DownloadUrlLink> urls = new ArrayList<>();
-    Set<DownloadScheme> allowedSchemes = Gerrit.getConfig().getDownloadSchemes();
+    Set<String> allowedSchemes = Gerrit.info().download().schemes();
 
     if (allowAnonymous
         && Gerrit.getConfig().getGitDaemonUrl() != null
-        && (allowedSchemes.contains(DownloadScheme.ANON_GIT) ||
-            allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
-      urls.add(new DownloadUrlLink.AnonGitLink(project, ref));
+        && allowedSchemes.contains("git")) {
+      urls.add(new DownloadUrlLink.AnonGitLink(project));
     }
 
     if (allowAnonymous
-        && (allowedSchemes.contains(DownloadScheme.ANON_HTTP) ||
-            allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
-      urls.add(new DownloadUrlLink.AnonHttpLink(project, ref));
+        && allowedSchemes.contains("anonymous http")) {
+      urls.add(new DownloadUrlLink.AnonHttpLink(project));
     }
 
     if (Gerrit.getConfig().getSshdAddress() != null
         && hasUserName()
-        && (allowedSchemes.contains(DownloadScheme.SSH) ||
-            allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
-      urls.add(new DownloadUrlLink.SshLink(project, ref));
+        && allowedSchemes.contains("ssh")) {
+      urls.add(new DownloadUrlLink.SshLink(project));
     }
 
     if ((hasUserName() || siteReliesOnHttp())
-        && (allowedSchemes.contains(DownloadScheme.HTTP)
-            || allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
-      urls.add(new DownloadUrlLink.HttpLink(project, ref, allowAnonymous));
+        && allowedSchemes.contains("http")) {
+      urls.add(new DownloadUrlLink.HttpLink(project, allowAnonymous));
     }
     return urls;
   }
@@ -196,21 +168,11 @@
   }
 
   protected DownloadScheme urlType;
+  protected String projectName;
   protected String urlData;
   protected String hostPageUrl = GWT.getHostPageBaseURL();
 
-  public DownloadUrlLink(DownloadScheme urlType, String text, String urlData) {
-    this(text);
-    this.urlType = urlType;
-    this.urlData = urlData;
-  }
-
-  public DownloadUrlLink(DownloadScheme urlType, String text) {
-    this(text);
-    this.urlType = urlType;
-  }
-
-  public DownloadUrlLink(String text) {
+  public DownloadUrlLink(DownloadScheme urlType, String text, String project) {
     super(text);
     setStyleName(Gerrit.RESOURCES.css().downloadLink());
     Roles.getTabRole().set(getElement());
@@ -219,6 +181,8 @@
     if (!hostPageUrl.endsWith("/")) {
       hostPageUrl += "/";
     }
+    this.urlType = urlType;
+    this.projectName = project;
   }
 
   public String getUrlData() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
index c833c5d..dda5fc2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
@@ -19,7 +19,7 @@
 import com.google.gwt.core.client.JsArray;
 
 public class EditFileInfo extends JavaScriptObject {
-  public final native JsArray<DiffWebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+  public final native JsArray<DiffWebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
 
   protected EditFileInfo() {
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
index dd36657..e463607 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -185,7 +185,7 @@
         .get(group1.add(new AsyncCallback<DiffInfo>() {
           @Override
           public void onSuccess(DiffInfo diffInfo) {
-            diffLinks = diffInfo.web_links();
+            diffLinks = diffInfo.webLinks();
           }
 
           @Override
@@ -377,7 +377,7 @@
     renderLinksToDiff();
 
     if (editInfo != null) {
-      renderLinks(Natives.asList(editInfo.web_links()));
+      renderLinks(Natives.asList(editInfo.webLinks()));
     } else if (diffLinks != null) {
       renderLinks(Natives.asList(diffLinks));
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
index 0b178f2..e2b1112 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
@@ -125,7 +125,7 @@
     } else {
       MemberInput input = MemberInput.create();
       for (String member : members) {
-        input.add_member(member);
+        input.addMember(member);
       }
       members(group).post(input, cb);
     }
@@ -139,7 +139,7 @@
     } else {
       MemberInput in = MemberInput.create();
       for (Integer id : ids) {
-        in.add_member(id.toString());
+        in.addMember(id.toString());
       }
       group(group).view("members.delete").post(in, cb);
     }
@@ -172,7 +172,7 @@
     } else {
       IncludedGroupInput input = IncludedGroupInput.create();
       for (String includedGroup : includedGroups) {
-        input.add_group(includedGroup);
+        input.addGroup(includedGroup);
       }
       groups(group).post(input, cb);
     }
@@ -187,7 +187,7 @@
     } else {
       IncludedGroupInput in = IncludedGroupInput.create();
       for (AccountGroup.UUID g : ids) {
-        in.add_group(g.get());
+        in.addGroup(g.get());
       }
       group(group).view("groups.delete").post(in, cb);
     }
@@ -235,7 +235,7 @@
 
   private static class MemberInput extends JavaScriptObject {
     final native void init() /*-{ this.members = []; }-*/;
-    final native void add_member(String n) /*-{ this.members.push(n); }-*/;
+    final native void addMember(String n) /*-{ this.members.push(n); }-*/;
 
     static MemberInput create() {
       MemberInput m = (MemberInput) createObject();
@@ -249,7 +249,7 @@
 
   private static class IncludedGroupInput extends JavaScriptObject {
     final native void init() /*-{ this.groups = []; }-*/;
-    final native void add_group(String n) /*-{ this.groups.push(n); }-*/;
+    final native void addGroup(String n) /*-{ this.groups.push(n); }-*/;
 
     static IncludedGroupInput create() {
       IncludedGroupInput g = (IncludedGroupInput) createObject();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
index 2538102..8642556 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
@@ -345,7 +345,7 @@
     if (c.getLine() > 0) {
       i.line(c.getLine());
     }
-    i.in_reply_to(c.getParentUuid());
+    i.inReplyTo(c.getParentUuid());
     i.message(c.getMessage());
     return i;
   }
@@ -359,7 +359,7 @@
             i.id()),
         i.line(),
         Gerrit.getUserAccount().getId(),
-        i.in_reply_to(),
+        i.inReplyTo(),
         i.updated());
     p.setMessage(i.message());
     p.setSide((short) (i.side() == Side.PARENT ? 0 : 1));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
index a5c1484..2479322 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
@@ -251,7 +251,7 @@
   }
 
   private List<WebLinkInfo> getWebLinks(DiffInfo diffInfo) {
-    return diffInfo.unified_web_links();
+    return diffInfo.unifiedWebLinks();
   }
 
   private String getSideBySideDiffUrl() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java
index ea3d8e3..0284aa3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java
@@ -30,7 +30,7 @@
   public final native String revision() /*-{ return this.revision; }-*/;
   public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;
   public final native NativeMap<ActionInfo> actions() /*-{ return this.actions }-*/;
-  public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+  public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
 
   protected BranchInfo() {
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
index 7aa5be5..b91c5de 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
@@ -35,23 +35,23 @@
   public final native String description()
   /*-{ return this.description }-*/;
 
-  public final native InheritedBooleanInfo require_change_id()
+  public final native InheritedBooleanInfo requireChangeId()
   /*-{ return this.require_change_id; }-*/;
 
-  public final native InheritedBooleanInfo use_content_merge()
+  public final native InheritedBooleanInfo useContentMerge()
   /*-{ return this.use_content_merge; }-*/;
 
-  public final native InheritedBooleanInfo use_contributor_agreements()
+  public final native InheritedBooleanInfo useContributorAgreements()
   /*-{ return this.use_contributor_agreements; }-*/;
 
-  public final native InheritedBooleanInfo create_new_change_for_all_not_in_target()
+  public final native InheritedBooleanInfo createNewChangeForAllNotInTarget()
   /*-{ return this.create_new_change_for_all_not_in_target; }-*/;
 
-  public final native InheritedBooleanInfo use_signed_off_by()
+  public final native InheritedBooleanInfo useSignedOffBy()
   /*-{ return this.use_signed_off_by; }-*/;
 
-  public final SubmitType submit_type() {
-    return SubmitType.valueOf(submit_typeRaw());
+  public final SubmitType submitType() {
+    return SubmitType.valueOf(submitTypeRaw());
   }
 
   public final native NativeMap<NativeMap<ConfigParameterInfo>> pluginConfig()
@@ -63,7 +63,7 @@
   public final native NativeMap<ActionInfo> actions()
   /*-{ return this.actions; }-*/;
 
-  private final native String submit_typeRaw()
+  private final native String submitTypeRaw()
   /*-{ return this.submit_type }-*/;
 
   public final ProjectState state() {
@@ -75,7 +75,7 @@
   private final native String stateRaw()
   /*-{ return this.state }-*/;
 
-  public final native MaxObjectSizeLimitInfo max_object_size_limit()
+  public final native MaxObjectSizeLimitInfo maxObjectSizeLimit()
   /*-{ return this.max_object_size_limit; }-*/;
 
   private final native NativeMap<CommentLinkInfo> commentlinks0()
@@ -131,13 +131,13 @@
     public final native boolean value()
     /*-{ return this.value ? true : false; }-*/;
 
-    public final native boolean inherited_value()
+    public final native boolean inheritedValue()
     /*-{ return this.inherited_value ? true : false; }-*/;
 
-    public final InheritableBoolean configured_value() {
-      return InheritableBoolean.valueOf(configured_valueRaw());
+    public final InheritableBoolean configuredValue() {
+      return InheritableBoolean.valueOf(configuredValueRaw());
     }
-    private final native String configured_valueRaw()
+    private final native String configuredValueRaw()
     /*-{ return this.configured_value }-*/;
 
     public final void setConfiguredValue(InheritableBoolean v) {
@@ -152,8 +152,8 @@
 
   public static class MaxObjectSizeLimitInfo extends JavaScriptObject {
     public final native String value() /*-{ return this.value; }-*/;
-    public final native String inherited_value() /*-{ return this.inherited_value; }-*/;
-    public final native String configured_value() /*-{ return this.configured_value }-*/;
+    public final native String inheritedValue() /*-{ return this.inherited_value; }-*/;
+    public final native String configuredValue() /*-{ return this.configured_value }-*/;
 
     protected MaxObjectSizeLimitInfo() {
     }
@@ -179,8 +179,8 @@
 
   public static class ConfigParameterValue extends JavaScriptObject {
     final native void init() /*-{ this.values = []; }-*/;
-    final native void add_value(String v) /*-{ this.values.push(v); }-*/;
-    final native void set_value(String v) /*-{ if(v)this.value = v; }-*/;
+    final native void addValue(String v) /*-{ this.values.push(v); }-*/;
+    final native void setValue(String v) /*-{ if(v)this.value = v; }-*/;
     public static ConfigParameterValue create() {
       ConfigParameterValue v = createObject().cast();
       return v;
@@ -189,13 +189,13 @@
     public final ConfigParameterValue values(String[] values) {
       init();
       for (String v : values) {
-        add_value(v);
+        addValue(v);
       }
       return this;
     }
 
     public final ConfigParameterValue value(String v) {
-      set_value(v);
+      setValue(v);
       return this;
     }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java
index c22b007..e000a97 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java
@@ -61,7 +61,7 @@
   }
 
   public static void add(ChangeInfo info) {
-    instance.changeToProject.put(info.legacy_id().get(), info.project());
+    instance.changeToProject.put(info.legacyId().get(), info.project());
   }
 
   private final LinkedHashMap<String, Entry> cache;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
index b121d07..d81dfe5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -84,7 +84,7 @@
     } else {
       DeleteBranchesInput d = DeleteBranchesInput.create();
       for (String ref : refs) {
-        d.add_branch(ref);
+        d.addBranch(ref);
       }
       project(name).view("branches:delete").post(d, cb);
     }
@@ -317,6 +317,6 @@
     }
 
     final native void init() /*-{ this.branches = []; }-*/;
-    final native void add_branch(String b) /*-{ this.branches.push(b); }-*/;
+    final native void addBranch(String b) /*-{ this.branches.push(b); }-*/;
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
index fe9872c..029e59b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
@@ -30,7 +30,7 @@
 
   public final native String name() /*-{ return this.name; }-*/;
   public final native String description() /*-{ return this.description; }-*/;
-  public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
+  public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
 
   public final ProjectState state() {
     return ProjectState.valueOf(getStringState());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
index cdad972..2633e3b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
@@ -71,8 +71,8 @@
       return ai.email();
     } else if (ai.name() != null) {
       return ai.name();
-    } else if (ai._account_id() != 0) {
-      return "" + ai._account_id();
+    } else if (ai._accountId() != 0) {
+      return "" + ai._accountId();
     } else {
       return "";
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java
index a2155d4..819a11f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java
@@ -87,7 +87,7 @@
     // Resetting the "original text" on focus ensures that we are
     // up to date with non-user updates of the text (calls to
     // setText()...) and also up to date with user changes which
-    // occured after enabling "widget".
+    // occurred after enabling "widget".
     tb.addFocusHandler(new FocusHandler() {
         @Override
         public void onFocus(FocusEvent event) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java
index 5f47d98..2744877 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.client.changes.Util;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -29,6 +30,7 @@
 import com.google.gwtexpui.globalkey.client.GlobalKey;
 import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
 
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -51,10 +53,10 @@
         String query = request.getQuery().toLowerCase();
         LinkedList<ChangeSuggestion> suggestions = new LinkedList<>();
         for (final ChangeInfo ci : changes) {
-          if (changeId.equals(ci.legacy_id())) {
+          if (changeId.equals(ci.legacyId())) {
             continue;  // do not suggest current change
           }
-          String id = String.valueOf(ci.legacy_id().get());
+          String id = String.valueOf(ci.legacyId().get());
           if (id.contains(query) || ci.subject().toLowerCase().contains(query)) {
             suggestions.add(new ChangeSuggestion(ci));
             if (suggestions.size() >= 50) { // limit to 50 suggestions
@@ -76,8 +78,10 @@
       public void onClick(ClickEvent event) {
         boolean checked = ((CheckBox) event.getSource()).getValue();
         if (checked) {
-          ChangeList.next("project:" + project + " AND branch:" + branch
-              + " AND is:open NOT age:90d", 0, 1000,
+          ChangeList.query(
+              "project:" + project + " AND branch:" + branch
+                  + " AND is:open NOT age:90d",
+              Collections.<ListChangesOption> emptySet(),
               new GerritCallback<ChangeList>() {
                 @Override
                 public void onSuccess(ChangeList result) {
@@ -136,12 +140,12 @@
 
     @Override
     public String getDisplayString() {
-      return String.valueOf(change.legacy_id().get()) + ": " + change.subject();
+      return String.valueOf(change.legacyId().get()) + ": " + change.subject();
     }
 
     @Override
     public String getReplacementString() {
-      return String.valueOf(change.legacy_id().get());
+      return String.valueOf(change.legacyId().get());
     }
   }
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
index f4be9d4..639e5e7 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -219,7 +219,7 @@
     if (lineAtHeight(height - 20) < line) {
       scrollToY(heightAtLine(line, "local") - 0.5 * height);
     }
-    setCursor(Pos.create(line));
+    setCursor(Pos.create(line, 0));
   }
 
   public final native ScrollInfo getScrollInfo() /*-{
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
index 2d69015..eac8510 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
@@ -35,8 +35,8 @@
 
     public static FromTo create(CommentRange range) {
       return create(
-          Pos.create(range.start_line() - 1, range.start_character()),
-          Pos.create(range.end_line() - 1, range.end_character()));
+          Pos.create(range.startLine() - 1, range.startCharacter()),
+          Pos.create(range.endLine() - 1, range.endCharacter()));
     }
 
     public final native Pos from() /*-{ return this.from }-*/;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
index 91fb6af..378c4d5 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -52,8 +52,6 @@
  */
 @Singleton
 class ContainerAuthFilter implements Filter {
-  public static final String REALM_NAME = "Gerrit Code Review";
-
   private final DynamicItem<WebSession> session;
   private final AccountCache accountCache;
   private final Config config;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index 7744a54..eadc536 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -24,13 +24,10 @@
 import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.change.ArchiveFormat;
 import com.google.gerrit.server.change.GetArchive;
-import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.DownloadConfig;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.contact.ContactStore;
 import com.google.gerrit.server.ssh.SshInfo;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -47,32 +44,28 @@
   private final Realm realm;
   private final Config cfg;
   private final AuthConfig authConfig;
-  private final DownloadConfig downloadConfig;
   private final GetArchive.AllowedFormats archiveFormats;
   private final GitWebConfig gitWebConfig;
-  private final AllProjectsName wildProject;
   private final SshInfo sshInfo;
 
-  private final ContactStore contactStore;
   private final ServletContext servletContext;
   private final String anonymousCowardName;
 
   @Inject
-  GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc,
-      final AuthConfig ac, final GitWebConfig gwc, final AllProjectsName wp,
-      final SshInfo si, final ContactStore cs,
-      final ServletContext sc, final DownloadConfig dc,
-      final GetArchive.AllowedFormats af,
-      @AnonymousCowardName final String acn) {
+  GerritConfigProvider(Realm r,
+      @GerritServerConfig Config gsc,
+      AuthConfig ac,
+      GitWebConfig gwc,
+      SshInfo si,
+      ServletContext sc,
+      GetArchive.AllowedFormats af,
+      @AnonymousCowardName String acn) {
     realm = r;
     cfg = gsc;
     authConfig = ac;
-    downloadConfig = dc;
     archiveFormats = af;
     gitWebConfig = gwc;
     sshInfo = si;
-    wildProject = wp;
-    contactStore = cs;
     servletContext = sc;
     anonymousCowardName = acn;
   }
@@ -109,14 +102,9 @@
         break;
     }
     config.setSwitchAccountUrl(cfg.getString("auth", null, "switchAccountUrl"));
-    config.setUseContributorAgreements(authConfig.isUseContributorAgreements());
     config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
     config.setGitHttpUrl(cfg.getString("gerrit", null, "gitHttpUrl"));
-    config.setUseContactInfo(contactStore != null && contactStore.isEnabled());
-    config.setDownloadSchemes(downloadConfig.getDownloadSchemes());
-    config.setDownloadCommands(downloadConfig.getDownloadCommands());
     config.setAuthType(authConfig.getAuthType());
-    config.setWildProject(wildProject);
     config.setDocumentationAvailable(servletContext
         .getResource("/Documentation/index.html") != null);
     config.setAnonymousCowardName(anonymousCowardName);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index ed84caf..c1c3b2b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -54,8 +54,6 @@
 @SuppressWarnings("serial")
 @Singleton
 class BecomeAnyAccountLoginServlet extends HttpServlet {
-  private static final boolean IS_DEV = Boolean.getBoolean("Gerrit.GwtDevMode");
-
   private final SchemaFactory<ReviewDb> schema;
   private final DynamicItem<WebSession> webSession;
   private final AccountManager accountManager;
@@ -120,14 +118,6 @@
       final StringBuilder rdr = new StringBuilder();
       rdr.append(req.getContextPath());
       rdr.append("/");
-      if (IS_DEV && req.getParameter("gwt.codesvr") != null) {
-        if (rdr.indexOf("?") < 0) {
-          rdr.append("?");
-        } else {
-          rdr.append("&");
-        }
-        rdr.append("gwt.codesvr=").append(req.getParameter("gwt.codesvr"));
-      }
 
       if (res.isNew()) {
         rdr.append('#' + PageLinks.REGISTER);
@@ -155,12 +145,6 @@
     if (doc == null) {
       throw new FileNotFoundException("No " + pageName + " in webapp");
     }
-    if (!IS_DEV) {
-      final Element devmode = HtmlDomUtil.find(doc, "gwtdevmode");
-      if (devmode != null) {
-        devmode.getParentNode().removeChild(devmode);
-      }
-    }
 
     Element userlistElement = HtmlDomUtil.find(doc, "userlist");
     ReviewDb db = schema.open();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java
index 15150cf..cf43041 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java
@@ -165,6 +165,8 @@
     myconfFile.setWritable(true, true /* owner only */);
     myconfFile.setReadable(true, true /* owner only */);
 
+    myconfFile.deleteOnExit();
+
     _env.set("GIT_DIR", ".");
     _env.set("GITWEB_CONFIG", myconf.toAbsolutePath().toString());
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
index f3895b8..e0d4b51 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -72,7 +72,6 @@
 public class HostPageServlet extends HttpServlet {
   private static final Logger log =
       LoggerFactory.getLogger(HostPageServlet.class);
-  private static final boolean IS_DEV = Boolean.getBoolean("Gerrit.GwtDevMode");
   private static final String HPD_ID = "gerrit_hostpagedata";
   private static final int DEFAULT_JS_LOAD_TIMEOUT = 5000;
 
@@ -129,32 +128,25 @@
     }
 
     String src = "gerrit_ui/gerrit_ui.nocache.js";
-    if (!IS_DEV) {
-      Element devmode = HtmlDomUtil.find(template, "gwtdevmode");
-      if (devmode != null) {
-        devmode.getParentNode().removeChild(devmode);
-      }
-
-      InputStream in = servletContext.getResourceAsStream("/" + src);
-      if (in != null) {
-        Hasher md = Hashing.md5().newHasher();
+    InputStream in = servletContext.getResourceAsStream("/" + src);
+    if (in != null) {
+      Hasher md = Hashing.md5().newHasher();
+      try {
         try {
-          try {
-            final byte[] buf = new byte[1024];
-            int n;
-            while ((n = in.read(buf)) > 0) {
-              md.putBytes(buf, 0, n);
-            }
-          } finally {
-            in.close();
+          final byte[] buf = new byte[1024];
+          int n;
+          while ((n = in.read(buf)) > 0) {
+            md.putBytes(buf, 0, n);
           }
-        } catch (IOException e) {
-          throw new IOException("Failed reading " + src, e);
+        } finally {
+          in.close();
         }
-        src += "?content=" + md.hash().toString();
-      } else {
-        log.debug("No " + src + " in webapp root; keeping noncache.js URL");
+      } catch (IOException e) {
+        throw new IOException("Failed reading " + src, e);
       }
+      src += "?content=" + md.hash().toString();
+    } else {
+      log.debug("No " + src + " in webapp root; keeping noncache.js URL");
     }
 
     noCacheName = src;
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html
index c660311..23d5856 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html
@@ -1,34 +1,6 @@
 <html>
   <head>
     <title>Gerrit Code Review</title>
-    <script id="gwtdevmode">
-      (function () {
-        var pn = 'gwt.codesvr';
-        var cn = 'gerrit_ui.' + pn;
-
-        var p_start = window.location.search.indexOf(pn + '=');
-        if (p_start != -1) {
-          p_start = p_start + pn.length + 1;
-          var p_end = window.location.search.indexOf(";", p_start);
-          if (p_end == -1) p_end = window.location.search.length;
-          var v = window.location.search.substring(p_start, p_end);
-
-          var e = new Date();
-          e.setDate(e.getDate() + 1);
-          document.cookie = cn + "=" + v + ';expires=' + e.toGMTString();
-
-        } else if (document.cookie.length != 0) {
-          var c_start = document.cookie.indexOf(cn + '=');
-          if (c_start != -1) {
-            c_start = c_start + cn.length + 1;
-            var c_end = document.cookie.indexOf(";", c_start);
-            if (c_end == -1) c_end = document.cookie.length;
-            var v = document.cookie.substring(c_start, c_end);
-            window.location.replace('?' + pn + '=' + v + document.location.hash);
-          }
-        }
-      })();
-    </script>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
   <body>
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html
index 9f3fa1e..d2a333e 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/HostPage.html
@@ -2,34 +2,6 @@
   <head>
     <title>Gerrit Code Review</title>
     <meta name="gwt:property" content="locale=en_US" />
-    <script id="gwtdevmode">
-      (function () {
-        var pn = 'gwt.codesvr';
-        var cn = 'gerrit_ui.' + pn;
-
-        var p_start = window.location.search.indexOf(pn + '=');
-        if (p_start != -1) {
-          p_start = p_start + pn.length + 1;
-          var p_end = window.location.search.indexOf(";", p_start);
-          if (p_end == -1) p_end = window.location.search.length;
-          var v = window.location.search.substring(p_start, p_end);
-
-          var e = new Date();
-          e.setDate(e.getDate() + 1);
-          document.cookie = cn + "=" + v + ';expires=' + e.toGMTString();
-
-        } else if (document.cookie.length != 0) {
-          var c_start = document.cookie.indexOf(cn + '=');
-          if (c_start != -1) {
-            c_start = c_start + cn.length + 1;
-            var c_end = document.cookie.indexOf(";", c_start);
-            if (c_end == -1) c_end = document.cookie.length;
-            var v = document.cookie.substring(c_start, c_end);
-            window.location.replace('?' + pn + '=' + v + document.location.hash);
-          }
-        }
-      })();
-    </script>
     <script id="gerrit_hostpagedata"></script>
     <style id="gerrit_sitecss" type="text/css"></style>
     <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 0e09eb4..64da702 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -31,7 +31,7 @@
 import com.google.common.util.concurrent.ListeningExecutorService;
 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.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
@@ -39,6 +39,7 @@
 import com.google.gerrit.server.index.ChangeField;
 import com.google.gerrit.server.index.ChangeField.ChangeProtoField;
 import com.google.gerrit.server.index.ChangeField.PatchSetApprovalProtoField;
+import com.google.gerrit.server.index.ChangeField.PatchSetProtoField;
 import com.google.gerrit.server.index.ChangeIndex;
 import com.google.gerrit.server.index.FieldDef;
 import com.google.gerrit.server.index.FieldDef.FillArgs;
@@ -51,6 +52,7 @@
 import com.google.gerrit.server.query.QueryParseException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gwtorm.protobuf.ProtobufCodec;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.ResultSet;
 import com.google.inject.Provider;
@@ -98,6 +100,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.sql.Timestamp;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -128,12 +131,13 @@
   private static final String ID_SORT_FIELD =
       sortFieldName(ChangeField.LEGACY_ID);
   private static final String MERGEABLE_FIELD = ChangeField.MERGEABLE.getName();
+  private static final String PATCH_SET_FIELD = ChangeField.PATCH_SET.getName();
   private static final String UPDATED_SORT_FIELD =
       sortFieldName(ChangeField.UPDATED);
 
   private static final ImmutableSet<String> FIELDS = ImmutableSet.of(
       ADDED_FIELD, APPROVAL_FIELD, CHANGE_FIELD, DELETED_FIELD, ID_FIELD,
-      MERGEABLE_FIELD);
+      MERGEABLE_FIELD, PATCH_SET_FIELD);
 
   private static final Map<String, String> CUSTOM_CHAR_MAPPING = ImmutableMap.of(
       "_", " ", ".", " ");
@@ -472,18 +476,19 @@
         cb.bytes, cb.offset, cb.length);
     ChangeData cd = changeDataFactory.create(db.get(), change);
 
-    // Approvals.
-    BytesRef[] approvalsBytes = doc.getBinaryValues(APPROVAL_FIELD);
-    if (approvalsBytes != null) {
-      List<PatchSetApproval> approvals =
-          Lists.newArrayListWithCapacity(approvalsBytes.length);
-      for (BytesRef ab : approvalsBytes) {
-        approvals.add(PatchSetApprovalProtoField.CODEC.decode(
-            ab.bytes, ab.offset, ab.length));
-      }
-      cd.setCurrentApprovals(approvals);
+    // Patch sets.
+    List<PatchSet> patchSets =
+        decodeProtos(doc, PATCH_SET_FIELD, PatchSetProtoField.CODEC);
+    if (!patchSets.isEmpty()) {
+      // Will be an empty list for schemas prior to when this field was stored;
+      // this cannot be valid since a change needs at least one patch set.
+      cd.setPatchSets(patchSets);
     }
 
+    // Approvals.
+    cd.setCurrentApprovals(
+        decodeProtos(doc, APPROVAL_FIELD, PatchSetApprovalProtoField.CODEC));
+
     // Changed lines.
     IndexableField added = doc.getField(ADDED_FIELD);
     IndexableField deleted = doc.getField(DELETED_FIELD);
@@ -504,6 +509,19 @@
     return cd;
   }
 
+  private static <T> List<T> decodeProtos(Document doc, String fieldName,
+      ProtobufCodec<T> codec) {
+    BytesRef[] bytesRefs = doc.getBinaryValues(fieldName);
+    if (bytesRefs.length == 0) {
+      return Collections.emptyList();
+    }
+    List<T> result = new ArrayList<>(bytesRefs.length);
+    for (BytesRef r : bytesRefs) {
+      result.add(codec.decode(r.bytes, r.offset, r.length));
+    }
+    return result;
+  }
+
   private Document toDocument(ChangeData cd) {
     Document result = new Document();
     for (Values<ChangeData> vs : schema.buildFields(cd, fillArgs)) {
diff --git a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java
index 4e4c774..36bca15 100644
--- a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java
+++ b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java
@@ -50,7 +50,9 @@
   protected void doLogout(HttpServletRequest req, HttpServletResponse rsp)
       throws IOException {
     super.doLogout(req, rsp);
-    oauthSession.get().logout();
+    if (req.getSession(false) != null) {
+      oauthSession.get().logout();
+    }
   }
 }
 
diff --git a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
index 739dffe..d24c8a0 100644
--- a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
+++ b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
@@ -26,11 +26,14 @@
 import com.google.gerrit.httpd.CanonicalWebUrl;
 import com.google.gerrit.httpd.WebSession;
 import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountException;
 import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.AuthResult;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.servlet.SessionScoped;
 
 import org.apache.commons.codec.binary.Base64;
@@ -52,18 +55,22 @@
   private static final SecureRandom randomState = newRandomGenerator();
   private final String state;
   private final DynamicItem<WebSession> webSession;
+  private final Provider<IdentifiedUser> identifiedUser;
   private final AccountManager accountManager;
   private final CanonicalWebUrl urlProvider;
   private OAuthServiceProvider serviceProvider;
   private OAuthToken token;
   private OAuthUserInfo user;
   private String redirectToken;
+  private boolean linkMode;
 
   @Inject
   OAuthSession(DynamicItem<WebSession> webSession,
+      Provider<IdentifiedUser> identifiedUser,
       AccountManager accountManager,
       CanonicalWebUrl urlProvider) {
     this.state = generateRandomState();
+    this.identifiedUser = identifiedUser;
     this.webSession = webSession;
     this.accountManager = accountManager;
     this.urlProvider = urlProvider;
@@ -79,10 +86,6 @@
 
   boolean login(HttpServletRequest request, HttpServletResponse response,
       OAuthServiceProvider oauth) throws IOException {
-    if (isLoggedIn()) {
-      return true;
-    }
-
     log.debug("Login " + this);
 
     if (isOAuthFinal(request)) {
@@ -122,46 +125,19 @@
 
   private void authenticateAndRedirect(HttpServletRequest req,
       HttpServletResponse rsp) throws IOException {
-    com.google.gerrit.server.account.AuthRequest areq =
-        new com.google.gerrit.server.account.AuthRequest(user.getExternalId());
+    AuthRequest areq = new AuthRequest(user.getExternalId());
     AuthResult arsp;
     try {
       String claimedIdentifier = user.getClaimedIdentity();
-      Account.Id actualId = accountManager.lookup(user.getExternalId());
       if (!Strings.isNullOrEmpty(claimedIdentifier)) {
-        Account.Id claimedId = accountManager.lookup(claimedIdentifier);
-        if (claimedId != null && actualId != null) {
-          if (claimedId.equals(actualId)) {
-            // Both link to the same account, that's what we expected.
-            log.debug("OAuth2: claimed identity equals current id");
-          } else {
-            // This is (for now) a fatal error. There are two records
-            // for what might be the same user.
-            //
-            log.error("OAuth accounts disagree over user identity:\n"
-                + "  Claimed ID: " + claimedId + " is " + claimedIdentifier
-                + "\n" + "  Delgate ID: " + actualId + " is "
-                + user.getExternalId());
-            rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
-            return;
-          }
-        } else if (claimedId != null && actualId == null) {
-          // Claimed account already exists: link to it.
-          //
-          log.info("OAuth2: linking claimed identity to {}",
-              claimedId.toString());
-          try {
-            accountManager.link(claimedId, areq);
-          } catch (OrmException e) {
-            log.error("Cannot link: " +  user.getExternalId()
-                + " to user identity:\n"
-                + "  Claimed ID: " + claimedId + " is " + claimedIdentifier);
-            rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
-            return;
-          }
+        if (!authenticateWithIdentityClaimedDuringHandshake(areq, rsp,
+            claimedIdentifier)) {
+          return;
         }
-      } else {
-        log.debug("OAuth2: claimed identity is empty");
+      } else if (linkMode) {
+        if (!authenticateWithLinkedIdentity(areq, rsp)) {
+          return;
+        }
       }
       areq.setUserName(user.getUserName());
       areq.setEmailAddress(user.getEmailAddress());
@@ -181,6 +157,59 @@
     rsp.sendRedirect(rdr.toString());
   }
 
+  private boolean authenticateWithIdentityClaimedDuringHandshake(
+      AuthRequest req, HttpServletResponse rsp, String claimedIdentifier)
+      throws AccountException, IOException {
+    Account.Id claimedId = accountManager.lookup(claimedIdentifier);
+    Account.Id actualId = accountManager.lookup(user.getExternalId());
+    if (claimedId != null && actualId != null) {
+      if (claimedId.equals(actualId)) {
+        // Both link to the same account, that's what we expected.
+        log.debug("OAuth2: claimed identity equals current id");
+      } else {
+        // This is (for now) a fatal error. There are two records
+        // for what might be the same user.
+        //
+        log.error("OAuth accounts disagree over user identity:\n"
+            + "  Claimed ID: " + claimedId + " is " + claimedIdentifier
+            + "\n" + "  Delgate ID: " + actualId + " is "
+            + user.getExternalId());
+        rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
+        return false;
+      }
+    } else if (claimedId != null && actualId == null) {
+      // Claimed account already exists: link to it.
+      //
+      log.info("OAuth2: linking claimed identity to {}",
+          claimedId.toString());
+      try {
+        accountManager.link(claimedId, req);
+      } catch (OrmException e) {
+        log.error("Cannot link: " +  user.getExternalId()
+            + " to user identity:\n"
+            + "  Claimed ID: " + claimedId + " is " + claimedIdentifier);
+        rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean authenticateWithLinkedIdentity(AuthRequest areq,
+      HttpServletResponse rsp) throws AccountException, IOException {
+    try {
+      accountManager.link(identifiedUser.get().getAccountId(), areq);
+    } catch (OrmException e) {
+      log.error("Cannot link: " + user.getExternalId()
+          + " to user identity: " + identifiedUser.get().getAccountId());
+      rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
+      return false;
+    } finally {
+      linkMode = false;
+    }
+    return true;
+  }
+
   void logout() {
     token = null;
     user = null;
@@ -224,4 +253,12 @@
   public OAuthServiceProvider getServiceProvider() {
     return serviceProvider;
   }
+
+  public void setLinkMode(boolean linkMode) {
+    this.linkMode = linkMode;
+  }
+
+  public boolean isLinkMode() {
+    return linkMode;
+  }
 }
diff --git a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthWebFilter.java b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthWebFilter.java
index 4021c57..2d73634 100644
--- a/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthWebFilter.java
+++ b/gerrit-oauth/src/main/java/com/google/gerrit/httpd/auth/oauth/OAuthWebFilter.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.httpd.HtmlDomUtil;
 import com.google.gerrit.httpd.LoginUrlToken;
 import com.google.gerrit.httpd.template.SiteHeaderFooter;
-import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -48,7 +47,6 @@
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 
 @Singleton
 /* OAuth web filter uses active OAuth session to perform OAuth requests */
@@ -56,7 +54,6 @@
   static final String GERRIT_LOGIN = "/login";
 
   private final Provider<String> urlProvider;
-  private final Provider<CurrentUser> currentUserProvider;
   private final Provider<OAuthSession> oauthSessionProvider;
   private final DynamicMap<OAuthServiceProvider> oauthServiceProviders;
   private final SiteHeaderFooter header;
@@ -64,12 +61,10 @@
 
   @Inject
   OAuthWebFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider,
-      Provider<CurrentUser> currentUserProvider,
       DynamicMap<OAuthServiceProvider> oauthServiceProviders,
       Provider<OAuthSession> oauthSessionProvider,
       SiteHeaderFooter header) {
     this.urlProvider = urlProvider;
-    this.currentUserProvider = currentUserProvider;
     this.oauthServiceProviders = oauthServiceProviders;
     this.oauthSessionProvider = oauthSessionProvider;
     this.header = header;
@@ -88,30 +83,20 @@
   public void doFilter(ServletRequest request, ServletResponse response,
       FilterChain chain) throws IOException, ServletException {
     HttpServletRequest httpRequest = (HttpServletRequest) request;
-    HttpSession httpSession = ((HttpServletRequest) request).getSession(false);
-    OAuthSession oauthSession = oauthSessionProvider.get();
-    if (currentUserProvider.get().isIdentifiedUser()) {
-      if (httpSession != null) {
-        httpSession.invalidate();
-      }
-      chain.doFilter(request, response);
-      return;
-    } else {
-      if (oauthSession.isLoggedIn()) {
-        oauthSession.logout();
-      }
-    }
-
     HttpServletResponse httpResponse = (HttpServletResponse) response;
 
+    OAuthSession oauthSession = oauthSessionProvider.get();
+    if (request.getParameter("link") != null) {
+      oauthSession.setLinkMode(true);
+      oauthSession.setServiceProvider(null);
+    }
+
     String provider = httpRequest.getParameter("provider");
     OAuthServiceProvider service = ssoProvider == null
         ? oauthSession.getServiceProvider()
         : ssoProvider;
 
-    if ((isGerritLogin(httpRequest)
-        || oauthSession.isOAuthFinal(httpRequest))
-        && !oauthSession.isLoggedIn()) {
+    if (isGerritLogin(httpRequest) || oauthSession.isOAuthFinal(httpRequest)) {
       if (service == null && Strings.isNullOrEmpty(provider)) {
         selectProvider(httpRequest, httpResponse, null);
         return;
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java
index bef165b..b8080c9 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/LoginForm.java
@@ -175,9 +175,9 @@
         oauthSession.logout();
       }
       if ((isGerritLogin(req)
-          || oauthSession.isOAuthFinal(req))
-          && !oauthSession.isLoggedIn()) {
+          || oauthSession.isOAuthFinal(req))) {
         oauthSession.setServiceProvider(oauthProvider);
+        oauthSession.setLinkMode(link);
         oauthSession.login(req, res, oauthProvider);
       }
     }
@@ -304,7 +304,7 @@
           oauthServiceProviders.byPlugin(pluginName);
         for (Map.Entry<String, Provider<OAuthServiceProvider>> e
             : m.entrySet()) {
-          addProvider(providers, pluginName, e.getKey(),
+          addProvider(providers, link, pluginName, e.getKey(),
               e.getValue().get().getName());
         }
     }
@@ -327,13 +327,18 @@
     }
   }
 
-  private static void addProvider(Element form, String pluginName,
-      String id, String serviceName) {
+  private static void addProvider(Element form, boolean link,
+      String pluginName, String id, String serviceName) {
     Element div = form.getOwnerDocument().createElement("div");
     div.setAttribute("id", id);
     Element hyperlink = form.getOwnerDocument().createElement("a");
-    hyperlink.setAttribute("href", String.format("?id=%s_%s",
+    StringBuilder u = new StringBuilder(String.format("?id=%s_%s",
         pluginName, id));
+    if (link) {
+      u.append("&link");
+    }
+    hyperlink.setAttribute("href", u.toString());
+
     hyperlink.setTextContent(serviceName +
         " (" + pluginName + " plugin)");
     div.appendChild(hyperlink);
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
index a02f52d..6d129bf 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
@@ -27,11 +27,13 @@
 import com.google.gerrit.httpd.LoginUrlToken;
 import com.google.gerrit.httpd.WebSession;
 import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountException;
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AuthResult;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.servlet.SessionScoped;
 
 import org.apache.commons.codec.binary.Base64;
@@ -55,19 +57,23 @@
   private static final SecureRandom randomState = newRandomGenerator();
   private final String state;
   private final DynamicItem<WebSession> webSession;
+  private final Provider<IdentifiedUser> identifiedUser;
   private final AccountManager accountManager;
   private final CanonicalWebUrl urlProvider;
   private OAuthServiceProvider serviceProvider;
   private OAuthToken token;
   private OAuthUserInfo user;
   private String redirectToken;
+  private boolean linkMode;
 
   @Inject
   OAuthSessionOverOpenID(DynamicItem<WebSession> webSession,
+      Provider<IdentifiedUser> identifiedUser,
       AccountManager accountManager,
       CanonicalWebUrl urlProvider) {
     this.state = generateRandomState();
     this.webSession = webSession;
+    this.identifiedUser = identifiedUser;
     this.accountManager = accountManager;
     this.urlProvider = urlProvider;
   }
@@ -82,10 +88,6 @@
 
   boolean login(HttpServletRequest request, HttpServletResponse response,
       OAuthServiceProvider oauth) throws IOException {
-    if (isLoggedIn()) {
-      return true;
-    }
-
     log.debug("Login " + this);
 
     if (isOAuthFinal(request)) {
@@ -96,7 +98,6 @@
 
       log.debug("Login-Retrieve-User " + this);
       token = oauth.getAccessToken(new OAuthVerifier(request.getParameter("code")));
-
       user = oauth.getUserInfo(token);
 
       if (isLoggedIn()) {
@@ -124,6 +125,7 @@
     try {
       String claimedIdentifier = user.getClaimedIdentity();
       Account.Id actualId = accountManager.lookup(user.getExternalId());
+      // Use case 1: claimed identity was provided during handshake phase
       if (!Strings.isNullOrEmpty(claimedIdentifier)) {
         Account.Id claimedId = accountManager.lookup(claimedIdentifier);
         if (claimedId != null && actualId != null) {
@@ -153,6 +155,18 @@
             return;
           }
         }
+      } else if (linkMode) {
+        // Use case 2: link mode activated from the UI
+        try {
+          accountManager.link(identifiedUser.get().getAccountId(), areq);
+        } catch (OrmException e) {
+          log.error("Cannot link: " + user.getExternalId()
+              + " to user identity: " + identifiedUser.get().getAccountId());
+          rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
+          return;
+        } finally {
+          linkMode = false;
+        }
       }
       areq.setUserName(user.getUserName());
       areq.setEmailAddress(user.getEmailAddress());
@@ -213,4 +227,12 @@
   public OAuthServiceProvider getServiceProvider() {
     return serviceProvider;
   }
+
+  public void setLinkMode(boolean linkMode) {
+    this.linkMode = linkMode;
+  }
+
+  public boolean isLinkMode() {
+    return linkMode;
+  }
 }
diff --git a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java
index 7766e69..c17079d 100644
--- a/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java
+++ b/gerrit-openid/src/main/java/com/google/gerrit/httpd/auth/openid/OAuthWebFilterOverOpenID.java
@@ -17,7 +17,6 @@
 import com.google.common.collect.Iterables;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.server.CurrentUser;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -34,7 +33,6 @@
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 
 
 /** OAuth web filter uses active OAuth session to perform OAuth requests */
@@ -42,16 +40,13 @@
 class OAuthWebFilterOverOpenID implements Filter {
   static final String GERRIT_LOGIN = "/login";
 
-  private final Provider<CurrentUser> currentUserProvider;
   private final Provider<OAuthSessionOverOpenID> oauthSessionProvider;
   private final DynamicMap<OAuthServiceProvider> oauthServiceProviders;
   private OAuthServiceProvider ssoProvider;
 
   @Inject
-  OAuthWebFilterOverOpenID(Provider<CurrentUser> currentUserProvider,
-      DynamicMap<OAuthServiceProvider> oauthServiceProviders,
+  OAuthWebFilterOverOpenID(DynamicMap<OAuthServiceProvider> oauthServiceProviders,
       Provider<OAuthSessionOverOpenID> oauthSessionProvider) {
-    this.currentUserProvider = currentUserProvider;
     this.oauthServiceProviders = oauthServiceProviders;
     this.oauthSessionProvider = oauthSessionProvider;
   }
@@ -69,15 +64,6 @@
   public void doFilter(ServletRequest request, ServletResponse response,
       FilterChain chain) throws IOException, ServletException {
     HttpServletRequest httpRequest = (HttpServletRequest) request;
-    HttpSession httpSession = ((HttpServletRequest) request).getSession(false);
-    if (currentUserProvider.get().isIdentifiedUser()) {
-      if (httpSession != null) {
-        httpSession.invalidate();
-      }
-      chain.doFilter(request, response);
-      return;
-    }
-
     HttpServletResponse httpResponse = (HttpServletResponse) response;
 
     OAuthSessionOverOpenID oauthSession = oauthSessionProvider.get();
@@ -85,9 +71,7 @@
         ? oauthSession.getServiceProvider()
         : ssoProvider;
 
-    if ((isGerritLogin(httpRequest)
-        || oauthSession.isOAuthFinal(httpRequest))
-        && !oauthSession.isLoggedIn()) {
+    if (isGerritLogin(httpRequest) || oauthSession.isOAuthFinal(httpRequest)) {
         if (service == null) {
           throw new IllegalStateException("service is unknown");
         }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/JythonShell.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/JythonShell.java
index 547c1a9..ff157ce 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/JythonShell.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/JythonShell.java
@@ -191,9 +191,9 @@
           + " is not found or not executable");
       }
     } catch (InvocationTargetException e) {
-      log.error("Exception occured while loading file " + p + " : ", e);
+      log.error("Exception occurred while loading file " + p + " : ", e);
     } catch (SecurityException e) {
-      log.error("SecurityException occured while loading file " + p + " : ", e);
+      log.error("SecurityException occurred while loading file " + p + " : ", e);
     }
   }
 
@@ -204,7 +204,7 @@
         new Object[] { in, p }
       );
     } catch (InvocationTargetException e) {
-      log.error("Exception occured while loading " + p + " : ", e);
+      log.error("Exception occurred while loading " + p + " : ", e);
     }
   }
 
diff --git a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java
index 33da24a..4bf1d81 100644
--- a/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java
+++ b/gerrit-reviewdb/src/test/java/com/google/gerrit/reviewdb/client/PatchSetTest.java
@@ -14,10 +14,7 @@
 
 package com.google.gerrit.reviewdb.client;
 
-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 static com.google.common.truth.Truth.assertThat;
 
 import org.junit.Test;
 
@@ -63,13 +60,13 @@
   }
 
   private static void assertRef(int changeId, int psId, String refName) {
-    assertTrue(PatchSet.isRef(refName));
-    assertEquals(new PatchSet.Id(new Change.Id(changeId), psId),
-        PatchSet.Id.fromRef(refName));
+    assertThat(PatchSet.isRef(refName)).isTrue();
+    assertThat(PatchSet.Id.fromRef(refName))
+        .isEqualTo(new PatchSet.Id(new Change.Id(changeId), psId));
   }
 
   private static void assertNotRef(String refName) {
-    assertFalse(PatchSet.isRef(refName));
-    assertNull(PatchSet.Id.fromRef(refName));
+    assertThat(PatchSet.isRef(refName)).isFalse();
+    assertThat(PatchSet.Id.fromRef(refName)).isNull();
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 169e03b..c9359b4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -50,7 +50,6 @@
 public final class StoredValues {
   public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class);
   public static final StoredValue<ChangeData> CHANGE_DATA = create(ChangeData.class);
-  public static final StoredValue<PatchSet> PATCH_SET = create(PatchSet.class);
 
   // Note: no guarantees are made about the user passed in the ChangeControl; do
   // not depend on this directly. Either use .forUser(otherUser) to get a
@@ -68,11 +67,20 @@
     }
   }
 
+  public static PatchSet getPatchSet(Prolog engine) throws SystemException {
+    ChangeData cd = CHANGE_DATA.get(engine);
+    try {
+      return cd.currentPatchSet();
+    } catch (OrmException e) {
+      throw new SystemException(e.getMessage());
+    }
+  }
+
   public static final StoredValue<PatchSetInfo> PATCH_SET_INFO = new StoredValue<PatchSetInfo>() {
     @Override
     public PatchSetInfo createValue(Prolog engine) {
       Change change = getChange(engine);
-      PatchSet ps = StoredValues.PATCH_SET.get(engine);
+      PatchSet ps = getPatchSet(engine);
       PrologEnvironment env = (PrologEnvironment) engine.control;
       PatchSetInfoFactory patchInfoFactory =
               env.getArgs().getPatchSetInfoFactory();
@@ -88,7 +96,7 @@
     @Override
     public PatchList createValue(Prolog engine) {
       PrologEnvironment env = (PrologEnvironment) engine.control;
-      PatchSet ps = StoredValues.PATCH_SET.get(engine);
+      PatchSet ps = getPatchSet(engine);
       PatchListCache plCache = env.getArgs().getPatchListCache();
       Change change = getChange(engine);
       Project.NameKey projectKey = change.getProject();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
index bd175a9..c31a411 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
@@ -142,7 +142,7 @@
 
   private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd)
       throws OrmException {
-    Collection<PatchSet> patchSets = cd.patches();
+    Collection<PatchSet> patchSets = cd.patchSets();
     TreeMap<Integer, PatchSet> result = Maps.newTreeMap();
     for (PatchSet ps : patchSets) {
       result.put(ps.getId().get(), ps);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index f10837f..305b66d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -17,9 +17,11 @@
 import static com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy.RECEIVE_COMMITS;
 import static com.google.gerrit.server.query.change.ChangeData.asChanges;
 
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Ordering;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.reviewdb.client.Change;
@@ -95,6 +97,17 @@
   private static final Logger log =
       LoggerFactory.getLogger(ChangeUtil.class);
 
+  public static final Function<PatchSet, Integer> TO_PS_ID =
+      new Function<PatchSet, Integer>() {
+        @Override
+        public Integer apply(PatchSet in) {
+          return in.getId().get();
+        }
+      };
+
+  public static final Ordering<PatchSet> PS_ID_ORDER = Ordering.natural()
+    .onResultOf(TO_PS_ID);
+
   /**
    * Generate a new unique identifier for change message entities.
    *
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java
index fb28a55..465c9ba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/access/ListAccess.java
@@ -263,7 +263,7 @@
     }
   }
 
-  public class PermissionInfo {
+  public static class PermissionInfo {
     public String label;
     public Boolean exclusive;
     public Map<String, PermissionRuleInfo> rules;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java
index d457555..34f83f7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AbstractRealm.java
@@ -17,8 +17,8 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Sets;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.Account.FieldName;
+import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.mail.EmailSender;
 import com.google.inject.Inject;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
index 445ac6e..b4ca530 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountDirectory.java
@@ -39,7 +39,7 @@
     USERNAME,
 
     /** Numeric account ID, may be deprecated. */
-    ID;
+    ID
   }
 
   public abstract void fillAccountInfo(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java
new file mode 100644
index 0000000..b12e7ce
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java
@@ -0,0 +1,71 @@
+// 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.account;
+
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.server.git.QueryList;
+import com.google.gerrit.server.git.ValidationError;
+import com.google.gerrit.server.git.VersionedMetaData;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/** Named Queries for user accounts. */
+public class VersionedAccountQueries extends VersionedMetaData {
+  private static final Logger log = LoggerFactory.getLogger(VersionedAccountQueries.class);
+
+  public static VersionedAccountQueries forUser(Account.Id id) {
+    return new VersionedAccountQueries(RefNames.refsUsers(id));
+  }
+
+  private final String ref;
+  private QueryList queryList;
+
+  private VersionedAccountQueries(String ref) {
+    this.ref = ref;
+  }
+
+  @Override
+  protected String getRefName() {
+    return ref;
+  }
+
+  public QueryList getQueryList() {
+    return queryList;
+  }
+
+  @Override
+  protected void onLoad() throws IOException, ConfigInvalidException {
+    ValidationError.Sink errors = new ValidationError.Sink() {
+      @Override
+      public void error(ValidationError error) {
+        log.error("Error parsing file " + QueryList.FILE_NAME + ": " +
+            error.getMessage());
+      }
+    };
+    queryList = QueryList.parse(readUTF8(QueryList.FILE_NAME), errors);
+  }
+
+  @Override
+  protected boolean onSave(CommitBuilder commit) throws IOException,
+      ConfigInvalidException {
+    throw new UnsupportedOperationException("Cannot yet save named queries");
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 8683404..1b420f7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -193,7 +193,7 @@
   public ChangeApi rebase(RebaseInput in) throws RestApiException {
     try {
       return changes.id(rebase.apply(revision, in)._number);
-    } catch (OrmException | EmailException e) {
+    } catch (OrmException | EmailException | IOException e) {
       throw new RestApiException("Cannot rebase ps", e);
     }
   }
@@ -298,6 +298,15 @@
   }
 
   @Override
+  public List<CommentInfo> commentsAsList() throws RestApiException {
+    try {
+      return listComments.getComments(revision);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot retrieve comments", e);
+    }
+  }
+
+  @Override
   public Map<String, List<CommentInfo>> drafts() throws RestApiException {
     try {
       return listDrafts.apply(revision);
@@ -307,6 +316,15 @@
   }
 
   @Override
+  public List<CommentInfo> draftsAsList() throws RestApiException {
+    try {
+      return listDrafts.getComments(revision);
+    } catch (OrmException e) {
+      throw new RestApiException("Cannot retrieve drafts", e);
+    }
+  }
+
+  @Override
   public DraftApi draft(String id) throws RestApiException {
     try {
       return draftFactory.create(drafts.parse(revision,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 41428c0..28614f7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -277,7 +277,7 @@
     ChangeData.ensureChangeLoaded(all);
     if (has(ALL_REVISIONS)) {
       ChangeData.ensureAllPatchSetsLoaded(all);
-    } else {
+    } else if (has(CURRENT_REVISION) || has(MESSAGES)) {
       ChangeData.ensureCurrentPatchSetLoaded(all);
     }
     Set<Change.Id> reviewed = Sets.newHashSet();
@@ -411,15 +411,22 @@
       out.removableReviewers = removableReviewers(ctl, out.labels.values());
     }
 
-    Map<PatchSet.Id, PatchSet> src = loadPatchSets(cd, limitToPsId);
-    if (has(MESSAGES)) {
+    boolean needMessages = has(MESSAGES);
+    boolean needRevisions = has(ALL_REVISIONS)
+        || has(CURRENT_REVISION)
+        || limitToPsId.isPresent();
+    Map<PatchSet.Id, PatchSet> src;
+    if (needMessages || needRevisions) {
+      src = loadPatchSets(cd, limitToPsId);
+    } else {
+      src = null;
+    }
+    if (needMessages) {
       out.messages = messages(ctl, cd, src);
     }
     finish(out);
 
-    if (has(ALL_REVISIONS)
-        || has(CURRENT_REVISION)
-        || limitToPsId.isPresent()) {
+    if (needRevisions) {
       out.revisions = revisions(ctl, src);
       if (out.revisions != null) {
         for (Map.Entry<String, RevisionInfo> entry : out.revisions.entrySet()) {
@@ -442,11 +449,7 @@
     if (cd.getSubmitRecords() != null) {
       return cd.getSubmitRecords();
     }
-    PatchSet ps = cd.currentPatchSet();
-    if (ps == null) {
-      return ImmutableList.of();
-    }
-    cd.setSubmitRecords(new SubmitRuleEvaluator(cd).setPatchSet(ps)
+    cd.setSubmitRecords(new SubmitRuleEvaluator(cd)
         .setFastEvalLabels(true)
         .setAllowDraft(true)
         .evaluate());
@@ -607,8 +610,14 @@
       LabelTypes labelTypes, boolean standard, boolean detailed)
       throws OrmException {
     Set<Account.Id> allUsers = Sets.newHashSet();
-    for (PatchSetApproval psa : cd.approvals().values()) {
-      allUsers.add(psa.getAccountId());
+    if (detailed) {
+      // Users expect to see all reviewers on closed changes, even if they
+      // didn't vote on the latest patch set. If we don't need detailed labels,
+      // we aren't including 0 votes for all users below, so we can just look at
+      // the latest patch set (in the next loop).
+      for (PatchSetApproval psa : cd.approvals().values()) {
+        allUsers.add(psa.getAccountId());
+      }
     }
 
     // We can only approximately reconstruct what the submit rule evaluator
@@ -616,6 +625,7 @@
     Set<String> labelNames = Sets.newHashSet();
     Multimap<Account.Id, PatchSetApproval> current = HashMultimap.create();
     for (PatchSetApproval a : cd.currentApprovals()) {
+      allUsers.add(a.getAccountId());
       LabelType type = labelTypes.byLabel(a.getLabelId());
       if (type != null) {
         labelNames.add(type.getName());
@@ -853,11 +863,11 @@
       Optional<PatchSet.Id> limitToPsId) throws OrmException {
     Collection<PatchSet> src;
     if (has(ALL_REVISIONS) || has(MESSAGES)) {
-      src = cd.patches();
+      src = cd.patchSets();
     } else {
       PatchSet ps;
       if (limitToPsId.isPresent()) {
-        ps = cd.patch(limitToPsId.get());
+        ps = cd.patchSet(limitToPsId.get());
         if (ps == null) {
           throw new OrmException("missing patch set " + limitToPsId.get());
         }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java
index d22d6ff..6e6f6fa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKind.java
@@ -26,5 +26,5 @@
   NO_CODE_CHANGE,
 
   /** Same tree, parent tree, same commit message. */
-  NO_CHANGE;
+  NO_CHANGE
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index 23039aa..8fdd445 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -317,7 +317,7 @@
         repo = repoManager.openRepository(change.getProject());
 
         ChangeData cd = changeDataFactory.create(db, change);
-        Collection<PatchSet> patchSetCollection = cd.patches();
+        Collection<PatchSet> patchSetCollection = cd.patchSets();
         PatchSet priorPs = patch;
         for (PatchSet ps : patchSetCollection) {
           if (ps.getId().get() < patch.getId().get() &&
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index 540f5d0..b8e1178 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.change;
 
+import com.google.common.base.Strings;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -182,9 +183,13 @@
       } else {
         // Change key not found on destination branch. We can create a new
         // change.
+        String newTopic = null;
+        if (!Strings.isNullOrEmpty(change.getTopic())) {
+          newTopic = change.getTopic() + "-" + newDest.getShortName();
+        }
         Change newChange = createNewChange(git, revWalk, changeKey, project,
             destRef, cherryPickCommit, refControl,
-            identifiedUser, change.getTopic() + "-" + newDest.getShortName());
+            identifiedUser, newTopic);
 
         addMessageToSourceChange(change, patch.getId(), destinationBranch,
             cherryPickCommit, identifiedUser, refControl);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
index 46fabd5..b155b84 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
@@ -16,7 +16,9 @@
 
 import static com.google.gerrit.server.PatchLineCommentsUtil.COMMENT_INFO_ORDER;
 
+import com.google.common.base.Function;
 import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
 import com.google.gerrit.extensions.client.Comment.Range;
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.common.CommentInfo;
@@ -96,6 +98,27 @@
     return out;
   }
 
+  List<CommentInfo> formatAsList(Iterable<PatchLineComment> l)
+      throws OrmException {
+    final AccountLoader accountLoader = fillAccounts
+        ? accountLoaderFactory.create(true)
+        : null;
+    List<CommentInfo> out = FluentIterable
+        .from(l)
+        .transform(new Function<PatchLineComment, CommentInfo>() {
+          @Override
+          public CommentInfo apply(PatchLineComment c) {
+            return toCommentInfo(c, accountLoader);
+          }
+        }).toSortedList(COMMENT_INFO_ORDER);
+
+    if (accountLoader != null) {
+      accountLoader.fill();
+    }
+
+    return out;
+  }
+
   private CommentInfo toCommentInfo(PatchLineComment c, AccountLoader loader) {
     CommentInfo r = new CommentInfo();
     if (fillPatchSet) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 264a3f7..87a1ca6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -14,13 +14,14 @@
 
 package com.google.gerrit.server.change;
 
+import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
+import static com.google.gerrit.server.ChangeUtil.TO_PS_ID;
+
 import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.changes.FixInput;
 import com.google.gerrit.extensions.common.ProblemInfo;
@@ -210,17 +211,6 @@
     }
   }
 
-  private static final Function<PatchSet, Integer> TO_PS_ID =
-      new Function<PatchSet, Integer>() {
-        @Override
-        public Integer apply(PatchSet in) {
-          return in.getId().get();
-        }
-      };
-
-  private static final Ordering<PatchSet> PS_ID_ORDER = Ordering.natural()
-    .onResultOf(TO_PS_ID);
-
   private boolean checkPatchSets() {
     List<PatchSet> all;
     try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
index 6cdae44..7f49192 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
@@ -144,7 +144,7 @@
     Map<PatchSet.Id, PatchSet> r =
         Maps.newHashMapWithExpectedSize(cds.size() * 2);
     for (ChangeData cd : cds) {
-      for (PatchSet p : cd.patches()) {
+      for (PatchSet p : cd.patchSets()) {
         r.put(p.getId(), p);
       }
     }
@@ -277,6 +277,9 @@
     public Integer _revisionNumber;
     public Integer _currentRevisionNumber;
 
+    public ChangeAndCommit() {
+    }
+
     ChangeAndCommit(@Nullable Change change, @Nullable PatchSet ps, RevCommit c) {
       if (change != null) {
         changeId = change.getKey().get();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
index 1ce898d..ef12b2a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
@@ -59,4 +59,11 @@
         .setFillAccounts(includeAuthorInfo())
         .format(listComments(rsrc));
   }
+
+  public List<CommentInfo> getComments(RevisionResource rsrc)
+      throws OrmException {
+    return commentJson.get()
+        .setFillAccounts(includeAuthorInfo())
+        .formatAsList(listComments(rsrc));
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
index 1e9bf2a..5aacef7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.change;
 
+import com.google.common.primitives.Ints;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.RebaseInput;
 import com.google.gerrit.extensions.client.ListChangesOption;
@@ -26,9 +27,10 @@
 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.PatchSetAncestor;
+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.GitRepositoryManager;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchChangeException;
@@ -37,26 +39,32 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.util.ArrayList;
 
 @Singleton
 public class Rebase implements RestModifyView<RevisionResource, RebaseInput>,
     UiAction<RevisionResource> {
 
-  private static final Logger log =
-      LoggerFactory.getLogger(Rebase.class);
+  private static final Logger log = LoggerFactory.getLogger(Rebase.class);
 
+  private final GitRepositoryManager repoManager;
   private final Provider<RebaseChange> rebaseChange;
   private final ChangeJson json;
   private final Provider<ReviewDb> dbProvider;
 
   @Inject
-  public Rebase(Provider<RebaseChange> rebaseChange, ChangeJson json,
+  public Rebase(GitRepositoryManager repoManager,
+      Provider<RebaseChange> rebaseChange,
+      ChangeJson json,
       Provider<ReviewDb> dbProvider) {
+    this.repoManager = repoManager;
     this.rebaseChange = rebaseChange;
     this.json = json
         .addOption(ListChangesOption.CURRENT_REVISION)
@@ -67,68 +75,24 @@
   @Override
   public ChangeInfo apply(RevisionResource rsrc, RebaseInput input)
       throws AuthException, ResourceNotFoundException,
-      ResourceConflictException, EmailException, OrmException {
+      ResourceConflictException, EmailException, OrmException, IOException {
     ChangeControl control = rsrc.getControl();
     Change change = rsrc.getChange();
-    if (!control.canRebase()) {
-      throw new AuthException("rebase not permitted");
-    } else if (!change.getStatus().isOpen()) {
-      throw new ResourceConflictException("change is "
-          + change.getStatus().name().toLowerCase());
-    } else if (!hasOneParent(rsrc.getPatchSet().getId())) {
-      throw new ResourceConflictException(
-          "cannot rebase merge commits or commit with no ancestor");
-    }
-
-    String baseRev = null;
-    if (input != null && input.base != null) {
-      String base = input.base.trim();
-      do {
-        if (base.equals("")) {
-          // remove existing dependency to other patch set
-          baseRev = change.getDest().get();
-          break;
-        }
-
-        ReviewDb db = dbProvider.get();
-        PatchSet basePatchSet = parseBase(base);
-        if (basePatchSet == null) {
-          throw new ResourceConflictException("base revision is missing: " + base);
-        } else if (!rsrc.getControl().isPatchVisible(basePatchSet, db)) {
-          throw new AuthException("base revision not accessible: " + base);
-        } else if (change.getId().equals(basePatchSet.getId().getParentKey())) {
-          throw new ResourceConflictException("cannot depend on self");
-        }
-
-        Change baseChange = db.changes().get(basePatchSet.getId().getParentKey());
-        if (baseChange != null) {
-          if (!baseChange.getProject().equals(change.getProject())) {
-            throw new ResourceConflictException("base change is in wrong project: "
-                                                + baseChange.getProject());
-          } else if (!baseChange.getDest().equals(change.getDest())) {
-            throw new ResourceConflictException("base change is targetting wrong branch: "
-                                                + baseChange.getDest());
-          } else if (baseChange.getStatus() == Status.ABANDONED) {
-            throw new ResourceConflictException("base change is abandoned: "
-                                                + baseChange.getKey());
-          } else if (isDescendantOf(baseChange.getId(), rsrc.getPatchSet().getRevision())) {
-            throw new ResourceConflictException("base change " + baseChange.getKey()
-                                                + " is a descendant of the current "
-                                                + " change - recursion not allowed");
-          }
-          baseRev = basePatchSet.getRevision().get();
-          break;
-        }
-      } while (false);  // just wanted to use the break statement
-    }
-
-    try {
-      rebaseChange.get().rebase(change, rsrc.getPatchSet().getId(),
-          rsrc.getUser(), baseRev);
+    try (Repository repo = repoManager.openRepository(change.getProject());
+        RevWalk rw = new RevWalk(repo)) {
+      if (!control.canRebase()) {
+        throw new AuthException("rebase not permitted");
+      } else if (!change.getStatus().isOpen()) {
+        throw new ResourceConflictException("change is "
+            + change.getStatus().name().toLowerCase());
+      } else if (!hasOneParent(rw, rsrc.getPatchSet())) {
+        throw new ResourceConflictException(
+            "cannot rebase merge commits or commit with no ancestor");
+      }
+      rebaseChange.get().rebase(repo, rw, change, rsrc.getPatchSet().getId(),
+          rsrc.getUser(), findBaseRev(rw, rsrc, input));
     } catch (InvalidChangeOperationException e) {
       throw new ResourceConflictException(e.getMessage());
-    } catch (IOException e) {
-      throw new ResourceConflictException(e.getMessage());
     } catch (NoSuchChangeException e) {
       throw new ResourceNotFoundException(change.getId().toString());
     }
@@ -136,89 +100,118 @@
     return json.format(change.getId());
   }
 
-  private boolean isDescendantOf(Change.Id child, RevId ancestor)
-      throws OrmException {
-    ReviewDb db = dbProvider.get();
-
-    ArrayList<RevId> parents = new ArrayList<>();
-    parents.add(ancestor);
-    while (!parents.isEmpty()) {
-      RevId parent = parents.remove(0);
-      // get direct descendants of change
-      for (PatchSetAncestor desc : db.patchSetAncestors().descendantsOf(parent)) {
-        PatchSet descPatchSet = db.patchSets().get(desc.getPatchSet());
-        Change.Id descChangeId = descPatchSet.getId().getParentKey();
-        if (child.equals(descChangeId)) {
-          PatchSet.Id descCurrentPatchSetId =
-              db.changes().get(descChangeId).currentPatchSetId();
-          // it's only bad if the descendant patch set is current
-          return descPatchSet.getId().equals(descCurrentPatchSetId);
-        } else {
-          // process indirect descendants as well
-          parents.add(descPatchSet.getRevision());
-        }
-      }
+  private String findBaseRev(RevWalk rw, RevisionResource rsrc,
+      RebaseInput input) throws AuthException, ResourceConflictException,
+      OrmException, IOException {
+    if (input == null || input.base == null) {
+      return null;
     }
 
-    return false;
+    Change change = rsrc.getChange();
+    String base = input.base.trim();
+    if (base.equals("")) {
+      // remove existing dependency to other patch set
+      return change.getDest().get();
+    }
+
+    ReviewDb db = dbProvider.get();
+    PatchSet basePatchSet = parseBase(base);
+    if (basePatchSet == null) {
+      throw new ResourceConflictException("base revision is missing: " + base);
+    } else if (!rsrc.getControl().isPatchVisible(basePatchSet, db)) {
+      throw new AuthException("base revision not accessible: " + base);
+    } else if (change.getId().equals(basePatchSet.getId().getParentKey())) {
+      throw new ResourceConflictException("cannot depend on self");
+    }
+
+    Change baseChange = db.changes().get(basePatchSet.getId().getParentKey());
+    if (baseChange == null) {
+      return null;
+    }
+    if (!baseChange.getProject().equals(change.getProject())) {
+      throw new ResourceConflictException(
+          "base change is in wrong project: " + baseChange.getProject());
+    } else if (!baseChange.getDest().equals(change.getDest())) {
+      throw new ResourceConflictException(
+          "base change is targeting wrong branch: " + baseChange.getDest());
+    } else if (baseChange.getStatus() == Status.ABANDONED) {
+      throw new ResourceConflictException(
+          "base change is abandoned: " + baseChange.getKey());
+    } else if (isMergedInto(rw, rsrc.getPatchSet(), basePatchSet)) {
+      throw new ResourceConflictException(
+          "base change " + baseChange.getKey()
+          + " is a descendant of the current  change - recursion not allowed");
+    }
+    return basePatchSet.getRevision().get();
   }
 
-  private PatchSet parseBase(final String base) throws OrmException {
+  private boolean isMergedInto(RevWalk rw, PatchSet base, PatchSet tip)
+      throws IOException {
+    ObjectId baseId = ObjectId.fromString(base.getRevision().get());
+    ObjectId tipId = ObjectId.fromString(tip.getRevision().get());
+    return rw.isMergedInto(rw.parseCommit(baseId), rw.parseCommit(tipId));
+  }
+
+  private PatchSet parseBase(String base) throws OrmException {
     ReviewDb db = dbProvider.get();
 
     PatchSet.Id basePatchSetId = PatchSet.Id.fromRef(base);
     if (basePatchSetId != null) {
-      // try parsing the base as a ref string
+      // Try parsing the base as a ref string.
       return db.patchSets().get(basePatchSetId);
     }
 
-    // try parsing base as a change number (assume current patch set)
+    // Try parsing base as a change number (assume current patch set).
     PatchSet basePatchSet = null;
-    try {
-      Change.Id baseChangeId = Change.Id.parse(base);
-      if (baseChangeId != null) {
-        for (PatchSet ps : db.patchSets().byChange(baseChangeId)) {
-          if (basePatchSet == null || basePatchSet.getId().get() < ps.getId().get()){
-            basePatchSet = ps;
-          }
-        }
-      }
-    } catch (NumberFormatException e) {  // probably a SHA1
-    }
-
-    // try parsing as SHA1
-    if (basePatchSet == null) {
-      for (PatchSet ps : db.patchSets().byRevision(new RevId(base))) {
-        if (basePatchSet == null || basePatchSet.getId().get() < ps.getId().get()) {
+    Integer baseChangeId = Ints.tryParse(base);
+    if (baseChangeId != null) {
+      for (PatchSet ps : db.patchSets().byChange(new Change.Id(baseChangeId))) {
+        if (basePatchSet == null
+            || basePatchSet.getId().get() < ps.getId().get()) {
           basePatchSet = ps;
         }
       }
+      if (basePatchSet != null) {
+        return basePatchSet;
+      }
     }
 
+    // Try parsing as SHA-1.
+    for (PatchSet ps : db.patchSets().byRevision(new RevId(base))) {
+      if (basePatchSet == null
+          || basePatchSet.getId().get() < ps.getId().get()) {
+        basePatchSet = ps;
+      }
+    }
     return basePatchSet;
   }
 
-  private boolean hasOneParent(final PatchSet.Id patchSetId) {
-    try {
-      // prevent rebase of exotic changes (merge commit, no ancestor).
-      return (dbProvider.get().patchSetAncestors()
-          .ancestorsOf(patchSetId).toList().size() == 1);
-    } catch (OrmException e) {
-      log.error("Failed to get ancestors of patch set "
-          + patchSetId.toRefName(), e);
-      return false;
-    }
+  private boolean hasOneParent(RevWalk rw, PatchSet ps) throws IOException {
+    // Prevent rebase of exotic changes (merge commit, no ancestor).
+    RevCommit c = rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+    return c.getParentCount() == 1;
   }
 
   @Override
   public UiAction.Description getDescription(RevisionResource resource) {
+    Project.NameKey project = resource.getChange().getProject();
+    boolean visible = resource.getChange().getStatus().isOpen()
+          && resource.isCurrent()
+          && resource.getControl().canRebase();
+    if (visible) {
+      try (Repository repo = repoManager.openRepository(project);
+          RevWalk rw = new RevWalk(repo)) {
+        visible = hasOneParent(rw, resource.getPatchSet());
+      } catch (IOException e) {
+        log.error("Failed to get ancestors of patch set "
+            + resource.getPatchSet().getId(), e);
+        visible = false;
+      }
+    }
     UiAction.Description descr = new UiAction.Description()
       .setLabel("Rebase")
       .setTitle("Rebase onto tip of branch or parent change")
-      .setVisible(resource.getChange().getStatus().isOpen()
-          && resource.isCurrent()
-          && resource.getControl().canRebase()
-          && hasOneParent(resource.getPatchSet().getId()));
+      .setVisible(visible);
     if (descr.isVisible()) {
       // Disable the rebase button in the RebaseDialog if
       // the change cannot be rebased.
@@ -239,7 +232,7 @@
     @Override
     public ChangeInfo apply(ChangeResource rsrc, RebaseInput input)
         throws AuthException, ResourceNotFoundException,
-        ResourceConflictException, EmailException, OrmException {
+        ResourceConflictException, EmailException, OrmException, IOException {
       PatchSet ps =
           rebase.dbProvider.get().patchSets()
               .get(rsrc.getChange().currentPatchSetId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java
index 5286a4a..96b513e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChange.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetAncestor;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -50,13 +49,16 @@
 import org.eclipse.jgit.merge.ThreeWayMerger;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.util.List;
 import java.util.TimeZone;
 
 @Singleton
 public class RebaseChange {
+  private static final Logger log = LoggerFactory.getLogger(RebaseChange.class);
+
   private final ChangeControl.GenericFactory changeControlFactory;
   private final Provider<ReviewDb> db;
   private final GitRepositoryManager gitManager;
@@ -65,12 +67,12 @@
   private final PatchSetInserter.Factory patchSetInserterFactory;
 
   @Inject
-  RebaseChange(final ChangeControl.GenericFactory changeControlFactory,
-      final Provider<ReviewDb> db,
-      @GerritPersonIdent final PersonIdent myIdent,
-      final GitRepositoryManager gitManager,
-      final MergeUtil.Factory mergeUtilFactory,
-      final PatchSetInserter.Factory patchSetInserterFactory) {
+  RebaseChange(ChangeControl.GenericFactory changeControlFactory,
+      Provider<ReviewDb> db,
+      @GerritPersonIdent PersonIdent myIdent,
+      GitRepositoryManager gitManager,
+      MergeUtil.Factory mergeUtilFactory,
+      PatchSetInserter.Factory patchSetInserterFactory) {
     this.changeControlFactory = changeControlFactory;
     this.db = db;
     this.gitManager = gitManager;
@@ -80,64 +82,64 @@
   }
 
   /**
-   * Rebases the change of the given patch set.
-   *
+   * Rebase the change of the given patch set.
+   * <p>
    * It is verified that the current user is allowed to do the rebase.
-   *
+   * <p>
    * If the patch set has no dependency to an open change, then the change is
    * rebased on the tip of the destination branch.
-   *
+   * <p>
    * If the patch set depends on an open change, it is rebased on the latest
    * patch set of this change.
-   *
+   * <p>
    * The rebased commit is added as new patch set to the change.
-   *
+   * <p>
    * E-mail notification and triggering of hooks happens for the creation of the
    * new patch set.
    *
-   * @param change the change to perform the rebase for
-   * @param patchSetId the id of the patch set
-   * @param uploader the user that creates the rebased patch set
-   * @param newBaseRev the commit that should be the new base
-   * @throws NoSuchChangeException thrown if the change to which the patch set
-   *         belongs does not exist or is not visible to the user
-   * @throws EmailException thrown if sending the e-mail to notify about the new
-   *         patch set fails
-   * @throws OrmException thrown in case accessing the database fails
-   * @throws IOException thrown if rebase is not possible or not needed
-   * @throws InvalidChangeOperationException thrown if rebase is not allowed
+   * @param git the repository.
+   * @param rw the RevWalk.
+   * @param change the change to rebase.
+   * @param patchSetId the patch set ID to rebase.
+   * @param uploader the user that creates the rebased patch set.
+   * @param newBaseRev the commit that should be the new base.
+   * @throws NoSuchChangeException if the change to which the patch set belongs
+   *     does not exist or is not visible to the user.
+   * @throws EmailException if sending the e-mail to notify about the new patch
+   *     set fails.
+   * @throws OrmException if accessing the database fails.
+   * @throws IOException if accessing the repository fails.
+   * @throws InvalidChangeOperationException if rebase is not possible or not
+   *     allowed.
    */
-  public void rebase(Change change, PatchSet.Id patchSetId, final IdentifiedUser uploader,
-      final String newBaseRev) throws NoSuchChangeException, EmailException, OrmException,
-      IOException, InvalidChangeOperationException {
-    final Change.Id changeId = patchSetId.getParentKey();
-    final ChangeControl changeControl =
+  public void rebase(Repository git, RevWalk rw, Change change,
+      PatchSet.Id patchSetId, IdentifiedUser uploader, String newBaseRev)
+      throws NoSuchChangeException, EmailException, OrmException, IOException,
+      InvalidChangeOperationException {
+    Change.Id changeId = patchSetId.getParentKey();
+    ChangeControl changeControl =
         changeControlFactory.validateFor(change, uploader);
     if (!changeControl.canRebase()) {
-      throw new InvalidChangeOperationException(
-          "Cannot rebase: New patch sets are not allowed to be added to change: "
-              + changeId.toString());
+      throw new InvalidChangeOperationException("Cannot rebase: New patch sets"
+          + " are not allowed to be added to change: " + changeId);
     }
-    try (Repository git = gitManager.openRepository(change.getProject());
-        RevWalk rw = new RevWalk(git);
-        ObjectInserter inserter = git.newObjectInserter()) {
+    try (ObjectInserter inserter = git.newObjectInserter()) {
       String baseRev = newBaseRev;
       if (baseRev == null) {
-          baseRev = findBaseRevision(patchSetId, db.get(),
-              change.getDest(), git, null, null, null);
+        baseRev = findBaseRevision(
+            patchSetId, db.get(), change.getDest(), git, rw);
       }
       ObjectId baseObjectId = git.resolve(baseRev);
       if (baseObjectId == null) {
         throw new InvalidChangeOperationException(
           "Cannot rebase: Failed to resolve baseRev: " + baseRev);
       }
-      final RevCommit baseCommit = rw.parseCommit(baseObjectId);
+      RevCommit baseCommit = rw.parseCommit(baseObjectId);
 
       PersonIdent committerIdent =
-          uploader.newCommitterIdent(TimeUtil.nowTs(),
-              serverTimeZone);
+          uploader.newCommitterIdent(TimeUtil.nowTs(), serverTimeZone);
 
-      rebase(git, rw, inserter, patchSetId, change,
+      rebase(git, rw, inserter, change, patchSetId,
           uploader, baseCommit, mergeUtilFactory.create(
               changeControl.getProjectControl().getProjectState(), true),
           committerIdent, true, ValidatePolicy.GERRIT);
@@ -147,158 +149,150 @@
   }
 
   /**
-   * Finds the revision of commit on which the given patch set should be based.
+   * Find the commit onto which a patch set should be rebased.
+   * <p>
+   * This is defined as the latest patch set of the change corresponding to
+   * this commit's parent, or the destination branch tip in the case where the
+   * parent's change is merged.
    *
-   * @param patchSetId the id of the patch set for which the new base commit
-   *        should be found
-   * @param db the ReviewDb
-   * @param destBranch the destination branch
-   * @param git the repository
-   * @param patchSetAncestors the original PatchSetAncestor of the given patch
-   *        set that should be based
-   * @param depPatchSetList the original patch set list on which the rebased
-   *        patch set depends
-   * @param depChangeList the original change list on whose patch set the
-   *        rebased patch set depends
-   * @return the revision of commit on which the given patch set should be based
-   * @throws IOException thrown if rebase is not possible or not needed
-   * @throws OrmException thrown in case accessing the database fails
+   * @param patchSetId patch set ID for which the new base commit should be
+   *     found.
+   * @param db the ReviewDb.
+   * @param destBranch the destination branch.
+   * @param git the repository.
+   * @param rw the RevWalk.
+   * @return the commit onto which the patch set should be rebased.
+   * @throws InvalidChangeOperationException if rebase is not possible or not
+   *     allowed.
+   * @throws IOException if accessing the repository fails.
+   * @throws OrmException if accessing the database fails.
    */
-    private static String findBaseRevision(final PatchSet.Id patchSetId,
-        final ReviewDb db, final Branch.NameKey destBranch, final Repository git,
-        List<PatchSetAncestor> patchSetAncestors, List<PatchSet> depPatchSetList,
-        List<Change> depChangeList) throws IOException, OrmException {
+  private static String findBaseRevision(PatchSet.Id patchSetId,
+      ReviewDb db, Branch.NameKey destBranch, Repository git, RevWalk rw)
+      throws InvalidChangeOperationException, IOException, OrmException {
+    String baseRev = null;
 
-      String baseRev = null;
+    PatchSet patchSet = db.patchSets().get(patchSetId);
+    if (patchSet == null) {
+      throw new InvalidChangeOperationException(
+          "Patch set " + patchSetId + " not found");
+    }
+    RevCommit commit = rw.parseCommit(
+        ObjectId.fromString(patchSet.getRevision().get()));
 
-      if (patchSetAncestors == null) {
-        patchSetAncestors =
-            db.patchSetAncestors().ancestorsOf(patchSetId).toList();
-      }
-
-      if (patchSetAncestors.size() > 1) {
-        throw new IOException(
-            "Cannot rebase a change with multiple parents. Parent commits: "
-                + patchSetAncestors.toString());
-      }
-      if (patchSetAncestors.size() == 0) {
-        throw new IOException(
-            "Cannot rebase a change without any parents (is this the initial commit?).");
-      }
-
-      RevId ancestorRev = patchSetAncestors.get(0).getAncestorRevision();
-      if (depPatchSetList == null || depPatchSetList.size() != 1 ||
-          !depPatchSetList.get(0).getRevision().equals(ancestorRev)) {
-        depPatchSetList = db.patchSets().byRevision(ancestorRev).toList();
-      }
-
-      for (PatchSet depPatchSet : depPatchSetList) {
-
-        Change.Id depChangeId = depPatchSet.getId().getParentKey();
-        Change depChange;
-        if (depChangeList == null || depChangeList.size() != 1 ||
-            !depChangeList.get(0).getId().equals(depChangeId)) {
-          depChange = db.changes().get(depChangeId);
-        } else {
-          depChange = depChangeList.get(0);
-        }
-        if (!depChange.getDest().equals(destBranch)) {
-          continue;
-        }
-
-        if (depChange.getStatus() == Status.ABANDONED) {
-          throw new IOException("Cannot rebase a change with an abandoned parent: "
-              + depChange.getKey().toString());
-        }
-
-        if (depChange.getStatus().isOpen()) {
-          if (depPatchSet.getId().equals(depChange.currentPatchSetId())) {
-            throw new IOException(
-                "Change is already based on the latest patch set of the dependent change.");
-          }
-          PatchSet latestDepPatchSet =
-              db.patchSets().get(depChange.currentPatchSetId());
-          baseRev = latestDepPatchSet.getRevision().get();
-        }
-        break;
-      }
-
-      if (baseRev == null) {
-        // We are dependent on a merged PatchSet or have no PatchSet
-        // dependencies at all.
-        Ref destRef = git.getRef(destBranch.get());
-        if (destRef == null) {
-          throw new IOException(
-              "The destination branch does not exist: "
-                  + destBranch.get());
-        }
-        baseRev = destRef.getObjectId().getName();
-        if (baseRev.equals(ancestorRev.get())) {
-          throw new IOException("Change is already up to date.");
-        }
-      }
-      return baseRev;
+    if (commit.getParentCount() > 1) {
+      throw new InvalidChangeOperationException(
+          "Cannot rebase a change with multiple parents.");
+    } else if (commit.getParentCount() == 0) {
+      throw new InvalidChangeOperationException(
+          "Cannot rebase a change without any parents"
+          + " (is this the initial commit?).");
     }
 
+    RevId parentRev = new RevId(commit.getParent(0).name());
+
+    for (PatchSet depPatchSet : db.patchSets().byRevision(parentRev)) {
+      Change.Id depChangeId = depPatchSet.getId().getParentKey();
+      Change depChange = db.changes().get(depChangeId);
+      if (!depChange.getDest().equals(destBranch)) {
+        continue;
+      }
+
+      if (depChange.getStatus() == Status.ABANDONED) {
+        throw new InvalidChangeOperationException(
+            "Cannot rebase a change with an abandoned parent: "
+            + depChange.getKey());
+      }
+
+      if (depChange.getStatus().isOpen()) {
+        if (depPatchSet.getId().equals(depChange.currentPatchSetId())) {
+          throw new InvalidChangeOperationException(
+              "Change is already based on the latest patch set of the"
+              + " dependent change.");
+        }
+        PatchSet latestDepPatchSet =
+            db.patchSets().get(depChange.currentPatchSetId());
+        baseRev = latestDepPatchSet.getRevision().get();
+      }
+      break;
+    }
+
+    if (baseRev == null) {
+      // We are dependent on a merged PatchSet or have no PatchSet
+      // dependencies at all.
+      Ref destRef = git.getRef(destBranch.get());
+      if (destRef == null) {
+        throw new InvalidChangeOperationException(
+            "The destination branch does not exist: " + destBranch.get());
+      }
+      baseRev = destRef.getObjectId().getName();
+      if (baseRev.equals(parentRev.get())) {
+        throw new InvalidChangeOperationException(
+            "Change is already up to date.");
+      }
+    }
+    return baseRev;
+  }
+
   /**
-   * Rebases the change of the given patch set on the given base commit.
-   *
+   * Rebase the change of the given patch set on the given base commit.
+   * <p>
    * The rebased commit is added as new patch set to the change.
+   * <p>
+   * E-mail notification and triggering of hooks is only done for the creation
+   * of the new patch set if {@code sendEmail} and {@code runHooks} are true,
+   * respectively.
    *
-   * E-mail notification and triggering of hooks is only done for the creation of
-   * the new patch set if `sendEmail` and `runHooks` are set to true.
-   *
-   * @param git the repository
-   * @param revWalk the RevWalk
-   * @param inserter the object inserter
-   * @param patchSetId the id of the patch set
-   * @param change the change that should be rebased
-   * @param uploader the user that creates the rebased patch set
-   * @param baseCommit the commit that should be the new base
-   * @param mergeUtil merge utilities for the destination project
-   * @param committerIdent the committer's identity
-   * @param runHooks if hooks should be run for the new patch set
-   * @param validate if commit validation should be run for the new patch set
-   * @return the new patch set which is based on the given base commit
-   * @throws NoSuchChangeException thrown if the change to which the patch set
-   *         belongs does not exist or is not visible to the user
-   * @throws OrmException thrown in case accessing the database fails
-   * @throws IOException thrown if rebase is not possible or not needed
-   * @throws InvalidChangeOperationException thrown if rebase is not allowed
+   * @param git the repository.
+   * @param inserter the object inserter.
+   * @param change the change to rebase.
+   * @param patchSetId the patch set ID to rebase.
+   * @param uploader the user that creates the rebased patch set.
+   * @param baseCommit the commit that should be the new base.
+   * @param mergeUtil merge utilities for the destination project.
+   * @param committerIdent the committer's identity.
+   * @param runHooks if hooks should be run for the new patch set.
+   * @param validate if commit validation should be run for the new patch set.
+   * @param rw the RevWalk.
+   * @return the new patch set, which is based on the given base commit.
+   * @throws NoSuchChangeException if the change to which the patch set belongs
+   *     does not exist or is not visible to the user.
+   * @throws OrmException if accessing the database fails.
+   * @throws IOException if rebase is not possible.
+   * @throws InvalidChangeOperationException if rebase is not possible or not
+   *     allowed.
    */
-  public PatchSet rebase(final Repository git, final RevWalk revWalk,
-      final ObjectInserter inserter, final PatchSet.Id patchSetId,
-      final Change change, final IdentifiedUser uploader, final RevCommit baseCommit,
-      final MergeUtil mergeUtil, PersonIdent committerIdent,
-      boolean runHooks, ValidatePolicy validate)
-          throws NoSuchChangeException,
-      OrmException, IOException, InvalidChangeOperationException,
-      MergeConflictException {
+  public PatchSet rebase(Repository git, RevWalk rw,
+      ObjectInserter inserter, Change change, PatchSet.Id patchSetId,
+      IdentifiedUser uploader, RevCommit baseCommit, MergeUtil mergeUtil,
+      PersonIdent committerIdent, boolean runHooks, ValidatePolicy validate)
+      throws NoSuchChangeException, OrmException, IOException,
+      InvalidChangeOperationException, MergeConflictException {
     if (!change.currentPatchSetId().equals(patchSetId)) {
       throw new InvalidChangeOperationException("patch set is not current");
     }
-    final PatchSet originalPatchSet = db.get().patchSets().get(patchSetId);
+    PatchSet originalPatchSet = db.get().patchSets().get(patchSetId);
 
-    final RevCommit rebasedCommit;
+    RevCommit rebasedCommit;
     ObjectId oldId = ObjectId.fromString(originalPatchSet.getRevision().get());
-    ObjectId newId = rebaseCommit(git, inserter, revWalk.parseCommit(oldId),
+    ObjectId newId = rebaseCommit(git, inserter, rw.parseCommit(oldId),
         baseCommit, mergeUtil, committerIdent);
 
-    rebasedCommit = revWalk.parseCommit(newId);
+    rebasedCommit = rw.parseCommit(newId);
 
-    final ChangeControl changeControl =
+    ChangeControl changeControl =
         changeControlFactory.validateFor(change, uploader);
 
     PatchSetInserter patchSetInserter = patchSetInserterFactory
-        .create(git, revWalk, changeControl, rebasedCommit)
+        .create(git, rw, changeControl, rebasedCommit)
         .setValidatePolicy(validate)
         .setDraft(originalPatchSet.isDraft())
         .setUploader(uploader.getAccountId())
         .setSendMail(false)
         .setRunHooks(runHooks);
 
-    final PatchSet.Id newPatchSetId = patchSetInserter.getPatchSetId();
-    final ChangeMessage cmsg = new ChangeMessage(
+    PatchSet.Id newPatchSetId = patchSetInserter.getPatchSetId();
+    ChangeMessage cmsg = new ChangeMessage(
         new ChangeMessage.Key(change.getId(),
             ChangeUtil.messageUUID(db.get())), uploader.getAccountId(),
             TimeUtil.nowTs(), patchSetId);
@@ -328,11 +322,13 @@
    */
   private ObjectId rebaseCommit(Repository git, ObjectInserter inserter,
       RevCommit original, RevCommit base, MergeUtil mergeUtil,
-      PersonIdent committerIdent) throws MergeConflictException, IOException {
+      PersonIdent committerIdent) throws MergeConflictException, IOException,
+      InvalidChangeOperationException {
     RevCommit parentCommit = original.getParent(0);
 
     if (base.equals(parentCommit)) {
-      throw new IOException("Change is already up to date.");
+      throw new InvalidChangeOperationException(
+          "Change is already up to date.");
     }
 
     ThreeWayMerger merger = mergeUtil.newThreeWayMerger(git, inserter);
@@ -365,8 +361,8 @@
         r.getPatchSet().getId(), r.getChange().getDest());
   }
 
-  public boolean canRebase(Project.NameKey project,
-      PatchSet.Id patchSetId, Branch.NameKey branch) {
+  public boolean canRebase(Project.NameKey project, PatchSet.Id patchSetId,
+      Branch.NameKey branch) {
     Repository git;
     try {
       git = gitManager.openRepository(project);
@@ -375,19 +371,14 @@
     } catch (IOException err) {
       return false;
     }
-    try {
-      findBaseRevision(
-          patchSetId,
-          db.get(),
-          branch,
-          git,
-          null,
-          null,
-          null);
+    try (RevWalk rw = new RevWalk(git)) {
+      findBaseRevision(patchSetId, db.get(), branch, git, rw);
       return true;
-    } catch (IOException e) {
+    } catch (InvalidChangeOperationException e) {
       return false;
-    } catch (OrmException e) {
+    } catch (OrmException | IOException e) {
+      log.warn("Error checking if patch set " + patchSetId + " on " + branch
+          + " can be rebased", e);
       return false;
     } finally {
       git.close();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java
index 4120e43..9e243b0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerSuggestionCache.java
@@ -14,22 +14,48 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
+import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.server.AccountExternalIdAccess;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.analysis.util.CharArraySet;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.IntField;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.RAMDirectory;
+import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -44,34 +70,115 @@
 public class ReviewerSuggestionCache {
   private static final Logger log = LoggerFactory
       .getLogger(ReviewerSuggestionCache.class);
-  private final LoadingCache<Boolean, List<Account>> cache;
+
+  private static final String ID = "id";
+  private static final String NAME = "name";
+  private static final String EMAIL = "email";
+  private static final String USERNAME = "username";
+  private static final String[] ALL = {ID, NAME, EMAIL, USERNAME};
+
+  private final LoadingCache<Boolean, IndexSearcher> cache;
+  private final Provider<ReviewDb> db;
 
   @Inject
-  ReviewerSuggestionCache(final Provider<ReviewDb> dbProvider) {
+  ReviewerSuggestionCache(Provider<ReviewDb> db,
+      @GerritServerConfig Config cfg) {
+    this.db = db;
+    long expiration = ConfigUtil.getTimeUnit(cfg,
+        "suggest", null, "fullTextSearchRefresh",
+        TimeUnit.HOURS.toMillis(1),
+        TimeUnit.MILLISECONDS);
     this.cache =
         CacheBuilder.newBuilder().maximumSize(1)
-            .expireAfterWrite(30, TimeUnit.SECONDS)
-            .build(new CacheLoader<Boolean, List<Account>>() {
+            .expireAfterWrite(expiration, TimeUnit.MILLISECONDS)
+            .build(new CacheLoader<Boolean, IndexSearcher>() {
               @Override
-              public List<Account> load(Boolean key) throws Exception {
-                return ImmutableList.copyOf(Iterables.filter(
-                    dbProvider.get().accounts().all(),
-                    new Predicate<Account>() {
-                      @Override
-                      public boolean apply(Account in) {
-                        return in.isActive();
-                      }
-                    }));
+              public IndexSearcher load(Boolean key) throws Exception {
+                return index();
               }
             });
   }
 
-  List<Account> get() {
+  List<AccountInfo> search(String query, int n) throws IOException {
+    IndexSearcher searcher = get();
+    if (searcher == null) {
+      return Collections.emptyList();
+    }
+
+    List<String> segments = Splitter.on(' ').omitEmptyStrings().splitToList(
+        query.toLowerCase());
+    BooleanQuery q = new BooleanQuery();
+    for (String field : ALL) {
+      BooleanQuery and = new BooleanQuery();
+      for (String s : segments) {
+        and.add(new PrefixQuery(new Term(field, s)), Occur.MUST);
+      }
+      q.add(and, Occur.SHOULD);
+    }
+
+    TopDocs results = searcher.search(q, n);
+    ScoreDoc[] hits = results.scoreDocs;
+
+    List<AccountInfo> result = new LinkedList<>();
+
+    for (ScoreDoc h : hits) {
+      Document doc = searcher.doc(h.doc);
+
+      AccountInfo info = new AccountInfo(
+          doc.getField(ID).numericValue().intValue());
+      info.name = doc.get(NAME);
+      info.email = doc.get(EMAIL);
+      info.username = doc.get(USERNAME);
+      result.add(info);
+    }
+
+    return result;
+  }
+
+  private IndexSearcher get() {
     try {
       return cache.get(true);
     } catch (ExecutionException e) {
       log.warn("Cannot fetch reviewers from cache", e);
-      return Collections.emptyList();
+      return null;
     }
   }
+
+  private IndexSearcher index() throws IOException, OrmException {
+    RAMDirectory idx = new RAMDirectory();
+    IndexWriterConfig config = new IndexWriterConfig(
+        new StandardAnalyzer(CharArraySet.EMPTY_SET));
+    config.setOpenMode(OpenMode.CREATE);
+
+    try (IndexWriter writer = new IndexWriter(idx, config)) {
+      for (Account a : db.get().accounts().all()) {
+        if (a.isActive()) {
+          addAccount(writer, a);
+        }
+      }
+    }
+
+    return new IndexSearcher(DirectoryReader.open(idx));
+  }
+
+  private void addAccount(IndexWriter writer, Account a)
+      throws IOException, OrmException {
+    Document doc = new Document();
+    doc.add(new IntField(ID, a.getId().get(), Store.YES));
+    if (a.getFullName() != null) {
+      doc.add(new TextField(NAME, a.getFullName(), Store.YES));
+    }
+    if (a.getPreferredEmail() != null) {
+      doc.add(new StringField(EMAIL, a.getPreferredEmail().toLowerCase(),
+          Store.YES));
+      doc.add(new TextField(EMAIL, a.getPreferredEmail(), Store.YES));
+    }
+    AccountExternalIdAccess extIdAccess = db.get().accountExternalIds();
+    String username = AccountState.getUserName(
+        extIdAccess.byAccount(a.getId()).toList());
+    if (username != null) {
+      doc.add(new StringField(USERNAME, username, Store.YES));
+    }
+    writer.addDocument(doc);
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java
index 2c82f58..4561ae4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/SuggestReviewers.java
@@ -55,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -279,41 +280,20 @@
   }
 
   private List<AccountInfo> suggestAccountFullTextSearch(
-      VisibilityControl visibilityControl) throws OrmException {
-    String str = query.toLowerCase();
-    Map<Account.Id, AccountInfo> accountMap = new LinkedHashMap<>();
-    List<Account> fullNameMatches = new ArrayList<>(fullTextMaxMatches);
-    List<Account> emailMatches = new ArrayList<>(fullTextMaxMatches);
+      VisibilityControl visibilityControl) throws IOException, OrmException {
+    List<AccountInfo> results = reviewerSuggestionCache.search(
+        query, fullTextMaxMatches);
 
-    for (Account a : reviewerSuggestionCache.get()) {
-      if (a.getFullName() != null
-          && a.getFullName().toLowerCase().contains(str)) {
-        fullNameMatches.add(a);
-      } else if (a.getPreferredEmail() != null
-          && emailMatches.size() < fullTextMaxMatches
-          && a.getPreferredEmail().toLowerCase().contains(str)) {
-        emailMatches.add(a);
-      }
-      if (fullNameMatches.size() >= fullTextMaxMatches) {
-        break;
+    Iterator<AccountInfo> it = results.iterator();
+    while (it.hasNext()) {
+      Account.Id accountId = new Account.Id(it.next()._accountId);
+      if (!(visibilityControl.isVisibleTo(accountId)
+          && accountControl.canSee(accountId))) {
+        it.remove();
       }
     }
-    for (Account a : fullNameMatches) {
-      addSuggestion(accountMap, a.getId(), visibilityControl);
-      if (accountMap.size() >= limit) {
-        break;
-      }
-    }
-    if (accountMap.size() < limit) {
-      for (Account a : emailMatches) {
-        addSuggestion(accountMap, a.getId(), visibilityControl);
-        if (accountMap.size() >= limit) {
-          break;
-        }
-      }
-    }
-    accountLoader.fill();
-    return Lists.newArrayList(accountMap.values());
+
+    return results;
   }
 
   private boolean addSuggestion(Map<Account.Id, AccountInfo> map,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 6ad264b..8e9d7e8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.config.CapabilityDefinition;
 import com.google.gerrit.extensions.config.DownloadCommand;
 import com.google.gerrit.extensions.config.DownloadScheme;
+import com.google.gerrit.extensions.events.GarbageCollectorListener;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.events.HeadUpdatedListener;
 import com.google.gerrit.extensions.events.LifecycleListener;
@@ -253,6 +254,7 @@
     DynamicSet.setOf(binder(), PreUploadHook.class);
     DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
     DynamicSet.setOf(binder(), ProjectDeletedListener.class);
+    DynamicSet.setOf(binder(), GarbageCollectorListener.class);
     DynamicSet.setOf(binder(), HeadUpdatedListener.class);
     DynamicSet.setOf(binder(), UsageDataPublishedListener.class);
     DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterUpdate.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
index 52bf7a5..fe81dfd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.config;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.config.DownloadCommand;
 import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -30,6 +32,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 public class GetServerInfo implements RestReadView<ConfigResource> {
@@ -112,34 +115,39 @@
   }
 
   public static class DownloadInfo {
-    public List<DownloadSchemeInfo> schemes;
-    public List<ArchiveFormat> archives;
+    public Map<String, DownloadSchemeInfo> schemes;
+    public List<String> archives;
 
     public DownloadInfo(DownloadConfig downloadConfig,
         DynamicMap<DownloadScheme> downloadSchemes,
         DynamicMap<DownloadCommand> downloadCommands) {
-      schemes = new ArrayList<>();
+      schemes = new HashMap<>();
       for (DynamicMap.Entry<DownloadScheme> e : downloadSchemes) {
         DownloadScheme scheme = e.getProvider().get();
-        if (scheme.isEnabled()) {
-          schemes.add(
-              new DownloadSchemeInfo(e.getExportName(), scheme, downloadCommands));
+        if (scheme.isEnabled() && scheme.getUrl("${project}") != null) {
+          schemes.put(e.getExportName(),
+              new DownloadSchemeInfo(scheme, downloadCommands));
         }
       }
-      archives = new ArrayList<>(downloadConfig.getArchiveFormats());
+      archives =
+          Lists.transform(new ArrayList<>(downloadConfig.getArchiveFormats()),
+              new Function<ArchiveFormat, String>() {
+                @Override
+                public String apply(ArchiveFormat archiveFormat) {
+                  return archiveFormat.name().toLowerCase(Locale.US);
+                }
+              });
     }
   }
 
   public static class DownloadSchemeInfo {
-    public String name;
     public String url;
     public Boolean isAuthRequired;
     public Boolean isAuthSupported;
     public Map<String, String> commands;
 
-    public DownloadSchemeInfo(String schemeName, DownloadScheme scheme,
+    public DownloadSchemeInfo(DownloadScheme scheme,
         DynamicMap<DownloadCommand> downloadCommands) {
-      name = schemeName;
       url = scheme.getUrl("${project}");
       isAuthRequired = toBoolean(scheme.isAuthRequired());
       isAuthSupported = toBoolean(scheme.isAuthSupported());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java
index df6d86b..006419b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ListCaches.java
@@ -41,7 +41,7 @@
   private final DynamicMap<Cache<?, ?>> cacheMap;
 
   public static enum OutputFormat {
-    LIST, TEXT_LIST;
+    LIST, TEXT_LIST
   }
 
   @Option(name = "--format", usage = "output format")
@@ -85,7 +85,7 @@
   }
 
   public enum CacheType {
-    MEM, DISK;
+    MEM, DISK
   }
 
   public static class CacheInfo {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java
index 7302ea1..2bbb731 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PostCaches.java
@@ -52,7 +52,7 @@
   }
 
   public static enum Operation {
-    FLUSH_ALL, FLUSH;
+    FLUSH_ALL, FLUSH
   }
 
   private final DynamicMap<Cache<?, ?>> cacheMap;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index f0c0bc1..4a61e3e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -151,7 +151,7 @@
     ru.newRev = newId != null ? newId.getName() : ObjectId.zeroId().getName();
     ru.oldRev = oldId != null ? oldId.getName() : ObjectId.zeroId().getName();
     ru.project = refName.getParentKey().get();
-    ru.refName = refName.getShortName();
+    ru.refName = refName.get();
     return ru;
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java
index f9844d5..f044342 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BranchOrderSection.java
@@ -14,9 +14,8 @@
 
 package com.google.gerrit.server.git;
 
-import com.google.gerrit.reviewdb.client.RefNames;
-
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.reviewdb.client.RefNames;
 
 import java.util.List;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java
index 0c39d67..a915a79 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GarbageCollection.java
@@ -16,6 +16,9 @@
 
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.extensions.events.GarbageCollectorListener;
+import com.google.gerrit.extensions.events.GarbageCollectorListener.Event;
+import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.GcConfig;
 import com.google.inject.Inject;
@@ -48,6 +51,7 @@
   private final GitRepositoryManager repoManager;
   private final GarbageCollectionQueue gcQueue;
   private final GcConfig gcConfig;
+  private final DynamicSet<GarbageCollectorListener> listeners;
 
   public interface Factory {
     GarbageCollection create();
@@ -55,10 +59,12 @@
 
   @Inject
   GarbageCollection(GitRepositoryManager repoManager,
-      GarbageCollectionQueue gcQueue, GcConfig config) {
+      GarbageCollectionQueue gcQueue, GcConfig config,
+      DynamicSet<GarbageCollectorListener> listeners) {
     this.repoManager = repoManager;
     this.gcQueue = gcQueue;
     this.gcConfig = config;
+    this.listeners = listeners;
   }
 
   public GarbageCollectionResult run(List<Project.NameKey> projectNames) {
@@ -93,6 +99,7 @@
         Properties statistics = gc.call();
         logGcInfo(p, "after: ", statistics);
         print(writer, "done.\n\n");
+        fire(p, statistics);
       } catch (RepositoryNotFoundException e) {
         logGcError(writer, p, e);
         result.addError(new GarbageCollectionResult.Error(
@@ -112,6 +119,27 @@
     return result;
   }
 
+  private void fire(final Project.NameKey p, final Properties statistics) {
+    Event event = new GarbageCollectorListener.Event() {
+      @Override
+      public String getProjectName() {
+        return p.get();
+      }
+
+      @Override
+      public Properties getStatistics() {
+        return statistics;
+      }
+    };
+    for (GarbageCollectorListener l : listeners) {
+      try {
+        l.onGarbageCollected(event);
+      } catch (RuntimeException e) {
+        log.warn("Failure in GarbageCollectorListener", e);
+      }
+    }
+  }
+
   private static void logGcInfo(Project.NameKey projectName, String msg) {
     logGcInfo(projectName, msg, null);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/QueryList.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/QueryList.java
new file mode 100644
index 0000000..0df866d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/QueryList.java
@@ -0,0 +1,41 @@
+// 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.git;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class QueryList extends TabFile {
+  public static final String FILE_NAME = "queries";
+  protected final Map<String, String> queriesByName;
+
+  private QueryList(List<Row> queriesByName) {
+    this.queriesByName = toMap(queriesByName);
+  }
+
+  public static QueryList parse(String text, ValidationError.Sink errors)
+      throws IOException {
+    return new QueryList(parse(text, FILE_NAME, errors));
+  }
+
+  public String getQuery(String name) {
+    return queriesByName.get(name);
+  }
+
+  public String asText() {
+    return asText("Name", "Query", queriesByName);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 0793d10..3fb515a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -2367,9 +2367,12 @@
     walk.reset();
     walk.sort(RevSort.NONE);
     try {
-      walk.markStart(walk.parseCommit(cmd.getNewId()));
+      RevObject parsedObject = walk.parseAny(cmd.getNewId());
+      if (!(parsedObject instanceof RevCommit)) {
+        return;
+      }
+      walk.markStart((RevCommit)parsedObject);
       markHeadsAsUninteresting(walk, cmd.getRefName());
-
       Set<ObjectId> existing = changeRefsById().keySet();
       for (RevCommit c; (c = walk.next()) != null;) {
         if (existing.contains(c)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
index ecf53fd..4a8163b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
@@ -110,8 +110,8 @@
     return defaultQueue;
   }
 
-  /** Create a new executor queue with one thread. */
-  public Executor createQueue(final int poolsize, final String prefix) {
+  /** Create a new executor queue. */
+  public Executor createQueue(int poolsize, String prefix) {
     final Executor r = new Executor(poolsize, prefix);
     r.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
     r.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
index e6fff19..dd981ad 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
@@ -89,7 +89,7 @@
                     .getSubmitter(n).getAccountId());
             PatchSet newPatchSet =
                 rebaseChange.rebase(args.repo, args.rw, args.inserter,
-                    n.getPatchsetId(), n.change(), uploader,
+                    n.change(), n.getPatchsetId(), uploader,
                     mergeTip.getCurrentTip(), args.mergeUtil,
                     args.serverIdent.get(), false, ValidatePolicy.NONE);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index d39fa3b..9054ba4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -287,7 +287,7 @@
         public Iterable<String> get(ChangeData input, FillArgs args)
             throws OrmException {
           Set<String> revisions = Sets.newHashSet();
-          for (PatchSet ps : input.patches()) {
+          for (PatchSet ps : input.patchSets()) {
             if (ps.getRevision() != null) {
               revisions.add(ps.getRevision().get());
             }
@@ -531,6 +531,25 @@
         }
       };
 
+  public static class PatchSetProtoField
+      extends FieldDef.Repeatable<ChangeData, byte[]> {
+    public static final ProtobufCodec<PatchSet> CODEC =
+        CodecFactory.encoder(PatchSet.class);
+
+    private PatchSetProtoField() {
+      super("_patch_set", FieldType.STORED_ONLY, true);
+    }
+
+    @Override
+    public Iterable<byte[]> get(ChangeData input, FieldDef.FillArgs args)
+        throws OrmException {
+      return toProtos(CODEC, input.patchSets());
+    }
+  }
+
+  /** Serialized patch set object, used for pre-populating results. */
+  public static final PatchSetProtoField PATCH_SET = new PatchSetProtoField();
+
   private static String getTopic(ChangeData input) throws OrmException {
     Change c = input.change();
     if (c == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
index 46c8297..cff654a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -173,6 +173,35 @@
       ChangeField.HASHTAG,
       ChangeField.COMMENTBY);
 
+  static final Schema<ChangeData> V17 = schema(
+      ChangeField.LEGACY_ID,
+      ChangeField.ID,
+      ChangeField.STATUS,
+      ChangeField.PROJECT,
+      ChangeField.PROJECTS,
+      ChangeField.REF,
+      ChangeField.TOPIC,
+      ChangeField.UPDATED,
+      ChangeField.FILE_PART,
+      ChangeField.PATH,
+      ChangeField.OWNER,
+      ChangeField.REVIEWER,
+      ChangeField.COMMIT,
+      ChangeField.TR,
+      ChangeField.LABEL,
+      ChangeField.REVIEWED,
+      ChangeField.COMMIT_MESSAGE,
+      ChangeField.COMMENT,
+      ChangeField.CHANGE,
+      ChangeField.APPROVAL,
+      ChangeField.MERGEABLE,
+      ChangeField.ADDED,
+      ChangeField.DELETED,
+      ChangeField.DELTA,
+      ChangeField.HASHTAG,
+      ChangeField.COMMENTBY,
+      ChangeField.PATCH_SET);
+
   private static Schema<ChangeData> schema(Collection<FieldDef<ChangeData, ?>> fields) {
     return new Schema<>(ImmutableList.copyOf(fields));
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index 41df287..0cfc659 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -114,7 +114,6 @@
   @Singleton
   @IndexExecutor(BATCH)
   ListeningExecutorService getBatchIndexExecutor(
-      @IndexExecutor(INTERACTIVE) ListeningExecutorService interactive,
       @GerritServerConfig Config config,
       WorkQueue workQueue) {
     if (batchExecutor != null) {
@@ -125,7 +124,7 @@
       threads = config.getInt("changeMerge", null, "threadPoolSize", 0);
     }
     if (threads <= 0) {
-      return interactive;
+      threads = Runtime.getRuntime().availableProcessors();
     }
     return MoreExecutors.listeningDecorator(
         workQueue.createQueue(threads, "Index-Batch"));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
index dcfb52c..926ef44 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -142,7 +142,7 @@
           new URLClassLoader(urls.toArray(new URL[urls.size()]),
               PluginLoader.parentFor(type));
 
-      JarScanner jarScanner = createJarScanner(srcJar);
+      JarScanner jarScanner = createJarScanner(tmp);
       ServerPlugin plugin = new ServerPlugin(name, description.canonicalUrl,
           description.user, srcJar, snapshot, jarScanner,
           description.dataDir, pluginLoader);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index 56233f0..4e651c2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -217,7 +217,8 @@
   private synchronized void unloadPlugin(Plugin plugin) {
     persistentCacheFactory.onStop(plugin);
     String name = plugin.getName();
-    log.info(String.format("Unloading plugin %s", name));
+    log.info(String.format("Unloading plugin %s, version %s",
+        name, plugin.getVersion()));
     plugin.stop(env);
     env.onStopPlugin(plugin);
     running.remove(name);
@@ -355,7 +356,9 @@
         String name = active.getName();
         try {
           log.info(String.format("Reloading plugin %s", name));
-          runPlugin(name, active.getSrcFile(), active);
+          Plugin newPlugin = runPlugin(name, active.getSrcFile(), active);
+          log.info(String.format("Reloaded plugin %s, version %s",
+              newPlugin.getName(), newPlugin.getVersion()));
         } catch (PluginInstallException e) {
           log.warn(String.format("Cannot reload plugin %s", name), e.getCause());
           throw e;
@@ -395,14 +398,14 @@
       }
 
       if (active != null) {
-        log.info(String.format("Reloading plugin %s, version %s",
-            active.getName(), active.getVersion()));
+        log.info(String.format("Reloading plugin %s", active.getName()));
       }
 
       try {
         Plugin loadedPlugin = runPlugin(name, path, active);
-        if (active == null && !loadedPlugin.isDisabled()) {
-          log.info(String.format("Loaded plugin %s, version %s",
+        if (!loadedPlugin.isDisabled()) {
+          log.info(String.format("%s plugin %s, version %s",
+              active == null ? "Loaded" : "Reloaded",
               loadedPlugin.getName(), loadedPlugin.getVersion()));
         }
       } catch (PluginInstallException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
index fdff1e7..d451b46 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -32,7 +32,7 @@
    * Get the cached data for a project by its unique name.
    *
    * @param projectName name of the project.
-   * @return the cached data; null if no such project exists or a error occured.
+   * @return the cached data; null if no such project exists or a error occurred.
    * @see #checkedGet(com.google.gerrit.reviewdb.client.Project.NameKey)
    */
   public ProjectState get(Project.NameKey projectName);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 2561b80..e8e29c1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -195,21 +195,25 @@
    *     rules, including any errors.
    */
   public List<SubmitRecord> evaluate() {
-    try {
-      initPatchSet();
-    } catch (OrmException e) {
-      return ruleError("Error looking up patch set "
-          + control.getChange().currentPatchSetId());
-    }
     Change c = control.getChange();
     if (!allowClosed && c.getStatus().isClosed()) {
       SubmitRecord rec = new SubmitRecord();
       rec.status = SubmitRecord.Status.CLOSED;
       return Collections.singletonList(rec);
     }
-    if ((c.getStatus() == Change.Status.DRAFT || patchSet.isDraft())
-        && !allowDraft) {
-      return cannotSubmitDraft();
+    if (!allowDraft) {
+      if (c.getStatus() == Change.Status.DRAFT) {
+        return cannotSubmitDraft();
+      }
+      try {
+        initPatchSet();
+      } catch (OrmException e) {
+        return ruleError("Error looking up patch set "
+            + control.getChange().currentPatchSetId());
+      }
+      if (patchSet.isDraft()) {
+        return cannotSubmitDraft();
+      }
     }
 
     List<Term> results;
@@ -501,8 +505,6 @@
 
   private PrologEnvironment getPrologEnvironment(CurrentUser user)
       throws RuleEvalException {
-    checkState(patchSet != null,
-        "getPrologEnvironment() called before initPatchSet()");
     ProjectState projectState = control.getProjectControl().getProjectState();
     PrologEnvironment env;
     try {
@@ -526,7 +528,6 @@
     }
     env.set(StoredValues.REVIEW_DB, cd.db());
     env.set(StoredValues.CHANGE_DATA, cd);
-    env.set(StoredValues.PATCH_SET, patchSet);
     env.set(StoredValues.CHANGE_CONTROL, control);
     if (user != null) {
       env.set(StoredValues.CURRENT_USER, user);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
index 1053d92..d167860 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
@@ -29,7 +29,7 @@
           new InvalidProvider<InternalChangeQuery>(),
           new InvalidProvider<ChangeQueryRewriter>(),
           null, null, null, null, null, null, null, null, null, null, null,
-          null, null, null, null, null, null, null, null));
+          null, null, null, null, null, null, null, null, null));
 
   private static final QueryRewriter.Definition<ChangeData, BasicChangeRewrites> mydef =
       new QueryRewriter.Definition<>(BasicChangeRewrites.class, BUILDER);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 802cf65..d94921e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -119,7 +119,7 @@
   public static void ensureAllPatchSetsLoaded(Iterable<ChangeData> changes)
       throws OrmException {
     for (ChangeData cd : changes) {
-      cd.patches();
+      cd.patchSets();
     }
   }
 
@@ -127,7 +127,7 @@
       throws OrmException {
     Map<PatchSet.Id, ChangeData> missing = Maps.newHashMap();
     for (ChangeData cd : changes) {
-      if (cd.currentPatchSet == null && cd.patches == null) {
+      if (cd.currentPatchSet == null && cd.patchSets == null) {
         missing.put(cd.change().currentPatchSetId(), cd);
       }
     }
@@ -204,7 +204,7 @@
   private String commitMessage;
   private List<FooterLine> commitFooters;
   private PatchSet currentPatchSet;
-  private Collection<PatchSet> patches;
+  private Collection<PatchSet> patchSets;
   private ListMultimap<PatchSet.Id, PatchSetApproval> allApprovals;
   private List<PatchSetApproval> currentApprovals;
   private Map<Integer, List<String>> files = new HashMap<>();
@@ -450,6 +450,10 @@
     return change;
   }
 
+  public void setChange(Change c) {
+    change = c;
+  }
+
   public Change reloadChange() throws OrmException {
     change = db.changes().get(legacyId);
     return change;
@@ -468,7 +472,7 @@
       if (c == null) {
         return null;
       }
-      for (PatchSet p : patches()) {
+      for (PatchSet p : patchSets()) {
         if (p.getId().equals(c.currentPatchSetId())) {
           currentPatchSet = p;
           return p;
@@ -536,23 +540,28 @@
    * @return patches for the change.
    * @throws OrmException an error occurred reading the database.
    */
-  public Collection<PatchSet> patches()
+  public Collection<PatchSet> patchSets()
       throws OrmException {
-    if (patches == null) {
-      patches = db.patchSets().byChange(legacyId).toList();
+    if (patchSets == null) {
+      patchSets = db.patchSets().byChange(legacyId).toList();
     }
-    return patches;
+    return patchSets;
+  }
+
+  public void setPatchSets(Collection<PatchSet> patchSets) {
+    this.currentPatchSet = null;
+    this.patchSets = patchSets;
   }
 
   /**
-   * @return patch with the given ID, or null if it does not exist.
+   * @return patch set with the given ID, or null if it does not exist.
    * @throws OrmException an error occurred reading the database.
    */
-  public PatchSet patch(PatchSet.Id psId) throws OrmException {
+  public PatchSet patchSet(PatchSet.Id psId) throws OrmException {
     if (currentPatchSet != null && currentPatchSet.getId().equals(psId)) {
       return currentPatchSet;
     }
-    for (PatchSet ps : patches()) {
+    for (PatchSet ps : patchSets()) {
       if (ps.getId().equals(psId)) {
         return ps;
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 5058f0c..a0a7a3c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -34,8 +34,11 @@
 import com.google.gerrit.server.account.CapabilityControl;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
+import com.google.gerrit.server.account.VersionedAccountQueries;
 import com.google.gerrit.server.change.ChangeTriplet;
 import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.AllUsersNameProvider;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -56,9 +59,13 @@
 import com.google.inject.ProvisionException;
 import com.google.inject.util.Providers;
 
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -107,6 +114,7 @@
   public static final String FIELD_PATH = "path";
   public static final String FIELD_PROJECT = "project";
   public static final String FIELD_PROJECTS = "projects";
+  public static final String FIELD_QUERY = "query";
   public static final String FIELD_REF = "ref";
   public static final String FIELD_REVIEWER = "reviewer";
   public static final String FIELD_REVIEWERIN = "reviewerin";
@@ -138,6 +146,7 @@
     final AccountResolver accountResolver;
     final GroupBackend groupBackend;
     final AllProjectsName allProjectsName;
+    final AllUsersNameProvider allUsersName;
     final PatchListCache patchListCache;
     final GitRepositoryManager repoManager;
     final ProjectCache projectCache;
@@ -165,6 +174,7 @@
         AccountResolver accountResolver,
         GroupBackend groupBackend,
         AllProjectsName allProjectsName,
+        AllUsersNameProvider allUsersName,
         PatchListCache patchListCache,
         GitRepositoryManager repoManager,
         ProjectCache projectCache,
@@ -177,9 +187,9 @@
       this(db, queryProvider, rewriter, userFactory, self,
           capabilityControlFactory, changeControlGenericFactory,
           changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend,
-          allProjectsName, patchListCache, repoManager, projectCache,
-          listChildProjects, indexes, submitStrategyFactory, conflictsCache,
-          trackingFooters,
+          allProjectsName, allUsersName, patchListCache, repoManager,
+          projectCache, listChildProjects, indexes, submitStrategyFactory,
+          conflictsCache, trackingFooters,
           cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true));
     }
 
@@ -197,6 +207,7 @@
         AccountResolver accountResolver,
         GroupBackend groupBackend,
         AllProjectsName allProjectsName,
+        AllUsersNameProvider allUsersName,
         PatchListCache patchListCache,
         GitRepositoryManager repoManager,
         ProjectCache projectCache,
@@ -219,6 +230,7 @@
      this.accountResolver = accountResolver;
      this.groupBackend = groupBackend;
      this.allProjectsName = allProjectsName;
+     this.allUsersName = allUsersName;
      this.patchListCache = patchListCache;
      this.repoManager = repoManager;
      this.projectCache = projectCache;
@@ -235,9 +247,9 @@
           Providers.of(otherUser),
           capabilityControlFactory, changeControlGenericFactory,
           changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend,
-          allProjectsName, patchListCache, repoManager, projectCache,
-          listChildProjects, indexes, submitStrategyFactory, conflictsCache,
-          trackingFooters, allowsDrafts);
+          allProjectsName, allUsersName, patchListCache, repoManager,
+          projectCache, listChildProjects, indexes, submitStrategyFactory,
+          conflictsCache, trackingFooters, allowsDrafts);
     }
 
     Arguments asUser(Account.Id otherId) {
@@ -765,6 +777,25 @@
     return Predicate.or(owner(ownerIds), commentby(ownerIds));
   }
 
+  @Operator
+  public Predicate<ChangeData> query(String name) throws QueryParseException {
+    AllUsersName allUsers = args.allUsersName.get();
+    try (Repository git = args.repoManager.openRepository(allUsers)) {
+      VersionedAccountQueries q = VersionedAccountQueries.forUser(self());
+      q.load(git);
+      String query = q.getQueryList().getQuery(name);
+      if (query != null) {
+        return parse(query);
+      }
+    } catch (RepositoryNotFoundException e) {
+      throw new QueryParseException("Unknown named query (no " +
+          allUsers.get() +" repo): " + name, e);
+    } catch (IOException | ConfigInvalidException e) {
+      throw new QueryParseException("Error parsing named query: " + name, e);
+    }
+    throw new QueryParseException("Unknown named query: " + name);
+  }
+
   @Override
   protected Predicate<ChangeData> defaultField(String query) throws QueryParseException {
     if (query.startsWith("refs/")) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
index 14daa4d..3dd7c61 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -32,7 +32,7 @@
 
   @Override
   public boolean match(final ChangeData object) throws OrmException {
-    for (PatchSet p : object.patches()) {
+    for (PatchSet p : object.patchSets()) {
       if (p.getRevision() != null && p.getRevision().get() != null) {
         final ObjectId id = ObjectId.fromString(p.getRevision().get());
         if (abbrevId.prefixCompare(id) == 0) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 222c2bb..f73e0e4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -209,11 +209,11 @@
 
           if (includePatchSets) {
             if (includeFiles) {
-              eventFactory.addPatchSets(c, d.patches(),
+              eventFactory.addPatchSets(c, d.patchSets(),
                 includeApprovals ? d.approvals().asMap() : null,
                 includeFiles, d.change(), labelTypes);
             } else {
-              eventFactory.addPatchSets(c, d.patches(),
+              eventFactory.addPatchSets(c, d.patchSets(),
                   includeApprovals ? d.approvals().asMap() : null,
                   labelTypes);
             }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
index 59549ac..a437477 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -443,7 +443,7 @@
       assertThat(new Account.Id(ci.author._accountId))
           .isEqualTo(plc.getAuthor());
     }
-    assertThat((int) ci.line).isEqualTo(plc.getLine());
+    assertThat(ci.line).isEqualTo(plc.getLine());
     assertThat(MoreObjects.firstNonNull(ci.side, Side.REVISION))
         .isEqualTo(plc.getSide() == 0 ? Side.PARENT : Side.REVISION);
     assertThat(TimeUtil.roundToSecond(ci.updated))
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java
index 2ad813d..8caba88 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/ConsistencyCheckerTest.java
@@ -37,10 +37,10 @@
 import com.google.gerrit.testutil.FakeAccountByEmailCache;
 import com.google.gerrit.testutil.InMemoryDatabase;
 import com.google.gerrit.testutil.InMemoryRepositoryManager;
+import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo;
 import com.google.gerrit.testutil.TestChanges;
 import com.google.inject.util.Providers;
 
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
@@ -59,7 +59,7 @@
   private InMemoryRepositoryManager repoManager;
   private ConsistencyChecker checker;
 
-  private TestRepository<InMemoryRepository> repo;
+  private TestRepository<Repo> repo;
   private Project.NameKey project;
   private Account.Id userId;
   private RevCommit tip;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/QueryListTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/QueryListTest.java
new file mode 100644
index 0000000..d022d3e
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/QueryListTest.java
@@ -0,0 +1,121 @@
+// 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.git;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.replay;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class QueryListTest extends TestCase {
+  public static final String Q_P = "project:foo";
+  public static final String Q_B = "branch:bar";
+  public static final String Q_COMPLEX = "branch:bar AND peers:'is:open\t'";
+
+  public static final String N_FOO = "foo";
+  public static final String N_BAR = "bar";
+
+  public static final String L_FOO = N_FOO + "\t" + Q_P + "\n";
+  public static final String L_BAR = N_BAR + "\t" + Q_B + "\n";
+  public static final String L_FOO_PROP = N_FOO + "   \t" + Q_P + "\n";
+  public static final String L_BAR_PROP = N_BAR + "   \t" + Q_B + "\n";
+  public static final String L_FOO_PAD_F = " " + N_FOO + "\t" + Q_P + "\n";
+  public static final String L_FOO_PAD_E = N_FOO + " \t" + Q_P + "\n";
+  public static final String L_BAR_PAD_F = N_BAR + "\t " + Q_B + "\n";
+  public static final String L_BAR_PAD_E = N_BAR + "\t" + Q_B + " \n";
+  public static final String L_COMPLEX = N_FOO + "\t" + Q_COMPLEX + "\t \n";
+  public static final String L_BAD = N_FOO + "\n";
+
+  public static final String HEADER = "# Name\tQuery\n";
+  public static final String C1 = "# A Simple Comment\n";
+  public static final String C2 = "# Comment with a tab\t and multi # # #\n";
+
+  public static final String F_SIMPLE = L_FOO + L_BAR;
+  public static final String F_PROPER = L_BAR_PROP + L_FOO_PROP; // alpha order
+  public static final String F_PAD_F = L_FOO_PAD_F + L_BAR_PAD_F;
+  public static final String F_PAD_E = L_FOO_PAD_E + L_BAR_PAD_E;
+
+  @Test
+  public void testParseSimple() throws Exception {
+    QueryList ql = QueryList.parse(F_SIMPLE, null);
+    assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
+    assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
+  }
+
+  @Test
+  public void testParseWHeader() throws Exception {
+    QueryList ql = QueryList.parse(HEADER + F_SIMPLE, null);
+    assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
+    assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
+  }
+
+  @Test
+  public void testParseWComments() throws Exception {
+    QueryList ql = QueryList.parse(C1 + F_SIMPLE + C2, null);
+    assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
+    assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
+  }
+
+  @Test
+  public void testParseFooComment() throws Exception {
+    QueryList ql = QueryList.parse("#" + L_FOO + L_BAR, null);
+    assertThat(ql.getQuery(N_FOO)).isNull();
+    assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
+  }
+
+  @Test
+  public void testParsePaddedFronts() throws Exception {
+    QueryList ql = QueryList.parse(F_PAD_F, null);
+    assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
+    assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
+  }
+
+  @Test
+  public void testParsePaddedEnds() throws Exception {
+    QueryList ql = QueryList.parse(F_PAD_E, null);
+    assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_P);
+    assertThat(ql.getQuery(N_BAR)).isEqualTo(Q_B);
+  }
+
+  @Test
+  public void testParseComplex() throws Exception {
+    QueryList ql = QueryList.parse(L_COMPLEX, null);
+    assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_COMPLEX);
+  }
+
+  @Test(expected = IOException.class)
+  public void testParseBad() throws Exception {
+    ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
+    replay(sink);
+    QueryList.parse(L_BAD, sink);
+  }
+
+  @Test
+  public void testAsText() throws Exception {
+    String expectedText = HEADER + "#\n" + F_PROPER;
+    QueryList ql = QueryList.parse(F_SIMPLE, null);
+    String asText = ql.asText();
+    assertThat(asText).isEqualTo(expectedText);
+
+    ql = QueryList.parse(asText, null);
+    asText = ql.asText();
+    assertThat(asText).isEqualTo(expectedText);
+  }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
index c9a2056..0c8625d 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -27,7 +27,7 @@
           FakeQueryBuilder.class),
         new ChangeQueryBuilder.Arguments(null, null, null, null, null, null,
           null, null, null, null, null, null, null, null, null, null, null,
-          indexes, null, null, null, null));
+          null, indexes, null, null, null, null));
   }
 
   @Operator
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 07cbaf3..fd36097 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -59,12 +59,12 @@
 import com.google.gerrit.testutil.ConfigSuite;
 import com.google.gerrit.testutil.InMemoryDatabase;
 import com.google.gerrit.testutil.InMemoryRepositoryManager;
+import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
 import com.google.inject.util.Providers;
 
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -191,7 +191,7 @@
 
   @Test
   public void byId() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
 
@@ -202,7 +202,7 @@
 
   @Test
   public void byKey() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change = newChange(repo, null, null, null, null).insert();
     String key = change.getKey().get();
 
@@ -215,7 +215,7 @@
 
   @Test
   public void byTriplet() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change = newChange(repo, null, null, null, "branch").insert();
     String k = change.getKey().get();
 
@@ -238,7 +238,7 @@
 
   @Test
   public void byStatus() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.getChange();
     change1.setStatus(Change.Status.NEW);
@@ -257,7 +257,7 @@
 
   @Test
   public void byStatusOpen() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.getChange();
     change1.setStatus(Change.Status.NEW);
@@ -287,7 +287,7 @@
 
   @Test
   public void byStatusClosed() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.getChange();
     change1.setStatus(Change.Status.MERGED);
@@ -315,7 +315,7 @@
 
   @Test
   public void byStatusPrefix() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.getChange();
     change1.setStatus(Change.Status.NEW);
@@ -337,7 +337,7 @@
 
   @Test
   public void byCommit() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins = newChange(repo, null, null, null, null);
     ins.insert();
     String sha = ins.getPatchSet().getRevision().get();
@@ -351,7 +351,7 @@
 
   @Test
   public void byOwner() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, userId.get(), null).insert();
     int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
         .getAccountId().get();
@@ -363,7 +363,7 @@
 
   @Test
   public void byOwnerIn() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, userId.get(), null).insert();
     int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
         .getAccountId().get();
@@ -375,8 +375,8 @@
 
   @Test
   public void byProject() throws Exception {
-    TestRepository<InMemoryRepository> repo1 = createProject("repo1");
-    TestRepository<InMemoryRepository> repo2 = createProject("repo2");
+    TestRepository<Repo> repo1 = createProject("repo1");
+    TestRepository<Repo> repo2 = createProject("repo2");
     Change change1 = newChange(repo1, null, null, null, null).insert();
     Change change2 = newChange(repo2, null, null, null, null).insert();
 
@@ -388,8 +388,8 @@
 
   @Test
   public void byProjectPrefix() throws Exception {
-    TestRepository<InMemoryRepository> repo1 = createProject("repo1");
-    TestRepository<InMemoryRepository> repo2 = createProject("repo2");
+    TestRepository<Repo> repo1 = createProject("repo1");
+    TestRepository<Repo> repo2 = createProject("repo2");
     Change change1 = newChange(repo1, null, null, null, null).insert();
     Change change2 = newChange(repo2, null, null, null, null).insert();
 
@@ -401,7 +401,7 @@
 
   @Test
   public void byBranchAndRef() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, "master").insert();
     Change change2 = newChange(repo, null, null, null, "branch").insert();
 
@@ -419,7 +419,7 @@
 
   @Test
   public void byTopic() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.getChange();
     change1.setTopic("feature1");
@@ -441,7 +441,7 @@
 
   @Test
   public void byMessageExact() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit1 = repo.parseBody(repo.commit().message("one").create());
     Change change1 = newChange(repo, commit1, null, null, null).insert();
     RevCommit commit2 = repo.parseBody(repo.commit().message("two").create());
@@ -454,7 +454,7 @@
 
   @Test
   public void fullTextWithNumbers() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit1 =
         repo.parseBody(repo.commit().message("12345 67890").create());
     Change change1 = newChange(repo, commit1, null, null, null).insert();
@@ -470,7 +470,7 @@
   @Test
   public void byLabel() throws Exception {
     accountManager.authenticate(AuthRequest.forUser("anotheruser"));
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins = newChange(repo, null, null, null, null);
     Change change = ins.insert();
 
@@ -510,7 +510,7 @@
 
   @Test
   public void limit() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change last = null;
     int n = 5;
     for (int i = 0; i < n; i++) {
@@ -538,7 +538,7 @@
 
   @Test
   public void start() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     List<Change> changes = Lists.newArrayList();
     for (int i = 0; i < 2; i++) {
       changes.add(newChange(repo, null, null, null, null).insert());
@@ -552,7 +552,7 @@
 
   @Test
   public void startWithLimit() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     List<Change> changes = Lists.newArrayList();
     for (int i = 0; i < 3; i++) {
       changes.add(newChange(repo, null, null, null, null).insert());
@@ -568,7 +568,7 @@
 
   @Test
   public void maxPages() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change = newChange(repo, null, null, null, null).insert();
 
     QueryRequest query = newQuery("status:new").withLimit(10);
@@ -582,7 +582,7 @@
   @Test
   public void updateOrder() throws Exception {
     clockStepMs = MILLISECONDS.convert(2, MINUTES);
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     List<ChangeInserter> inserters = Lists.newArrayList();
     List<Change> changes = Lists.newArrayList();
     for (int i = 0; i < 5; i++) {
@@ -607,7 +607,7 @@
   @Test
   public void updatedOrderWithMinuteResolution() throws Exception {
     clockStepMs = MILLISECONDS.convert(2, MINUTES);
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
@@ -629,7 +629,7 @@
 
   @Test
   public void updatedOrderWithSubMinuteResolution() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins1 = newChange(repo, null, null, null, null);
     Change change1 = ins1.insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
@@ -652,7 +652,7 @@
 
   @Test
   public void filterOutMoreThanOnePageOfResults() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change = newChange(repo, null, null, userId.get(), null).insert();
     int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
         .getAccountId().get();
@@ -666,7 +666,7 @@
 
   @Test
   public void filterOutAllResults() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
         .getAccountId().get();
     for (int i = 0; i < 5; i++) {
@@ -679,7 +679,7 @@
 
   @Test
   public void byFileExact() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit = repo.parseBody(
         repo.commit().message("one")
         .add("dir/file1", "contents1").add("dir/file2", "contents2")
@@ -696,7 +696,7 @@
 
   @Test
   public void byFileRegex() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit = repo.parseBody(
         repo.commit().message("one")
         .add("dir/file1", "contents1").add("dir/file2", "contents2")
@@ -710,7 +710,7 @@
 
   @Test
   public void byPathExact() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit = repo.parseBody(
         repo.commit().message("one")
         .add("dir/file1", "contents1").add("dir/file2", "contents2")
@@ -727,7 +727,7 @@
 
   @Test
   public void byPathRegex() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit = repo.parseBody(
         repo.commit().message("one")
         .add("dir/file1", "contents1").add("dir/file2", "contents2")
@@ -740,7 +740,7 @@
 
   @Test
   public void byComment() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     ChangeInserter ins = newChange(repo, null, null, null, null);
     Change change = ins.insert();
 
@@ -762,7 +762,7 @@
   public void byAge() throws Exception {
     long thirtyHours = MILLISECONDS.convert(30, HOURS);
     clockStepMs = thirtyHours;
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
     clockStepMs = 0; // Queried by AgePredicate constructor.
@@ -784,7 +784,7 @@
   @Test
   public void byBefore() throws Exception {
     clockStepMs = MILLISECONDS.convert(30, HOURS);
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
     clockStepMs = 0;
@@ -804,7 +804,7 @@
   @Test
   public void byAfter() throws Exception {
     clockStepMs = MILLISECONDS.convert(30, HOURS);
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
     clockStepMs = 0;
@@ -818,7 +818,7 @@
 
   @Test
   public void bySize() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
 
     // added = 3, deleted = 0, delta = 3
     RevCommit commit1 = repo.parseBody(
@@ -855,7 +855,7 @@
   }
 
   private List<Change> setUpHashtagChanges() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
 
@@ -897,7 +897,7 @@
 
   @Test
   public void byDefault() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
 
     Change change1 = newChange(repo, null, null, null, null).insert();
 
@@ -943,7 +943,7 @@
 
   @Test
   public void implicitVisibleTo() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, userId.get(), null).insert();
     ChangeInserter ins2 = newChange(repo, null, null, userId.get(), null);
     Change change2 = ins2.getChange();
@@ -961,7 +961,7 @@
 
   @Test
   public void explicitVisibleTo() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, userId.get(), null).insert();
     ChangeInserter ins2 = newChange(repo, null, null, userId.get(), null);
     Change change2 = ins2.getChange();
@@ -980,7 +980,7 @@
 
   @Test
   public void byCommentBy() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
     Change change2 = newChange(repo, null, null, null, null).insert();
 
@@ -1006,7 +1006,7 @@
 
   @Test
   public void byFrom() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     Change change1 = newChange(repo, null, null, null, null).insert();
 
     int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
@@ -1029,7 +1029,7 @@
 
   @Test
   public void conflicts() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit1 = repo.parseBody(
         repo.commit()
             .add("file1", "contents1")
@@ -1060,7 +1060,7 @@
   }
 
   protected ChangeInserter newChange(
-      TestRepository<InMemoryRepository> repo,
+      TestRepository<Repo> repo,
       @Nullable RevCommit commit, @Nullable String key, @Nullable Integer owner,
       @Nullable String branch) throws Exception {
     if (commit == null) {
@@ -1108,8 +1108,7 @@
     }
   }
 
-  protected TestRepository<InMemoryRepository> createProject(String name)
-      throws Exception {
+  protected TestRepository<Repo> createProject(String name) throws Exception {
     gApi.projects().create(name).get();
     return new TestRepository<>(
         repoManager.openRepository(new Project.NameKey(name)));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/ChangeDataTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/ChangeDataTest.java
new file mode 100644
index 0000000..ca1e2b1
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/ChangeDataTest.java
@@ -0,0 +1,42 @@
+// 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.query.change;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+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.testutil.TestChanges;
+
+import org.junit.Test;
+
+public class ChangeDataTest {
+  @Test
+  public void setPatchSetsClearsCurrentPatchSet() throws Exception {
+    ChangeData cd = ChangeData.createForTest(new Change.Id(1), 1);
+    cd.setChange(TestChanges.newChange(
+          new Project.NameKey("project"), new Account.Id(1000)));
+    PatchSet curr1 = cd.currentPatchSet();
+    int currId = curr1.getId().get();
+    PatchSet ps1 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 1));
+    PatchSet ps2 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 2));
+    cd.setPatchSets(ImmutableList.of(ps1, ps2));
+    PatchSet curr2 = cd.currentPatchSet();
+    assertThat(curr2).isNotSameAs(curr1);
+  }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
index 5627d33..6122d65 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
@@ -16,10 +16,10 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.testutil.InMemoryModule;
+import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -35,7 +35,7 @@
 
   @Test
   public void fullTextWithSpecialChars() throws Exception {
-    TestRepository<InMemoryRepository> repo = createProject("repo");
+    TestRepository<Repo> repo = createProject("repo");
     RevCommit commit1 =
         repo.parseBody(repo.commit().message("foo_bar_foo").create());
     Change change1 = newChange(repo, commit1, null, null, null).insert();
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
index 385a49e..ec53b29 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryRepositoryManager.java
@@ -35,16 +35,22 @@
     return new Repo(name);
   }
 
-  private static class Description extends DfsRepositoryDescription {
+  public static class Description extends DfsRepositoryDescription {
+    private final Project.NameKey name;
     private String desc;
 
     private Description(Project.NameKey name) {
       super(name.get());
+      this.name = name;
       desc = "In-memory repository " + name.get();
     }
+
+    public Project.NameKey getProject() {
+      return name;
+    }
   }
 
-  private static class Repo extends InMemoryRepository {
+  public static class Repo extends InMemoryRepository {
     private Repo(Project.NameKey name) {
       super(new Description(name));
     }
@@ -58,13 +64,13 @@
   private Map<String, Repo> repos = Maps.newHashMap();
 
   @Override
-  public synchronized InMemoryRepository openRepository(Project.NameKey name)
+  public synchronized Repo openRepository(Project.NameKey name)
       throws RepositoryNotFoundException {
     return get(name);
   }
 
   @Override
-  public synchronized InMemoryRepository createRepository(Project.NameKey name)
+  public synchronized Repo createRepository(Project.NameKey name)
       throws RepositoryCaseMismatchException, RepositoryNotFoundException {
     Repo repo;
     try {
@@ -80,7 +86,7 @@
   }
 
   @Override
-  public synchronized InMemoryRepository openMetadataRepository(
+  public synchronized Repo openMetadataRepository(
       Project.NameKey name) throws RepositoryNotFoundException {
     return openRepository(name);
   }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index c3c710d..d45d76e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -23,7 +23,7 @@
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
 
-@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+@RequiresCapability(GlobalCapability.VIEW_PLUGINS)
 @CommandMetaData(name = "ls", description = "List the installed plugins",
   runsAt = MASTER_OR_SLAVE)
 final class PluginLsCommand extends SshCommand {
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index e94c9b9..3bc8b58 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -211,6 +211,7 @@
   private Injector createDbInjector() {
     final List<Module> modules = new ArrayList<>();
     AbstractModule secureStore = createSecureStoreModule();
+    modules.add(secureStore);
     if (sitePath != null) {
       Module sitePathModule = new AbstractModule() {
         @Override
@@ -244,7 +245,6 @@
       });
 
     } else {
-      modules.add(secureStore);
       modules.add(new LifecycleModule() {
         @Override
         protected void configure() {
diff --git a/lib/jgit/BUCK b/lib/jgit/BUCK
index acb96fb..dcefb64 100644
--- a/lib/jgit/BUCK
+++ b/lib/jgit/BUCK
@@ -1,13 +1,13 @@
 include_defs('//lib/maven.defs')
 
-REPO = GERRIT  # Leave here even if set to MAVEN_CENTRAL.
-VERS = '3.7.0.201502260915-r.63-gf1a15f7'
+REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
+VERS = '4.0.0.201505050340-m2'
 
 maven_jar(
   name = 'jgit',
   id = 'org.eclipse.jgit:org.eclipse.jgit:' + VERS,
-  bin_sha1 = 'f0690b06d4270cc823b03ca88fb996897edbc410',
-  src_sha1 = 'c0b8232d4c5e8422198b35a90409c9826af68fa3',
+  bin_sha1 = '1cc3120d39ed2b55584e631634e65c5d2e6c1cf7',
+  src_sha1 = '425f578cc9d5ccb8f3b050a5ab1e2d7a0becb25d',
   license = 'jgit',
   repository = REPO,
   unsign = True,
@@ -22,7 +22,7 @@
 maven_jar(
   name = 'jgit-servlet',
   id = 'org.eclipse.jgit:org.eclipse.jgit.http.server:' + VERS,
-  sha1 = '1284d550981a037ffe92441faf5d16cdfc09396d',
+  sha1 = '2a9f55d1d92afef795542b995db6ab261007857f',
   license = 'jgit',
   repository = REPO,
   deps = [':jgit'],
@@ -36,7 +36,7 @@
 maven_jar(
   name = 'jgit-archive',
   id = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + VERS,
-  sha1 = '6d96c9c27cb85c6db165b160fb0e8038b66b764a',
+  sha1 = 'ee3954753067818f8f734981a01c13ac33425f2c',
   license = 'jgit',
   repository = REPO,
   deps = [':jgit',
@@ -53,7 +53,7 @@
 maven_jar(
   name = 'junit',
   id = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + VERS,
-  sha1 = '48e25624fda973f2e9b7fa70a5204ee7a72e1bb3',
+  sha1 = '6cc19f8f0a1791e26d4225625ecba6a31d9b830e',
   license = 'DO_NOT_DISTRIBUTE',
   repository = REPO,
   unsign = True,
diff --git a/plugins/commit-message-length-validator b/plugins/commit-message-length-validator
index ecec6df..8d295ed 160000
--- a/plugins/commit-message-length-validator
+++ b/plugins/commit-message-length-validator
@@ -1 +1 @@
-Subproject commit ecec6dfe7ed67431d96acf9dabac3885463900e8
+Subproject commit 8d295ed48e8f52eef5661b6eb10d6402d197c776
diff --git a/plugins/download-commands b/plugins/download-commands
index baa09c2..1cf6921 160000
--- a/plugins/download-commands
+++ b/plugins/download-commands
@@ -1 +1 @@
-Subproject commit baa09c2e265a2b264a5fb4571e7eefda04def0c4
+Subproject commit 1cf69212a7489e88d8c73377f0f77f8a5965db75
diff --git a/plugins/replication b/plugins/replication
index 7cf2973..f03316c 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 7cf2973c07b9568b4724ae6480db50e8e636bc19
+Subproject commit f03316c26b5991cba96280de59a4119619a18a58
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index 4d1a7b6..b5c6f81 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit 4d1a7b61034d52859cc7349af41d1068e954556d
+Subproject commit b5c6f81d979e78fbd734d946b18270ec8319eaf6