Merge changes Iacd3f2db,Iad2bd925,I1c11924d,I7c1461c4,I6b2c4f4d, ...

* changes:
  Merge branch 'stable-2.15'
  Merge branch 'stable-2.15'
  Merge branch 'stable-2.15'
  ReindexIT: Split Elasticsearch test out to a separate class
  Merge branch 'stable-2.15'
  Acceptance tests: Replace embedded ES with docker testcontainer
  Elasticsearch: replace native API in prod w/ REST
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index 5c24731..a170e07 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -1,9 +1,7 @@
-= Gerrit Code Review - Developer Setup
+= Gerrit Code Review: Developer Setup
 
-Google Bazel is needed to compile the code, and an SQL database to
-house the review metadata.  H2 is recommended for development
-databases, as it requires no external server process.
-
+To build a developer instance, you'll need link:https://bazel.build/[Bazel] to
+compile the code.
 
 == Getting the Source
 
@@ -21,59 +19,39 @@
 [[compile_project]]
 == Compiling
 
-Please refer to <<dev-bazel#,Building with Bazel>>.
-
-== Switching between branches
-
-When switching between branches with `git checkout`, be aware that
-submodule revisions are not altered.  This may result in the wrong
-plugin revisions being present, unneeded plugins being present, or
-expected plugins being missing.
-
-After switching branches, make sure the submodules are at the correct
-revisions for the new branch with the commands:
-
-----
-  git submodule update
-  git clean -fdx
-----
-
-CAUTION: If you decide to store your Eclipse/IntelliJ project files in the
-Gerrit source directories, executing `git clean -fdx` will remove them and hence
-screw up your project.
-
+For details, see <<dev-bazel#,Building with Bazel>>.
 
 == Configuring Eclipse
 
-To use the Eclipse IDE for development, please see
+To use the Eclipse IDE for development, see
 link:dev-eclipse.html[Eclipse Setup].
 
-For details on how to configure the Eclipse workspace with Bazel,
-refer to: link:dev-bazel.html#eclipse[Eclipse integration with Bazel].
-
+To configure the Eclipse workspace with Bazel, see
+link:dev-bazel.html#eclipse[Eclipse integration with Bazel].
 
 == Configuring IntelliJ IDEA
 
-Please refer to <<dev-intellij#,IntelliJ Setup>> for detailed
-instructions.
+See <<dev-intellij#,IntelliJ Setup>> for details.
 
-== Mac OS X
+== MacOS
 
-On Mac OS X ensure "Java For Mac OS X 10.5 Update 4" (or later) has
-been installed, and that `JAVA_HOME` is set to the
+On MacOS, ensure that "Java for MacOS X 10.5 Update 4" (or higher) is installed
+and that `JAVA_HOME` is set to the
 link:install.html#Requirements[required Java version].
 
 Java installations can typically be found in
 "/System/Library/Frameworks/JavaVM.framework/Versions".
 
-You can check the installed Java version by running `java -version` in
-the terminal.
+To check the installed version of Java, open a terminal window and run:
+
+`java -version`
 
 [[init]]
 == Site Initialization
 
-After compiling <<compile_project,(above)>>, run Gerrit's 'init' command to
-create a testing site for development use:
+After you compile the project <<compile_project,(above)>>, run the Gerrit
+`init`
+command to create a test site:
 
 ----
   $(bazel info output_base)/external/local_jdk/bin/java \
@@ -81,24 +59,26 @@
 ----
 
 [[special_bazel_java_version]]
-NOTE: You must use the same Java version that Bazel used for the build.
-This Java version is available at
-`$(bazel info output_base)/external/local_jdk/bin/java`.
+NOTE: You must use the same Java version that Bazel used for the build, which
+is available at `$(bazel info output_base)/external/local_jdk/bin/java`.
 
-During initialization, make two changes to the default settings:
+During initialization, change two settings from the defaults:
 
-* Change the listen addresses from '*' to 'localhost' to prevent outside
-  connections from contacting the development instance; and
-* Change the auth type from 'OPENID' to 'DEVELOPMENT_BECOME_ANY_ACCOUNT' to
-  allow yourself to create and act as arbitrary test accounts on your
-  development instance.
+*  To ensure the development instance is not externally accessible, change the
+listen addresses from '*' to 'localhost'.
+*  To allow yourself to create and act as arbitrary test accounts on your
+development instance, change the auth type from 'OPENID' to 'DEVELOPMENT_BECOME_ANY_ACCOUNT'.
 
-Continue through init until it completes. The daemon will automatically start in
-the background and a web browser will launch to the start page. From here you
-can sign in as the account created during init, register additional accounts,
-create projects, and more.
+After initializing the test site, Gerrit starts serving in the background. A
+web browser displays the Start page.
 
-When you want to shut down the daemon, simply run:
+On the Start page, you can:
+
+.  Log in as the account you created during the initialization process.
+.  Register additional accounts.
+.  Create projects.
+
+To shut down the daemon, run:
 
 ----
   ../gerrit_testsite/bin/gerrit.sh stop
@@ -108,9 +88,11 @@
 [[localdev]]
 == Working with the Local Server
 
-If you need to create additional accounts on your development instance, click
-'become' in the upper right corner, select 'Switch User', and then register
-a new account.
+To create more accounts on your development instance:
+
+.  Click 'become' in the upper right corner.
+.  Select 'Switch User'.
+.  Register a new account.
 
 Use the `ssh` protocol to clone from and push to the local server. For
 example, to clone a repository that you've created through the admin
@@ -120,34 +102,31 @@
 git clone ssh://username@localhost:29418/projectname
 ----
 
-Then you'll be able to create changes the same way users do, with
+To create changes as users of Gerrit would, run:
 
 ----
 git push origin HEAD:refs/for/master
 ----
 
-
-
 == Testing
 
-
 [[tests]]
-=== Running the Acceptance Tests
+=== Running the acceptance tests
 
-Gerrit has a set of integration tests that test the Gerrit daemon via
-REST, SSH and the git protocol.
+Gerrit contains acceptance tests that validate the Gerrit daemon via REST, SSH,
+and the Git protocol.
 
 A new review site is created for each test and the Gerrit daemon is
-started on that site. When the test has finished the Gerrit daemon is
-shutdown.
+then started on that site. When the test is completed, the Gerrit daemon is
+shut down.
 
-For instructions on running the integration tests with Bazel,
-please refer to:  <<dev-bazel#tests,Running Unit Tests with Bazel>>.
+For instructions on running the acceptance tests with Bazel,
+see <<dev-bazel#tests,Running Unit Tests with Bazel>>.
 
 [[run_daemon]]
 === Running the Daemon
 
-The daemon can be directly launched from the build area, without
+The daemon can be launched directly from the build area, without
 copying to the test site:
 
 ----
@@ -156,133 +135,101 @@
      --console-log
 ----
 
-NOTE: Please refer to <<special_bazel_java_version,this explanation>>
-for details why using `java -jar` isn't sufficient.
+NOTE: To learn why using `java -jar` isn't sufficient, see
+<<special_bazel_java_version,this explanation>>.
 
-If you want to debug the Gerrit server of this test site, you can open a debug
-port (for example port 5005) by inserting
+To debug the Gerrit server of this test site:
+
+.  Open a debug port (such as port 5005). To do so, insert the following code
+immediately after `-jar` in the previous command. To learn how to attach
+IntelliJ, see <<dev-intellij#remote-debug,Debugging a remote Gerrit server>>.
 
 ----
 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
 ----
 
-directly after `-jar` of the previous command. Please refer to
-<<dev-intellij#remote-debug,Debugging a remote Gerrit server>> for instructions
-of how to attach IntelliJ.
-
 === Running the Daemon with Gerrit Inspector
 
 link:dev-inspector.html[Gerrit Inspector] is an interactive scriptable
-environment to inspect and modify internal state of the system.
+environment you can use to inspect and modify the internal state of the system.
 
-This environment is available on the system console after
-the system starts. Leaving the Inspector will shutdown the Gerrit
-instance.
+Gerrit Inspector appears on the system console whenever the system starts.
+Leaving the Inspector shuts down the Gerrit instance.
 
-The environment allows interactive work as well as running of
-Python scripts for troubleshooting.
+To troubleshoot, the Inspector enables interactive work as well as running of
+Python scripts.
 
-Gerrit Inspect can be started by adding '-s' option to the
-command used to launch the daemon:
+To start the Inspector, add the '-s' option to the daemon start command:
 
 ----
   $(bazel info output_base)/external/local_jdk/bin/java \
      -jar bazel-bin/gerrit.war daemon -d ../gerrit_testsite -s
 ----
 
-NOTE: Please refer to <<special_bazel_java_version,this explanation>>
-for details why using `java -jar` isn't sufficient.
+NOTE: To learn why using `java -jar` isn't sufficient, see
+<<special_bazel_java_version,this explanation>>.
 
