Merge changes I8c164877,Idfd86ba8

* changes:
  ReceiveCommits: Retry scanning for changes to auto-close
  RetryHelper: Rename and parameterize the metrics
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index 7f9825f..ee3cf87f 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.externalids.ExternalIdNotes;
+import com.google.gerrit.server.account.externalids.ExternalIdNotes.ExternalIdNotesLoader;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -161,6 +162,62 @@
   }
 
   /**
+   * Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
+   *
+   * <p>Using this class no reindex will be performed for the affected accounts and they will also
+   * not be evicted from the account cache.
+   *
+   * <p>The Gerrit server identity will be used as author and committer for all commits that update
+   * the accounts.
+   */
+  @Singleton
+  public static class ServerNoReindex {
+    private final GitRepositoryManager repoManager;
+    private final GitReferenceUpdated gitRefUpdated;
+    private final AllUsersName allUsersName;
+    private final OutgoingEmailValidator emailValidator;
+    private final Provider<PersonIdent> serverIdentProvider;
+    private final Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
+    private final RetryHelper retryHelper;
+    private final ExternalIdNotes.FactoryNoReindex extIdNotesFactory;
+
+    @Inject
+    public ServerNoReindex(
+        GitRepositoryManager repoManager,
+        GitReferenceUpdated gitRefUpdated,
+        AllUsersName allUsersName,
+        OutgoingEmailValidator emailValidator,
+        @GerritPersonIdent Provider<PersonIdent> serverIdentProvider,
+        Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
+        RetryHelper retryHelper,
+        ExternalIdNotes.FactoryNoReindex extIdNotesFactory) {
+      this.repoManager = repoManager;
+      this.gitRefUpdated = gitRefUpdated;
+      this.allUsersName = allUsersName;
+      this.emailValidator = emailValidator;
+      this.serverIdentProvider = serverIdentProvider;
+      this.metaDataUpdateInternalFactory = metaDataUpdateInternalFactory;
+      this.retryHelper = retryHelper;
+      this.extIdNotesFactory = extIdNotesFactory;
+    }
+
+    public AccountsUpdate create() {
+      PersonIdent serverIdent = serverIdentProvider.get();
+      return new AccountsUpdate(
+          repoManager,
+          gitRefUpdated,
+          null,
+          allUsersName,
+          emailValidator,
+          metaDataUpdateInternalFactory,
+          retryHelper,
+          extIdNotesFactory,
+          serverIdent,
+          serverIdent);
+    }
+  }
+
+  /**
    * Factory to create an AccountsUpdate instance for updating accounts by the current user.
    *
    * <p>The identity of the current user will be used as author for all commits that update the
@@ -229,7 +286,7 @@
   private final OutgoingEmailValidator emailValidator;
   private final Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
   private final RetryHelper retryHelper;
-  private final ExternalIdNotes.Factory extIdNotesFactory;
+  private final ExternalIdNotesLoader extIdNotesLoader;
   private final PersonIdent committerIdent;
   private final PersonIdent authorIdent;
   private final Runnable afterReadRevision;
@@ -242,7 +299,7 @@
       OutgoingEmailValidator emailValidator,
       Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
       RetryHelper retryHelper,
-      ExternalIdNotes.Factory extIdNotesFactory,
+      ExternalIdNotesLoader extIdNotesLoader,
       PersonIdent committerIdent,
       PersonIdent authorIdent) {
     this(
@@ -253,7 +310,7 @@
         emailValidator,
         metaDataUpdateInternalFactory,
         retryHelper,
-        extIdNotesFactory,
+        extIdNotesLoader,
         committerIdent,
         authorIdent,
         Runnables.doNothing());
@@ -268,7 +325,7 @@
       OutgoingEmailValidator emailValidator,
       Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
       RetryHelper retryHelper,
-      ExternalIdNotes.Factory extIdNotesFactory,
+      ExternalIdNotesLoader extIdNotesLoader,
       PersonIdent committerIdent,
       PersonIdent authorIdent,
       Runnable afterReadRevision) {
@@ -280,7 +337,7 @@
     this.metaDataUpdateInternalFactory =
         checkNotNull(metaDataUpdateInternalFactory, "metaDataUpdateInternalFactory");
     this.retryHelper = checkNotNull(retryHelper, "retryHelper");
-    this.extIdNotesFactory = checkNotNull(extIdNotesFactory, "extIdNotesFactory");
+    this.extIdNotesLoader = checkNotNull(extIdNotesLoader, "extIdNotesLoader");
     this.committerIdent = checkNotNull(committerIdent, "committerIdent");
     this.authorIdent = checkNotNull(authorIdent, "authorIdent");
     this.afterReadRevision = afterReadRevision;
@@ -494,7 +551,7 @@
             update.getDeletedExternalIds()),
         accountId);
 
-    ExternalIdNotes extIdNotes = extIdNotesFactory.load(allUsersRepo);
+    ExternalIdNotes extIdNotes = extIdNotesLoader.load(allUsersRepo);
     extIdNotes.replace(update.getDeletedExternalIds(), update.getCreatedExternalIds());
     extIdNotes.upsert(update.getUpdatedExternalIds());
     return extIdNotes;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
index f6ded07..f663247 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
@@ -78,8 +78,12 @@
 
   private static final int MAX_NOTE_SZ = 1 << 19;
 
+  public interface ExternalIdNotesLoader {
+    ExternalIdNotes load(Repository allUsersRepo) throws IOException, ConfigInvalidException;
+  }
+
   @Singleton
-  public static class Factory {
+  public static class Factory implements ExternalIdNotesLoader {
     private final ExternalIdCache externalIdCache;
     private final AccountCache accountCache;
 
@@ -89,6 +93,7 @@
       this.accountCache = accountCache;
     }
 
+    @Override
     public ExternalIdNotes load(Repository allUsersRepo)
         throws IOException, ConfigInvalidException {
       return new ExternalIdNotes(externalIdCache, accountCache, allUsersRepo).load();
@@ -96,7 +101,7 @@
   }
 
   @Singleton
-  public static class FactoryNoReindex {
+  public static class FactoryNoReindex implements ExternalIdNotesLoader {
     private final ExternalIdCache externalIdCache;
 
     @Inject
@@ -104,6 +109,7 @@
       this.externalIdCache = externalIdCache;
     }
 
+    @Override
     public ExternalIdNotes load(Repository allUsersRepo)
         throws IOException, ConfigInvalidException {
       return new ExternalIdNotes(externalIdCache, null, allUsersRepo).load();
diff --git a/java/com/google/gerrit/server/schema/AllUsersCreator.java b/java/com/google/gerrit/server/schema/AllUsersCreator.java
index 8a11ca8..8e38754 100644
--- a/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -37,7 +37,9 @@
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 
 /** Creates the {@code All-Users} repository. */
