Merge "Register core plugin that implements the standard download commands"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 4d77abe..c644ef9 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -388,6 +388,10 @@
 more agreements.
 +
 By default this is false (no agreements are used).
++
+To enable the actual usage of contributor agreement the project
+specific config option in the `project.config` must be set:
+link:config-project-config.html[receive.requireContributorAgreement].
 
 auth.allowGoogleAccountUpgrade::
 +
@@ -2263,9 +2267,10 @@
 Gerrit administrators can use this setting to prevent developers
 from pushing objects which are too large to Gerrit.
 +
-This setting can also be set in the `project.config` in order to further
-reduce the global setting. The project specific setting is only honored
-when it further reduces the global limit.
+This setting can also be set in the `project.config`
+link:config-project-config.html[receive.maxObjectSizeLimit] in order
+to further reduce the global setting. The project specific setting is
+only honored when it further reduces the global limit.
 +
 Default is zero.
 +
@@ -2942,7 +2947,7 @@
 
 [[user.anonymousCoward]]user.anonymousCoward::
 +
-Username that this displayed in the Gerrit WebUI and in e-mail
+Username that is displayed in the Gerrit WebUI and in e-mail
 notifications if the full name of the user is not set.
 +
 By default "Anonymous Coward" is used.
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
new file mode 100644
index 0000000..8529b67
--- /dev/null
+++ b/Documentation/config-project-config.txt
@@ -0,0 +1,225 @@
+Gerrit Code Review - Project Configuration File Format
+======================================================
+
+This page explains the storage format of Gerrit's project configuration
+and access control models.
+
+The web UI access control panel is a front end for human-readable
+configuration files under the +refs/meta/config+ namespace in the
+affected project.  Direct manipulation of these files is mainly
+relevant in an automation scenario of the access controls.
+
+
+The +refs/meta/config+ namespace
+--------------------------------
+
+The namespace contains three different files that play different
+roles in the permission model.  With read permission to that reference,
+it is possible to fetch the +refs/meta/config+ reference to a local
+repository.  A nice side effect is that you can also upload changes
+to project permissions and review them just like with regular code
+changes. The preview changes option is also provided on the UI. Please note
+that you will have to configure push rights for the +refs/meta/config+ name
+space if you'd like to use the possibility to automate permission updates.
+
+
+[[file-project_config]]
+The file +project.config+
+-------------------------
+
+The +project.config+ file contains the link between groups and their
+permitted actions on reference patterns in this project and any projects
+that inherit its permissions.
+
+The format in this file corresponds to the Git config file format, so
+if you want to automate your permissions it is a good idea to use the
++git config+ command when writing to the file. This way you know you
+don't accidentally break the format of the file.
+
+Here follows a +git config+ command example:
+
+----
+$ git config -f project.config project.description "Rights inherited by all other projects"
+----
+
+Below you will find an example of the +project.config+ file format:
+
+----
+[project]
+       description = Rights inherited by all other projects
+[access "refs/*"]
+       read = group Administrators
+[capability]
+       administrateServer = group Administrators
+[receive]
+       requireContributorAgreement = false
+----
+
+As you can see, there are several sections.
+
+The link:#project-section[+project+ section] appears once per project.
+
+The link:#access-section[+access+ section] appears once per reference pattern,
+such as `refs/*` or `refs/heads/*`.  Only one access section per pattern is
+allowed.  You will find examples of keys and values in each category section
+<<access_category,below>>.
+
+The link:#receive-section[+receive+ section] appears once per project.
+
+The link:#submit-section[+submit+ section] appears once per project.
+
+The link:#capability-section[+capability+] section only appears once, and only
+in the +All-Projects+ repository.  It controls core features that are configured
+on a global level.  You can find examples of these
+<<capability_category,below>>.
+
+
+[[project-section]]
+Project section
+~~~~~~~~~~~~~~~
+
+The project section includes configuration of project settings.
+
+These are the keys:
+
+- Description
+
+
+[[receive-section]]
+Receive section
+~~~~~~~~~~~~~~~
+
+The receive section includes configuration of project-specific
+receive settings:
+
+[[receive.requireContributorAgreement]]receive.requireContributorAgreement::
++
+Controls whether or not a user must complete a contributor agreement before
+they can upload changes. Default is `INHERIT`. If `All-Project` enables this
+option then the dependent project must set it to false if users are not
+required to sign a contributor agreement prior to submitting changes for that
+specific project. To use that feature the global option in `gerrit.config`
+must be enabled:
+link:config-gerrit.html#auth.contributorAgreements[auth.contributorAgreements].
+
+[[receive.requireSignedOffBy]]receive.requireSignedOffBy::
++
+Sign-off can be a requirement for some projects (for example Linux kernel uses
+it). Sign-off is a line at the end of the commit message which certifies who
+is the author of the commit. Its main purpose is to improve tracking of who
+did  what, especially with patches. Default is `INHERIT`, which means that this
+property is inherited from the parent project.
+
+[[receive.requireChangeId]]receive.requireChangeId::
++
+Controls whether or not the Change-Id must be included in the commit message
+in the last paragraph. Default is `INHERIT`, which means that this property
+is inherited from the parent project.
+
+[[receive.maxObjectSizeLimit]]receive.maxObjectSizeLimit::
++
+Maximum allowed Git object size that receive-pack will accept. If an object
+is larger than the given size the pack-parsing will abort and the push
+operation will fail. If set to zero then there is no limit.
++
+Project owners can use this setting to prevent developers from pushing
+objects which are too large to Gerrit. This setting can also be set it
+`gerrit.config` globally link:config-gerrit.html#receive.maxObjectSizeLimit[
+receive.maxObjectSizeLimit].
++
+The project specific setting in `project.config` is only honored when it
+further reduces the global limit.
++
+Default is zero.
++
+Common unit suffixes of k, m, or g are supported.
+
+[[submit-section]]
+Submit section
+~~~~~~~~~~~~~~
+
+The submit section includes configuration of project-specific
+submit settings:
+
+- 'mergeContent': Defines whether to automatically merge changes.  Valid values
+are 'true', 'false', or 'INHERIT'.  Default is 'INHERIT'.
+
+- 'action': defines the link:project-setup.html#submit_type[submit type].  Valid
+values are 'fast forward only', 'merge if necessary', 'rebase if necessary',
+'merge always' and 'cherry pick'.  The default is 'merge if necessary'.
+
+Merge strategy
+
+
+[[access-section]]
+Access section
+~~~~~~~~~~~~~~
+
+Each +access+ section includes a reference and access rights connected
+to groups.  Each group listed must exist in the link:#file-groups[+groups+ file].
+
+Please refer to the
+link:access-control.html#access_categories[Access Categories]
+documentation for a full list of available access rights.
+
+
+[[capability-section]]
+Capability section
+~~~~~~~~~~~~~~~~~~
+
+The +capability+ section only appears once, and only in the +All-Projects+
+repository.  It controls Gerrit administration capabilities that are configured
+on a global level.
+
+Please refer to the
+link:access-control.html#global_capabilities[Global Capabilities]
+documentation for a full list of available capabilities.
+
+
+[[file-groups]]
+The file +groups+
+-----------------
+
+Each group in this list is linked with its UUID so that renaming of
+groups is possible without having to rewrite every +groups+ file
+in every repository where it's used.
+
+This is what the default groups file for +All-Projects.git+ looks like:
+
+----
+# UUID                                         Group Name
+#
+3d6da7dc4e99e6f6e5b5196e21b6f504fc530bba       Administrators
+global:Anonymous-Users                         Anonymous Users
+global:Project-Owners                          Project Owners
+global:Registered-Users                        Registered Users
+----
+
+This file can't be written to by the +git config+ command.
+
+In order to reference a group in +project.config+, it must be listed in
+the +groups+ file.  When editing permissions through the web UI this
+file is maintained automatically, but when pushing updates to
++refs/meta/config+ this must be dealt with by hand.  Gerrit will refuse
++project.config+ files that refer to groups not listed in +groups+.
+
+The UUID of a group can be found on the General tab of the group's page
+in the web UI or via the +-v+ option to
+link:cmd-ls-groups.html[the +ls-groups+ SSH command].
+
+
+[[file-rules_pl]]
+The file +rules.pl+
+-------------------
+
+The +rules.pl+ files allows you to replace or amend the default Prolog
+rules that control e.g. what conditions need to be fulfilled for a
+change to be submittable.  This file content should be
+interpretable by the 'Prolog Cafe' interpreter.
+
+You can read more about the +rules.pl+ file and the prolog rules on
+link:prolog-cookbook.html[the Prolog cookbook page].
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 8bbea7e..15c145a 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -345,12 +345,13 @@
   [-] BUILDING...FINISHED 0.2s
 ----
 
-Overwrite Buck's settings
-~~~~~~~~~~~~~~~~~~~~~~~~~
+Override Buck's settings
+~~~~~~~~~~~~~~~~~~~~~~~~
 
-In the latest version of Buck the wrapper script `buck_common` will source
-one of these files (if they exist): /etc/buck.conf, $HOME/.buck/buck.conf or
-$HOME/.buckrc. The trivial case to overwrite the Buck's default 1GB heap size:
+User-specific configuration can be placed in one of the following files:
+`/etc/buck.conf`, `$HOME/.buck/buck.conf` or `$HOME/.buckrc`.
+
+For example to override Buck's default 1GB heap size:
 
 ----
   cat > $HOME/.buckrc <<EOF
@@ -361,7 +362,7 @@
   EOF
 ----
 
-Or to debug BUCK, set BUCK_DEBUG_MODE to anything non-empty, then connect to
+Or to debug BUCK, set `BUCK_DEBUG_MODE` to anything non-empty, then connect to
 port 8888:
 
 ----
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 6023d05..2e74b7c 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -403,6 +403,44 @@
 $ ssh -p 29418 review.example.com helloworld print
 ----
 
+Multiple SSH commands can be bound to the same implementation class. For
+example a Gerrit Shell plugin can bind different shell commands to the same
+implementation class:
+
+[source,java]
+----
+public class SshShellModule extends PluginCommandModule {
+  @Override
+  protected void configureCommands() {
+    command("ls").to(ShellCommand.class);
+    command("ps").to(ShellCommand.class);
+    [...]
+  }
+}
+----
+
+With the possible implementation:
+
+[source,java]
+----
+public class ShellCommand extends SshCommand {
+  @Override
+  protected void run() throws UnloggedFailure {
+    String cmd = getName().substring(getPluginName().length() + 1);
+    ProcessBuilder proc = new ProcessBuilder(cmd);
+    Process cmd = proc.start();
+    [...]
+  }
+}
+----
+
+And the call:
+
+----
+$ ssh -p 29418 review.example.com shell ls
+$ ssh -p 29418 review.example.com shell ps
+----
+
 [[configuration]]
 Configuration
 -------------
@@ -837,7 +875,7 @@
   @Override
   public List<MenuEntry> getEntries() {
     return Lists.newArrayList(
-               new MenuEntry("Projects", Lists.newArrayList(
+               new MenuEntry(GerritTopMenu.PROJECTS, Lists.newArrayList(
                       new MenuItem("Browse Repositories", "https://gerrit.googlesource.com/"))));
   }
 }
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 8ce2904..9b8acc4 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -33,6 +33,7 @@
 .. link:project-setup.html[Project Setup]
 .. link:access-control.html[Access Controls]
 ... link:config-labels.html[Review Labels]
+... link:config-project-config.html[Access Controls Configuration Format]
 .. Multi-project management
 ... Submodules
 ... Repo
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 7db83a4..02bb549 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -977,12 +977,15 @@
 |Field Name    ||Description
 |`_account_id` ||The numeric ID of the account.
 |`name`        |optional|The full name of the user. +
-Only set if detailed account information is requested.
+Only set if link:rest-api-changes.html#detailed-accounts[detailed
+account information] is requested.
 |`email`       |optional|
 The email address the user prefers to be contacted through. +
-Only set if detailed account information is requested.
+Only set if link:rest-api-changes.html#detailed-accounts[detailed
+account information] is requested.
 |`username`    |optional|The username of the user. +
-Only set if detailed account information is requested.
+Only set if link:rest-api-changes.html#detailed-accounts[detailed
+account information] is requested.
 |===========================
 
 [[account-input]]
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 08411f5..8c1fa16 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -209,8 +209,8 @@
 
 [[detailed-accounts]]
 --
-* `DETAILED_ACCOUNTS`: include `_account_id` and `email` fields when
-  referencing accounts.
+* `DETAILED_ACCOUNTS`: include `_account_id`, `email` and `username`
+  fields when referencing accounts.
 --
 
 [[messages]]