-Gerrit Inspector examines Java libraries first, then loads
-its initialization scripts and then starts a command line
-prompt on the console:
+Inspector examines Java libraries, loads the initialization scripts, and
+starts a command line prompt on the console:
 
 ----
   Welcome to the Gerrit Inspector
   Enter help() to see the above again, EOF to quit and stop Gerrit
   Jython 2.5.2 (Release_2_5_2:7206, Mar 2 2011, 23:12:06)
-  [OpenJDK 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0 running for Gerrit 2.3-rc0-163-g01967ef
+  [OpenJDK 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0 running for
+  Gerrit 2.3-rc0-163-g01967ef
   >>>
 ----
 
-With the Inspector enabled Gerrit can be used normally and all
-interfaces (HTTP, SSH etc.) are available.
+When the Inspector is enabled, you can use Gerrit as usual and all
+interfaces (including HTTP and SSH) are available.
 
-Care must be taken not to modify internal state of the system
-when using the Inspector.
+CAUTION: When using the Inspector, be careful not to modify the internal state
+of the system.
 
-=== Querying the Database
+=== Querying the database
 
-The embedded H2 database can be queried and updated from the
-command line.  If the daemon is not currently running:
+The embedded H2 database can be queried and updated from the command line. If
+the daemon is not running, run:
 
 ----
   $(bazel info output_base)/external/local_jdk/bin/java \
      -jar bazel-bin/gerrit.war gsql -d ../gerrit_testsite -s
 ----
 
-NOTE: Please refer to <<special_bazel_java_version,this explanation>>
-for details why using `java -jar` isn't sufficient.
+NOTE: To learn why using `java -jar` isn't sufficient, see
+<<special_bazel_java_version,this explanation>>.
 
-Or, if it is running and the database is in use, connect over SSH
-using an administrator user account:
+Alternatively, if the daemon is running and the database is in use, use an
+administrator user account to connect over SSH:
 
 ----
   ssh -p 29418 user@localhost gerrit gsql
 ----
 
 
-[[debug-javascript]]
-=== Debugging JavaScript
+== Switching between branches
 
-When debugging browser specific issues add `?dbg=1` to the URL so the
-resulting JavaScript more closely matches the Java sources.  The debug
-pages use the GWT pretty format, where function and variable names
-match the Java sources.
+When using `git checkout` without `--recurse-submodules` to switch between
+branches, submodule revisions are not altered, which can result in:
+
+*  Incorrect or unneeded plugin revisions.
+*  Missing plugins.
+
+After you switch branches, ensure that you have the correct versions of
+the submodules.
+
+CAUTION: If you store Eclipse or IntelliJ project files in the Gerrit source
+directories, do *_not_* run `git clean -fdx`. Doing so may remove untracked files and damage your project. For more information, see
+link:https://git-scm.com/docs/git-clean[git-clean].
+
+Run the following:
 
 ----
-  http://localhost:8080/?dbg=1
+  git submodule update
+  git clean -ffd
 ----
 
-
-== Client-Server RPC
-
-The client-server RPC implementation is gwtjsonrpc, not the stock RPC
-system that comes with GWT.  This buys us automatic XSRF protection.
-It also makes all of the messages readable and writable by any JSON
-implementation, facilitating "mashups" and 3rd party clients.
-
-The programming API is virtually identical, except service interfaces
-extend RemoteJsonService instead of RemoteService.
-
-
-== Why GWT?
-
-We like it.  Plus we can write Java code once and run it both in
-the browser and on the server side.
-
-
-== External Links
-
-Google Web Toolkit:
-
-* http://www.gwtproject.org/download.html[Download]
-
-Apache SSHD:
-
-* http://mina.apache.org/sshd/[SSHD]
-
-H2:
-
-* http://www.h2database.com/[H2]
-* http://www.h2database.com/html/grammar.html[SQL Reference]
-
-PostgreSQL:
-
-* http://www.postgresql.org/download/[Download]
-* http://www.postgresql.org/docs/[Documentation]
-
-
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/install.txt b/Documentation/install.txt
index 0f121a0..cc19b3f 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -1,17 +1,19 @@
 = Gerrit Code Review - Standalone Daemon Installation Guide
 
-[[requirements]]
-== Requirements
-To run the Gerrit service, the following requirements must be met on
-the host:
+[[prerequisites]]
+== Prerequisites
+
+To run the Gerrit service, the following requirement must be met on the host:
 
 * JRE, minimum version 1.8 http://www.oracle.com/technetwork/java/javase/downloads/index.html[Download]
 
-You'll also need an SQL database to house the review metadata. You have the
-choice of either using the embedded H2 or to host your own MySQL or PostgreSQL.
+By default, Gerrit uses link:note-db.html[NoteDB] as the storage backend. (If
+desired, you can _optionally_ use an external database such as MySQL or
+PostgreSQL.)
 
 [[cryptography]]
 == Configure Java for Strong Cryptography
+
 Support for extra strength cryptographic ciphers: _AES128CTR_, _AES256CTR_,
 _ARCFOUR256_, and _ARCFOUR128_ can be enabled by downloading the _Java
 Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files_
diff --git a/Documentation/linux-quickstart.txt b/Documentation/linux-quickstart.txt
index 84deeb5..2464c3a 100644
--- a/Documentation/linux-quickstart.txt
+++ b/Documentation/linux-quickstart.txt
@@ -1,64 +1,63 @@
 = Quickstart for Installing Gerrit on Linux
 
-This quickstart shows you how to install Gerrit on a Linux machine.
+This content explains how to install a basic instance of Gerrit on a Linux
+machine.
 
 [NOTE]
 ====
-The installation steps provided in this quickstart are for
-demonstration purposes only. They are not intended for use in a production
-environment.
+This quickstart is provided for demonstration purposes only. The Gerrit instance
+they install must not be used in a production environment.
 
-For a more detailed installation guide, see
+Instead, to install a Gerrit production environment, see
 link:install.html[Standalone Daemon Installation Guide].
 ====
 
-== Before you begin
+== Before you start
 
-To complete this quickstart, you need:
+Be sure you have:
 
-. A Unix-based server such as any of the Linux flavors or BSD.
-. Java SE Runtime Environment version 1.8 or later.
+. A Unix-based server, including any Linux flavor, MacOS, or Berkeley Software
+    Distribution (BSD).
+. Java SE Runtime Environment 1.8 (or higher).
 
 == Download Gerrit
 
 From the Linux machine on which you want to install Gerrit:
 
 . Open a terminal window.
-. Download the Gerrit archive. See
-link:https://gerrit-releases.storage.googleapis.com/index.html[Gerrit Code
-Review - Releases] for a list of available archives.
+. Download the desired Gerrit archive.
 
-The steps in this quickstart used Gerrrit 2.14.2, which you can download using
-a command such as:
+To view previous archives, see
+link:https://gerrit-releases.storage.googleapis.com/index.html[Gerrit Code Review: Releases]. The steps below install Gerrit 2.15.1:
 
 ....
-wget https://www.gerritcodereview.com/download/gerrit-2.14.2.war
+wget https://www.gerritcodereview.com/download/gerrit-2.15.1.war
 ....
 
-NOTE: If you want to build and install Gerrit from the source files, see
-link:dev-readme.html[Developer Setup].
+NOTE: To build and install Gerrit from the source files, see
+link:dev-readme.html[Gerrit Code Review: Developer Setup].
 
 == Install and initialize Gerrit
 
-From the command line, type the following:
+From the command line, enter:
 
 ....
 java -jar gerrit*.war init --batch --dev -d ~/gerrit_testsite
 ....
 
-The preceding command uses two parameters:
+This command takes two parameters:
 
-* `--batch`. This parameter assigns default values to a variety of Gerrit
-  configuration options. To learn more about these configuration options, see
-  link:config-gerrit.html[Configuration].
-* `--dev`. This parameter configures the server to use the authentication
-  option, `DEVELOPMENT_BECOME_ANY_ACCOUNT`. This authentication type makes it
-  easy for you to switch between different users to explore how Gerrit works.
-  To learn more about setting up Gerrit for development, see
-  link:dev-readme.html[Developer Setup].
+* `--batch` assigns default values to several Gerrit configuration
+    options. To learn more about these options, see
+    link:config-gerrit.html[Configuration].
+* `--dev` configures the Gerrit server to use the authentication
+  option, `DEVELOPMENT_BECOME_ANY_ACCOUNT`, which enables you to
+  switch between different users to explore how Gerrit works. To learn more
+  about setting up Gerrit for development, see
+  link:dev-readme.html[Gerrit Code Review: Developer Setup].
 
-This command displays a number of messages in the terminal window. The following
-is an example of these messages:
+While this command executes, status messages are displayed in the terminal
+window. For example:
 
 ....
 Generating SSH host key ... rsa(simple)... done
@@ -67,14 +66,15 @@
 Starting Gerrit Code Review: OK
 ....
 
-The last message you should see is `Starting Gerrit Code Review: OK`. This
-message informs you that the Gerrit service is now running.
+The last message confirms that the Gerrit service is running:
+
+`Starting Gerrit Code Review: OK`.
 
 == Update the listen URL
 
-Another recommended task is to change the URL that Gerrit listens to from `*`
-to `localhost`. This change helps prevent outside connections from contacting
-the instance.
+To prevent outside connections from contacting your new Gerrit instance
+(strongly recommended), change the URL on which Gerrit listens from `*` to
+`localhost`. For example:
 
 ....
 git config --file ~/gerrit_testsite/etc/gerrit.config httpd.listenUrl 'http://localhost:8080'
@@ -83,7 +83,7 @@
 == Restart the Gerrit service
 
 You must restart the Gerrit service for your authentication type and listen URL
-changes to take effect.
+changes to take effect:
 
 ....
 ~/gerrit_testsite/bin/gerrit.sh restart
@@ -91,8 +91,7 @@
 
 == Viewing Gerrit
 
-At this point, you have a basic installation of Gerrit. You can view this
-installation by opening a browser and entering the following URL:
+To view your new basic installation of Gerrit, go to:
 
 ....
 http://localhost:8080
@@ -100,10 +99,10 @@
 
 == Next steps
 
-Through this quickstart, you now have a simple version of Gerrit running on your
-Linux machine. You can use this installation to explore the UI and become
-familiar with some of Gerrit's features. For a more detailed installation guide,
-see link:install.html[Standalone Daemon Installation Guide].
+Now that you have a simple version of Gerrit running, use the installation to
+explore the user interface and learn about Gerrit. For more detailed
+installation instructions, see
+link:[Standalone Daemon Installation Guide](install.html).
 
 GERRIT
 ------
diff --git a/WORKSPACE b/WORKSPACE
index c8ebd0f..5de2ec2 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -11,12 +11,11 @@
     urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"],
 )
 