@@ -76,6 +78,8 @@
     } catch (RepositoryNotFoundException notFound) {
       try (Repository git = mgr.createRepository(allUsersName)) {
         initAllUsers(git);
+        RefUpdate u = git.updateRef(Constants.HEAD);
+        u.link(RefNames.REFS_CONFIG);
       } catch (RepositoryNotFoundException err) {
         String name = allUsersName.get();
         throw new IOException("Cannot create repository " + name, err);
diff --git a/java/com/google/gerrit/server/schema/SchemaVersion.java b/java/com/google/gerrit/server/schema/SchemaVersion.java
index 61a6235..4b8c13f 100644
--- a/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -35,7 +35,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_165> C = Schema_165.class;
+  public static final Class<Schema_166> C = Schema_166.class;
 
   public static int getBinaryVersion() {
     return guessVersion(C);
diff --git a/java/com/google/gerrit/server/schema/Schema_166.java b/java/com/google/gerrit/server/schema/Schema_166.java
new file mode 100644
index 0000000..aa6f4e6
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_166.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.sql.SQLException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+
+/** Set HEAD for All-Users to refs/meta/config. */
+public class Schema_166 extends SchemaVersion {
+  private final GitRepositoryManager repoManager;
+  private final AllUsersName allUsersName;
+
+  @Inject
+  Schema_166(
+      Provider<Schema_165> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
+    super(prior);
+    this.repoManager = repoManager;
+    this.allUsersName = allUsersName;
+  }
+
+  @Override
+  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
+    try (Repository git = repoManager.openRepository(allUsersName)) {
+      RefUpdate u = git.updateRef(Constants.HEAD);
+      u.link(RefNames.REFS_CONFIG);
+    } catch (IOException e) {
+      throw new OrmException(String.format("Failed to update HEAD for %s", allUsersName.get()), e);
+    }
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 0e39ee1..cc5b5d3 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -66,6 +66,7 @@
 import java.util.function.Predicate;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -650,7 +651,8 @@
             RefNames.refsGroups(nonInteractiveUsers),
             RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS,
             RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS,
-            RefNames.REFS_CONFIG);
+            RefNames.REFS_CONFIG,
+            Constants.HEAD);
 
     List<String> expectedMetaRefs =
         new ArrayList<>(ImmutableList.of(mr.getPatchSetId().toRefName()));
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
index ffde073..4af329a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -182,8 +182,7 @@
   public void listProjectWithType() throws Exception {
     Map<String, ProjectInfo> result =
         gApi.projects().list().withType(FilterType.PERMISSIONS).getAsMap();
-    assertThat(result).hasSize(1);
-    assertThat(result).containsKey(allProjects.get());
+    assertThat(result.keySet()).containsExactly(allProjects.get(), allUsers.get());
 
     assertThatNameList(filter(gApi.projects().list().withType(FilterType.ALL).get()))
         .containsExactly(allProjects, allUsers, project)
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
index 37b2c21..a7b5e54 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
@@ -117,40 +117,32 @@
         if (linkCopy.name === 'Repositories' && this._repoName) {
           linkCopy.subsection = {
             name: this._repoName,
-            view: 'gr-repo',
+            view: Gerrit.Nav.View.REPO,
             noBaseUrl: true,
-            url: `/admin/repos/${this.encodeURL(this._repoName, true)}`,
+            url: Gerrit.Nav.getUrlForRepo(this._repoName),
             children: [{
               name: 'Access',
-              detailType: 'access',
-              view: 'gr-repo-access',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},access`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.ACCESS,
+              url: Gerrit.Nav.getUrlForRepoAccess(this._repoName),
             },
             {
               name: 'Commands',
-              detailType: 'commands',
-              view: 'gr-repo-commands',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},commands`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.COMMANDS,
+              url: Gerrit.Nav.getUrlForRepoCommands(this._repoName),
             },
             {
               name: 'Branches',
-              detailType: 'branches',
-              view: 'gr-repo-detail-list',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},branches`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.BRANCHES,
+              url: Gerrit.Nav.getUrlForRepoBranches(this._repoName),
             },
             {
               name: 'Tags',
-              detailType: 'tags',
-              view: 'gr-repo-detail-list',
-              noBaseUrl: true,
-              url: `/admin/repos/` +
-                  `${this.encodeURL(this._repoName, true)},tags`,
+              view: Gerrit.Nav.View.REPO,
+              detailType: Gerrit.Nav.RepoDetailView.TAGS,
+              url: Gerrit.Nav.getUrlForRepoTags(this._repoName),
             }],
           };
         }
@@ -268,6 +260,13 @@
         return '';
       }
 
+      if (params.view === Gerrit.Nav.View.REPO &&
+          itemView === Gerrit.Nav.View.REPO) {
+        if (!params.detail && !opt_detailType) { return 'selected'; }
+        if (params.detail === opt_detailType) { return 'selected'; }
+        return '';
+      }
+
       if (params.detailType && params.detailType !== opt_detailType) {
         return '';
       }
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
index 6a90488..bd79872 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html
@@ -258,9 +258,8 @@
 
         test('repo', done => {
           element.params = {
-            view: Gerrit.Nav.View.ADMIN,
-            project: 'foo',
-            adminView: 'gr-repo',
+            view: Gerrit.Nav.View.REPO,
+            repoName: 'foo',
           };
           element._repoName = 'foo';
           element.reload().then(() => {
@@ -275,10 +274,9 @@
 
         test('repo access', done => {
           element.params = {
-            view: Gerrit.Nav.View.ADMIN,
-            adminView: 'gr-repo-access',
-            detailType: 'access',
-            repo: 'foo',
+            view: Gerrit.Nav.View.REPO,
+            detail: Gerrit.Nav.RepoDetailView.ACCESS,
+            repoName: 'foo',
           };
           element._repoName = 'foo';
           element.reload().then(() => {
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index e93e710..526cc86 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -195,6 +195,7 @@
           <span class="date">
             <gr-date-formatter
                 has-tooltip
+                show-date-and-time
                 date-str="[[message.date]]"></gr-date-formatter>
           </span>
         </template>
@@ -202,6 +203,7 @@
           <a class="date" href$="[[_computeMessageHash(message)]]" on-tap="_handleLinkTap">
             <gr-date-formatter
                 has-tooltip
+                show-date-and-time
                 date-str="[[message.date]]"></gr-date-formatter>
           </a>
         </template>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index f8c7e74..7b17f22 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -55,6 +55,12 @@
     //    - `groupId`, required, String, the ID of the group.
     //    - `detail`, optional, String, the name of the group detail view.
     //      Takes any value from Gerrit.Nav.GroupDetailView.
+    //
+    //  - Gerrit.Nav.View.REPO:
+    //    - `repoName`, required, String, the name of the repo
+    //    - `detail`, optional, String, the name of the repo detail view.
+    //      Takes any value from Gerrit.Nav.RepoDetailView.
+
 
     window.Gerrit = window.Gerrit || {};
 
@@ -79,6 +85,7 @@
         EDIT: 'edit',
         GROUP: 'group',
         PLUGIN_SCREEN: 'plugin-screen',
+        REPO: 'repo',
         SEARCH: 'search',
         SETTINGS: 'settings',
       },
@@ -88,6 +95,13 @@
         LOG: 'log',
       },
 
+      RepoDetailView: {
+        ACCESS: 'access',
+        BRANCHES: 'branches',
+        COMMANDS: 'commands',
+        TAGS: 'tags',
+      },
+
       WeblinkType: {
         CHANGE: 'change',
         FILE: 'file',
@@ -362,6 +376,65 @@
       },
 
       /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepo(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoTags(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.TAGS,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoBranches(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.BRANCHES,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoAccess(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.ACCESS,
+        });
+      },
+
+      /**
+       * @param {string} repoName
+       * @return {string}
+       */
+      getUrlForRepoCommands(repoName) {
+        return this._getUrlFor({
+          view: Gerrit.Nav.View.REPO,
+          repoName,
+          detail: Gerrit.Nav.RepoDetailView.COMMANDS,
+        });
+      },
+
+      /**
        * @param {string} groupId
        * @return {string}
        */
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index f309747..37f9f23 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -233,6 +233,8 @@
         url = this._generateDiffOrEditUrl(params);
       } else if (params.view === Views.GROUP) {
         url = this._generateGroupUrl(params);
+      } else if (params.view === Views.REPO) {
+        url = this._generateRepoUrl(params);
       } else if (params.view === Views.SETTINGS) {
         url = this._generateSettingsUrl(params);
       } else {
@@ -448,6 +450,24 @@
      * @param {!Object} params
      * @return {string}
      */
+    _generateRepoUrl(params) {
+      let url = `/admin/repos/${this.encodeURL(params.repoName + '', true)}`;
+      if (params.detail === Gerrit.Nav.RepoDetailView.ACCESS) {
+        url += ',access';
+      } else if (params.detail === Gerrit.Nav.RepoDetailView.BRANCHES) {
+        url += ',branches';
+      } else if (params.detail === Gerrit.Nav.RepoDetailView.TAGS) {
+        url += ',tags';
+      } else if (params.detail === Gerrit.Nav.RepoDetailView.COMMANDS) {
+        url += ',commands';
+      }
+      return url;
+    },
+
+    /**
+     * @param {!Object} params
+     * @return {string}
+     */
     _generateSettingsUrl(params) {
       return '/settings';
     },
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
index 21552d9..fb41ca6 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
@@ -30,7 +30,7 @@
       }
     </style>
     <span>
-      [[_computeDateStr(dateStr, _timeFormat, _relative)]]
+      [[_computeDateStr(dateStr, _timeFormat, _relative, showDateAndTime)]]
     </span>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index 65a4c68..3f7b8db 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -37,6 +37,10 @@
         value: null,
         notify: true,
       },
+      showDateAndTime: {
+        type: Boolean,
+        value: false,
+      },
 
       /**
        * When true, the detailed date appears in a GR-TOOLTIP rather than in the
@@ -131,7 +135,7 @@
           diff < 180 * Duration.DAY;
     },
 
-    _computeDateStr(dateStr, timeFormat, relative) {
+    _computeDateStr(dateStr, timeFormat, relative, showDateAndTime) {
       if (!dateStr) { return ''; }
       const date = moment(util.parseDate(dateStr));
       if (!date.isValid()) { return ''; }
@@ -147,8 +151,13 @@
       let format = TimeFormats.MONTH_DAY_YEAR;
       if (this._isWithinDay(now, date)) {
         format = timeFormat;
-      } else if (this._isWithinHalfYear(now, date)) {
-        format = TimeFormats.MONTH_DAY;
+      } else {
+        if (this._isWithinHalfYear(now, date)) {
+          format = TimeFormats.MONTH_DAY;
+        }
+        if (this.showDateAndTime) {
+          format = `${format} ${timeFormat}`;
+        }
       }
       return date.format(format);
     },
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
index 2c15ef6..b418a42 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
@@ -55,7 +55,8 @@
       return d;
     }
 
-    function testDates(nowStr, dateStr, expected, expectedTooltip, done) {
+    function testDates(nowStr, dateStr, expected, expectedWithDateAndTime,
+        expectedTooltip, done) {
       // Normalize and convert the date to mimic server response.
       dateStr = normalizedDate(dateStr)
           .toJSON().replace('T', ' ').slice(0, -1);
@@ -65,6 +66,9 @@
         const span = element.$$('span');
         assert.equal(span.textContent.trim(), expected);
         assert.equal(element.title, expectedTooltip);
+        element.showDateAndTime = true;
+        flushAsynchronousOperations();
+        assert.equal(span.textContent.trim(), expectedWithDateAndTime);
         done();
       });
     }
@@ -98,25 +102,33 @@
       test('Within 24 hours on same day', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-07-29 15:34:14.985000000',
-            '15:34', 'Jul 29, 2015, 15:34:14', done);
+            '15:34',
+            '15:34',
+            'Jul 29, 2015, 15:34:14', done);
       });
 
       test('Within 24 hours on different days', done => {
         testDates('2015-07-29 03:34:14.985000000',
             '2015-07-28 20:25:14.985000000',
-            'Jul 28', 'Jul 28, 2015, 20:25:14', done);
+            'Jul 28',
+            'Jul 28 20:25',
+            'Jul 28, 2015, 20:25:14', done);
       });
 
       test('More than 24 hours but less than six months', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-06-15 03:25:14.985000000',
-            'Jun 15', 'Jun 15, 2015, 03:25:14', done);
+            'Jun 15',
+            'Jun 15 03:25',
+            'Jun 15, 2015, 03:25:14', done);
       });
 
       test('More than six months', done => {
         testDates('2015-09-15 20:34:00.000000000',
             '2015-01-15 03:25:00.000000000',
-            'Jan 15, 2015', 'Jan 15, 2015, 03:25:00', done);
+            'Jan 15, 2015',
+            'Jan 15, 2015 03:25',
+            'Jan 15, 2015, 03:25:00', done);
       });
     });
 
@@ -135,7 +147,9 @@
       test('Within 24 hours on same day', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-07-29 15:34:14.985000000',
-            '3:34 PM', 'Jul 29, 2015, 3:34:14 PM', done);
+            '3:34 PM',
+            '3:34 PM',
+            'Jul 29, 2015, 3:34:14 PM', done);
       });
     });
 
@@ -153,13 +167,17 @@
       test('Within 24 hours on same day', done => {
         testDates('2015-07-29 20:34:14.985000000',
             '2015-07-29 15:34:14.985000000',
-            '5 hours ago', 'Jul 29, 2015, 3:34:14 PM', done);
+            '5 hours ago',
+            '5 hours ago',
+            'Jul 29, 2015, 3:34:14 PM', done);
       });
 
       test('More than six months', done => {
         testDates('2015-09-15 20:34:00.000000000',
             '2015-01-15 03:25:00.000000000',
-            '8 months ago', 'Jan 15, 2015, 3:25:00 AM', done);
+            '8 months ago',
+            '8 months ago',
+            'Jan 15, 2015, 3:25:00 AM', done);
       });
     });
 
diff --git a/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html b/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
index 64d16c5..c5e38b3 100644
--- a/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
+++ b/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
@@ -15,7 +15,7 @@
     </style>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
-  <body>
+  <body class="login" id="login_ldap">
     <div id="gerrit_topmenu" style="height:45px;" class="gerritTopMenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_body" class="gerritBody">
diff --git a/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html b/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html
index f7814c0..67c40c3 100644
--- a/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html
+++ b/resources/com/google/gerrit/httpd/auth/oauth/LoginForm.html
@@ -24,7 +24,7 @@
     </style>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
-  <body>
+  <body class="login" id="login_oauth">
     <div id="gerrit_topmenu" style="height:45px;" class="gerritTopMenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_body" class="gerritBody">
diff --git a/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html b/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html
index 07e09f5..4923143 100644
--- a/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html
+++ b/resources/com/google/gerrit/httpd/auth/openid/LoginForm.html
@@ -39,7 +39,7 @@
     </style>
     <style id="gerrit_sitecss" type="text/css"></style>
   </head>
-  <body>
+  <body class="login" id="login_openid">
     <div id="gerrit_topmenu" style="height:45px;" class="gerritTopMenu"></div>
     <div id="gerrit_header"></div>
     <div id="gerrit_body" class="gerritBody">
diff --git a/tools/bzl/asciidoc.bzl b/tools/bzl/asciidoc.bzl
index 9fb53c0..62fa4c6 100644
--- a/tools/bzl/asciidoc.bzl
+++ b/tools/bzl/asciidoc.bzl
@@ -122,9 +122,9 @@
 }
 
 _asciidoc = rule(
-    attrs = _asciidoc_attrs + {
+    attrs = dict(_asciidoc_attrs.items() + {
         "outs": attr.output_list(mandatory = True),
-    },
+    }.items()),
     implementation = _asciidoc_impl,
 )
 
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index 2e74091..945b8e1 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -177,10 +177,10 @@
 
 js_component = rule(
     _js_component,
-    attrs = _common_attrs + {
+    attrs = dict(_common_attrs.items() + {
         "srcs": attr.label_list(allow_files = [".js"]),
         "license": attr.label(allow_single_file = True),
-    },
+    }.items()),
     outputs = {
         "zip": "%{name}.zip",
     },
@@ -188,14 +188,14 @@
 
 _bower_component = rule(
     _bower_component_impl,
-    attrs = _common_attrs + {
+    attrs = dict(_common_attrs.items() + {
         "zipfile": attr.label(allow_single_file = [".zip"]),
         "license": attr.label(allow_single_file = True),
         "version_json": attr.label(allow_files = [".json"]),
 
         # If set, define by hand, and don't regenerate this entry in bower2bazel.
         "seed": attr.bool(default = False),
-    },
+    }.items()),
 )
 
 # TODO(hanwen): make license mandatory.