@@ -422,7 +422,8 @@
     "owner": {
       "_account_id": 1000096,
       "name": "John Doe",
-      "email": "john.doe@example.com"
+      "email": "john.doe@example.com",
+      "username": "jdoe"
     },
     "labels": {
       "Verified": {
@@ -431,13 +432,15 @@
             "value": 0,
             "_account_id": 1000096,
             "name": "John Doe",
-            "email": "john.doe@example.com"
+            "email": "john.doe@example.com",
+            "username": "jdoe"
           },
           {
             "value": 0,
             "_account_id": 1000097,
             "name": "Jane Roe",
-            "email": "jane.roe@example.com"
+            "email": "jane.roe@example.com",
+            "username": "jroe"
           }
         ],
         "values": {
@@ -450,25 +453,29 @@
         "recommended": {
           "_account_id": 1000097,
           "name": "Jane Roe",
-          "email": "jane.roe@example.com"
+          "email": "jane.roe@example.com",
+          "username": "jroe"
         },
         "disliked": {
           "_account_id": 1000096,
           "name": "John Doe",
-          "email": "john.doe@example.com"
+          "email": "john.doe@example.com",
+          "username": "jdoe"
         },
         "all": [
           {
             "value": -1,
             "_account_id": 1000096,
             "name": "John Doe",
-            "email": "john.doe@example.com"
+            "email": "john.doe@example.com",
+            "username": "jdoe"
           },
           {
             "value": 1,
             "_account_id": 1000097,
             "name": "Jane Roe",
-            "email": "jane.roe@example.com"
+            "email": "jane.roe@example.com",
+            "username": "jroe"
           }
         ]
         "values": {
@@ -498,12 +505,14 @@
       {
         "_account_id": 1000096,
         "name": "John Doe",
-        "email": "john.doe@example.com"
+        "email": "john.doe@example.com",
+        "username": "jdoe"
       },
       {
         "_account_id": 1000097,
         "name": "Jane Roe",
-        "email": "jane.roe@example.com"
+        "email": "jane.roe@example.com",
+        "username": "jroe"
       }
     ],
     "messages": [
@@ -512,7 +521,8 @@
         "author": {
           "_account_id": 1000096,
           "name": "John Doe",
-          "email": "john.doe@example.com"
+          "email": "john.doe@example.com",
+          "username": "jdoe"
         },
         "updated": "2013-03-23 21:34:02.419000000",
         "message": "Patch Set 1:\n\nThis is the first message.",
@@ -523,7 +533,8 @@
         "author": {
           "_account_id": 1000097,
           "name": "Jane Roe",
-          "email": "jane.roe@example.com"
+          "email": "jane.roe@example.com",
+          "username": "jroe"
         },
         "updated": "2013-03-23 21:36:52.332000000",
         "message": "Patch Set 1:\n\nThis is the second message.\n\nWith a line break.",
diff --git a/ReleaseNotes/ReleaseNotes-2.8.txt b/ReleaseNotes/ReleaseNotes-2.8.txt
index ab2c6ef..c07ae6c 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.txt
@@ -679,3 +679,15 @@
 * Various spelling mistakes are corrected in the documentation and previous
 release notes.
 
+
+Upgrades
+~~~~~~~~
+
+* Update JGit to 3.0.0.201306101825-r.41-g84d2738
+* Update gwtorm to 1.7
+* Update guice to 4.0-beta
+* Update guava to 15.0
+* Update H2 to 1.3.173
+* Update bouncycastle to 1.44
+* asciidoctor 0.1.4 is now required to build the documentation
+* jsr305 library was removed
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
index cb785b9..12add3e 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -30,8 +30,10 @@
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.ssh.SshKeyCache;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
+
 import com.jcraft.jsch.JSch;
 import com.jcraft.jsch.JSchException;
 import com.jcraft.jsch.KeyPair;
@@ -77,7 +79,7 @@
         db.accountExternalIds().insert(Collections.singleton(extMailto));
       }
 
-      Account a = new Account(id);
+      Account a = new Account(id, TimeUtil.nowTs());
       a.setFullName(fullName);
       a.setPreferredEmail(email);
       db.accounts().insert(Collections.singleton(a));
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
new file mode 100644
index 0000000..d23ad6d
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
@@ -0,0 +1,188 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.project;
+
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.inject.Inject;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class DeleteBranchIT extends AbstractDaemonTest {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private MetaDataUpdate.Server metaDataUpdateFactory;
+
+  @Inject
+  private ProjectCache projectCache;
+
+  @Inject
+  private GroupCache groupCache;
+
+  @Inject
+  private AllProjectsNameProvider allProjects;
+
+  private RestSession adminSession;
+  private RestSession userSession;
+
+  private Project.NameKey project;
+  private Branch.NameKey branch;
+
+  @Before
+  public void setUp() throws Exception {
+    TestAccount admin = accounts.admin();
+    adminSession = new RestSession(server, admin);
+
+    TestAccount user = accounts.create("user", "user@example.com", "User");
+    userSession = new RestSession(server, user);
+
+    project = new Project.NameKey("p");
+    branch = new Branch.NameKey(project, "test");
+
+    initSsh(admin);
+    SshSession sshSession = new SshSession(server, admin);
+    try {
+      createProject(sshSession, project.get(), null, true);
+    } finally {
+      sshSession.close();
+    }
+
+    adminSession.put("/projects/" + project.get()
+        + "/branches/" + branch.getShortName()).consume();
+  }
+
+  @Test
+  public void deleteBranch_Forbidden() throws IOException {
+    RestResponse r =
+        userSession.delete("/projects/" + project.get()
+            + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_FORBIDDEN, r.getStatusCode());
+    r.consume();
+  }
+
+  @Test
+  public void deleteBranchByAdmin() throws IOException {
+    RestResponse r =
+        adminSession.delete("/projects/" + project.get()
+            + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_NO_CONTENT, r.getStatusCode());
+    r.consume();
+
+    r = adminSession.get("/projects/" + project.get()
+        + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_NOT_FOUND, r.getStatusCode());
+    r.consume();
+  }
+
+  @Test
+  public void deleteBranchByProjectOwner() throws IOException,
+      ConfigInvalidException {
+    grantOwner();
+
+    RestResponse r =
+        userSession.delete("/projects/" + project.get()
+            + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_NO_CONTENT, r.getStatusCode());
+    r.consume();
+
+    r = userSession.get("/projects/" + project.get()
+        + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_NOT_FOUND, r.getStatusCode());
+    r.consume();
+  }
+
+  @Test
+  public void deleteBranchByAdminForcePushBlocked() throws IOException,
+      ConfigInvalidException {
+    blockForcePush();
+    RestResponse r =
+        adminSession.delete("/projects/" + project.get()
+            + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_NO_CONTENT, r.getStatusCode());
+    r.consume();
+
+    r = adminSession.get("/projects/" + project.get()
+        + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_NOT_FOUND, r.getStatusCode());
+    r.consume();
+  }
+
+  @Test
+  public void deleteBranchByProjectOwnerForcePushBlocked_Forbidden()
+      throws IOException, ConfigInvalidException {
+    grantOwner();
+    blockForcePush();
+    RestResponse r =
+        userSession.delete("/projects/" + project.get()
+            + "/branches/" + branch.getShortName());
+    assertEquals(HttpStatus.SC_FORBIDDEN, r.getStatusCode());
+    r.consume();
+  }
+
+  private void blockForcePush() throws IOException, ConfigInvalidException {
+    MetaDataUpdate md = metaDataUpdateFactory.create(allProjects.get());
+    md.setMessage(String.format("Block force %s", Permission.PUSH));
+    ProjectConfig config = ProjectConfig.read(md);
+    AccessSection s = config.getAccessSection("refs/heads/*", true);
+    Permission p = s.getPermission(Permission.PUSH, true);
+    AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Anonymous Users"));
+    PermissionRule rule = new PermissionRule(config.resolve(adminGroup));
+    rule.setForce(true);
+    rule.setBlock();
+    p.add(rule);
+    config.commit(md);
+    projectCache.evict(config.getProject());
+  }
+
+  private void grantOwner() throws IOException, ConfigInvalidException {
+    MetaDataUpdate md = metaDataUpdateFactory.create(project);
+    md.setMessage(String.format("Grant %s", Permission.OWNER));
+    ProjectConfig config = ProjectConfig.read(md);
+    AccessSection s = config.getAccessSection("refs/*", true);
+    Permission p = s.getPermission(Permission.OWNER, true);
+    AccountGroup adminGroup = groupCache.get(new AccountGroup.NameKey("Registered Users"));
+    PermissionRule rule = new PermissionRule(config.resolve(adminGroup));
+    p.add(rule);
+    config.commit(md);
+    projectCache.evict(config.getProject());
+  }
+}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
index 41474da..1f120ed7 100644
--- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -11,6 +11,7 @@
 import com.google.common.hash.Funnel;
 import com.google.common.hash.Funnels;
 import com.google.common.hash.PrimitiveSink;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.TypeLiteral;
 
 import org.h2.jdbc.JdbcSQLException;
@@ -114,7 +115,7 @@
   @Override
   public void put(final K key, V val) {
     final ValueHolder<V> h = new ValueHolder<V>(val);
-    h.created = System.currentTimeMillis();
+    h.created = TimeUtil.nowMs();
     mem.put(key, h);
     executor.execute(new Runnable() {
       @Override
@@ -182,7 +183,7 @@
     cal.set(Calendar.MILLISECOND, 0);
     cal.add(Calendar.DAY_OF_MONTH, 1);
 
-    long delay = cal.getTimeInMillis() - System.currentTimeMillis();
+    long delay = cal.getTimeInMillis() - TimeUtil.nowMs();
     service.schedule(new Runnable() {
       @Override
       public void run() {
@@ -245,7 +246,7 @@
       }
 
       final ValueHolder<V> h = new ValueHolder<V>(loader.load(key));
-      h.created = System.currentTimeMillis();
+      h.created = TimeUtil.nowMs();
       executor.execute(new Runnable() {
         @Override
         public void run() {
@@ -461,7 +462,7 @@
         c.touch =c.conn.prepareStatement("UPDATE data SET accessed=? WHERE k=?");
       }
       try {
-        c.touch.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
+        c.touch.setTimestamp(1, TimeUtil.nowTs());
         keyType.set(c.touch, 2, key);
         c.touch.executeUpdate();
       } finally {
@@ -490,7 +491,7 @@
           keyType.set(c.put, 1, key);
           c.put.setObject(2, holder.value);
           c.put.setTimestamp(3, new Timestamp(holder.created));
-          c.put.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
+          c.put.setTimestamp(4, TimeUtil.nowTs());
           c.put.executeUpdate();
           holder.clean = true;
         } finally {
diff --git a/gerrit-extension-api/BUCK b/gerrit-extension-api/BUCK
index 4145636..5aa57dd 100644
--- a/gerrit-extension-api/BUCK
+++ b/gerrit-extension-api/BUCK
@@ -1,14 +1,21 @@
-SRCS = glob(['src/main/java/com/google/gerrit/extensions/**/*.java'])
+SRC = 'src/main/java/com/google/gerrit/extensions/'
+
+gwt_module(
+  name = 'client',
+  srcs = glob([SRC + 'webui/GerritTopMenu.java']),
+  gwtxml = SRC + 'Extensions.gwt.xml',
+  visibility = ['PUBLIC'],
+)
 
 java_library2(
   name = 'api',
-  srcs = SRCS,
+  srcs = glob([SRC + '**/*.java']),
   compile_deps = ['//lib/guice:guice'],
   visibility = ['PUBLIC'],
 )
 
 java_sources(
   name = 'api-src',
-  srcs = SRCS,
+  srcs = glob([SRC + '**/*.java']),
   visibility = ['PUBLIC'],
 )
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/Extensions.gwt.xml b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/Extensions.gwt.xml
new file mode 100644
index 0000000..ef6a827
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/Extensions.gwt.xml
@@ -0,0 +1,18 @@
+<!--
+ Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<module>
+  <source path='webui' />
+</module>
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java
index 2911ded..49a697e 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/GitReferenceUpdatedListener.java
@@ -16,20 +16,15 @@
 
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 
-import java.util.List;
-
 /** Notified when one or more references are modified. */
 @ExtensionPoint
 public interface GitReferenceUpdatedListener {
-  public interface Update {
-    String getRefName();
-    String getOldObjectId();
-    String getNewObjectId();
-  }
 
   public interface Event {
     String getProjectName();
-    List<Update> getUpdates();
+    String getRefName();
+    String getOldObjectId();
+    String getNewObjectId();
   }
 
   void onGitReferenceUpdated(Event event);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GerritTopMenu.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GerritTopMenu.java
new file mode 100644
index 0000000..e3821fc
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GerritTopMenu.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.webui;
+
+public enum GerritTopMenu {
+  ALL, MY, DIFFERENCES, PROJECTS, PEOPLE, PLUGINS, DOCUMENTATION;
+
+  public final String menuName;
+
+  private GerritTopMenu() {
+    menuName = name().substring(0, 1) + name().substring(1).toLowerCase();
+  }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/TopMenu.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/TopMenu.java
index ecb7d42..7389519fc 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/TopMenu.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/TopMenu.java
@@ -20,11 +20,14 @@
 
 @ExtensionPoint
 public interface TopMenu {
-
   public class MenuEntry {
     public final String name;
     public final List<MenuItem> items;
 
+    public MenuEntry(GerritTopMenu gerritMenu, List<MenuItem> items) {
+      this(gerritMenu.menuName, items);
+    }
+
     public MenuEntry(String name, List<MenuItem> items) {
       this.name = name;
       this.items = items;
diff --git a/gerrit-gwtui/BUCK b/gerrit-gwtui/BUCK
index 6024481..1ae823e 100644
--- a/gerrit-gwtui/BUCK
+++ b/gerrit-gwtui/BUCK
@@ -70,6 +70,7 @@
     '//gerrit-gwtexpui:SafeHtml',
     '//gerrit-gwtexpui:UserAgent',
     '//gerrit-common:client',
+    '//gerrit-extension-api:client',
     '//gerrit-patch-jgit:client',
     '//gerrit-prettify:client',
     '//gerrit-reviewdb:client',
@@ -117,6 +118,7 @@
   deps = [
     ':ui_module',
     '//gerrit-common:client',
+    '//gerrit-extension-api:client',
     '//lib:junit',
     '//lib/gwt:dev',
     '//lib/gwt:user',
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
index d33a525..9fe04bc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
@@ -24,6 +24,7 @@
   <inherits name='com.google.gwtexpui.linker.ServerPlannedIFrameLinker'/>
   <inherits name='com.google.gwtexpui.progress.Progress'/>
   <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
+  <inherits name='com.google.gerrit.extensions.Extensions'/>
   <inherits name='com.google.gerrit.prettify.PrettyFormatter'/>
   <inherits name='com.google.gerrit.Common'/>
   <inherits name='com.google.gerrit.UserAgent'/>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 2f59435..e73670f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -42,6 +42,7 @@
 import com.google.gerrit.common.data.GitwebConfig;
 import com.google.gerrit.common.data.HostPageData;
 import com.google.gerrit.common.data.SystemInfoService;
+import com.google.gerrit.extensions.webui.GerritTopMenu;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountDiffPreference;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
@@ -113,13 +114,6 @@
   private static AccountDiffPreference myAccountDiffPref;
   private static String xGerritAuth;
 
-  private static final String ALL_BAR = "All";
-  private static final String MY_BAR = "My";
-  private static final String DIFF_BAR = "Differences";
-  private static final String PROJECTS_BAR = "Projects";
-  private static final String PEOPLE_BAR = "People";
-  private static final String PLUGINS_BAR = "Plugins";
-  private static final String DOCUMENTATION_BAR = "Documentation";
   private static Map<String, LinkMenuBar> menuBars;
 
   private static MorphingTabPanel menuLeft;
@@ -210,7 +204,7 @@
    * @param view the loaded view.
    */
   public static void updateMenus(Screen view) {
-    LinkMenuBar diffBar = menuBars.get(DIFF_BAR);
+    LinkMenuBar diffBar = menuBars.get(GerritTopMenu.DIFFERENCES.menuName);
     if (view instanceof PatchScreen) {
       patchScreen = (PatchScreen) view;
       menuLeft.setVisible(diffBar, true);
@@ -560,7 +554,7 @@
     };
     gBody.add(body);
 
-    RpcStatus.INSTANCE = new RpcStatus(topMenu);
+    RpcStatus.INSTANCE = new RpcStatus();
     JsonUtil.addRpcStartHandler(RpcStatus.INSTANCE);
     JsonUtil.addRpcCompleteHandler(RpcStatus.INSTANCE);
     JsonUtil.setDefaultXsrfManager(new XsrfManager() {
@@ -671,7 +665,7 @@
     LinkMenuBar m;
 
     m = new LinkMenuBar();
-    menuBars.put(ALL_BAR, m);
+    menuBars.put(GerritTopMenu.ALL.menuName, m);
     addLink(m, C.menuAllOpen(), PageLinks.toChangeQuery("status:open"));
     addLink(m, C.menuAllMerged(), PageLinks.toChangeQuery("status:merged"));
     addLink(m, C.menuAllAbandoned(), PageLinks.toChangeQuery("status:abandoned"));
@@ -679,7 +673,7 @@
 
     if (signedIn) {
       m = new LinkMenuBar();
-      menuBars.put(MY_BAR, m);
+      menuBars.put(GerritTopMenu.MY.menuName, m);
       addLink(m, C.menuMyChanges(), PageLinks.MINE);
       addLink(m, C.menuMyDrafts(), PageLinks.toChangeQuery("is:draft"));
       addLink(m, C.menuMyDraftComments(), PageLinks.toChangeQuery("has:draft"));
@@ -693,7 +687,7 @@
 
     patchScreen = null;
     LinkMenuBar diffBar = new LinkMenuBar();
-    menuBars.put(DIFF_BAR, diffBar);
+    menuBars.put(GerritTopMenu.DIFFERENCES.menuName, diffBar);
     menuLeft.addInvisible(diffBar, C.menuDiff());
     addDiffLink(diffBar, CC.patchTableDiffSideBySide(), PatchScreen.Type.SIDE_BY_SIDE);
     addDiffLink(diffBar, CC.patchTableDiffUnified(), PatchScreen.Type.UNIFIED);
@@ -710,7 +704,7 @@
         }
       }
     };
-    menuBars.put(PROJECTS_BAR, projectsBar);
+    menuBars.put(GerritTopMenu.PROJECTS.menuName, projectsBar);
     addLink(projectsBar, C.menuProjectsList(), PageLinks.ADMIN_PROJECTS);
     addProjectLink(projectsBar, C.menuProjectsInfo(), ProjectScreen.INFO);
     addProjectLink(projectsBar, C.menuProjectsBranches(), ProjectScreen.BRANCH);
@@ -722,13 +716,13 @@
 
     if (signedIn) {
       final LinkMenuBar peopleBar = new LinkMenuBar();
-      menuBars.put(PEOPLE_BAR, peopleBar);
+      menuBars.put(GerritTopMenu.PEOPLE.menuName, peopleBar);
       final LinkMenuItem groupsListMenuItem =
           addLink(peopleBar, C.menuPeopleGroupsList(), PageLinks.ADMIN_GROUPS);
       menuLeft.add(peopleBar, C.menuPeople());
 
       final LinkMenuBar pluginsBar = new LinkMenuBar();
-      menuBars.put(PLUGINS_BAR, pluginsBar);
+      menuBars.put(GerritTopMenu.PLUGINS.menuName, pluginsBar);
       AccountCapabilities.all(new GerritCallback<AccountCapabilities>() {
         @Override
         public void onSuccess(AccountCapabilities result) {
@@ -754,7 +748,7 @@
 
     if (getConfig().isDocumentationAvailable()) {
       m = new LinkMenuBar();
-      menuBars.put(DOCUMENTATION_BAR, m);
+      menuBars.put(GerritTopMenu.DOCUMENTATION.menuName, m);
       addDocLink(m, C.menuDocumentationIndex(), "index.html");
       addDocLink(m, C.menuDocumentationSearch(), "user-search.html");
       addDocLink(m, C.menuDocumentationUpload(), "user-upload.html");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index 44b4479..0cb3fa2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -198,8 +198,6 @@
   String rightBorder();
   String rightmost();
   String rpcStatus();
-  String rpcStatusLoading();
-  String rpcStatusPanel();
   String screen();
   String screenHeader();
   String screenNoHeader();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
index 955c8e2..cd715c6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
@@ -14,10 +14,9 @@
 
 package com.google.gerrit.client;
 
-import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwtjsonrpc.client.event.RpcCompleteEvent;
 import com.google.gwtjsonrpc.client.event.RpcCompleteHandler;
 import com.google.gwtjsonrpc.client.event.RpcStartEvent;
@@ -41,17 +40,12 @@
   private final Label loading;
   private int activeCalls;
 
-  RpcStatus(final Panel p) {
-    final FlowPanel r = new FlowPanel();
-    r.setStyleName(Gerrit.RESOURCES.css().rpcStatusPanel());
-    p.add(r);
-
+  RpcStatus() {
     loading = new InlineLabel();
     loading.setText(Gerrit.C.rpcStatusWorking());
     loading.setStyleName(Gerrit.RESOURCES.css().rpcStatus());
-    loading.addStyleName(Gerrit.RESOURCES.css().rpcStatusLoading());
     loading.setVisible(false);
-    r.add(loading);
+    RootPanel.get().add(loading);
   }
 
   @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
index 7ec0844..f4db4ff 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -326,6 +326,9 @@
   }
 
   private void initProjectActions(ConfigInfo info) {
+    actionsGrid.clear(true);
+    actionsGrid.removeAllRows();
+
     NativeMap<ActionInfo> actions = info.actions();
     if (actions == null || actions.isEmpty()) {
       return;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
index 2f9a670..9ba04dd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
@@ -39,15 +39,18 @@
 import com.google.gerrit.client.rpc.Natives;
 import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.BranchLink;
 import com.google.gerrit.client.ui.ChangeLink;
 import com.google.gerrit.client.ui.CommentLinkProcessor;
+import com.google.gerrit.client.ui.InlineHyperlink;
 import com.google.gerrit.client.ui.Screen;
 import com.google.gerrit.client.ui.UserActivityMonitor;
+import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.changes.ListChangesOption;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.reviewdb.client.Project.SubmitType;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JsArray;
@@ -57,8 +60,6 @@
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.resources.client.CssResource;
@@ -71,7 +72,6 @@
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
 import com.google.gwt.user.client.ui.ToggleButton;
 import com.google.gwtexpui.clippy.client.CopyableLabel;
 import com.google.gwtexpui.globalkey.client.GlobalKey;
@@ -135,8 +135,8 @@
   @UiField Element changeIdText;
   @UiField Element ownerText;
   @UiField Element statusText;
-  @UiField Element projectText;
-  @UiField Element branchText;
+  @UiField InlineHyperlink projectLink;
+  @UiField InlineHyperlink branchLink;
   @UiField Element submitActionText;
   @UiField Element notMergeable;
   @UiField CopyableLabel idText;
@@ -203,10 +203,6 @@
 
   @Override
   protected void onUnload() {
-    if (updateAvailable != null) {
-      updateAvailable.hide(true);
-      updateAvailable = null;
-    }
     if (updateCheck != null) {
       updateCheck.cancel();
       updateCheck = null;
@@ -291,6 +287,26 @@
         style, headerLine, download);
   }
 
+  private void initProjectLink(ChangeInfo info) {
+    projectLink.setText(info.project());
+    projectLink.setTargetHistoryToken(
+        PageLinks.toChangeQuery(
+            PageLinks.projectQuery(
+                info.project_name_key(),
+                info.status())));
+  }
+
+  private void initBranchLink(ChangeInfo info) {
+    branchLink.setText(info.branch());
+    branchLink.setTargetHistoryToken(
+        PageLinks.toChangeQuery(
+            BranchLink.query(
+                info.project_name_key(),
+                info.status(),
+                info.branch(),
+                info.topic())));
+  }
+
   private void initEditMessageAction(ChangeInfo info, String revision) {
     NativeMap<ActionInfo> actions = info.revision(revision).actions();
     if (actions != null && actions.containsKey("message")) {
@@ -636,13 +652,13 @@
     initIncludedInAction(info);
     initRevisionsAction(info, revision);
     initDownloadAction(info, revision);
+    initProjectLink(info);
+    initBranchLink(info);
     actions.display(info, revision);
 
     star.setValue(info.starred());
     permalink.setHref(ChangeLink.permalink(changeId));
     changeIdText.setInnerText(String.valueOf(info.legacy_id()));
-    projectText.setInnerText(info.project());
-    branchText.setInnerText(info.branch());
     idText.setText("Change-Id: " + info.change_id());
     idText.setPreviewText(info.change_id());
     reload.set(info);
@@ -742,18 +758,12 @@
           lastDisplayedUpdate = newTime;
         }
       };
-      updateAvailable.addCloseHandler(new CloseHandler<PopupPanel>() {
-        @Override
-        public void onClose(CloseEvent<PopupPanel> event) {
-          updateAvailable = null;
-        }
-      });
     }
     updateAvailable.set(
         Natives.asList(nm).subList(om.length(), nm.length()),
         newInfo.updated());
-    if (!updateAvailable.isShowing()) {
-      updateAvailable.popup();
+    if (!updateAvailable.isAttached()) {
+      add(updateAvailable);
     }
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
index 395328e..a58cd09 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
@@ -18,6 +18,7 @@
     xmlns:ui='urn:ui:com.google.gwt.uibinder'
     xmlns:c='urn:import:com.google.gerrit.client.change'
     xmlns:g='urn:import:com.google.gwt.user.client.ui'
+    xmlns:x='urn:import:com.google.gerrit.client.ui'
     xmlns:clippy='urn:import:com.google.gwtexpui.clippy.client'>
   <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
   <ui:style type='com.google.gerrit.client.change.ChangeScreen2.Style'>
@@ -316,11 +317,19 @@
             </tr>
             <tr>
               <th><ui:msg>Project</ui:msg></th>
-              <td ui:field='projectText'/>
+              <td><x:InlineHyperlink ui:field='projectLink'
+                     title='Search for changes on this project'>
+                     <ui:attribute name='title'/>
+                  </x:InlineHyperlink>
+              </td>
             </tr>
             <tr>
               <th><ui:msg>Branch</ui:msg></th>
-              <td ui:field='branchText'/>
+              <td><x:InlineHyperlink ui:field='branchLink'
+                     title='Search for changes on this branch'>
+                     <ui:attribute name='title'/>
+                  </x:InlineHyperlink>
+              </td>
             </tr>
             <tr>
               <th><ui:msg>Strategy</ui:msg></th>
@@ -340,7 +349,7 @@
               <td ui:field='actionDate'/>
             </tr>
             <tr>
-              <th><ui:msg>Id</ui:msg></th>
+              <th><ui:msg>Change-Id</ui:msg></th>
               <td><clippy:CopyableLabel styleName='{style.clippy}' ui:field='idText'/></td>
             </tr>
             <tr>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
index 86b313b..5cb0443 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
@@ -22,6 +22,9 @@
 import com.google.gerrit.client.changes.ChangeInfo.GitPerson;
 import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
 import com.google.gerrit.client.ui.CommentLinkProcessor;
+import com.google.gerrit.client.ui.InlineHyperlink;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.AnchorElement;
 import com.google.gwt.dom.client.Element;
@@ -38,9 +41,9 @@
 
   @UiField Element commitName;
   @UiField AnchorElement browserLink;
-  @UiField Element authorNameEmail;
+  @UiField InlineHyperlink authorNameEmail;
   @UiField Element authorDate;
-  @UiField Element committerNameEmail;
+  @UiField InlineHyperlink committerNameEmail;
   @UiField Element committerDate;
   @UiField Element commitMessageText;
 
@@ -55,8 +58,10 @@
     CommitInfo commit = revInfo.commit();
 
     commitName.setInnerText(revision);
-    format(commit.author(), authorNameEmail, authorDate);
-    format(commit.committer(), committerNameEmail, committerDate);
+    formatLink(commit.author(), authorNameEmail,
+        authorDate, change.status());
+    formatLink(commit.committer(), committerNameEmail,
+        committerDate, change.status());
     commitMessageText.setInnerSafeHtml(commentLinkProcessor.apply(
         new SafeHtmlBuilder().append(commit.message()).linkify()));
 
@@ -69,8 +74,25 @@
     }
   }
 
-  private void format(GitPerson person, Element name, Element date) {
-    name.setInnerText(person.name() + " <" + person.email() + ">");
+  private static void formatLink(GitPerson person, InlineHyperlink name,
+      Element date, Status status) {
+    name.setText(renderName(person));
+    name.setTargetHistoryToken(PageLinks
+        .toAccountQuery(owner(person), status));
     date.setInnerText(FormatUtil.mediumFormat(person.date()));
   }
+
+  private static String renderName(GitPerson person) {
+    return person.name() + " <" + person.email() + ">";
+  }
+
+  public static String owner(GitPerson person) {
+    if (person.email() != null) {
+      return person.email();
+    } else if (person.name() != null) {
+      return person.name();
+    } else {
+      return "";
+    }
+  }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
index c1a6d24..a095926 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
@@ -16,7 +16,8 @@
 -->
 <ui:UiBinder
     xmlns:ui='urn:ui:com.google.gwt.uibinder'
-    xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+    xmlns:g='urn:import:com.google.gwt.user.client.ui'
+    xmlns:x='urn:import:com.google.gerrit.client.ui'>
   <ui:style>
     .commitHeader {
       border-spacing: 0;
@@ -46,12 +47,20 @@
       </tr>
       <tr>
         <th><ui:msg>Author</ui:msg></th>
-        <td ui:field='authorNameEmail'/>
+        <td><x:InlineHyperlink ui:field='authorNameEmail'
+              title='Search for changes by this user'>
+              <ui:attribute name='title'/>
+            </x:InlineHyperlink>
+        </td>
         <td ui:field='authorDate'/>
       </tr>
       <tr>
         <th><ui:msg>Committer</ui:msg></th>
-        <td ui:field='committerNameEmail'/>
+        <td><x:InlineHyperlink ui:field='committerNameEmail'
+              title='Search for changes by this user'>
+              <ui:attribute name='title'/>
+            </x:InlineHyperlink>
+        </td>
         <td ui:field='committerDate'/>
       </tr>
     </table>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
index 2837b15..1dece8b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
@@ -14,52 +14,34 @@
 
 package com.google.gerrit.client.change;
 
-import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.resources.client.CssResource;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.ScrollEvent;
-import com.google.gwt.user.client.Window.ScrollHandler;
 import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.RootPanel;
 
 import java.sql.Timestamp;
 import java.util.HashSet;
 import java.util.List;
 
 /** Displays the "New Message From ..." panel in bottom right on updates. */
-abstract class UpdateAvailableBar extends PopupPanel {
+abstract class UpdateAvailableBar extends Composite {
   interface Binder extends UiBinder<HTMLPanel, UpdateAvailableBar> {}
   private static final Binder uiBinder = GWT.create(Binder.class);
 
-  static interface Style extends CssResource {
-    String popup();
-  }
-
   private Timestamp updated;
-  private HandlerRegistration resizer;
-  private HandlerRegistration scroller;
 
-  @UiField Style style;
   @UiField Element author;
   @UiField Anchor show;
   @UiField Anchor ignore;
 
   UpdateAvailableBar() {
-    super(/* autoHide = */ false, /* modal = */ false);
-    add(uiBinder.createAndBindUi(this));
-    setStyleName(style.popup());
+    initWidget(uiBinder.createAndBindUi(this));
   }
 
   void set(List<MessageInfo> newMessages, Timestamp newTime) {
@@ -76,63 +58,6 @@
     }
     author.setInnerText(r.toString());
     updated = newTime;
-
-    if (isShowing()) {
-      setPopupPosition(
-          Window.getScrollLeft() + Window.getClientWidth() - getOffsetWidth(),
-          Window.getScrollTop() + Window.getClientHeight() - getOffsetHeight());
-    }
-  }
-
-  void popup() {
-    setPopupPositionAndShow(new PositionCallback() {
-      @Override
-      public void setPosition(int w, int h) {
-        w += 7; // Initial information is wrong, adjust with some slop.
-        h += 19;
-        setPopupPosition(
-            Window.getScrollLeft() + Window.getClientWidth() - w,
-            Window.getScrollTop() + Window.getClientHeight() - h);
-      }
-    });
-    if (resizer == null) {
-      resizer = Window.addResizeHandler(new ResizeHandler() {
-        @Override
-        public void onResize(ResizeEvent event) {
-          setPopupPosition(
-              Window.getScrollLeft() + event.getWidth() - getOffsetWidth(),
-              Window.getScrollTop() + event.getHeight() - getOffsetHeight());
-        }
-      });
-    }
-    if (scroller == null) {
-      scroller = Window.addWindowScrollHandler(new ScrollHandler() {
-        @Override
-        public void onWindowScroll(ScrollEvent event) {
-          RootPanel b = Gerrit.getBottomMenu();
-          int br = b.getAbsoluteLeft() + b.getOffsetWidth();
-          int bp = b.getAbsoluteTop() + b.getOffsetHeight();
-          int wr = event.getScrollLeft() + Window.getClientWidth();
-          int wp = event.getScrollTop() + Window.getClientHeight();
-          setPopupPosition(
-              Math.min(br, wr) - getOffsetWidth(),
-              Math.min(bp, wp) - getOffsetHeight());
-        }
-      });
-    }
-  }
-
-  @Override
-  public void hide() {
-    if (resizer != null) {
-      resizer.removeHandler();
-      resizer = null;
-    }
-    if (scroller != null) {
-      scroller.removeHandler();
-      scroller = null;
-    }
-    super.hide();
   }
 
   @UiHandler("show")
@@ -143,7 +68,7 @@
   @UiHandler("ignore")
   void onIgnore(ClickEvent e) {
     onIgnore(updated);
-    hide();
+    removeFromParent();
   }
 
   abstract void onShow();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml
index a6cd124..1c46b8c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml
@@ -18,8 +18,12 @@
     xmlns:ui='urn:ui:com.google.gwt.uibinder'
     xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
     xmlns:g='urn:import:com.google.gwt.user.client.ui'>
-  <ui:style type='com.google.gerrit.client.change.UpdateAvailableBar.Style'>
+  <ui:style>
     .popup {
+      position: fixed;
+      bottom: 0;
+      right: 0;
+      z-index: 10;
       padding: 5px;
     }
     .bar {
@@ -39,21 +43,23 @@
       margin-left: 0.5em;
     }
   </ui:style>
-  <g:HTMLPanel styleName='{style.bar}'>
-    <ui:msg>Update from <span ui:field='author'/></ui:msg>
-    <g:Anchor ui:field='show'
-        styleName='{style.action}'
-        href='javascript:;'
-        title='Refresh screen and display updates'>
-      <ui:attribute name='title'/>
-      <ui:msg>Show</ui:msg>
-    </g:Anchor>
-    <g:Anchor ui:field='ignore'
-        styleName='{style.action}'
-        href='javascript:;'
-        title='Ignore this update'>
-      <ui:attribute name='title'/>
-      <ui:msg>Ignore</ui:msg>
-    </g:Anchor>
+  <g:HTMLPanel styleName='{style.popup}'>
+    <div class='{style.bar}'>
+      <ui:msg>Update from <span ui:field='author'/></ui:msg>
+      <g:Anchor ui:field='show'
+          styleName='{style.action}'
+          href='javascript:;'
+          title='Refresh screen and display updates'>
+        <ui:attribute name='title'/>
+        <ui:msg>Show</ui:msg>
+      </g:Anchor>
+      <g:Anchor ui:field='ignore'
+          styleName='{style.action}'
+          href='javascript:;'
+          title='Ignore this update'>
+        <ui:attribute name='title'/>
+        <ui:msg>Ignore</ui:msg>
+      </g:Anchor>
+    </div>
   </g:HTMLPanel>
 </ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
index 4c8579c..18d497c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
@@ -22,13 +22,15 @@
     @external .CodeMirror-linenumber, .CodeMirror-vscrollbar .CodeMirror-scroll;
     @external .cm-keymap-fat-cursor, CodeMirror-cursor;
     @external .cm-searching, .cm-trailingspace, .cm-tab;
-    .difftable .CodeMirror-lines {
-      padding: 0;
-    }
+
+    .difftable { max-width: 1484px; }
+    .difftable .CodeMirror-lines { padding: 0; }
     .difftable .CodeMirror pre {
       padding: 0;
       padding-bottom: 0.11em;
       overflow: hidden;
+      border-right: 0;
+      width: auto;
     }
     .difftable .CodeMirror pre span {
       padding-bottom: 0.11em;
@@ -41,13 +43,15 @@
       width: 10px;
     }
     .table {
+      width: 100%;
       table-layout: fixed;
       border-spacing: 0;
     }
 
-    .a, .b { padding: 0; }
-    .a { width: 702px; min-width: 702px; }
-    .b { width: 718px; min-width: 718px; }
+    .a, .b {
+      padding: 0;
+      width: 50%;
+    }
 
     /* Hide left side scrollbar, right side controls both views. */
     .a .CodeMirror-scroll { padding-right: 0; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml
index 53402fc..2814a63 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml
@@ -21,8 +21,7 @@
   <ui:style>
   .header {
     position: relative;
-    width: 1420px;
-    min-width: 1420px;
+    max-width: 1484px;
   }
   .reviewed input {
     margin: 0;
@@ -34,7 +33,7 @@
   .navigation {
     position: absolute;
     top: 0;
-    right: 0;
+    right: 15px;
     font-family: Arial Unicode MS, sans-serif;
   }
   </ui:style>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
index bff8990..f5ae149 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
@@ -37,8 +37,7 @@
     cmB.on("scroll", new ScrollCallback(cmB, cmA, DisplaySide.B));
   }
 
-  private void updateScreenHeader(CodeMirror cm) {
-    ScrollInfo si = cm.getScrollInfo();
+  private void updateScreenHeader(ScrollInfo si) {
     if (si.getTop() == 0 && !Gerrit.isHeaderVisible()) {
       Gerrit.setHeaderVisible(true);
       diffTable.updateFileCommentVisibility(false);
@@ -77,8 +76,9 @@
         fixup.scheduleRepeating(20);
       }
       if (active == this) {
-        updateScreenHeader(src);
-        dst.scrollToY(src.getScrollInfo().getTop());
+        ScrollInfo si = src.getScrollInfo();
+        updateScreenHeader(si);
+        dst.scrollTo(si.getLeft(), si.getTop());
         state = 0;
       }
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
index 58b476c..7caf76d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide2.java
@@ -155,8 +155,8 @@
     // TODO: Re-implement necessary GlobalKey bindings.
     addDomHandler(GlobalKey.STOP_PROPAGATION, KeyPressEvent.getType());
     keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
-    add(header = new Header(keysNavigation, base, revision, path));
-    add(diffTable = new DiffTable(this, base, revision, path));
+    header = new Header(keysNavigation, base, revision, path);
+    diffTable = new DiffTable(this, base, revision, path);
     add(uiBinder.createAndBindUi(this));
   }
 
@@ -234,8 +234,8 @@
   public void onShowView() {
     super.onShowView();
     resizeCodeMirror();
-
     Window.enableScrolling(false);
+
     cmA.setOption("viewportMargin", 10);
     cmB.setOption("viewportMargin", 10);
     cmB.setCursor(LineCharacter.create(0));
@@ -253,8 +253,13 @@
       resizeHandler.removeHandler();
       resizeHandler = null;
     }
-    cmA.getWrapperElement().removeFromParent();
-    cmB.getWrapperElement().removeFromParent();
+    if (cmA != null) {
+      cmA.getWrapperElement().removeFromParent();
+    }
+    if (cmB != null) {
+      cmB.getWrapperElement().removeFromParent();
+    }
+
     Window.enableScrolling(true);
     Gerrit.setHeaderVisible(true);
   }
@@ -449,7 +454,7 @@
       .set("lineNumbers", true)
       .set("tabSize", pref.getTabSize())
       .set("mode", getContentType(meta))
-      .set("lineWrapping", true)
+      .set("lineWrapping", false)
       .set("styleSelectedText", true)
       .set("showTrailingSpace", pref.isShowWhitespaceErrors())
       .set("keyMap", "vim_ro")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 3d893cd..651ab51 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -360,25 +360,18 @@
 }
 
 /** RPC Status **/
-.rpcStatusPanel {
-  position: absolute;
-  left: 50%;
-  float: left;
-  top: 6px;
-}
-
 .rpcStatus {
   position: fixed;
+  top: 6px;
+  left: 50%;
   padding-top: 4px;
   padding-bottom: 4px;
   padding-left: 10px;
   padding-right: 10px;
   text-align: center;
   font-weight: bold;
-}
-
-.rpcStatusLoading {
   background: #FFF1A8;
+  z-index: 10;
 }
 
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
index a0789ec..fd3e35c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
@@ -66,6 +66,7 @@
 
 import org.eclipse.jgit.diff.Edit;
 
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -250,7 +251,7 @@
   }
 
   protected boolean hasDifferences(PatchScript script) {
-    return hasEdits(script) || hasMeta(script);
+    return hasEdits(script) || hasMeta(script) || hasComments(script);
   }
 
   public boolean isPureMetaChange(PatchScript script) {
@@ -267,6 +268,12 @@
     return false;
   }
 
+  // True if one of the two patch sets has comments
+  private boolean hasComments(PatchScript script) {
+    return !script.getCommentDetail().getCommentsA().isEmpty()
+        || !script.getCommentDetail().getCommentsB().isEmpty();
+  }
+
   // True if this change is a mode change or a pure rename/copy
   private boolean hasMeta(PatchScript script) {
     return !script.getPatchHeader().isEmpty();
@@ -517,9 +524,10 @@
             throw new RuntimeException("unexpected file id " + file);
         }
 
-        final PatchLineComment newComment =
-            new PatchLineComment(new PatchLineComment.Key(parentKey, null),
-                line, Gerrit.getUserAccount().getId(), null);
+        final PatchLineComment newComment = new PatchLineComment(
+            new PatchLineComment.Key(parentKey, null), line,
+            Gerrit.getUserAccount().getId(), null,
+            new Timestamp(System.currentTimeMillis()));
         newComment.setSide(side);
         newComment.setMessage("");
 
@@ -963,7 +971,8 @@
       PatchLineComment newComment =
           new PatchLineComment(new PatchLineComment.Key(comment.getKey()
               .getParentKey(), null), comment.getLine(), Gerrit
-              .getUserAccount().getId(), comment.getKey().get());
+              .getUserAccount().getId(), comment.getKey().get(),
+              new Timestamp(System.currentTimeMillis()));
       newComment.setSide(comment.getSide());
       return newComment;
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
index ef52176..7f11df7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
@@ -65,7 +65,7 @@
     add(l);
   }
 
-  private static String owner(AccountInfo ai) {
+  public static String owner(AccountInfo ai) {
     if (ai.email() != null) {
       return ai.email();
     } else if (ai.name() != null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java
index fddae84..c9a0590 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java
@@ -57,7 +57,7 @@
     }
   }
 
-  private static String query(Project.NameKey project, Change.Status status,
+  public static String query(Project.NameKey project, Change.Status status,
       String branch, String topic) {
     String query = PageLinks.projectQuery(project, status);
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java
index c3639ce..ddbba3b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java
@@ -14,14 +14,15 @@
 
 package com.google.gerrit.httpd;
 
+import com.google.common.base.Strings;
 import com.google.gerrit.audit.AuditEvent;
 import com.google.gerrit.audit.AuditService;
-import com.google.common.base.Strings;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -80,7 +81,7 @@
     final String sid = webSession.get().getSessionId();
     final CurrentUser currentUser = webSession.get().getCurrentUser();
     final String what = "sign out";
-    final long when = System.currentTimeMillis();
+    final long when = TimeUtil.nowMs();
 
     try {
       doLogout(req, rsp);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
index a5338f8..03eca9f 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
@@ -22,6 +22,7 @@
 import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64;
 import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
 import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
+import static com.google.gerrit.server.util.TimeUtil.nowMs;
 import static java.util.concurrent.TimeUnit.HOURS;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.MINUTES;
@@ -53,10 +54,6 @@
   private static final Logger log = LoggerFactory.getLogger(WebSessionManager.class);
   static final String CACHE_NAME = "web_sessions";
 
-  static long now() {
-    return System.currentTimeMillis();
-  }
-
   private final long sessionMaxAgeMillis;
   private final SecureRandom prng;
   private final Cache<String, Val> self;
@@ -117,7 +114,7 @@
     final long halfAgeRefresh = sessionMaxAgeMillis >>> 1;
     final long minRefresh = MILLISECONDS.convert(1, HOURS);
     final long refresh = Math.min(halfAgeRefresh, minRefresh);
-    final long now = now();
+    final long now = nowMs();
     final long refreshCookieAt = now + refresh;
     final long expiresAt = now + sessionMaxAgeMillis;
     if (sid == null) {
@@ -150,7 +147,7 @@
 
   Val get(final Key key) {
     Val val = self.getIfPresent(key.token);
-    if (val != null && val.expiresAt <= now()) {
+    if (val != null && val.expiresAt <= nowMs()) {
       self.invalidate(key.token);
       return null;
     }
@@ -223,7 +220,7 @@
     }
 
     boolean needsCookieRefresh() {
-      return refreshCookieAt <= now();
+      return refreshCookieAt <= nowMs();
     }
 
     boolean isPersistentCookie() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
index 1ecd929..47a9263 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtexpui.server.CacheHeaders;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -333,7 +334,7 @@
     md.update(req.getRemoteAddr().getBytes("UTF-8"));
     md.update(buf, 0, 4);
 
-    NB.encodeInt64(buf, 0, System.currentTimeMillis());
+    NB.encodeInt64(buf, 0, TimeUtil.nowMs());
     md.update(buf, 0, 8);
 
     rng.nextBytes(buf);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index becacf1..517b017 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -16,7 +16,9 @@
 
 import static com.google.common.base.Charsets.UTF_8;
 import static com.google.common.base.Preconditions.checkNotNull;
+
 import static java.math.RoundingMode.CEILING;
+
 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
 import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
 import static javax.servlet.http.HttpServletResponse.SC_CREATED;
@@ -76,6 +78,7 @@
 import com.google.gerrit.server.OptionUtil;
 import com.google.gerrit.server.OutputFormat;
 import com.google.gerrit.server.account.CapabilityUtils;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gson.ExclusionStrategy;
 import com.google.gson.FieldAttributes;
 import com.google.gson.FieldNamingPolicy;
@@ -185,7 +188,7 @@
   @Override
   protected final void service(HttpServletRequest req, HttpServletResponse res)
       throws ServletException, IOException {
-    long auditStartTs = System.currentTimeMillis();
+    long auditStartTs = TimeUtil.nowMs();
     res.setHeader("Content-Disposition", "attachment");
     res.setHeader("X-Content-Type-Options", "nosniff");
     int status = SC_OK;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
index 55ecb01..5a0d328 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.httpd.WebSession;
 import com.google.gerrit.server.AccessPath;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gson.GsonBuilder;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
 import com.google.gwtjsonrpc.server.ActiveCall;
@@ -238,7 +239,7 @@
         final HttpServletResponse o) {
       super(i, o);
       this.session = session;
-      this.when = System.currentTimeMillis();
+      this.when = TimeUtil.nowMs();
     }
 
     @Override
@@ -290,7 +291,7 @@
     }
 
     public long getElapsed() {
-      return System.currentTimeMillis() - when;
+      return TimeUtil.nowMs() - when;
     }
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
index d3e0f84..b6549ea 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -42,6 +42,7 @@
 import com.google.gerrit.server.contact.ContactStore;
 import com.google.gerrit.server.mail.EmailTokenVerifier;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.VoidResult;
 import com.google.gwtorm.server.OrmException;
@@ -139,7 +140,7 @@
         if (useContactInfo) {
           if (ContactInformation.hasAddress(info)
               || (me.isContactFiled() && ContactInformation.hasData(info))) {
-            me.setContactFiled();
+            me.setContactFiled(TimeUtil.nowTs());
           }
           if (ContactInformation.hasData(info)) {
             try {
@@ -198,8 +199,8 @@
         if (m == null) {
           m = new AccountGroupMember(key);
           db.accountGroupMembersAudit().insert(
-              Collections.singleton(
-                  new AccountGroupMemberAudit(m, account.getId())));
+              Collections.singleton(new AccountGroupMemberAudit(
+                  m, account.getId(), TimeUtil.nowTs())));
           db.accountGroupMembers().insert(Collections.singleton(m));
           accountCache.evict(m.getAccountId());
         }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java
index 66b5ec1..a9b908d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
@@ -91,8 +92,9 @@
         }
 
         final PatchLineComment nc =
-            new PatchLineComment(new PatchLineComment.Key(patchKey, ChangeUtil
-                .messageUUID(db)), comment.getLine(), me, comment.getParentUuid());
+            new PatchLineComment(new PatchLineComment.Key(patchKey,
+                ChangeUtil.messageUUID(db)), comment.getLine(), me,
+                comment.getParentUuid(), TimeUtil.nowTs());
         nc.setSide(comment.getSide());
         nc.setMessage(comment.getMessage());
         nc.setRange(comment.getRange());
@@ -104,7 +106,7 @@
         if (!me.equals(comment.getAuthor())) {
           throw new NoSuchChangeException(changeId);
         }
-        comment.updated();
+        comment.updated(TimeUtil.nowTs());
         db.patchComments().update(Collections.singleton(comment));
         db.commit();
         return comment;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index d686f68..3681888 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -105,7 +106,8 @@
         user.getAccountId(),
         new Branch.NameKey(
             config.getProject().getNameKey(),
-            GitRepositoryManager.REF_CONFIG));
+            GitRepositoryManager.REF_CONFIG),
+        TimeUtil.nowTs());
 
     ps.setCreatedOn(change.getCreatedOn());
     ps.setUploader(change.getOwner());
diff --git a/gerrit-launcher/BUCK b/gerrit-launcher/BUCK
index e5ff3d0..344e53d 100644
--- a/gerrit-launcher/BUCK
+++ b/gerrit-launcher/BUCK
@@ -1,6 +1,7 @@
 java_library(
   name = 'launcher',
   srcs = glob(['src/main/java/**/*.java']),
+  deps = ['//lib/joda:joda-time'],
   visibility = [
     '//gerrit-acceptance-tests/...',
     '//gerrit-main:main_lib',
diff --git a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
index afe61d2..2dd20a2 100644
--- a/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/gerrit-launcher/src/main/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -17,6 +17,8 @@
 import static java.util.concurrent.TimeUnit.DAYS;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
+import org.joda.time.DateTimeUtils;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -506,7 +508,7 @@
     //
     final File[] tmpEntries = tmp.listFiles();
     if (tmpEntries != null) {
-      final long now = System.currentTimeMillis();
+      final long now = DateTimeUtils.currentTimeMillis();
       final long expired = now - MILLISECONDS.convert(7, DAYS);
       for (final File tmpEntry : tmpEntries) {
         if (tmpEntry.isDirectory() && tmpEntry.lastModified() < expired) {
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
index d5666c2..c3570a1 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.lucene;
 
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java
index 08338eb..d3dc963 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.lucene;
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
index 5ee335d..6aef509 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.util.TimeUtil;
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.AsyncAppender;
@@ -95,7 +96,7 @@
     final LoggingEvent event = new LoggingEvent( //
         Logger.class.getName(), // fqnOfCategoryClass
         log, // logger
-        System.currentTimeMillis(), // when
+        TimeUtil.nowMs(), // when
         Level.INFO, // level
         "", // message text
         "HTTPD", // thread name
@@ -162,7 +163,7 @@
       dateFormat = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z");
       dateFormat.setTimeZone(tz);
 
-      lastTimeMillis = System.currentTimeMillis();
+      lastTimeMillis = TimeUtil.nowMs();
       lastTimeString = dateFormat.format(new Date(lastTimeMillis));
     }
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index 35df8d9..da08804 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Singleton;
@@ -593,10 +594,10 @@
     ProcessBuilder proc = new ProcessBuilder(buck, "build", target)
         .directory(root)
         .redirectErrorStream(true);
-    if (properties.contains("PATH")) {
+    if (properties.containsKey("PATH")) {
       proc.environment().put("PATH", properties.getProperty("PATH"));
     }
-    long start = System.currentTimeMillis();
+    long start = TimeUtil.nowMs();
     Process rebuild = proc.start();
     byte[] out;
     InputStream in = rebuild.getInputStream();
@@ -619,7 +620,7 @@
       System.exit(status);
     }
 
-    long time = System.currentTimeMillis() - start;
+    long time = TimeUtil.nowMs() - start;
     log.info(String.format("UPDATED    %s in %.3fs", target, time / 1000.0));
   }
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java
index fbd543d..aa584e8 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java
@@ -57,7 +57,7 @@
     try {
       myWar = GerritLauncher.getDistributionArchive();
     } catch (FileNotFoundException e) {
-      System.err.println("warn: Cannot find gerrit.war");
+      System.err.println("warn: Cannot find distribution archive (e.g. gerrit.war)");
       myWar = null;
     }
 
@@ -75,7 +75,7 @@
       if (siteWar.exists()) {
         copy = ui.yesno(true, "Upgrade %s", siteWar.getPath());
       } else {
-        copy = ui.yesno(true, "Copy gerrit.war to %s", siteWar.getPath());
+        copy = ui.yesno(true, "Copy %s to %s", myWar.getName(), siteWar.getPath());
         if (copy) {
           container.unset("war");
         } else {
@@ -84,7 +84,7 @@
       }
       if (copy) {
         if (!ui.isBatch()) {
-          System.err.format("Copying gerrit.war to %s", siteWar.getPath());
+          System.err.format("Copying %s to %s", myWar.getName(), siteWar.getPath());
           System.err.println();
         }
 
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
index 3ae61e4..5cfa5e1 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -144,10 +144,11 @@
    *
    * @param newId unique id, see
    *        {@link com.google.gerrit.reviewdb.server.ReviewDb#nextAccountId()}.
+   * @param registeredOn when the account was registered.
    */
-  public Account(final Account.Id newId) {
-    accountId = newId;
-    registeredOn = new Timestamp(System.currentTimeMillis());
+  public Account(Account.Id newId, Timestamp registeredOn) {
+    this.accountId = newId;
+    this.registeredOn = registeredOn;
 
     generalPreferences = new AccountGeneralPreferences();
     generalPreferences.resetToDefaults();
@@ -203,8 +204,8 @@
     return contactFiledOn;
   }
 
-  public void setContactFiled() {
-    contactFiledOn = new Timestamp(System.currentTimeMillis());
+  public void setContactFiled(Timestamp ts) {
+    contactFiledOn = ts;
   }
 
   public boolean isActive() {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
index 8ea78e2..07e7d03 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
@@ -86,11 +86,6 @@
     addedBy = adder;
   }
 
-  public AccountGroupByIdAud(final AccountGroupById m,
-      final Account.Id adder) {
-    this(m, adder, now());
-  }
-
   public AccountGroupByIdAud.Key getKey() {
     return key;
   }
@@ -99,17 +94,8 @@
     return removedOn == null;
   }
 
-  public void removed(final Account.Id deleter) {
-    removedBy = deleter;
-    removedOn = now();
-  }
-
   public void removed(final Account.Id deleter, final Timestamp when) {
     removedBy = deleter;
     removedOn = when;
   }
-
-  private static Timestamp now() {
-    return new Timestamp(System.currentTimeMillis());
-  }
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
index 523134b..d3798db 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
@@ -79,11 +79,6 @@
   }
 
   public AccountGroupMemberAudit(final AccountGroupMember m,
-      final Account.Id adder) {
-    this(m, adder, now());
-  }
-
-  public AccountGroupMemberAudit(final AccountGroupMember m,
       final Account.Id adder, Timestamp addedOn) {
     final Account.Id who = m.getAccountId();
     final AccountGroup.Id group = m.getAccountGroupId();
@@ -99,17 +94,13 @@
     return removedOn == null;
   }
 
-  public void removed(final Account.Id deleter) {
+  public void removed(final Account.Id deleter, final Timestamp when) {
     removedBy = deleter;
-    removedOn = now();
+    removedOn = when;
   }
 
   public void removedLegacy() {
     removedBy = addedBy;
     removedOn = key.addedOn;
   }
-
-  private static Timestamp now() {
-    return new Timestamp(System.currentTimeMillis());
-  }
 }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
index 7dedf14..76a38b9 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
@@ -388,11 +388,11 @@
   protected Change() {
   }
 
-  public Change(final Change.Key newKey, final Change.Id newId,
-      final Account.Id ownedBy, final Branch.NameKey forBranch) {
+  public Change(Change.Key newKey, Change.Id newId, Account.Id ownedBy,
+      Branch.NameKey forBranch, Timestamp ts) {
     changeKey = newKey;
     changeId = newId;
-    createdOn = new Timestamp(System.currentTimeMillis());
+    createdOn = ts;
     lastUpdatedOn = createdOn;
     owner = ownedBy;
     dest = forBranch;
@@ -435,10 +435,6 @@
     return rowVersion;
   }
 
-  public void resetLastUpdatedOn() {
-    lastUpdatedOn = new Timestamp(System.currentTimeMillis());
-  }
-
   public String getSortKey() {
     return sortKey;
   }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ChangeMessage.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
index e05f5e7..b98104a 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
@@ -78,11 +78,6 @@
   }
 
   public ChangeMessage(final ChangeMessage.Key k, final Account.Id a,
-      final PatchSet.Id psid) {
-    this(k, a, new Timestamp(System.currentTimeMillis()), psid);
-  }
-
-  public ChangeMessage(final ChangeMessage.Key k, final Account.Id a,
       final Timestamp wo, final PatchSet.Id psid) {
     key = k;
     author = a;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
index 0119c3e..916219b 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
@@ -123,14 +123,14 @@
   protected PatchLineComment() {
   }
 
-  public PatchLineComment(final PatchLineComment.Key id, final int line,
-      final Account.Id a, String parentUuid) {
+  public PatchLineComment(PatchLineComment.Key id, int line, Account.Id a,
+      String parentUuid, Timestamp when) {
     key = id;
     lineNbr = line;
     author = a;
     this.parentUuid = parentUuid;
     setStatus(Status.DRAFT);
-    updated();
+    updated(when);
   }
 
   public PatchLineComment.Key getKey() {
@@ -177,8 +177,8 @@
     message = s;
   }
 
-  public void updated() {
-    writtenOn = new Timestamp(System.currentTimeMillis());
+  public void updated(Timestamp when) {
+    writtenOn = when;
   }
 
   public void setWrittenOn(Timestamp ts) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
index 8d79d66..2c0f1f4 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
@@ -140,11 +140,11 @@
   protected PatchSetApproval() {
   }
 
-  public PatchSetApproval(final PatchSetApproval.Key k, final short v) {
+  public PatchSetApproval(PatchSetApproval.Key k, short v, Timestamp ts) {
     key = k;
     changeOpen = true;
     setValue(v);
-    setGranted();
+    setGranted(ts);
   }
 
   public PatchSetApproval(final PatchSet.Id psId, final PatchSetApproval src) {
@@ -183,10 +183,6 @@
     return granted;
   }
 
-  public void setGranted() {
-    granted = new Timestamp(System.currentTimeMillis());
-  }
-
   public void setGranted(Timestamp ts) {
     granted = ts;
   }
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index b5799aa..1db7555 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -45,6 +45,7 @@
     '//lib/guice:guice-assistedinject',
     '//lib/guice:guice-servlet',
     '//lib/jgit:jgit',
+    '//lib/joda:joda-time',
     '//lib/log:api',
     '//lib/prolog:prolog-cafe',
   ],
@@ -136,6 +137,7 @@
     '//lib/guice:guice',
     '//lib/jgit:jgit',
     '//lib/jgit:junit',
+    '//lib/joda:joda-time',
     '//lib/prolog:prolog-cafe',
   ],
   source_under_test = [':server'],
diff --git a/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java b/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
index 173ced6..cdb24e7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/audit/AuditEvent.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.util.TimeUtil;
 
 public class AuditEvent {
 
@@ -94,7 +95,7 @@
     this.params = Objects.firstNonNull(params, EMPTY_PARAMS);
     this.uuid = new UUID();
     this.result = result;
-    this.elapsed = System.currentTimeMillis() - timeAtStart;
+    this.elapsed = TimeUtil.nowMs() - timeAtStart;
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 984b55f..96a7b7e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -608,7 +608,7 @@
 
     @Override
     public void postEvent(final Branch.NameKey branchName,
-        final ChangeEvent event) throws OrmException {
+        final ChangeEvent event) {
       fireEvent(branchName, event);
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
index 97c0bdf..3399272 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
@@ -188,8 +188,6 @@
    *
    * @param branchName The branch that the event is related to
    * @param event The event to post
-   * @throws OrmException
    */
-  public void postEvent(Branch.NameKey branchName, ChangeEvent event)
-      throws OrmException;
+  public void postEvent(Branch.NameKey branchName, ChangeEvent event);
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
index f9aac59..a0196fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -222,7 +223,7 @@
   /** Takes compiled prolog .class files, puts them into the jar file. */
   private void createJar(File archiveFile, List<String> toBeJared,
       File tempDir, ObjectId metaConfig, ObjectId rulesId) throws IOException {
-    long now = System.currentTimeMillis();
+    long now = TimeUtil.nowMs();
     File tmpjar = File.createTempFile(".rulec_", ".jar", archiveFile.getParentFile());
     try {
       Manifest mf = new Manifest();
@@ -315,4 +316,4 @@
     }
     dir.delete();
   }
-}
\ No newline at end of file
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
index da2f98f..deb6f30 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.reviewdb.client.PatchSetInfo;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.change.PatchSetInserter.ChangeKind;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -136,7 +137,7 @@
     for (Account.Id account : need) {
       PatchSetApproval psa = new PatchSetApproval(
           new PatchSetApproval.Key(ps.getId(), account, labelId),
-          (short) 0);
+          (short) 0, TimeUtil.nowTs());
       psa.cache(change);
       cells.add(psa);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 560bee3..24a4b16 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -15,8 +15,12 @@
 package com.google.gerrit.server;
 
 import static com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy.RECEIVE_COMMITS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.primitives.Ints;
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.reviewdb.client.Change;
@@ -46,6 +50,7 @@
 import com.google.gerrit.server.project.RefControl;
 import com.google.gerrit.server.util.IdGenerator;
 import com.google.gerrit.server.util.MagicBranch;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmConcurrencyException;
 import com.google.gwtorm.server.OrmException;
 
@@ -80,8 +85,14 @@
 import java.util.regex.Matcher;
 
 public class ChangeUtil {
+  /**
+   * Epoch for sort key calculations, Tue Sep 30 2008 17:00:00.
+   * <p>
+   * We overrun approximately 4,083 years later, so ~6092.
+   */
   @VisibleForTesting
-  public static final long SORT_KEY_EPOCH = 1222819200L; // Oct 1 2008 00:00
+  public static final long SORT_KEY_EPOCH_MINS =
+      MINUTES.convert(1222819200L, SECONDS);
 
   private static final Object uuidLock = new Object();
   private static final int SEED = 0x2418e6f9;
@@ -134,7 +145,7 @@
   }
 
   public static void updated(final Change c) {
-    c.resetLastUpdatedOn();
+    c.setLastUpdatedOn(TimeUtil.nowTs());
     computeSortKey(c);
   }
 
@@ -262,7 +273,8 @@
           new Change.Key("I" + computedChangeId.name()),
           new Change.Id(db.nextChangeId()),
           user.getAccountId(),
-          changeToRevert.getDest());
+          changeToRevert.getDest(),
+          TimeUtil.nowTs());
       change.setTopic(changeToRevert.getTopic());
       ChangeInserter ins =
           changeInserterFactory.create(refControl, change, revertCommit);
@@ -293,9 +305,9 @@
             change.getDest().getParentKey().get(), ru.getResult()));
       }
 
-      final ChangeMessage cmsg =
-          new ChangeMessage(new ChangeMessage.Key(changeId,
-              ChangeUtil.messageUUID(db)), user.getAccountId(), patchSetId);
+      final ChangeMessage cmsg = new ChangeMessage(
+          new ChangeMessage.Key(changeId, ChangeUtil.messageUUID(db)),
+          user.getAccountId(), TimeUtil.nowTs(), patchSetId);
       final StringBuilder msgBuf =
           new StringBuilder("Patch Set " + patchSetId.get() + ": Reverted");
       msgBuf.append("\n\n");
@@ -465,14 +477,12 @@
     db.patchSets().delete(Collections.singleton(patch));
   }
 
-  public static String sortKey(long lastUpdated, int id){
-    // The encoding uses minutes since Wed Oct 1 00:00:00 2008 UTC.
-    // We overrun approximately 4,085 years later, so ~6093.
-    //
-    final long lastUpdatedOn = (lastUpdated / 1000L) - SORT_KEY_EPOCH;
-    final StringBuilder r = new StringBuilder(16);
+  public static String sortKey(long lastUpdatedMs, int id){
+    long lastUpdatedMins = MINUTES.convert(lastUpdatedMs, MILLISECONDS);
+    long minsSinceEpoch = lastUpdatedMins - SORT_KEY_EPOCH_MINS;
+    StringBuilder r = new StringBuilder(16);
     r.setLength(16);
-    formatHexInt(r, 0, (int) (lastUpdatedOn / 60));
+    formatHexInt(r, 0, Ints.checkedCast(minsSinceEpoch));
     formatHexInt(r, 8, id);
     return r.toString();
   }
@@ -484,10 +494,10 @@
     return Long.parseLong(sortKey, 16);
   }
 
-  public static void computeSortKey(final Change c) {
-    long lastUpdated = c.getLastUpdatedOn().getTime();
+  public static void computeSortKey(Change c) {
+    long lastUpdatedMs = c.getLastUpdatedOn().getTime();
     int id = c.getId().get();
-    c.setSortKey(sortKey(lastUpdated, id));
+    c.setSortKey(sortKey(lastUpdatedMs, id));
   }
 
   public static PatchSet.Id nextPatchSetId(Map<String, Ref> allRefs,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
index b74daa5..0e10080 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
@@ -115,7 +116,7 @@
   }
 
   private static AccountState missing(Account.Id accountId) {
-    Account account = new Account(accountId);
+    Account account = new Account(accountId, TimeUtil.nowTs());
     Collection<AccountExternalId> ids = Collections.emptySet();
     Set<AccountGroup.UUID> anon = ImmutableSet.of(AccountGroup.ANONYMOUS_USERS);
     return new AccountState(account, anon, ids);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java
index cde59e1..dfc9546 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfo.java
@@ -38,6 +38,7 @@
         Collections.unmodifiableSet(EnumSet.of(
             FillOptions.NAME,
             FillOptions.EMAIL,
+            FillOptions.USERNAME,
             FillOptions.AVATARS));
 
     public interface Factory {
@@ -83,7 +84,7 @@
       try {
         directory.fillAccountInfo(
             Iterables.concat(created.values(), provided),
-            DETAILED_OPTIONS);
+            detailed ? DETAILED_OPTIONS : EnumSet.of(FillOptions.NAME));
       } catch (DirectoryException e) {
         Throwables.propagateIfPossible(e.getCause(), OrmException.class);
         throw new OrmException(e);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index a34a86c..f068812 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
@@ -258,7 +259,7 @@
     }
 
     final Account.Id newId = new Account.Id(db.nextAccountId());
-    final Account account = new Account(newId);
+    final Account account = new Account(newId, TimeUtil.nowTs());
     final AccountExternalId extId = createId(newId, who);
 
     extId.setEmailAddress(who.getEmailAddress());
@@ -293,8 +294,8 @@
       final AccountGroup.Id adminId = g.getId();
       final AccountGroupMember m =
           new AccountGroupMember(new AccountGroupMember.Key(newId, adminId));
-      db.accountGroupMembersAudit().insert(
-          Collections.singleton(new AccountGroupMemberAudit(m, newId)));
+      db.accountGroupMembersAudit().insert(Collections.singleton(
+          new AccountGroupMemberAudit(m, newId, TimeUtil.nowTs())));
       db.accountGroupMembers().insert(Collections.singleton(m));
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
index 4b61cec..340746e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.server.account.CreateAccount.Input;
 import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.ssh.SshKeyCache;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -149,7 +150,7 @@
       }
     }
 
-    Account a = new Account(id);
+    Account a = new Account(id, TimeUtil.nowTs());
     a.setFullName(input.name);
     a.setPreferredEmail(input.email);
     db.accounts().insert(Collections.singleton(a));
@@ -162,7 +163,8 @@
       AccountGroupMember m =
           new AccountGroupMember(new AccountGroupMember.Key(id, groupId));
       db.accountGroupMembersAudit().insert(Collections.singleton(
-          new AccountGroupMemberAudit(m, currentUser.getAccountId())));
+          new AccountGroupMemberAudit(
+              m, currentUser.getAccountId(), TimeUtil.nowTs())));
       db.accountGroupMembers().insert(Collections.singleton(m));
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
index 9dc0d5a..92e9b86 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -147,8 +148,8 @@
           new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId));
       memberships.add(membership);
 
-      final AccountGroupMemberAudit audit =
-          new AccountGroupMemberAudit(membership, currentUser.getAccountId());
+      final AccountGroupMemberAudit audit = new AccountGroupMemberAudit(
+          membership, currentUser.getAccountId(), TimeUtil.nowTs());
       membershipsAudit.add(audit);
     }
     db.accountGroupMembers().insert(memberships);
@@ -170,8 +171,8 @@
         new AccountGroupById(new AccountGroupById.Key(groupId, includeUUID));
       includeList.add(groupInclude);
 
-      final AccountGroupByIdAud audit =
-        new AccountGroupByIdAud(groupInclude, currentUser.getAccountId());
+      final AccountGroupByIdAud audit = new AccountGroupByIdAud(
+          groupInclude, currentUser.getAccountId(), TimeUtil.nowTs());
       includesAudit.add(audit);
     }
     db.accountGroupById().insert(includeList);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 9debd5d..c7f6d16 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -923,7 +923,7 @@
     Boolean reviewed;
     Boolean mergeable;
 
-    String _sortkey;
+    public String _sortkey;
     public int _number;
 
     AccountInfo owner;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index e129432..f206a3d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.RefControl;
 import com.google.gerrit.server.ssh.NoSshInfo;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -211,7 +212,7 @@
     Change change =
         new Change(changeKey, new Change.Id(db.nextChangeId()),
             currentUser.getAccountId(), new Branch.NameKey(project,
-                destRef.getName()));
+                destRef.getName()), TimeUtil.nowTs());
     ChangeInserter ins =
         changeInserterFactory.create(refControl, change, cherryPickCommit);
     PatchSet newPatchSet = ins.getPatchSet();
@@ -247,10 +248,10 @@
 
   private ChangeMessage buildChangeMessage(PatchSet.Id patchSetId, Change dest)
       throws OrmException {
-    ChangeMessage cmsg =
-        new ChangeMessage(new ChangeMessage.Key(patchSetId.getParentKey(),
-            ChangeUtil.messageUUID(db)), currentUser.getAccountId(),
-            patchSetId);
+    ChangeMessage cmsg = new ChangeMessage(
+        new ChangeMessage.Key(
+            patchSetId.getParentKey(), ChangeUtil.messageUUID(db)),
+        currentUser.getAccountId(), TimeUtil.nowTs(), patchSetId);
     StringBuilder msgBuf =
         new StringBuilder("Patch Set " + patchSetId.get()
             + ": Cherry Picked");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java
index fe372f3..f4c148a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java
@@ -10,7 +10,7 @@
 // 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.change;
+// limitations under the License.
 
 package com.google.gerrit.server.change;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
index 1b7bbe7..229e072 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.change.PutDraft.Input;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -62,7 +63,7 @@
         new PatchLineComment.Key(
             new Patch.Key(rsrc.getPatchSet().getId(), in.path),
             ChangeUtil.messageUUID(db.get())),
-        line, rsrc.getAccountId(), Url.decode(in.inReplyTo));
+        line, rsrc.getAccountId(), Url.decode(in.inReplyTo), TimeUtil.nowTs());
     c.setSide(in.side == Side.PARENT ? (short) 0 : (short) 1);
     c.setMessage(in.message.trim());
     c.setRange(in.range);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
index 294ff1a..6ecc544 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Index.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.change;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index c64db76..e4f4f1f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -18,7 +18,6 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.Sets;
-
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.reviewdb.client.Account;
@@ -46,6 +45,7 @@
 import com.google.gerrit.server.project.RefControl;
 import com.google.gerrit.server.ssh.NoSshInfo;
 import com.google.gerrit.server.ssh.SshInfo;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.AtomicUpdate;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -63,7 +63,6 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.sql.Timestamp;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -169,8 +168,9 @@
   }
 
   public PatchSetInserter setMessage(String message) throws OrmException {
-    changeMessage = new ChangeMessage(new ChangeMessage.Key(change.getId(),
-        ChangeUtil.messageUUID(db)), user.getAccountId(), patchSet.getId());
+    changeMessage = new ChangeMessage(
+        new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
+        user.getAccountId(), TimeUtil.nowTs(), patchSet.getId());
     changeMessage.setMessage(message);
     return this;
   }
@@ -332,7 +332,7 @@
     if (patchSet == null) {
       patchSet = new PatchSet(
           ChangeUtil.nextPatchSetId(git, change.currentPatchSetId()));
-      patchSet.setCreatedOn(new Timestamp(System.currentTimeMillis()));
+      patchSet.setCreatedOn(TimeUtil.nowTs());
       patchSet.setUploader(change.getOwner());
       patchSet.setRevision(new RevId(commit.name()));
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 3b1f1fb..367087c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -47,6 +47,7 @@
 import com.google.gerrit.server.change.PostReview.Input;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -361,7 +362,7 @@
                   ChangeUtil.messageUUID(db.get())),
               c.line,
               rsrc.getAccountId(),
-              parent);
+              parent, TimeUtil.nowTs());
         } else if (parent != null) {
           e.setParentUuid(parent);
         }
@@ -452,7 +453,7 @@
                 rsrc.getPatchSet().getId(),
                 rsrc.getAccountId(),
                 lt.getLabelId()),
-            ent.getValue());
+            ent.getValue(), TimeUtil.nowTs());
         c.setGranted(timestamp);
         c.cache(change);
         ins.add(c);
@@ -481,7 +482,7 @@
             rsrc.getAccountId(),
             rsrc.getControl().getLabelTypes().getLabelTypes().get(0)
                 .getLabelId()),
-            (short) 0);
+            (short) 0, TimeUtil.nowTs());
         c.setGranted(timestamp);
         c.cache(change);
         ins.add(c);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index b066ab7..13deeb0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -53,6 +53,7 @@
 import com.google.gerrit.server.mail.AddReviewerSender;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -314,7 +315,8 @@
     LabelId id =
         Iterables.getLast(ctl.getLabelTypes().getLabelTypes()).getLabelId();
     PatchSetApproval dummyApproval = new PatchSetApproval(
-        new PatchSetApproval.Key(patchSetId, reviewerId, id), (short) 0);
+        new PatchSetApproval.Key(patchSetId, reviewerId, id), (short) 0,
+        TimeUtil.nowTs());
     dummyApproval.cache(ctl.getChange());
     return dummyApproval;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
index 005d493..803af17 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.change.PutDraft.Input;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -84,7 +85,7 @@
               c.getKey().get()),
           c.getLine(),
           rsrc.getAuthorId(),
-          c.getParentUuid());
+          c.getParentUuid(), TimeUtil.nowTs());
       db.get().patchComments().insert(Collections.singleton(update(c, in)));
     } else {
       db.get().patchComments().update(Collections.singleton(update(c, in)));
@@ -104,7 +105,7 @@
       e.setRange(in.range);
       e.setLine(in.range != null ? in.range.getEndLine() : in.line);
     }
-    e.updated();
+    e.updated(TimeUtil.nowTs());
     return e;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
index f8255b8..acf96e6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutTopic.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.server.change.PutTopic.Input;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.AtomicUpdate;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -91,7 +92,7 @@
       IdentifiedUser currentUser = ((IdentifiedUser) control.getCurrentUser());
       ChangeMessage cmsg = new ChangeMessage(
           new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
-          currentUser.getAccountId(),
+          currentUser.getAccountId(), TimeUtil.nowTs(),
           change.currentPatchSetId());
       StringBuilder msgBuf = new StringBuilder(summary);
       if (!Strings.isNullOrEmpty(input.message)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index f446bc5..a507096 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.server.git.MergeQueue;
 import com.google.gerrit.server.index.ChangeIndexer;
 import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.AtomicUpdate;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -178,7 +179,7 @@
 
   public Change submit(RevisionResource rsrc, IdentifiedUser caller)
       throws OrmException, IOException {
-    final Timestamp timestamp = new Timestamp(System.currentTimeMillis());
+    final Timestamp timestamp = TimeUtil.nowTs();
     Change change = rsrc.getChange();
     ReviewDb db = dbProvider.get();
     db.changes().beginTransaction(change.getId());
@@ -226,7 +227,7 @@
               rev.getId(),
               caller.getAccountId(),
               LabelId.SUBMIT),
-          (short) 1);
+          (short) 1, TimeUtil.nowTs());
     }
     submit.setValue((short) 1);
     submit.setGranted(timestamp);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
index dceecbe..b766cea 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 
@@ -302,9 +303,9 @@
         .setRunHooks(runHooks);
 
     final PatchSet.Id newPatchSetId = patchSetInserter.getPatchSetId();
-    final ChangeMessage cmsg =
-        new ChangeMessage(new ChangeMessage.Key(change.getId(),
-            ChangeUtil.messageUUID(db)), uploader.getAccountId(), patchSetId);
+    final ChangeMessage cmsg = new ChangeMessage(
+        new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
+        uploader.getAccountId(), TimeUtil.nowTs(), patchSetId);
 
     cmsg.setMessage("Patch Set " + newPatchSetId.get()
         + ": Patch Set " + patchSetId.get() + " was rebased");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java
index f9e9949..ca4a9d2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.config;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
index bd1c0d7..17c9060 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.reviewdb.client.ContactInformation;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.UrlEncoded;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.ProvisionException;
@@ -213,7 +214,7 @@
       throws ContactInformationStoreException {
     Timestamp on = account.getContactFiledOn();
     if (on == null) {
-      on = new Timestamp(System.currentTimeMillis());
+      on = TimeUtil.nowTs();
     }
 
     final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
index 04fccc2..9e48e81 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.server.extensions.events;
 
-import com.google.common.collect.ImmutableList;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.reviewdb.client.Project;
@@ -26,7 +25,6 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Collections;
-import java.util.List;
 
 public class GitReferenceUpdated {
   private static final Logger log = LoggerFactory
@@ -85,25 +83,18 @@
     }
 
     @Override
-    public List<GitReferenceUpdatedListener.Update> getUpdates() {
-      GitReferenceUpdatedListener.Update update =
-          new GitReferenceUpdatedListener.Update() {
-            @Override
-            public String getRefName() {
-              return ref;
-            }
+    public String getRefName() {
+      return ref;
+    }
 
-            @Override
-            public String getOldObjectId() {
-              return oldObjectId;
-            }
+    @Override
+    public String getOldObjectId() {
+      return oldObjectId;
+    }
 
-            @Override
-            public String getNewObjectId() {
-              return newObjectId;
-            }
-          };
-      return ImmutableList.of(update);
+    @Override
+    public String getNewObjectId() {
+      return newObjectId;
     }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java
index 3262b23..32dc303 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeCache.java
@@ -72,11 +72,8 @@
 
   @Override
   public void onGitReferenceUpdated(GitReferenceUpdatedListener.Event event) {
-    for (GitReferenceUpdatedListener.Update u : event.getUpdates()) {
-      if (u.getRefName().startsWith("refs/changes/")) {
-        cache.invalidate(new Project.NameKey(event.getProjectName()));
-        break;
-      }
+    if (event.getRefName().startsWith("refs/changes/")) {
+      cache.invalidate(new Project.NameKey(event.getProjectName()));
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
index 12219e2..a6b0a12 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.server.ssh.SshInfo;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.RequestScopePropagator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -155,7 +156,7 @@
   @Override
   public synchronized void recheckAfter(final Branch.NameKey branch,
       final long delay, final TimeUnit delayUnit) {
-    final long now = System.currentTimeMillis();
+    final long now = TimeUtil.nowMs();
     final long at = now + MILLISECONDS.convert(delay, delayUnit);
     RecheckJob e = recheck.get(branch);
     if (e == null) {
@@ -216,7 +217,7 @@
   }
 
   private synchronized void recheck(final RecheckJob e) {
-    final long remainingDelay = e.recheckAt - System.currentTimeMillis();
+    final long remainingDelay = e.recheckAt - TimeUtil.nowMs();
     if (MILLISECONDS.convert(10, SECONDS) < remainingDelay) {
       // Woke up too early, the job deadline was pushed back.
       // Reschedule for the new deadline. We allow for a small
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CherryPick.java
index f4ed7cd..ce7a708 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CherryPick.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 
 import org.eclipse.jgit.lib.ObjectId;
@@ -32,7 +33,6 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 
 import java.io.IOException;
-import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -159,7 +159,7 @@
     PatchSet.Id id =
         ChangeUtil.nextPatchSetId(args.repo, n.change.currentPatchSetId());
     final PatchSet ps = new PatchSet(id);
-    ps.setCreatedOn(new Timestamp(System.currentTimeMillis()));
+    ps.setCreatedOn(TimeUtil.nowTs());
     ps.setUploader(submitAudit.getAccountId());
     ps.setRevision(new RevId(newCommit.getId().getName()));
     insertAncestors(args.db, ps.getId(), newCommit);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index f20de2d..aa334f3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -15,10 +15,12 @@
 package com.google.gerrit.server.git;
 
 import static com.google.gerrit.server.git.MergeUtil.getSubmitter;
+
 import static java.util.concurrent.TimeUnit.HOURS;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.MINUTES;
 import static java.util.concurrent.TimeUnit.SECONDS;
+
 import static org.eclipse.jgit.lib.RefDatabase.ALL;
 
 import com.google.common.base.Objects;
@@ -59,6 +61,7 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.util.RequestScopePropagator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.AtomicUpdate;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
@@ -705,7 +708,7 @@
     final Capable capable;
     final Change c = commit.change;
     final boolean submitStillPossible = isSubmitForMissingCommitsStillPossible(commit);
-    final long now = System.currentTimeMillis();
+    final long now = TimeUtil.nowMs();
     final long waitUntil = c.getLastUpdatedOn().getTime() + DEPENDENCY_DELAY;
     if (submitStillPossible && now < waitUntil) {
       // If we waited a short while we might still be able to get
@@ -791,9 +794,8 @@
     } catch (OrmException e) {
       return null;
     }
-    final ChangeMessage m =
-        new ChangeMessage(new ChangeMessage.Key(c.getId(), uuid), null,
-            c.currentPatchSetId());
+    ChangeMessage m = new ChangeMessage(new ChangeMessage.Key(c.getId(), uuid),
+        null, TimeUtil.nowTs(), c.currentPatchSetId());
     m.setMessage(body);
     return m;
   }
@@ -956,7 +958,7 @@
       @Nullable PatchSetApproval submitter,
       ChangeMessage msg) {
     if (submitter != null
-        && System.currentTimeMillis() - submitter.getGranted().getTime()
+        && TimeUtil.nowMs() - submitter.getGranted().getTime()
           > MAX_SUBMIT_WINDOW) {
       return RetryStatus.UNSUBMIT;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 594b4e4..06e88e4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
 import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromApprovals;
 import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
+
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
 import static org.eclipse.jgit.lib.RefDatabase.ALL;
 import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
@@ -94,6 +95,7 @@
 import com.google.gerrit.server.ssh.SshInfo;
 import com.google.gerrit.server.util.MagicBranch;
 import com.google.gerrit.server.util.RequestScopePropagator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.util.cli.CmdLineParser;
 import com.google.gwtorm.server.AtomicUpdate;
 import com.google.gwtorm.server.OrmException;
@@ -124,9 +126,9 @@
 import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
 import org.eclipse.jgit.transport.BaseReceivePack;
 import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ServiceMayNotContinueException;
 import org.eclipse.jgit.transport.ReceiveCommand.Result;
 import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
 import org.eclipse.jgit.transport.UploadPack;
 import org.kohsuke.args4j.CmdLineException;
 import org.kohsuke.args4j.Option;
@@ -135,7 +137,6 @@
 
 import java.io.IOException;
 import java.io.StringWriter;
-import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -1427,7 +1428,8 @@
       change = new Change(changeKey,
           new Change.Id(db.nextChangeId()),
           currentUser.getAccountId(),
-          magicBranch.dest);
+          magicBranch.dest,
+          TimeUtil.nowTs());
       change.setTopic(magicBranch.topic);
       ins = changeInserterFactory.create(ctl, change, c)
           .setDraft(magicBranch.isDraft());
@@ -1740,7 +1742,7 @@
       PatchSet.Id id =
           ChangeUtil.nextPatchSetId(allRefs, change.currentPatchSetId());
       newPatchSet = new PatchSet(id);
-      newPatchSet.setCreatedOn(new Timestamp(System.currentTimeMillis()));
+      newPatchSet.setCreatedOn(TimeUtil.nowTs());
       newPatchSet.setUploader(currentUser.getAccountId());
       newPatchSet.setRevision(toRevId(newCommit));
       if (magicBranch != null && magicBranch.isDraft()) {
@@ -2235,9 +2237,9 @@
       }
     }
     msgBuf.append(".");
-    final ChangeMessage msg =
-        new ChangeMessage(new ChangeMessage.Key(change.getId(), ChangeUtil
-            .messageUUID(db)), currentUser.getAccountId(), result.info.getKey());
+    final ChangeMessage msg = new ChangeMessage(
+        new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
+        currentUser.getAccountId(), TimeUtil.nowTs(), result.info.getKey());
     msg.setMessage(msgBuf.toString());
 
     db.changeMessages().insert(Collections.singleton(msg));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
index 9d0dbf8..195ea1c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddIncludedGroups.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.account.GroupIncludeCache;
 import com.google.gerrit.server.group.AddIncludedGroups.Input;
 import com.google.gerrit.server.group.GroupJson.GroupInfo;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -115,7 +116,8 @@
         if (agi == null) {
           agi = new AccountGroupById(agiKey);
           newIncludedGroups.put(d.getGroupUUID(), agi);
-          newIncludedGroupsAudits.add(new AccountGroupByIdAud(agi, me));
+          newIncludedGroupsAudits.add(
+              new AccountGroupByIdAud(agi, me, TimeUtil.nowTs()));
         }
       }
       result.add(json.format(d));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
index b3002da41..5ebf504 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/AddMembers.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.group.AddMembers.Input;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -132,7 +133,8 @@
         if (m == null) {
           m = new AccountGroupMember(key);
           newAccountGroupMembers.put(m.getAccountId(), m);
-          newAccountGroupMemberAudits.add(new AccountGroupMemberAudit(m, me));
+          newAccountGroupMemberAudits.add(
+              new AccountGroupMemberAudit(m, me, TimeUtil.nowTs()));
         }
       }
       result.add(loader.get(a.getId()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
index 6c98c8f..c5d95b0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteIncludedGroups.java
@@ -34,6 +34,7 @@
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.account.GroupIncludeCache;
 import com.google.gerrit.server.group.AddIncludedGroups.Input;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -122,7 +123,7 @@
       }
 
       if (audit != null) {
-        audit.removed(me);
+        audit.removed(me, TimeUtil.nowTs());
         auditUpdates.add(audit);
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
index 07276ca..186d4b6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/DeleteMembers.java
@@ -33,6 +33,7 @@
 import com.google.gerrit.server.account.AccountsCollection;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.AddMembers.Input;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -107,10 +108,10 @@
       }
 
       if (audit != null) {
-        audit.removed(me);
+        audit.removed(me, TimeUtil.nowTs());
         auditUpdates.add(audit);
       } else {
-        audit = new AccountGroupMemberAudit(m, me);
+        audit = new AccountGroupMemberAudit(m, me, TimeUtil.nowTs());
         audit.removedLegacy();
         auditInserts.add(audit);
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
index 63befe8..7865d5e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
index ef2fb46..0654e80 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
index 51a6578..0c90e32 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
index 4d8c951..414715d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
@@ -147,9 +147,8 @@
       return sqlRewriter.rewrite(in);
     }
     in = basicRewrites.rewrite(in);
-    // Add 1 to specified limit to match behavior of QueryProcessor.
     int limit = ChangeQueryBuilder.hasLimit(in)
-        ? ChangeQueryBuilder.getLimit(in) + 1
+        ? ChangeQueryBuilder.getLimit(in)
         : MAX_LIMIT;
 
     Predicate<ChangeData> out = rewriteImpl(in, index, limit);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/Schema.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/Schema.java
index e1262cd..bda2ace 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/Schema.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/Schema.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
index 9f9a3a2..89fc6b9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -151,7 +152,7 @@
       setMissingHeader(hdrs, "Importance", importance);
     }
     if(expiryDays > 0) {
-      Date expiry = new Date(System.currentTimeMillis() +
+      Date expiry = new Date(TimeUtil.nowMs() +
         expiryDays * 24 * 60 * 60 * 1000L );
       setMissingHeader(hdrs, "Expiry-Date",
         new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(expiry));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java
index 7081d70..606d5da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.plugins;
 
 import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -52,7 +53,7 @@
       self = null;
 
       if (0 < left) {
-        long waiting = System.currentTimeMillis() - start;
+        long waiting = TimeUtil.nowMs() - start;
         PluginLoader.log.warn(String.format(
             "%d plugins still waiting to be reclaimed after %d minutes",
             pending,
@@ -76,7 +77,7 @@
 
   synchronized void clean(int expect) {
     if (self == null && pending == 0) {
-      start = System.currentTimeMillis();
+      start = TimeUtil.nowMs();
     }
     pending = expect;
     ensureScheduled();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
index 7baaa43..9b9b517 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
@@ -23,11 +23,10 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
 import com.google.gerrit.reviewdb.client.Project.SubmitType;
-import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.actions.ActionInfo;
 import com.google.gerrit.server.extensions.webui.UiActions;
 import com.google.gerrit.server.git.TransferConfig;
-import com.google.inject.Provider;
+import com.google.inject.util.Providers;
 
 import java.util.Map;
 
@@ -48,10 +47,9 @@
   public ThemeInfo theme;
 
   public ConfigInfo(ProjectControl control,
-      ProjectState projectState,
       TransferConfig config,
-      DynamicMap<RestView<ProjectResource>> views,
-      Provider<CurrentUser> currentUser) {
+      DynamicMap<RestView<ProjectResource>> views) {
+    ProjectState projectState = control.getProjectState();
     Project p = control.getProject();
     this.description = Strings.emptyToNull(p.getDescription());
 
@@ -108,7 +106,7 @@
     actions = Maps.newTreeMap();
     for (UiAction.Description d : UiActions.from(
         views, new ProjectResource(control),
-        currentUser)) {
+        Providers.of(control.getCurrentUser()))) {
       actions.put(d.getId(), new ActionInfo(d));
     }
     this.theme = projectState.getTheme();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
index 9c08ea2..6f78651 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
@@ -26,7 +26,6 @@
 
   private final TransferConfig config;
   private final DynamicMap<RestView<ProjectResource>> views;
-  private final Provider<CurrentUser> currentUser;
 
   @Inject
   public GetConfig(TransferConfig config,
@@ -34,15 +33,10 @@
       Provider<CurrentUser> currentUser) {
     this.config = config;
     this.views = views;
-    this.currentUser = currentUser;
   }
 
   @Override
   public ConfigInfo apply(ProjectResource resource) {
-    return new ConfigInfo(resource.getControl(),
-        resource.getControl().getProjectState(),
-        config,
-        views,
-        currentUser);
+    return new ConfigInfo(resource.getControl(), config, views);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index 1918a03..cb25af3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -139,9 +139,11 @@
           throw new ResourceConflictException("Cannot update " + projectName);
         }
       }
-      return new ConfigInfo(rsrc.getControl(),
-          projectStateFactory.create(projectConfig),
-          config, views, currentUser);
+
+      ProjectState state = projectStateFactory.create(projectConfig);
+      return new ConfigInfo(
+          state.controlFor(currentUser.get()),
+          config, views);
     } catch (ConfigInvalidException err) {
       throw new ResourceConflictException("Cannot read project " + projectName, err);
     } catch (IOException err) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index e292a76..849d6f2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -308,7 +308,9 @@
       case REST_API:
       case JSON_RPC:
       case SSH_COMMAND:
-        return isOwner() || canPushWithForce();
+        return getCurrentUser().getCapabilities().canAdministrateServer()
+            || (isOwner() && !isForceBlocked(Permission.PUSH))
+            || canPushWithForce();
 
       case GIT:
         return canPushWithForce();
@@ -496,6 +498,22 @@
     return blocks.isEmpty() && !allows.isEmpty();
   }
 
+  /** True if for this permission force is blocked for the user. Works only for non labels. */
+  private boolean isForceBlocked(String permissionName) {
+    List<PermissionRule> access = access(permissionName);
+    Set<ProjectRef> allows = Sets.newHashSet();
+    Set<ProjectRef> blocks = Sets.newHashSet();
+    for (PermissionRule rule : access) {
+      if (rule.isBlock()) {
+        blocks.add(relevant.getRuleProps(rule));
+      } else if (rule.getForce()) {
+        allows.add(relevant.getRuleProps(rule));
+      }
+    }
+    blocks.removeAll(allows);
+    return !blocks.isEmpty();
+  }
+
   /** Rules for the given permission, or the empty list. */
   private List<PermissionRule> access(String permissionName) {
     List<PermissionRule> rules = effective.get(permissionName);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java
index 5f457c3..9a867d4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AgePredicate.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.index.ChangeField;
 import com.google.gerrit.server.index.TimestampRangePredicate;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 
@@ -37,7 +38,7 @@
 
     long s = ConfigUtil.getTimeUnit(getValue(), 0, SECONDS);
     long ms = MILLISECONDS.convert(s, SECONDS);
-    this.cut = System.currentTimeMillis() - ms;
+    this.cut = TimeUtil.nowMs() - ms;
   }
 
   public Timestamp getMinTimestamp() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 24186b8..44c71a9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -101,6 +101,10 @@
     queries.add(query);
   }
 
+  public String getQuery(int i) {
+    return queries.get(i);
+  }
+
   @Override
   public Object apply(TopLevelResource rsrc)
       throws BadRequestException, AuthException, OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index bdce7f4..5624f45 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.Predicate;
 import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gson.Gson;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.ResultSet;
@@ -292,7 +293,7 @@
 
       try {
         final QueryStatsAttribute stats = new QueryStatsAttribute();
-        stats.runTimeMilliseconds = System.currentTimeMillis();
+        stats.runTimeMilliseconds = TimeUtil.nowMs();
 
         List<ChangeData> results = queryChanges(queryString);
         ChangeAttribute c = null;
@@ -362,7 +363,7 @@
           stats.resumeSortKey = c.sortKey;
         }
         stats.runTimeMilliseconds =
-            System.currentTimeMillis() - stats.runTimeMilliseconds;
+            TimeUtil.nowMs() - stats.runTimeMilliseconds;
         show(stats);
       } catch (OrmException err) {
         log.error("Cannot execute query: " + queryString, err);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_65.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_65.java
index 1cdf25c..624a51b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_65.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_65.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.git.VersionedMetaData.BatchMetaDataUpdate;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.jdbc.JdbcSchema;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -348,7 +349,7 @@
           "       reviewed_on, review_comments " +
           "FROM account_agreements WHERE status = 'V'");
       try {
-        long minTime = System.currentTimeMillis();
+        long minTime = TimeUtil.nowMs();
         while (rs.next()) {
           Account.Id accountId = new Account.Id(rs.getInt(1));
           Account.Id reviewerId = new Account.Id(rs.getInt(4));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/TimeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/TimeUtil.java
new file mode 100644
index 0000000..4c4b96e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/TimeUtil.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.util;
+
+import org.joda.time.DateTimeUtils;
+
+import java.sql.Timestamp;
+
+/** Static utility methods for dealing with dates and times. */
+public class TimeUtil {
+  public static long nowMs() {
+    return DateTimeUtils.currentTimeMillis();
+  }
+
+  public static Timestamp nowTs() {
+    return new Timestamp(nowMs());
+  }
+
+  private TimeUtil() {
+  }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
index 4cd05ea..0d0ab1a 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
@@ -22,6 +22,7 @@
 
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.project.Util;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -79,7 +80,8 @@
     Change change =
         new Change(new Change.Key("Ibeef"), new Change.Id(1),
             new Account.Id(2),
-            new Branch.NameKey(localKey, "refs/heads/master"));
+            new Branch.NameKey(localKey, "refs/heads/master"),
+            TimeUtil.nowTs());
     env.set(StoredValues.CHANGE, change);
     env.set(StoredValues.CHANGE_CONTROL, util.user(local).controlFor(change));
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
index 4d86596a..df39003 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.rules;
 
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.inject.Guice;
 import com.google.inject.Module;
 
@@ -116,7 +117,7 @@
 
   public void runPrologBasedTests() {
     int errors = 0;
-    long start = System.currentTimeMillis();
+    long start = TimeUtil.nowMs();
 
     for (Term test : tests) {
       PrologEnvironment env = envFactory.create(machine);
@@ -159,7 +160,7 @@
       }
     }
 
-    long end = System.currentTimeMillis();
+    long end = TimeUtil.nowMs();
     System.out.println("-------------------------------");
     System.out.format("Prolog tests: %d, Failures: %d, Time elapsed %.3f sec",
         tests.size(), errors, (end - start) / 1000.0);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
index f1d986d..18b3c98 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -30,14 +30,15 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.CommentRange;
 import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.AccountInfo;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.ListResultSet;
 import com.google.gwtorm.server.ResultSet;
 import com.google.inject.AbstractModule;
@@ -109,7 +110,7 @@
     PatchSet ps2 = new PatchSet(psId2);
     expect(revRes2.getPatchSet()).andReturn(ps2);
 
-    long timeBase = System.currentTimeMillis();
+    long timeBase = TimeUtil.nowMs();
     plc1 = newPatchLineComment(psId1, "Comment1", null,
         "FileOne.txt", Side.REVISION, 1, account1, timeBase,
         "First Comment", new CommentRange(1, 2, 3, 4));
@@ -216,7 +217,7 @@
     Patch.Key p = new Patch.Key(psId, filename);
     PatchLineComment.Key id = new PatchLineComment.Key(p, uuid);
     PatchLineComment plc =
-        new PatchLineComment(id, line, authorId, inReplyToUuid);
+        new PatchLineComment(id, line, authorId, inReplyToUuid, TimeUtil.nowTs());
     plc.setMessage(message);
     plc.setRange(range);
     plc.setSide(side == Side.PARENT ? (short) 0 : (short) 1);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java
index 322ffe2..e9a4cf3 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/SubmoduleOpTest.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.SubmoduleSubscriptionAccess;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.client.KeyUtil;
 import com.google.gwtorm.server.ListResultSet;
 import com.google.gwtorm.server.ResultSet;
@@ -608,9 +609,9 @@
 
     final CodeReviewCommit codeReviewCommit =
         new CodeReviewCommit(sourceMergeTip.toObjectId());
-    final Change submittedChange =
-        new Change(new Change.Key(sourceMergeTip.toObjectId().getName()),
-            new Change.Id(1), new Account.Id(1), sourceBranchNameKey);
+    final Change submittedChange = new Change(
+        new Change.Key(sourceMergeTip.toObjectId().getName()), new Change.Id(1),
+        new Account.Id(1), sourceBranchNameKey, TimeUtil.nowTs());
     codeReviewCommit.change = submittedChange;
 
     final Map<Change.Id, CodeReviewCommit> mergedCommits =
@@ -712,9 +713,9 @@
 
     final CodeReviewCommit codeReviewCommit =
         new CodeReviewCommit(sourceMergeTip.toObjectId());
-    final Change submittedChange =
-        new Change(new Change.Key(sourceMergeTip.toObjectId().getName()),
-            new Change.Id(1), new Account.Id(1), sourceBranchNameKey);
+    final Change submittedChange = new Change(
+        new Change.Key(sourceMergeTip.toObjectId().getName()), new Change.Id(1),
+        new Account.Id(1), sourceBranchNameKey, TimeUtil.nowTs());
     codeReviewCommit.change = submittedChange;
 
     final Map<Change.Id, CodeReviewCommit> mergedCommits =
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
index c92c9a6..d8a0f65 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
index f9ef2dd..63e62a0 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
index f9bb4f3..d9016e9 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
@@ -152,7 +152,7 @@
     Predicate<ChangeData> out = rewrite(in);
     assertSame(AndSource.class, out.getClass());
     assertEquals(ImmutableList.of(
-          query(in.getChild(0), 4),
+          query(in.getChild(0), 3),
           in.getChild(1)),
         out.getChildren());
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
index ba1f53d..81518f5 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexedChangeQueryTest.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.index;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
index 2db2b67..721059c 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.util.TimeUtil;
 
 import junit.framework.TestCase;
 
@@ -276,7 +277,7 @@
 
   private AccountState makeUser(final String name, final String email) {
     final Account.Id userId = new Account.Id(42);
-    final Account account = new Account(userId);
+    final Account account = new Account(userId, TimeUtil.nowTs());
     account.setFullName(name);
     account.setPreferredEmail(email);
     final AccountState s =
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java
index 22f51cc..5dacd49 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractIndexQueryChangesTest.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.query.change;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index f504760..1e8b87a 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -10,16 +10,20 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.query.change;
 
+import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.google.common.base.Charsets;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -45,6 +49,7 @@
 import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.testutil.InMemoryDatabase;
 import com.google.gerrit.testutil.InMemoryRepositoryManager;
 import com.google.inject.Inject;
@@ -55,13 +60,15 @@
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.joda.time.DateTimeUtils;
+import org.joda.time.DateTimeUtils.MillisProvider;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
-import java.sql.Timestamp;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
 
 @Ignore
 public abstract class AbstractQueryChangesTest {
@@ -84,8 +91,7 @@
   protected ReviewDb db;
   protected Account.Id userId;
   protected CurrentUser user;
-  protected int clockStepMs = 1;
-  private long ts = ChangeUtil.SORT_KEY_EPOCH * 1000;
+  protected volatile long clockStepMs;
 
   protected abstract Injector createInjector();
 
@@ -118,12 +124,36 @@
 
   @After
   public void tearDownInjector() {
-    lifecycle.stop();
+    if (lifecycle != null) {
+      lifecycle.stop();
+    }
     requestContext.setContext(null);
-    db.close();
+    if (db != null) {
+      db.close();
+    }
     InMemoryDatabase.drop(schemaFactory);
   }
 
+  @Before
+  public void setMillisProvider() {
+    clockStepMs = 1;
+    final AtomicLong clockMs = new AtomicLong(
+        MILLISECONDS.convert(ChangeUtil.SORT_KEY_EPOCH_MINS, MINUTES)
+        + MILLISECONDS.convert(60, DAYS));
+
+    DateTimeUtils.setCurrentMillisProvider(new MillisProvider() {
+      @Override
+      public long getMillis() {
+        return clockMs.getAndAdd(clockStepMs);
+      }
+    });
+  }
+
+  @After
+  public void resetMillisProvider() {
+    DateTimeUtils.setCurrentMillisSystem();
+  }
+
   @Test
   public void byId() throws Exception {
     TestRepository<InMemoryRepository> repo = createProject("repo");
@@ -371,16 +401,171 @@
   @Test
   public void limit() throws Exception {
     TestRepository<InMemoryRepository> repo = createProject("repo");
-    Change change1 = newChange(repo, null, null, null, null).insert();
+    Change last = null;
+    int n = 5;
+    for (int i = 0; i < n; i++) {
+      last = newChange(repo, null, null, null, null).insert();
+    }
+
+    List<ChangeInfo> results;
+    for (int i = 1; i <= n + 2; i++) {
+      results = query("status:new limit:" + i);
+      assertEquals(Math.min(i, n), results.size());
+      assertResultEquals(last, results.get(0));
+    }
+  }
+
+  @Test
+  public void pagination() throws Exception {
+    TestRepository<InMemoryRepository> repo = createProject("repo");
+    List<Change> changes = Lists.newArrayList();
+    for (int i = 0; i < 5; i++) {
+      changes.add(newChange(repo, null, null, null, null).insert());
+    }
+
+    // Page forward and back through 3 pages of results.
+    QueryChanges q;
+    List<ChangeInfo> results;
+    results = query("status:new limit:2");
+    assertEquals(2, results.size());
+    assertResultEquals(changes.get(4), results.get(0));
+    assertResultEquals(changes.get(3), results.get(1));
+
+    q = newQuery("status:new limit:2");
+    q.setSortKeyBefore(results.get(1)._sortkey);
+    results = query(q);
+    assertEquals(2, results.size());
+    assertResultEquals(changes.get(2), results.get(0));
+    assertResultEquals(changes.get(1), results.get(1));
+
+    q = newQuery("status:new limit:2");
+    q.setSortKeyBefore(results.get(1)._sortkey);
+    results = query(q);
+    assertEquals(1, results.size());
+    assertResultEquals(changes.get(0), results.get(0));
+
+    q = newQuery("status:new limit:2");
+    q.setSortKeyAfter(results.get(0)._sortkey);
+    results = query(q);
+    assertEquals(2, results.size());
+    assertResultEquals(changes.get(2), results.get(0));
+    assertResultEquals(changes.get(1), results.get(1));
+
+    q = newQuery("status:new limit:2");
+    q.setSortKeyAfter(results.get(0)._sortkey);
+    results = query(q);
+    assertEquals(2, results.size());
+    assertResultEquals(changes.get(4), results.get(0));
+    assertResultEquals(changes.get(3), results.get(1));
+  }
+
+  @Test
+  public void sortKeyWithMinuteResolution() throws Exception {
+    clockStepMs = MILLISECONDS.convert(2, MINUTES);
+    TestRepository<InMemoryRepository> repo = createProject("repo");
+    ChangeInserter ins1 = newChange(repo, null, null, null, null);
+    Change change1 = ins1.insert();
+    ChangeControl ctl1 = changeControlFactory.controlFor(change1, user);
     Change change2 = newChange(repo, null, null, null, null).insert();
 
-    // TODO(dborowitz): Limit is broken in SQL (returns N+1 results) with some
-    // kinds of predicates but not others.
-    //assertResultEquals(change2, queryOne("status:new limit:1"));
-    List<ChangeInfo> results = query("status:new limit:2");
+    assertTrue(lastUpdatedMs(change1) < lastUpdatedMs(change2));
+
+    List<ChangeInfo> results;
+    results = query("status:new");
     assertEquals(2, results.size());
     assertResultEquals(change2, results.get(0));
     assertResultEquals(change1, results.get(1));
+
+    PostReview.Input input = new PostReview.Input();
+    input.message = "toplevel";
+    postReview.apply(new RevisionResource(
+        new ChangeResource(ctl1), ins1.getPatchSet()), input);
+    change1 = db.changes().get(change1.getId());
+
+    assertTrue(lastUpdatedMs(change1) > lastUpdatedMs(change2));
+    assertTrue(lastUpdatedMs(change1) - lastUpdatedMs(change2)
+        > MILLISECONDS.convert(1, MINUTES));
+
+    results = query("status:new");
+    assertEquals(2, results.size());
+    // change1 moved to the top.
+    assertResultEquals(change1, results.get(0));
+    assertResultEquals(change2, results.get(1));
+  }
+
+  @Test
+  public void sortKeyWithSubMinuteResolution() throws Exception {
+    TestRepository<InMemoryRepository> repo = createProject("repo");
+    ChangeInserter ins1 = newChange(repo, null, null, null, null);
+    Change change1 = ins1.insert();
+    ChangeControl ctl1 = changeControlFactory.controlFor(change1, user);
+    Change change2 = newChange(repo, null, null, null, null).insert();
+
+    assertTrue(lastUpdatedMs(change1) < lastUpdatedMs(change2));
+
+    List<ChangeInfo> results;
+    results = query("status:new");
+    assertEquals(2, results.size());
+    assertResultEquals(change2, results.get(0));
+    assertResultEquals(change1, results.get(1));
+
+    PostReview.Input input = new PostReview.Input();
+    input.message = "toplevel";
+    postReview.apply(new RevisionResource(
+        new ChangeResource(ctl1), ins1.getPatchSet()), input);
+    change1 = db.changes().get(change1.getId());
+
+    assertTrue(lastUpdatedMs(change1) > lastUpdatedMs(change2));
+    assertTrue(lastUpdatedMs(change1) - lastUpdatedMs(change2)
+        < MILLISECONDS.convert(1, MINUTES));
+
+    results = query("status:new");
+    assertEquals(2, results.size());
+    // Same order as before change1 was modified.
+    assertResultEquals(change2, results.get(0));
+    assertResultEquals(change1, results.get(1));
+  }
+
+  @Test
+  public void sortKeyBreaksTiesOnChangeId() throws Exception {
+    clockStepMs = 0;
+    TestRepository<InMemoryRepository> repo = createProject("repo");
+    Change change1 = newChange(repo, null, null, null, null).insert();
+    Change change2 = newChange(repo, null, null, null, null).insert();
+    assertEquals(change1.getLastUpdatedOn(), change2.getLastUpdatedOn());
+
+    List<ChangeInfo> results = query("status:new");
+    assertEquals(2, results.size());
+    assertResultEquals(change2, results.get(0));
+    assertResultEquals(change1, results.get(1));
+  }
+
+  @Test
+  public void filterOutMoreThanOnePageOfResults() throws Exception {
+    TestRepository<InMemoryRepository> repo = createProject("repo");
+    Change change = newChange(repo, null, null, userId.get(), null).insert();
+    int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
+        .getAccountId().get();
+    for (int i = 0; i < 5; i++) {
+      newChange(repo, null, null, user2, null).insert();
+    }
+
+    assertResultEquals(change, queryOne("status:new ownerin:Administrators"));
+    assertResultEquals(change,
+        queryOne("status:new ownerin:Administrators limit:2"));
+  }
+
+  @Test
+  public void filterOutAllResults() throws Exception {
+    TestRepository<InMemoryRepository> repo = createProject("repo");
+    int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
+        .getAccountId().get();
+    for (int i = 0; i < 5; i++) {
+      newChange(repo, null, null, user2, null).insert();
+    }
+
+    assertTrue(query("status:new ownerin:Administrators").isEmpty());
+    assertTrue(query("status:new ownerin:Administrators limit:2").isEmpty());
   }
 
   protected ChangeInserter newChange(
@@ -411,9 +596,7 @@
     }
 
     Change change = new Change(new Change.Key(key), id, ownerId,
-        new Branch.NameKey(project, branch));
-    change.setLastUpdatedOn(new Timestamp(ts));
-    ts += clockStepMs;
+        new Branch.NameKey(project, branch), TimeUtil.nowTs());
     return changeFactory.create(
         projectControlFactory.controlFor(project,
           userFactory.create(ownerId)).controlFor(change).getRefControl(),
@@ -438,25 +621,33 @@
         repoManager.openRepository(new Project.NameKey(name)));
   }
 
-  @SuppressWarnings({"rawtypes", "unchecked"})
-  protected List<ChangeInfo> query(Object query) throws Exception {
+  protected QueryChanges newQuery(Object query) {
     QueryChanges q = queryProvider.get();
     q.addQuery(query.toString());
+    return q;
+  }
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  protected List<ChangeInfo> query(QueryChanges q) throws Exception {
     Object result = q.apply(TLR);
     assertTrue(
         String.format("expected List<ChangeInfo>, found %s for [%s]",
-          result, query),
+          result, q.getQuery(0)),
         result instanceof List);
     List results = (List) result;
     if (!results.isEmpty()) {
       assertTrue(
           String.format("expected ChangeInfo, found %s for [%s]",
-            result, query),
+            result, q.getQuery(0)),
           results.get(0) instanceof ChangeInfo);
     }
     return (List<ChangeInfo>) result;
   }
 
+  protected List<ChangeInfo> query(Object query) throws Exception {
+    return query(newQuery(query));
+  }
+
   protected ChangeInfo queryOne(Object query) throws Exception {
     List<ChangeInfo> results = query(query);
     assertTrue(
@@ -465,4 +656,8 @@
         results.size() == 1);
     return results.get(0);
   }
+
+  private static long lastUpdatedMs(Change c) {
+    return c.getLastUpdatedOn().getTime();
+  }
 }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
index 3756632..ab12986 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.query.change;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/SqlQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/SqlQueryChangesTest.java
index 37210ab..f235efc 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/SqlQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/SqlQueryChangesTest.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.server.query.change;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
index c4d5b93..9714124 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.testutil;
 
@@ -19,7 +19,6 @@
 
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.DisabledChangeHooks;
-import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
@@ -55,6 +54,7 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.Module;
 import com.google.inject.Provider;
 import com.google.inject.Provides;
 import com.google.inject.ProvisionException;
@@ -66,6 +66,8 @@
 import org.eclipse.jgit.lib.PersonIdent;
 
 import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -121,9 +123,7 @@
       bind(SocketAddress.class).annotatedWith(RemotePeer.class)
           .toInstance(new InetSocketAddress(InetAddress.getLocalHost(), 1234));
     } catch (UnknownHostException e) {
-      ProvisionException pe = new ProvisionException(e.getMessage());
-      pe.initCause(e);
-      throw pe;
+      throw newProvisionException(e);
     }
     bind(PersonIdent.class)
         .annotatedWith(GerritPersonIdent.class)
@@ -165,10 +165,7 @@
     if (indexType != null) {
       switch (indexType) {
         case LUCENE:
-          int version = cfg.getInt("index", "lucene", "testVersion", -1);
-          checkState(ChangeSchemas.ALL.containsKey(version),
-              "invalid index.lucene.testVersion %s", version);
-          install(new LuceneIndexModule(version, 0, null));
+          install(luceneIndexModule());
           break;
         case SQL:
           install(new NoIndexModule());
@@ -186,4 +183,37 @@
       SchemaCreator schemaCreator) throws OrmException {
     return new InMemoryDatabase(schemaVersion, schemaCreator);
   }
+
+  private Module luceneIndexModule() {
+    try {
+      int version = cfg.getInt("index", "lucene", "testVersion", -1);
+      checkState(ChangeSchemas.ALL.containsKey(version),
+          "invalid index.lucene.testVersion %s", version);
+      Class<?> clazz =
+          Class.forName("com.google.gerrit.lucene.LuceneIndexModule");
+      Constructor<?> c =
+          clazz.getConstructor(Integer.class, int.class, String.class);
+      return (Module) c.newInstance(Integer.valueOf(version), 0, (String) null);
+    } catch (ClassNotFoundException e) {
+      throw newProvisionException(e);
+    } catch (SecurityException e) {
+      throw newProvisionException(e);
+    } catch (NoSuchMethodException e) {
+      throw newProvisionException(e);
+    } catch (IllegalArgumentException e) {
+      throw newProvisionException(e);
+    } catch (InstantiationException e) {
+      throw newProvisionException(e);
+    } catch (IllegalAccessException e) {
+      throw newProvisionException(e);
+    } catch (InvocationTargetException e) {
+      throw newProvisionException(e);
+    }
+  }
+
+  private static ProvisionException newProvisionException(Throwable cause) {
+    ProvisionException pe = new ProvisionException(cause.getMessage());
+    pe.initCause(cause);
+    return pe;
+  }
 }
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java
index 7e32bac..ddb86c3 100644
--- a/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java
+++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.solr;
 
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
index 00fcc59..4f616b9 100644
--- a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
+++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.solr;
 
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java
index 8c614f7..e73c6c1 100644
--- a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java
+++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrIndexModule.java
@@ -10,7 +10,7 @@
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
+// limitations under the License.
 
 package com.google.gerrit.solr;
 
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
index 8673b28..4ac8f64 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.server.git.WorkQueue.CancelableRunnable;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.sshd.SshScope.Context;
 import com.google.gerrit.util.cli.CmdLineParser;
 import com.google.gerrit.util.cli.EndOfOptionsHandler;
@@ -127,11 +128,11 @@
   }
 
   @Nullable
-  String getPluginName() {
+  protected String getPluginName() {
     return pluginName;
   }
 
-  String getName() {
+  protected String getName() {
     return commandName;
   }
 
@@ -139,7 +140,7 @@
     this.commandName = prefix;
   }
 
-  String[] getArguments() {
+  public String[] getArguments() {
     return argv;
   }
 
@@ -429,7 +430,7 @@
         int rc = 0;
         final Context old = sshScope.set(context);
         try {
-          context.started = System.currentTimeMillis();
+          context.started = TimeUtil.nowMs();
           thisThread.setName("SSH " + taskName);
 
           if (thunk instanceof ProjectCommandRunnable) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
index 8c17407..b3b6815 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.util.IdGenerator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.sshd.SshScope.Context;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -109,7 +110,7 @@
     final LoggingEvent event = new LoggingEvent( //
         Logger.class.getName(), // fqnOfCategoryClass
         log, // logger
-        System.currentTimeMillis(), // when
+        TimeUtil.nowMs(), // when
         Level.INFO, // level
         "AUTH FAILURE FROM " + sd.getRemoteAddressAsString(), // message text
         "SSHD", // thread name
@@ -133,7 +134,7 @@
 
   void onExecute(DispatchCommand dcmd, int exitValue) {
     final Context ctx = context.get();
-    ctx.finished = System.currentTimeMillis();
+    ctx.finished = TimeUtil.nowMs();
 
     String cmd = extractWhat(dcmd);
 
@@ -219,7 +220,7 @@
     final LoggingEvent event = new LoggingEvent( //
         Logger.class.getName(), // fqnOfCategoryClass
         log, // logger
-        System.currentTimeMillis(), // when
+        TimeUtil.nowMs(), // when
         Level.INFO, // level
         msg, // message text
         "SSHD", // thread name
@@ -473,7 +474,7 @@
   }
 
   private long extractCreated(final Context ctx) {
-    return (ctx != null) ? ctx.created : System.currentTimeMillis();
+    return (ctx != null) ? ctx.created : TimeUtil.nowMs();
   }
 
   private CurrentUser extractCurrentUser(final Context ctx) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java
index 9b19e02..440f236 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.server.util.RequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestContext;
 import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Key;
@@ -165,7 +166,7 @@
   }
 
   Context newContext(SchemaFactory<ReviewDb> sf, SshSession s, String cmd) {
-    return new Context(sf, s, cmd, System.currentTimeMillis());
+    return new Context(sf, s, cmd, TimeUtil.nowMs());
   }
 
   private Context newContinuingContext(Context ctx) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
index 3f0bc4c..5649843 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/QueryShell.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.common.Version;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import com.google.gwtorm.jdbc.JdbcSchema;
@@ -354,7 +355,7 @@
   }
 
   private void executeStatement(final String sql) {
-    final long start = System.currentTimeMillis();
+    final long start = TimeUtil.nowMs();
     final boolean hasResultSet;
     try {
       hasResultSet = statement.execute(sql);
@@ -374,7 +375,7 @@
 
       } else {
         final int updateCount = statement.getUpdateCount();
-        final long ms = System.currentTimeMillis() - start;
+        final long ms = TimeUtil.nowMs() - start;
         switch (outputFormat) {
           case JSON_SINGLE:
           case JSON: {
@@ -490,7 +491,7 @@
       tail = new JsonObject();
       tail.addProperty("type", "query-stats");
       tail.addProperty("rowCount", rowCnt);
-      final long ms = System.currentTimeMillis() - start;
+      final long ms = TimeUtil.nowMs() - start;
       tail.addProperty("runTimeMilliseconds", ms);
     }
 
@@ -629,7 +630,7 @@
 
     if (start != 0) {
       final int rowCount = rows.size();
-      final long ms = System.currentTimeMillis() - start;
+      final long ms = TimeUtil.nowMs() - start;
       println("(" + rowCount + (rowCount == 1 ? " row" : " rows")
           + "; " + ms + " ms)");
     }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
index b02d9c8..89ba6ba 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.server.config.SitePath;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.git.WorkQueue.Task;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshDaemon;
 import com.google.inject.Inject;
@@ -59,7 +60,7 @@
   static class StartupListener implements LifecycleListener {
     @Override
     public void start() {
-      serverStarted = System.currentTimeMillis();
+      serverStarted = TimeUtil.nowMs();
     }
 
     @Override
@@ -270,7 +271,7 @@
       return;
     }
 
-    long now = System.currentTimeMillis();
+    long now = TimeUtil.nowMs();
     Collection<IoSession> list = acceptor.getManagedSessions().values();
     long oldest = now;
     for (IoSession s : list) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
index c0e5d6e..f8531ed 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.util.IdGenerator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gerrit.sshd.SshDaemon;
@@ -94,7 +95,7 @@
 
     hostNameWidth = wide ? Integer.MAX_VALUE : columns - 9 - 9 - 10 - 32;
 
-    final long now = System.currentTimeMillis();
+    final long now = TimeUtil.nowMs();
     stdout.print(String.format("%-8s %8s %8s   %-15s %s\n", //
         "Session", "Start", "Idle", "User", "Remote Host"));
     stdout.print("--------------------------------------------------------------\n");
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java
index 2f1d0bf..7759c94 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.util.IdGenerator;
+import com.google.gerrit.server.util.TimeUtil;
 import com.google.gerrit.sshd.AdminHighPriorityCommand;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
@@ -102,7 +103,7 @@
         + "--------------------------------\n");
 
     int numberOfPendingTasks = 0;
-    final long now = System.currentTimeMillis();
+    final long now = TimeUtil.nowMs();
     final boolean viewAll = currentUser.getCapabilities().canViewQueue();
 
     for (final Task<?> task : pending) {
@@ -186,7 +187,7 @@
   }
 
   private static String startTime(final Date when) {
-    return format(when, System.currentTimeMillis() - when.getTime());
+    return format(when, TimeUtil.nowMs() - when.getTime());
   }
 
   private static String format(final Date when, final long timeFromNow) {
diff --git a/lib/asciidoctor/java/DocIndexer.java b/lib/asciidoctor/java/DocIndexer.java
index ce6d9fe..497cba5 100644
--- a/lib/asciidoctor/java/DocIndexer.java
+++ b/lib/asciidoctor/java/DocIndexer.java
@@ -89,7 +89,7 @@
       BufferedReader titleReader = new BufferedReader(
           new InputStreamReader(new FileInputStream(file), "UTF-8"));
       String title = titleReader.readLine();
-      if (title.startsWith("[[")) {
+      if (title != null && title.startsWith("[[")) {
         // Generally the first line of the txt is the title. In a few cases the
         // first line is a "[[tag]]" and the second line is the title.
         title = titleReader.readLine();
diff --git a/lib/gwt/BUCK b/lib/gwt/BUCK
index c28c048..0ccb22b 100644
--- a/lib/gwt/BUCK
+++ b/lib/gwt/BUCK
@@ -39,10 +39,9 @@
 
 maven_jar(
   name = 'gwt-test-utils',
-  id = 'com.googlecode.gwt-test-utils:gwt-test-utils:0.46-SNAPSHOT-20130930.170040-1',
-  sha1 = 'e8f5bd1b8c75be333da36f41613436a8eb175dad',
+  id = 'com.googlecode.gwt-test-utils:gwt-test-utils:0.45',
+  sha1 = 'ed16fa85defc685802e11cc61f8bc70454412fdb',
   license = 'Apache2.0',
-  repository = 'http://oss.sonatype.org/content/repositories/snapshots',
   deps = [
     ':javassist',
     '//lib/log:api',
diff --git a/lib/joda/BUCK b/lib/joda/BUCK
new file mode 100644
index 0000000..d45ce78
--- /dev/null
+++ b/lib/joda/BUCK
@@ -0,0 +1,25 @@
+include_defs('//lib/maven.defs')
+
+EXCLUDE = [
+  'META-INF/LICENSE.txt',
+  'META-INF/NOTICE.txt',
+]
+
+maven_jar(
+  name = 'joda-time',
+  id = 'joda-time:joda-time:2.3',
+  sha1 = '56498efd17752898cfcc3868c1b6211a07b12b8f',
+  deps = [':joda-convert'],
+  license = 'Apache2.0',
+  exclude = EXCLUDE,
+  visibility = ['PUBLIC'],
+)
+
+maven_jar(
+  name = 'joda-convert',
+  id = 'org.joda:joda-convert:1.2',
+  bin_sha1 = '35ec554f0cd00c956cc69051514d9488b1374dec',
+  license = 'Apache2.0',
+  exclude = EXCLUDE,
+  visibility = ['//lib/joda:joda-time'],
+)
diff --git a/plugins/replication b/plugins/replication
index 87d7a64..68ab447 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 87d7a646419badac537f83ca8b8c1858e06d9b25
+Subproject commit 68ab44794695bf5f7faec3bcae27bce42052a68b
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index 7392674..9905f7a 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit 73926747285a04ea28ca1933e24762f60cfc3986
+Subproject commit 9905f7af6c4e258365413a03f092898b167ea25a