-# davido's fork with https://github.com/bazelbuild/rules_closure/pull/235 included
 http_archive(
     name = "io_bazel_rules_closure",
-    sha256 = "314e4eb701696e267cb911609e2e333e321fe641981a33144f460068ff4e1af3",
-    strip_prefix = "rules_closure-0.11.0",
-    url = "https://github.com/davido/rules_closure/archive/0.11.0.tar.gz",
+    sha256 = "a80acb69c63d5f6437b099c111480a4493bad4592015af2127a2f49fb7512d8d",
+    strip_prefix = "rules_closure-0.7.0",
+    url = "https://github.com/bazelbuild/rules_closure/archive/0.7.0.tar.gz",
 )
 
 # File is specific to Polymer and copied from the Closure Github -- should be
@@ -762,60 +761,60 @@
     sha1 = "df4b50061e8e4c348ce243b921f53ee63ba9bbe1",
 )
 
-JETTY_VERS = "9.3.18.v20170406"
+JETTY_VERS = "9.4.9.v20180320"
 
 maven_jar(
     name = "jetty_servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VERS,
-    sha1 = "534e7fa0e4fb6e08f89eb3f6a8c48b4f81ff5738",
+    sha1 = "d4453b746bc581af6ec5bce09228dc802bec1040",
 )
 
 maven_jar(
     name = "jetty_security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VERS,
-    sha1 = "16b900e91b04511f42b706c925c8af6023d2c05e",
+    sha1 = "dadd28ef757d9b8cdd1d7eef7fcbfa0b482c4648",
 )
 
 maven_jar(
     name = "jetty_servlets",
     artifact = "org.eclipse.jetty:jetty-servlets:" + JETTY_VERS,
-    sha1 = "f9311d1d8e6124d2792f4db5b29514d0ecf46812",
+    sha1 = "cb40696bb683655b7abb4ca72ad08708cd99ca7b",
 )
 
 maven_jar(
     name = "jetty_server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VERS,
-    sha1 = "0a32feea88cba2d43951d22b60861c643454bb3f",
+    sha1 = "08847f7278e8ace7a1f5847e71563c8a10546582",
 )
 
 maven_jar(
     name = "jetty_jmx",
     artifact = "org.eclipse.jetty:jetty-jmx:" + JETTY_VERS,
-    sha1 = "f988136dc5aa634afed6c5a35d910ee9599c6c23",
+    sha1 = "ff0978e1c74c4e08517df4d1950e61450ea987b1",
 )
 
 maven_jar(
     name = "jetty_continuation",
     artifact = "org.eclipse.jetty:jetty-continuation:" + JETTY_VERS,
-    sha1 = "3c5d89c8204d4a48a360087f95e4cbd4520b5de0",
+    sha1 = "590a07c7daf76c755e2daefb1aa0a91b41b26d87",
 )
 
 maven_jar(
     name = "jetty_http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VERS,
-    sha1 = "30ece6d732d276442d513b94d914de6fa1075fae",
+    sha1 = "64d93698196ea7a66b33c754a0eac2a97d5af4b6",
 )
 
 maven_jar(
     name = "jetty_io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VERS,
-    sha1 = "36cb411ee89be1b527b0c10747aa3153267fc3ec",
+    sha1 = "938d67c72405285d2a7a6efb10d870a1b16fa2e0",
 )
 
 maven_jar(
     name = "jetty_util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VERS,
-    sha1 = "8600b7d028a38cb462eff338de91390b3ff5040e",
+    sha1 = "8a602b93581f6af54839728f51d51ab830bdd44d",
 )
 
 maven_jar(
diff --git a/contrib/populate-fixture-data.py b/contrib/populate-fixture-data.py
index 07a0f01..22e0c1b 100755
--- a/contrib/populate-fixture-data.py
+++ b/contrib/populate-fixture-data.py
@@ -275,40 +275,6 @@
 
 
 def main():
-<<<<<<< HEAD
-  p = optparse.OptionParser()
-  p.add_option("-u", "--user_count", action="store",
-               default=100,
-               type='int',
-               help="number of users to generate")
-  p.add_option("-p", "--port", action="store",
-               default=8080,
-               type='int',
-               help="port of server")
-  (options, _) = p.parse_args()
-  global BASE_URL
-  BASE_URL = BASE_URL % options.port
-  print(BASE_URL)
-
-  set_up()
-  gerrit_users = get_random_users(options.user_count)
-
-  group_names = create_gerrit_groups()
-  for idx, u in enumerate(gerrit_users):
-    u["groups"].append(group_names[idx % len(group_names)])
-    if idx % 5 == 0:
-      # Also add to security group
-      u["groups"].append(group_names[4])
-
-  generate_ssh_keys(gerrit_users)
-  create_gerrit_users(gerrit_users)
-
-  project_names = create_gerrit_projects(group_names)
-
-  for idx, u in enumerate(gerrit_users):
-    for _ in range(random.randint(1, 5)):
-      create_change(u, project_names[4 * idx / len(gerrit_users)])
-=======
     p = optparse.OptionParser()
     p.add_option("-u", "--user_count", action="store",
                  default=100,
@@ -342,6 +308,4 @@
         for _ in xrange(random.randint(1, 5)):
             create_change(u, project_names[4 * idx / len(gerrit_users)])
 
->>>>>>> 730efd14f4... Python cleanups, round 1: whitespace
-
 main()
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index 62e9033..6913f98 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -25,6 +25,7 @@
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
 <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 <link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -61,6 +62,18 @@
         margin: 1em;
         text-align: center;
       }
+      iron-icon {
+        color: var(--default-button-text-color);
+        height: 1.2rem;
+        margin-right: .2rem;
+        width: 1.2rem;
+      }
+      #moreActions iron-icon {
+        margin: 0;
+      }
+      .hidden {
+        display: none;
+      }
       @media screen and (max-width: 50em) {
         #mainContent,
         section,
@@ -96,14 +109,17 @@
               items="[[_topLevelPrimaryActions]]"
               as="action">
             <gr-button
+                link
                 title$="[[action.title]]"
                 has-tooltip="[[_computeHasTooltip(action.title)]]"
-                primary$="[[action.__primary]]"
                 data-action-key$="[[action.__key]]"
                 data-action-type$="[[action.__type]]"
                 data-label$="[[action.label]]"
                 disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
-                on-tap="_handleActionTap">[[action.label]]</gr-button>
+                on-tap="_handleActionTap">
+                <iron-icon class$="[[_computeHasIcon(action)]]" icon$="gr-icons:[[action.icon]]"></iron-icon>
+              [[action.label]]
+            </gr-button>
           </template>
         </section>
         <section id="secondaryActions"
@@ -113,27 +129,32 @@
               items="[[_topLevelSecondaryActions]]"
               as="action">
             <gr-button
+                link
                 title$="[[action.title]]"
                 has-tooltip="[[_computeHasTooltip(action.title)]]"
-                primary$="[[action.__primary]]"
                 data-action-key$="[[action.__key]]"
                 data-action-type$="[[action.__type]]"
                 data-label$="[[action.label]]"
                 disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
-                on-tap="_handleActionTap">[[action.label]]</gr-button>
+                on-tap="_handleActionTap">
+              <iron-icon class$="[[_computeHasIcon(action)]]" icon$="gr-icons:[[action.icon]]"></iron-icon>
+              [[action.label]]
+            </gr-button>
           </template>
         </section>
       <gr-button hidden$="[[!_loading]]" disabled>Loading actions...</gr-button>
       <gr-dropdown
           id="moreActions"
+          link
           tabindex="0"
-          down-arrow
           vertical-offset="32"
           horizontal-align="right"
           on-tap-item="_handleOveflowItemTap"
           hidden$="[[_shouldHideActions(_menuActions.*, _loading)]]"
           disabled-ids="[[_disabledMenuActions]]"
-          items="[[_menuActions]]">More</gr-dropdown>
+          items="[[_menuActions]]">
+          <iron-icon icon="gr-icons:more-vert"></iron-icon>
+        </gr-dropdown>
     </div>
     <gr-overlay id="overlay" with-backdrop>
       <gr-confirm-rebase-dialog id="confirmRebase"
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index cfdf88c..7ae2ef6 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -172,6 +172,22 @@
     __type: 'change',
   };
 
+  // Set of keys that have icons. As more icons are added to gr-icons.html, this
+  // set should be expanded.
+  const ACTIONS_WITH_ICONS = new Set([
+    ChangeActions.ABANDON,
+    ChangeActions.DELETE_EDIT,
+    ChangeActions.EDIT,
+    ChangeActions.PUBLISH_EDIT,
+    ChangeActions.REBASE_EDIT,
+    ChangeActions.RESTORE,
+    ChangeActions.REVERT,
+    ChangeActions.STOP_EDIT,
+    QUICK_APPROVE_ACTION.key,
+    RevisionActions.REBASE,
+    RevisionActions.SUBMIT,
+  ]);
+
   const AWAIT_CHANGE_ATTEMPTS = 5;
   const AWAIT_CHANGE_TIMEOUT_MS = 1000;
 
@@ -1270,7 +1286,13 @@
 
       return revisionActionValues
           .concat(changeActionValues)
-          .sort(this._actionComparator.bind(this));
+          .sort(this._actionComparator.bind(this))
+          .map(action => {
+            if (ACTIONS_WITH_ICONS.has(action.__key)) {
+              action.icon = action.__key;
+            }
+            return action;
+          });
     },
 
     _getActionPriority(action) {
@@ -1386,5 +1408,9 @@
     _computeHasTooltip(title) {
       return !!title;
     },
+
+    _computeHasIcon(action) {
+      return action.icon ? '' : 'hidden';
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index cb9f4c5..5f0a620 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -1294,6 +1294,7 @@
       this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
       Promise.all(allDataPromises).then(() => {
         this.$.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
+        this.$.reporting.changeFullyLoaded();
       });
 
       return coreDataPromise
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index 8ce59f2..04e3794 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -21,6 +21,7 @@
 <link rel="import" href="../../../behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html">
 <link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
 <link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
 <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../gr-account-dropdown/gr-account-dropdown.html">
@@ -101,25 +102,37 @@
           color: var(--primary-text-color);
         }
       }
+      .settingsButton {
+        margin-left: .5em;
+      }
       .browse {
         color: var(--header-text-color);
         /* Same as gr-button */
         margin: 5px 4px;
         text-decoration: none;
       }
-      .accountContainer:not(.loggedIn):not(.loggedOut) .loginButton,
-      .accountContainer:not(.loggedIn):not(.loggedOut) gr-account-dropdown,
-      .accountContainer.loggedIn .loginButton,
-      .accountContainer.loggedOut gr-account-dropdown {
+      .settingsButton,
+      gr-account-dropdown {
         display: none;
       }
+      :host([loading]) .accountContainer,
+      :host([logged-in]) .loginButton {
+        display: none;
+      }
+      :host([logged-in]) .settingsButton,
+      :host([logged-in]) gr-account-dropdown {
+        display: inline;
+      }
+      iron-icon {
+        color: var(--header-text-color);
+      }
       .accountContainer {
         align-items: center;
         display: flex;
         margin: 0 -.5em 0 .5em;
-        white-space: nowrap;
         overflow: hidden;
         text-overflow: ellipsis;
+        white-space: nowrap;
       }
       .loginButton {
         color: var(--header-text-color);
@@ -181,6 +194,12 @@
             name="header-browse-source"></gr-endpoint-decorator>
         <div class="accountContainer" id="accountContainer">
           <a class="loginButton" href$="[[_loginURL]]">Sign in</a>
+          <a
+              class="settingsButton"
+              href$="[[_generateSettingsLink()]]"
+              title="Settings">
+            <iron-icon icon="gr-icons:settings"></iron-icon>
+          </a>
           <gr-account-dropdown account="[[_account]]"></gr-account-dropdown>
         </div>
       </div>
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index 42c744f..dad7b7c 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -74,6 +74,14 @@
         type: String,
         notify: true,
       },
+      loggedIn: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
+      loading: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
 
       /** @type {?Object} */
       _account: Object,
@@ -192,6 +200,7 @@
     },
 
     _loadAccount() {
+      this.loading = true;
       const promises = [
         this.$.restAPI.getAccount(),
         Gerrit.awaitPluginsLoaded(),
@@ -200,8 +209,8 @@
       return Promise.all(promises).then(result => {
         const account = result[0];
         this._account = account;
-        this.$.accountContainer.classList.toggle('loggedIn', account != null);
-        this.$.accountContainer.classList.toggle('loggedOut', account == null);
+        this.loggedIn = !!account;
+        this.loading = false;
 
         return this.getAdminLinks(account,
             this.$.restAPI.getAccountCapabilities.bind(this.$.restAPI),
@@ -254,5 +263,9 @@
       // Groups are not yet supported.
       return !linkObj.url.startsWith('/groups');
     },
+
+    _generateSettingsLink() {
+      return this.getBaseUrl() + '/settings/';
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
index 5d51546..30e8e1f 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
@@ -53,6 +53,30 @@
       sandbox.restore();
     });
 
+    test('link visibility', () => {
+      element.loading = true;
+      assert.equal(getComputedStyle(element.$$('.accountContainer')).display,
+          'none');
+      element.loading = false;
+      element.loggedIn = false;
+      assert.notEqual(getComputedStyle(element.$$('.accountContainer')).display,
+          'none');
+      assert.notEqual(getComputedStyle(element.$$('.loginButton')).display,
+          'none');
+      assert.equal(getComputedStyle(element.$$('gr-account-dropdown')).display,
+          'none');
+      assert.equal(getComputedStyle(element.$$('.settingsButton')).display,
+          'none');
+      element.loggedIn = true;
+      assert.equal(getComputedStyle(element.$$('.loginButton')).display,
+          'none');
+      assert.notEqual(getComputedStyle(element.$$('gr-account-dropdown'))
+          .display,
+          'none');
+      assert.notEqual(getComputedStyle(element.$$('.settingsButton')).display,
+          'none');
+    });
+
     test('fix my menu item', () => {
       assert.deepEqual([
         {url: 'https://awesometown.com/#hashyhash'},
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
index cbb2c09..935de6b 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
@@ -16,7 +16,6 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 
 <dom-module id="gr-reporting">
   <script src="gr-jank-detector.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 63808d1..feff01d 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -70,11 +70,13 @@
 
   const TIMER = {
     CHANGE_DISPLAYED: 'ChangeDisplayed',
+    CHANGE_LOAD_FULL: 'ChangeFullyLoaded',
     DASHBOARD_DISPLAYED: 'DashboardDisplayed',
     DIFF_VIEW_DISPLAYED: 'DiffViewDisplayed',
     FILE_LIST_DISPLAYED: 'FileListDisplayed',
     PLUGINS_LOADED: 'PluginsLoaded',
     STARTUP_CHANGE_DISPLAYED: 'StartupChangeDisplayed',
+    STARTUP_CHANGE_LOAD_FULL: 'StartupChangeFullyLoaded',
     STARTUP_DASHBOARD_DISPLAYED: 'StartupDashboardDisplayed',
     STARTUP_DIFF_VIEW_DISPLAYED: 'StartupDiffViewDisplayed',
     STARTUP_FILE_LIST_DISPLAYED: 'StartupFileListDisplayed',
@@ -84,6 +86,7 @@
   const STARTUP_TIMERS = {};
   STARTUP_TIMERS[TIMER.PLUGINS_LOADED] = 0;
   STARTUP_TIMERS[TIMER.STARTUP_CHANGE_DISPLAYED] = 0;
+  STARTUP_TIMERS[TIMER.STARTUP_CHANGE_LOAD_FULL] = 0;
   STARTUP_TIMERS[TIMER.STARTUP_DASHBOARD_DISPLAYED] = 0;
   STARTUP_TIMERS[TIMER.STARTUP_DIFF_VIEW_DISPLAYED] = 0;
   STARTUP_TIMERS[TIMER.STARTUP_FILE_LIST_DISPLAYED] = 0;
@@ -148,8 +151,13 @@
       return window.performance.now();
     },
 
+    _arePluginsLoaded() {
+      return this._baselines &&
+        !this._baselines.hasOwnProperty(TIMER.PLUGINS_LOADED);
+    },
+
     reporter(...args) {
-      const report = (Gerrit._arePluginsLoaded() && !pending.length) ?
+      const report = (this._arePluginsLoaded() && !pending.length) ?
         this.defaultReporter : this.cachingReporter;
       report.apply(this, args);
     },
@@ -174,7 +182,7 @@
       if (type === ERROR.TYPE) {
         console.error(eventValue.error || eventName);
       }
-      if (Gerrit._arePluginsLoaded()) {
+      if (this._arePluginsLoaded()) {
         if (pending.length) {
           for (const args of pending.splice(0)) {
             this.reporter(...args);
@@ -225,6 +233,7 @@
         delete this._baselines[prop];
       }
       this.time(TIMER.CHANGE_DISPLAYED);
+      this.time(TIMER.CHANGE_LOAD_FULL);
       this.time(TIMER.DASHBOARD_DISPLAYED);
       this.time(TIMER.DIFF_VIEW_DISPLAYED);
       this.time(TIMER.FILE_LIST_DISPLAYED);
@@ -251,6 +260,14 @@
       }
     },
 
+    changeFullyLoaded() {
+      if (this._baselines.hasOwnProperty(TIMER.STARTUP_CHANGE_LOAD_FULL)) {
+        this.timeEnd(TIMER.STARTUP_CHANGE_LOAD_FULL);
+      } else {
+        this.timeEnd(TIMER.CHANGE_LOAD_FULL);
+      }
+    },
+
     diffViewDisplayed() {
       if (this._baselines.hasOwnProperty(TIMER.STARTUP_DIFF_VIEW_DISPLAYED)) {
         this.timeEnd(TIMER.STARTUP_DIFF_VIEW_DISPLAYED);
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
index 5849042..3c6d4bf 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -100,6 +100,7 @@
           'lifecycle', 'UI Latency', 'Jank count', 42));
       assert.isTrue(element.time.calledWithExactly('DashboardDisplayed'));
       assert.isTrue(element.time.calledWithExactly('ChangeDisplayed'));
+      assert.isTrue(element.time.calledWithExactly('ChangeFullyLoaded'));
       assert.isTrue(element.time.calledWithExactly('DiffViewDisplayed'));
       assert.isTrue(element.time.calledWithExactly('FileListDisplayed'));
       assert.isFalse(element._baselines.hasOwnProperty('garbage'));
@@ -116,6 +117,17 @@
       assert.isTrue(element.timeEnd.calledWithExactly('ChangeDisplayed'));
     });
 
+    test('changeFullyLoaded', () => {
+      sandbox.spy(element, 'timeEnd');
+      element.changeFullyLoaded();
+      assert.isFalse(
+          element.timeEnd.calledWithExactly('ChangeFullyLoaded'));
+      assert.isTrue(
+          element.timeEnd.calledWithExactly('StartupChangeFullyLoaded'));
+      element.changeFullyLoaded();
+      assert.isTrue(element.timeEnd.calledWithExactly('ChangeFullyLoaded'));
+    });
+
     test('diffViewDisplayed', () => {
       sandbox.spy(element, 'timeEnd');
       element.diffViewDisplayed();
@@ -190,11 +202,9 @@
       setup(() => {
         element.reporter.restore();
         sandbox.stub(element, 'defaultReporter');
-        sandbox.stub(Gerrit, '_arePluginsLoaded');
       });
 
       test('pluginsLoaded reports time', () => {
-        Gerrit._arePluginsLoaded.returns(true);
         sandbox.stub(element, 'now').returns(42);
         element.pluginsLoaded();
         assert.isTrue(element.defaultReporter.calledWithExactly(
@@ -203,21 +213,18 @@
       });
 
       test('pluginsLoaded reports plugins', () => {
-        Gerrit._arePluginsLoaded.returns(true);
         element.pluginsLoaded(['foo', 'bar']);
-        assert.isTrue(element.defaultReporter.calledWithExactly(
+        assert.isTrue(element.defaultReporter.calledWith(
             'lifecycle', 'Plugins installed', 'foo,bar'
         ));
       });
 
       test('caches reports if plugins are not loaded', () => {
-        Gerrit._arePluginsLoaded.returns(false);
         element.timeEnd('foo');
         assert.isFalse(element.defaultReporter.called);
       });
 
       test('reports if plugins are loaded', () => {
-        Gerrit._arePluginsLoaded.returns(true);
         element.pluginsLoaded();
         assert.isTrue(element.defaultReporter.called);
       });
@@ -225,14 +232,19 @@
       test('reports cached events preserving order', () => {
         element.time('foo');
         element.time('bar');
-        Gerrit._arePluginsLoaded.returns(false);
         element.timeEnd('foo');
-        Gerrit._arePluginsLoaded.returns(true);
+        element.pluginsLoaded();
         element.timeEnd('bar');
-        assert.isTrue(element.defaultReporter.firstCall.calledWith(
+        assert.isTrue(element.defaultReporter.getCall(0).calledWith(
             'timing-report', 'UI Latency', 'foo'
         ));
-        assert.isTrue(element.defaultReporter.secondCall.calledWith(
+        assert.isTrue(element.defaultReporter.getCall(1).calledWith(
+            'timing-report', 'UI Latency', 'PluginsLoaded'
+        ));
+        assert.isTrue(element.defaultReporter.getCall(2).calledWith(
+            'lifecycle', 'Plugins installed'
+        ));
+        assert.isTrue(element.defaultReporter.getCall(3).calledWith(
             'timing-report', 'UI Latency', 'bar'
         ));
       });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
index 9f07d90..9409faa 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
@@ -34,13 +34,31 @@
       GrDiffBuilderSideBySide.prototype);
   GrDiffBuilderImage.prototype.constructor = GrDiffBuilderImage;
 
-  GrDiffBuilderImage.prototype.renderDiffImages = function() {
+  GrDiffBuilderImage.prototype.renderDiff = function() {
     const section = this._createElement('tbody', 'image-diff');
 
     this._emitImagePair(section);
     this._emitImageLabels(section);
 
     this._outputEl.appendChild(section);
+    this._outputEl.appendChild(this._createEndpoint());
+  };
+
+  GrDiffBuilderImage.prototype._createEndpoint = function() {
+    const endpoint = this._createElement('gr-endpoint-decorator');
+    endpoint.setAttribute('name', 'imageDiff');
+    endpoint.appendChild(
+        this._createEndpointParam('baseImage', this._baseImage));
+    endpoint.appendChild(
+        this._createEndpointParam('revisionImage', this._revisionImage));
+    return endpoint;
+  };
+
+  GrDiffBuilderImage.prototype._createEndpointParam = function(name, value) {
+    const endpointParam = this._createElement('gr-endpoint-param');
+    endpointParam.setAttribute('name', name);
+    endpointParam.value = value;
+    return endpointParam;
   };
 
   GrDiffBuilderImage.prototype._emitImagePair = function(section) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index 9aec1e8..e8f4b21 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -179,7 +179,7 @@
           return this.$.processor.process(this.diff.content, isBinary)
               .then(() => {
                 if (this.isImageDiff) {
-                  this._builder.renderDiffImages();
+                  this._builder.renderDiff();
                 }
                 this.dispatchEvent(new CustomEvent('render-content',
                     {bubbles: true}));
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 6a2bfe0..c9e05e4 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -220,8 +220,9 @@
           view="[[params.view]]"
           on-close="_handleKeyboardShortcutDialogClose"></gr-keyboard-shortcuts-dialog>
     </gr-overlay>
-    <gr-overlay id="registration" with-backdrop>
+    <gr-overlay id="registrationOverlay" with-backdrop>
       <gr-registration-dialog
+          id="registrationDialog"
           settings-url="[[_settingsUrl]]"
           on-account-detail-update="_handleAccountDetailUpdate"
           on-close="_handleRegistrationDialogClose">
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 3b66ae8..2a247b9 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -191,7 +191,10 @@
         this.async(() => this.set('_showPluginScreen', true), 1);
       }
       if (this.params.justRegistered) {
-        this.$.registration.open();
+        this.$.registrationOverlay.open();
+        this.$.registrationDialog.loadData().then(() => {
+          this.$.registrationOverlay.refit();
+        });
       }
       this.$.header.unfloat();
     },
@@ -272,7 +275,7 @@
 
     _handleRegistrationDialogClose(e) {
       this.params.justRegistered = false;
-      this.$.registration.close();
+      this.$.registrationOverlay.close();
     },
 
     _computeShadowClass(isShadowDom) {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
index 79c8a3b..7a8cd6f 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
@@ -32,6 +32,16 @@
       main {
         max-width: 46em;
       }
+      :host(.loading) main {
+        display: none;
+      }
+      .loadingMessage {
+        display: none;
+        font-style: italic;
+      }
+      :host(.loading) .loadingMessage {
+        display: block;
+      }
       hr {
         margin-top: 1em;
         margin-bottom: 1em;
@@ -54,9 +64,13 @@
       input {
         width: 20em;
       }
+      section.hide {
+        display: none;
+      }
     </style>
     <div class="container gr-form-styles">
       <header>Please confirm your contact information</header>
+      <div class="loadingMessage">Loading...</div>
       <main>
         <p>
           The following contact information was automatically obtained when you
@@ -73,7 +87,7 @@
               bind-value="{{_account.name}}"
               disabled="[[_saving]]">
         </section>
-        <section>
+        <section class$="[[_computeUsernameClass(_usernameMutable)]]">
           <div class="title">Username</div>
           <input
               is="iron-input"
@@ -108,7 +122,7 @@
             id="saveButton"
             primary
             link
-            disabled="[[_computeSaveDisabled(_account.name, _account.username, _account.email, _saving)]]"
+            disabled="[[_computeSaveDisabled(_account.name, _account.email, _saving)]]"
             on-tap="_handleSave">Save</gr-button>
       </footer>
     </div>
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index 051668c..c6cd578 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -43,32 +43,56 @@
           return {email: null, name: null, username: null};
         },
       },
+      _usernameMutable: {
+        type: Boolean,
+        computed: '_computeUsernameMutable(_serverConfig, _account.username)',
+      },
+      _loading: {
+        type: Boolean,
+        value: true,
+        observer: '_loadingChanged',
+      },
       _saving: {
         type: Boolean,
         value: false,
       },
+      _serverConfig: Object,
     },
 
     hostAttributes: {
       role: 'dialog',
     },
 
-    attached() {
-      this.$.restAPI.getAccount().then(account => {
+    loadData() {
+      this._loading = true;
+
+      const loadAccount = this.$.restAPI.getAccount().then(account => {
         // Using Object.assign here allows preservation of the default values
         // supplied in the value generating function of this._account, unless
         // they are overridden by properties in the account from the response.
         this._account = Object.assign({}, this._account, account);
       });
+
+      const loadConfig = this.$.restAPI.getConfig().then(config => {
+        this._serverConfig = config;
+      });
+
+      return Promise.all([loadAccount, loadConfig]).then(() => {
+        this._loading = false;
+      });
     },
 
     _save() {
       this._saving = true;
       const promises = [
         this.$.restAPI.setAccountName(this.$.name.value),
-        this.$.restAPI.setAccountUsername(this.$.username.value),
         this.$.restAPI.setPreferredAccountEmail(this.$.email.value || ''),
       ];
+
+      if (this._usernameMutable) {
+        promises.push(this.$.restAPI.setAccountUsername(this.$.username.value));
+      }
+
       return Promise.all(promises).then(() => {
         this._saving = false;
         this.fire('account-detail-update');
@@ -90,8 +114,21 @@
       this.fire('close');
     },
 
-    _computeSaveDisabled(name, username, email, saving) {
-      return !name || !username || !email || saving;
+    _computeSaveDisabled(name, email, saving) {
+      return !name || !email || saving;
+    },
+
+    _computeUsernameMutable(config, username) {
+      return config.auth.editable_account_fields.includes('USER_NAME') &&
+          !username;
+    },
+
+    _computeUsernameClass(usernameMutable) {
+      return usernameMutable ? '' : 'hide';
+    },
+
+    _loadingChanged() {
+      this.classList.toggle('loading', this._loading);
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
index e4560ff..93a3188 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
@@ -45,13 +45,13 @@
     let sandbox;
     let _listeners;
 
-    setup(done => {
+    setup(() => {
       sandbox = sinon.sandbox.create();
       _listeners = {};
 
       account = {
         name: 'name',
-        username: 'username',
+        username: null,
         email: 'email',
         secondary_emails: [
           'email2',
@@ -61,8 +61,6 @@
 
       stub('gr-rest-api-interface', {
         getAccount() {
-        // Once the account is resolved, we can let the test proceed.
-          flush(done);
           return Promise.resolve(account);
         },
         setAccountName(name) {
@@ -77,9 +75,15 @@
           account.email = email;
           return Promise.resolve();
         },
+        getConfig() {
+          return Promise.resolve(
+              {auth: {editable_account_fields: ['USER_NAME']}});
+        },
       });
 
       element = fixture('basic');
+
+      return element.loadData();
     });
 
     teardown(() => {
@@ -136,7 +140,7 @@
 
         // Nothing should be committed yet.
         assert.equal(account.name, 'name');
-        assert.equal(account.username, 'username');
+        assert.isNotOk(account.username);
         assert.equal(account.email, 'email');
 
         // Save and verify new values are committed.
@@ -158,12 +162,22 @@
 
     test('save btn disabled', () => {
       const compute = element._computeSaveDisabled;
-      assert.isTrue(compute('', '', '', false));
-      assert.isTrue(compute('', 'test', 'test', false));
-      assert.isTrue(compute('test', '', 'test', false));
-      assert.isTrue(compute('test', 'test', '', false));
-      assert.isTrue(compute('test', 'test', 'test', true));
-      assert.isFalse(compute('test', 'test', 'test', false));
+      assert.isTrue(compute('', '', false));
+      assert.isTrue(compute('', 'test', false));
+      assert.isTrue(compute('test', '', false));
+      assert.isTrue(compute('test', 'test', true));
+      assert.isFalse(compute('test', 'test', false));
+    });
+
+    test('_computeUsernameMutable', () => {
+      assert.isTrue(element._computeUsernameMutable(
+          {auth: {editable_account_fields: ['USER_NAME']}}, null));
+      assert.isFalse(element._computeUsernameMutable(
+          {auth: {editable_account_fields: ['USER_NAME']}}, 'abc'));
+      assert.isFalse(element._computeUsernameMutable(
+          {auth: {editable_account_fields: []}}, null));
+      assert.isFalse(element._computeUsernameMutable(
+          {auth: {editable_account_fields: []}}, 'abc'));
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
index fc029ce..bc63acf 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
@@ -16,9 +16,10 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
 
 <dom-module id="gr-avatar">
   <template>
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
index f559148..f32e940b 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -29,37 +29,40 @@
         type: Number,
         value: 16,
       },
+      _hasAvatars: {
+        type: Boolean,
+        value: false,
+      },
     },
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
     ],
 
-    created() {
-      this.hidden = true;
-    },
-
     attached() {
-      this.$.restAPI.getConfig().then(cfg => {
-        const hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
-        if (hasAvatars) {
-          this.hidden = false;
+      Promise.all([
+        this.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(([cfg]) => {
+        this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
+        if (this._hasAvatars && this.account) {
           // src needs to be set if avatar becomes visible
-          this._updateAvatarURL(this.account);
+          this._updateAvatarURL();
+        } else {
+          this.hidden = true;
         }
       });
     },
 
     _accountChanged(account) {
-      this._updateAvatarURL(account);
+      this._updateAvatarURL();
     },
 
-    _updateAvatarURL(account) {
-      if (!this.hidden && account) {
-        const url = this._buildAvatarURL(this.account);
-        if (url) {
-          this.style.backgroundImage = 'url("' + url + '")';
-        }
+    _updateAvatarURL() {
+      if (this.hidden || !this._hasAvatars) { return; }
+      const url = this._buildAvatarURL(this.account);
+      if (url) {
+        this.style.backgroundImage = 'url("' + url + '")';
       }
     },
 
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
index 333f0e8..f137c7f 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -38,7 +38,7 @@
 
     setup(() => {
       stub('gr-rest-api-interface', {
-        getConfig() { return Promise.resolve({}); },
+        getConfig() { return Promise.resolve({plugin: {has_avatars: true}}); },
       });
       element = fixture('basic');
     });
@@ -97,23 +97,36 @@
     });
 
     test('dom for existing account', () => {
-      assert.isTrue(element.hasAttribute('hidden'),
-          'element not hidden initially');
-      element.hidden = false;
+      assert.isFalse(element.hasAttribute('hidden'));
       element.imageSize = 64;
       element.account = {
         _account_id: 123,
       };
-      assert.isFalse(element.hasAttribute('hidden'), 'element hidden');
-      assert.isTrue(
-          element.style.backgroundImage.includes('/accounts/123/avatar?s=64'));
+      assert.strictEqual(element.style.backgroundImage, '');
+      // Emulate plugins loaded.
+      Gerrit._setPluginsPending([]);
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isFalse(element.hasAttribute('hidden'));
+        assert.isTrue(
+            element.style.backgroundImage.includes('/accounts/123/avatar?s=64'));
+      });
     });
 
     test('dom for non available account', () => {
-      assert.isTrue(element.hasAttribute('hidden'),
-          'element not hidden initially');
-      element.account = undefined;
-      assert.isTrue(element.hasAttribute('hidden'), 'element not hidden');
+      assert.isFalse(element.hasAttribute('hidden'));
+      element.account = null;
+      assert.isFalse(element.hasAttribute('hidden'));
+      // Emulate plugins loaded.
+      Gerrit._setPluginsPending([]);
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isTrue(element.hasAttribute('hidden'));
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html b/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
index cbae987..c19e3d5 100644
--- a/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
+++ b/polygerrit-ui/app/elements/shared/gr-icons/gr-icons.html
@@ -40,6 +40,12 @@
       <g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></g>
       <!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/iron-icons.html -->
       <g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></g>
+      <!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/iron-icons.html -->
+      <g id="more-vert"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></g>
+      <!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/iron-icons.html -->
+      <g id="deleteEdit"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></g>
+      <!-- This SVG is a copy from iron-icons https://github.com/PolymerElements/iron-icons/blob/master/editor-icons.html -->
+      <g id="publishEdit"><path d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"/></g>
       <!-- This SVG is a copy from material.io https://material.io/icons/#ic_hourglass_full-->
       <g id="hourglass"><path d="M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6z"/><path d="M0 0h24v24H0V0z" fill="none"/></g>
       <!-- This is a custom PolyGerrit SVG -->
@@ -51,8 +57,25 @@
       <!-- This is a custom PolyGerrit SVG -->
       <g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></g>
       <!-- This is a custom PolyGerrit SVG -->
-      <g id="robot"><path d="M4.137453,5.61015591 L4.54835569,1.5340419 C4.5717665,1.30180904 4.76724872,1.12504213 5.00065859,1.12504213 C5.23327176,1.12504213 5.42730868,1.30282046 5.44761309,1.53454578 L5.76084628,5.10933916 C6.16304484,5.03749412 6.57714381,5 7,5 L17,5 C20.8659932,5 24,8.13400675 24,12 L24,15.1250421 C24,18.9910354 20.8659932,22.1250421 17,22.1250421 L7,22.1250421 C3.13400675,22.1250421 2.19029351e-15,18.9910354 0,15.1250421 L0,12 C-3.48556243e-16,9.15382228 1.69864167,6.70438358 4.137453,5.61015591 Z M5.77553049,6.12504213 C3.04904264,6.69038358 1,9.10590202 1,12 L1,15.1250421 C1,18.4387506 3.6862915,21.1250421 7,21.1250421 L17,21.1250421 C20.3137085,21.1250421 23,18.4387506 23,15.1250421 L23,12 C23,8.6862915 20.3137085,6 17,6 L7,6 C6.60617231,6 6.2212068,6.03794347 5.84855971,6.11037415 L5.84984496,6.12504213 L5.77553049,6.12504213 Z M6.93003717,6.95027711 L17.1232083,6.95027711 C19.8638332,6.95027711 22.0855486,9.17199258 22.0855486,11.9126175 C22.0855486,14.6532424 19.8638332,16.8749579 17.1232083,16.8749579 L6.93003717,16.8749579 C4.18941226,16.8749579 1.9676968,14.6532424 1.9676968,11.9126175 C1.9676968,9.17199258 4.18941226,6.95027711 6.93003717,6.95027711 Z M7.60124392,14.0779303 C9.03787127,14.0779303 10.2024878,12.9691885 10.2024878,11.6014862 C10.2024878,10.2337839 9.03787127,9.12504213 7.60124392,9.12504213 C6.16461657,9.12504213 5,10.2337839 5,11.6014862 C5,12.9691885 6.16461657,14.0779303 7.60124392,14.0779303 Z M16.617997,14.1098288 C18.0638768,14.1098288 19.2359939,12.9939463 19.2359939,11.6174355 C19.2359939,10.2409246 18.0638768,9.12504213 16.617997,9.12504213 C15.1721172,9.12504213 14,10.2409246 14,11.6174355 C14,12.9939463 15.1721172,14.1098288 16.617997,14.1098288 Z M9.79751216,18.1250421 L15,18.1250421 L15,19.1250421 C15,19.6773269 14.5522847,20.1250421 14,20.1250421 L10.7975122,20.1250421 C10.2452274,20.1250421 9.79751216,19.6773269 9.79751216,19.1250421 L9.79751216,18.1250421 Z"></path>
-    </g>
+      <g id="robot"><path d="M4.137453,5.61015591 L4.54835569,1.5340419 C4.5717665,1.30180904 4.76724872,1.12504213 5.00065859,1.12504213 C5.23327176,1.12504213 5.42730868,1.30282046 5.44761309,1.53454578 L5.76084628,5.10933916 C6.16304484,5.03749412 6.57714381,5 7,5 L17,5 C20.8659932,5 24,8.13400675 24,12 L24,15.1250421 C24,18.9910354 20.8659932,22.1250421 17,22.1250421 L7,22.1250421 C3.13400675,22.1250421 2.19029351e-15,18.9910354 0,15.1250421 L0,12 C-3.48556243e-16,9.15382228 1.69864167,6.70438358 4.137453,5.61015591 Z M5.77553049,6.12504213 C3.04904264,6.69038358 1,9.10590202 1,12 L1,15.1250421 C1,18.4387506 3.6862915,21.1250421 7,21.1250421 L17,21.1250421 C20.3137085,21.1250421 23,18.4387506 23,15.1250421 L23,12 C23,8.6862915 20.3137085,6 17,6 L7,6 C6.60617231,6 6.2212068,6.03794347 5.84855971,6.11037415 L5.84984496,6.12504213 L5.77553049,6.12504213 Z M6.93003717,6.95027711 L17.1232083,6.95027711 C19.8638332,6.95027711 22.0855486,9.17199258 22.0855486,11.9126175 C22.0855486,14.6532424 19.8638332,16.8749579 17.1232083,16.8749579 L6.93003717,16.8749579 C4.18941226,16.8749579 1.9676968,14.6532424 1.9676968,11.9126175 C1.9676968,9.17199258 4.18941226,6.95027711 6.93003717,6.95027711 Z M7.60124392,14.0779303 C9.03787127,14.0779303 10.2024878,12.9691885 10.2024878,11.6014862 C10.2024878,10.2337839 9.03787127,9.12504213 7.60124392,9.12504213 C6.16461657,9.12504213 5,10.2337839 5,11.6014862 C5,12.9691885 6.16461657,14.0779303 7.60124392,14.0779303 Z M16.617997,14.1098288 C18.0638768,14.1098288 19.2359939,12.9939463 19.2359939,11.6174355 C19.2359939,10.2409246 18.0638768,9.12504213 16.617997,9.12504213 C15.1721172,9.12504213 14,10.2409246 14,11.6174355 C14,12.9939463 15.1721172,14.1098288 16.617997,14.1098288 Z M9.79751216,18.1250421 L15,18.1250421 L15,19.1250421 C15,19.6773269 14.5522847,20.1250421 14,20.1250421 L10.7975122,20.1250421 C10.2452274,20.1250421 9.79751216,19.6773269 9.79751216,19.1250421 L9.79751216,18.1250421 Z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="abandon"><path d="M17.65675,17.65725 C14.77275,20.54125 10.23775,20.75625 7.09875,18.31525 L18.31475,7.09925 C20.75575,10.23825 20.54075,14.77325 17.65675,17.65725 M6.34275,6.34325 C9.22675,3.45925 13.76275,3.24425 16.90075,5.68525 L5.68475,16.90125 C3.24375,13.76325 3.45875,9.22725 6.34275,6.34325 M19.07075,4.92925 C15.16575,1.02425 8.83375,1.02425 4.92875,4.92925 C1.02375,8.83425 1.02375,15.16625 4.92875,19.07125 C8.83375,22.97625 15.16575,22.97625 19.07075,19.07125 C22.97575,15.16625 22.97575,8.83425 19.07075,4.92925"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="edit"><path d="M3,17.2525 L3,21.0025 L6.75,21.0025 L17.81,9.9425 L14.06,6.1925 L3,17.2525 L3,17.2525 Z M20.71,7.0425 C21.1,6.6525 21.1,6.0225 20.71,5.6325 L18.37,3.2925 C17.98,2.9025 17.35,2.9025 16.96,3.2925 L15.13,5.1225 L18.88,8.8725 L20.71,7.0425 L20.71,7.0425 Z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="rebase"><path d="M15.5759,19.4241 L14.5861,20.4146 L11.7574,23.2426 L10.3434,21.8286 L12.171569,20 L7.82933006,20 C7.41754308,21.1652555 6.30635522,22 5,22 C3.343,22 2,20.657 2,19 C2,17.6936448 2.83474451,16.5824569 4,16.1706699 L4,7.82933006 C2.83474451,7.41754308 2,6.30635522 2,5 C2,3.343 3.343,2 5,2 C6.30635522,2 7.41754308,2.83474451 7.82933006,4 L12.1715,4 L10.3431,2.1716 L11.7571,0.7576 L15.36365,4.3633 L16.0000001,4.99920039 C16.0004321,3.34256796 17.3432665,2 19,2 C20.657,2 22,3.343 22,5 C22,6.30635522 21.1652555,7.41754308 20,7.82933006 L20,16.1706699 C21.1652555,16.5824569 22,17.6936448 22,19 C22,20.657 20.657,22 19,22 C17.343,22 16,20.657 16,19 L15.5759,19.4241 Z M12.1715,18 L10.3431,16.1716 L11.7571,14.7576 L15.36365,18.3633 L16.0000001,18.9992004 C16.0003407,17.6931914 16.8349823,16.5823729 18,16.1706699 L18,7.82933006 C16.8347445,7.41754308 16,6.30635522 16,5 L15.5759,5.4241 L14.5861,6.4146 L11.7574,9.2426 L10.3434,7.8286 L12.171569,6 L7.82933006,6 C7.52807271,6.85248394 6.85248394,7.52807271 6,7.82933006 L6,16.1706699 C6.85248394,16.4719273 7.52807271,17.1475161 7.82933006,18 L12.1715,18 Z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="rebaseEdit"><path d="M15.5759,19.4241 L14.5861,20.4146 L11.7574,23.2426 L10.3434,21.8286 L12.171569,20 L7.82933006,20 C7.41754308,21.1652555 6.30635522,22 5,22 C3.343,22 2,20.657 2,19 C2,17.6936448 2.83474451,16.5824569 4,16.1706699 L4,7.82933006 C2.83474451,7.41754308 2,6.30635522 2,5 C2,3.343 3.343,2 5,2 C6.30635522,2 7.41754308,2.83474451 7.82933006,4 L12.1715,4 L10.3431,2.1716 L11.7571,0.7576 L15.36365,4.3633 L16.0000001,4.99920039 C16.0004321,3.34256796 17.3432665,2 19,2 C20.657,2 22,3.343 22,5 C22,6.30635522 21.1652555,7.41754308 20,7.82933006 L20,16.1706699 C21.1652555,16.5824569 22,17.6936448 22,19 C22,20.657 20.657,22 19,22 C17.343,22 16,20.657 16,19 L15.5759,19.4241 Z M12.1715,18 L10.3431,16.1716 L11.7571,14.7576 L15.36365,18.3633 L16.0000001,18.9992004 C16.0003407,17.6931914 16.8349823,16.5823729 18,16.1706699 L18,7.82933006 C16.8347445,7.41754308 16,6.30635522 16,5 L15.5759,5.4241 L14.5861,6.4146 L11.7574,9.2426 L10.3434,7.8286 L12.171569,6 L7.82933006,6 C7.52807271,6.85248394 6.85248394,7.52807271 6,7.82933006 L6,16.1706699 C6.85248394,16.4719273 7.52807271,17.1475161 7.82933006,18 L12.1715,18 Z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="restore"><path d="M12,8 L12,13 L16.28,15.54 L17,14.33 L13.5,12.25 L13.5,8 L12,8 Z M13,3 C8.03,3 4,7.03 4,12 L1,12 L4.89,15.89 L4.96,16.03 L9,12 L6,12 C6,8.13 9.13,5 13,5 C16.87,5 20,8.13 20,12 C20,15.87 16.87,19 13,19 C11.07,19 9.32,18.21 8.06,16.94 L6.64,18.36 C8.27,19.99 10.51,21 13,21 C17.97,21 22,16.97 22,12 C22,7.03 17.97,3 13,3 Z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="revert"><gcpath d="M12.3,8.5 C9.64999995,8.5 7.24999995,9.49 5.39999995,11.1 L1.79999995,7.5 L1.79999995,16.5 L10.8,16.5 L7.17999995,12.88 C8.56999995,11.72 10.34,11 12.3,11 C15.84,11 18.85,13.31 19.9,16.5 L22.27,15.72 C20.88,11.53 16.95,8.5 12.3,8.5"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="stopEdit"><path d="M4 4 20 4 20 20 4 20z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="submit"><path d="M22.23,5 L11.65,15.58 L7.47000001,11.41 L6.06000001,12.82 L11.65,18.41 L23.649,6.41 L22.23,5 Z M16.58,5 L10.239,11.34 L11.65,12.75 L17.989,6.41 L16.58,5 Z M0.400000006,12.82 L5.99000001,18.41 L7.40000001,17 L1.82000001,11.41 L0.400000006,12.82 Z"></path></g>
+      <!-- This is a custom PolyGerrit SVG -->
+      <g id="review"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index 5978e37..baa025e 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -201,6 +201,31 @@
       assert.equal(element.$.output.innerHTML.match(/(CC=<a)/g).length, 1);
     });
 
+    test('only {http,https,mailto} protocols are linkified', function() {
+      element.content = 'xx mailto:test@google.com yy';
+      let links = element.$.output.querySelectorAll('a');
+      assert.equal(links.length, 1);
+      assert.equal(links[0].getAttribute('href'), 'mailto:test@google.com');
+
+      element.content = 'xx http://google.com yy';
+      links = element.$.output.querySelectorAll('a');
+      assert.equal(links.length, 1);
+      assert.equal(links[0].getAttribute('href'), 'http://google.com');
+
+      element.content = 'xx https://google.com yy';
+      links = element.$.output.querySelectorAll('a');
+      assert.equal(links.length, 1);
+      assert.equal(links[0].getAttribute('href'), 'https://google.com');
+
+      element.content = 'xx ssh://google.com yy';
+      links = element.$.output.querySelectorAll('a');
+      assert.equal(links.length, 0);
+
+      element.content = 'xx ftp://google.com yy';
+      links = element.$.output.querySelectorAll('a');
+      assert.equal(links.length, 0);
+    });
+
     test('overlapping links', function() {
       element.config = {
         b1: {
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
index a84411f..90c1bd2 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
@@ -17,6 +17,8 @@
 
 'use strict';
 
+const URL_PROTOCOL_PATTERN = /^(https?:\/\/|mailto:)/;
+
 function GrLinkTextParser(linkConfig, callback, opt_removeZeroWidthSpace) {
   this.linkConfig = linkConfig;
   this.callback = callback;
@@ -138,7 +140,11 @@
     text = text.replace(/^(CC|R)=\u200B/gm, '$1=');
   }
 
-  if (href) {
+  // If the href is provided then ba-linkify has recognized it as a URL. If the
+  // source text does not include a protocol, the protocol will be added by
+  // ba-linkify. Create the link if the href is provided and its protocol
+  // matches the expected pattern.
+  if (href && URL_PROTOCOL_PATTERN.test(href)) {
     this.addText(text, href);
   } else {
     this.parseLinks(text, this.linkConfig);
diff --git a/polygerrit-ui/app/styles/gr-change-list-styles.html b/polygerrit-ui/app/styles/gr-change-list-styles.html
index e485a52..6d7469b 100644
--- a/polygerrit-ui/app/styles/gr-change-list-styles.html
+++ b/polygerrit-ui/app/styles/gr-change-list-styles.html
@@ -139,37 +139,26 @@
       .truncatedProject {
         display: none;
       }
+      @media only screen and (max-width: 150em) {
+        .assignee,
+        .branch,
+        .owner {
+          overflow: hidden;
+          max-width: 18rem;
+          text-overflow: ellipsis;
+        }
+        .truncatedProject {
+          display: inline-block;
+        }
+        .fullProject {
+          display: none;
+        }
+      }
       @media only screen and (max-width: 100em) {
         .assignee,
         .branch,
         .owner {
-          overflow: hidden;
           max-width: 10rem;
-          text-overflow: ellipsis;
-        }
-        .truncatedProject {
-          display: inline-block;
-        }
-        .fullProject {
-          display: none;
-        }
-      }
-      .truncatedProject {
-        display: none;
-      }
-      @media only screen and (max-width: 90em) {
-        .assignee,
-        .branch,
-        .owner {
-          overflow: hidden;
-          max-width: 10rem;
-          text-overflow: ellipsis;
-        }
-        .truncatedProject {
-          display: inline-block;
-        }
-        .fullProject {
-          display: none;
         }
       }
       @media only screen and (max-width: 50em) {
diff --git a/polygerrit-ui/app/styles/gr-table-styles.html b/polygerrit-ui/app/styles/gr-table-styles.html
index cf4e84e..79d8100 100644
--- a/polygerrit-ui/app/styles/gr-table-styles.html
+++ b/polygerrit-ui/app/styles/gr-table-styles.html
@@ -30,6 +30,9 @@
       .genericList tr {
         border-bottom: 1px solid var(--border-color);
       }
+      .genericList tr:hover {
+        background-color: var(--hover-background-color);
+      }
       .genericList th {
         white-space: nowrap;
       }