Merge "Console log and reporting on invalid comment ranges"
diff --git a/.gitignore b/.gitignore
index e702ac9..1b93f20a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
 .DS_Store
 .gwt_work_dir
 /.apt_generated
+/.bazel_path
 /.buckd
 /.classpath
 /.factorypath
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index b0a07c7..6a01607 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1543,6 +1543,10 @@
 +
 Connect to a MySQL database server.
 +
+* `MARIADB`
++
+Connect to a MariaDB database server.
++
 * `ORACLE`
 +
 Connect to an Oracle database server.
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
index 8667f43..3fd0c91 100644
--- a/Documentation/database-setup.txt
+++ b/Documentation/database-setup.txt
@@ -74,6 +74,14 @@
 Visit MySQL's link:http://dev.mysql.com/doc/[documentation] for further
 information regarding using MySQL.
 
+[[createdb_mariadb]]
+=== MariaDB
+
+Refer to MySQL section above how to create MariaDB database.
+
+Visit MariaDB's link:https://mariadb.com/kb/en/mariadb/[documentation] for further
+information regarding using MariaDB.
+
 [[createdb_oracle]]
 === Oracle
 
diff --git a/WORKSPACE b/WORKSPACE
index a74a29d..fc074fc 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -186,15 +186,15 @@
 
 maven_jar(
     name = "gwtorm_client",
-    artifact = "com.google.gerrit:gwtorm:1.17",
-    sha1 = "97bdc872f00388910c9af70771f07bbb32f1b949",
-    src_sha1 = "889e35d7295b1af49161a28daaea9905ffa76a63",
+    artifact = "com.google.gerrit:gwtorm:1.18",
+    sha1 = "f326dec463439a92ccb32f05b38345e21d0b5ecf",
+    src_sha1 = "e0b973d5cafef3d145fa80cdf032fcead1186d29",
 )
 
 maven_jar(
     name = "protobuf",
-    artifact = "com.google.protobuf:protobuf-java:2.5.0",
-    sha1 = "a10732c76bfacdbd633a7eb0f7968b1059a65dfa",
+    artifact = "com.google.protobuf:protobuf-java:3.0.0-beta-2",
+    sha1 = "de80fe047052445869b96f6def6baca7182c95af",
 )
 
 maven_jar(
@@ -595,8 +595,14 @@
 # Keep this version of Soy synchronized with the version used in Gitiles.
 maven_jar(
     name = "soy",
-    artifact = "com.google.template:soy:2016-08-09",
-    sha1 = "43d33651e95480d515fe26c10a662faafe3ad1e4",
+    artifact = "com.google.template:soy:2017-02-01",
+    sha1 = "8638940b207779fe3b75e55b6e65abbefb6af678",
+)
+
+maven_jar(
+    name = "html_types",
+    artifact = "com.google.common.html.types:types:1.0.4",
+    sha1 = "2adf4c8bfccc0ff7346f9186ac5aa57d829ad065",
 )
 
 maven_jar(
@@ -607,8 +613,8 @@
 
 maven_jar(
     name = "dropwizard_core",
-    artifact = "io.dropwizard.metrics:metrics-core:3.1.2",
-    sha1 = "224f03afd2521c6c94632f566beb1bb5ee32cf07",
+    artifact = "io.dropwizard.metrics:metrics-core:3.2.1",
+    sha1 = "f453a6b2660c369c62b83ded443391ae1bfd23a0",
 )
 
 # This version must match the version that also appears in
@@ -900,23 +906,23 @@
 
 maven_jar(
     name = "elasticsearch",
-    artifact = "org.elasticsearch:elasticsearch:2.4.0",
-    sha1 = "aeb9704a76fa8654c348f38fcbb993a952a7ab07",
+    artifact = "org.elasticsearch:elasticsearch:2.4.4",
+    sha1 = "e69930bc794c539d34778e665d6f8ccbffd42c6f",
 )
 
 # Java REST client for Elasticsearch.
-JEST_VERSION = "2.0.3"
+JEST_VERSION = "2.4.0"
 
 maven_jar(
     name = "jest_common",
     artifact = "io.searchbox:jest-common:" + JEST_VERSION,
-    sha1 = "f304c66894aaf2f6c17a886bc826f09c7a161cf9",
+    sha1 = "ea779ebe7c438a53dce431f85b0d4e1d8faee2ac",
 )
 
 maven_jar(
     name = "jest",
     artifact = "io.searchbox:jest:" + JEST_VERSION,
-    sha1 = "b8f9ed1423489b361804e47f640515ea9f1fa08d",
+    sha1 = "e2a604a584e6633545ac6b1fe99ef888ab96dae9",
 )
 
 maven_jar(
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 2131142..24b3724 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -648,28 +648,32 @@
       String changeId, String ref, TestAccount testAccount, TestRepository<?> repo)
       throws Exception {
     Collections.shuffle(RANDOM);
-    return amendChange(changeId, ref, testAccount, repo, PushOneCommit.SUBJECT,
-        PushOneCommit.FILE_NAME, new String(Chars.toArray(RANDOM)));
+    return amendChange(
+        changeId,
+        ref,
+        testAccount,
+        repo,
+        PushOneCommit.SUBJECT,
+        PushOneCommit.FILE_NAME,
+        new String(Chars.toArray(RANDOM)));
   }
 
-  protected PushOneCommit.Result amendChange(String changeId, String subject, String fileName,
-      String content) throws Exception {
+  protected PushOneCommit.Result amendChange(
+      String changeId, String subject, String fileName, String content) throws Exception {
     return amendChange(changeId, "refs/for/master", admin, testRepo, subject, fileName, content);
   }
 
   protected PushOneCommit.Result amendChange(
-      String changeId, String ref, TestAccount testAccount, TestRepository<?> repo, String subject,
-      String fileName, String content)
+      String changeId,
+      String ref,
+      TestAccount testAccount,
+      TestRepository<?> repo,
+      String subject,
+      String fileName,
+      String content)
       throws Exception {
     PushOneCommit push =
-        pushFactory.create(
-            db,
-            testAccount.getIdent(),
-            repo,
-            subject,
-            fileName,
-            content,
-            changeId);
+        pushFactory.create(db, testAccount.getIdent(), repo, subject, fileName, content, changeId);
     return push.to(ref);
   }
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
index a395132..d137fba3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -99,6 +99,17 @@
   }
 
   @Test
+  public void addMembersWithAtSign() throws Exception {
+    String g = createGroup("users");
+    TestAccount u10 = accounts.create("u10", "u10@example.com", "Full Name 10");
+    TestAccount u11_at =
+        accounts.create("u11@something", "u11@example.com", "Full Name 11 With At");
+    TestAccount u11 = accounts.create("u11", "u11.another@example.com", "Full Name 11 Without At");
+    gApi.groups().id(g).addMembers(u10.username, u11_at.username);
+    assertMembers(g, u10, u11_at);
+  }
+
+  @Test
   public void includeRemoveGroup() throws Exception {
     String p = createGroup("parent");
     String g = createGroup("newGroup");
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index 77ca14f..d8aa35c 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -419,8 +419,8 @@
     RevCommit headAfterChange1 = change1.getCommit();
     PushOneCommit.Result change2 = createChange("subject 2", "fileName 2", "content 2");
     testRepo.reset(headAfterChange1);
-    change1 = amendChange(change1.getChangeId(), "subject 1 amend", "fileName 2",
-        "rework content 2");
+    change1 =
+        amendChange(change1.getChangeId(), "subject 1 amend", "fileName 2", "rework content 2");
     submit(change1.getChangeId());
     headAfterChange1 = getRemoteHead();
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index ab543e8..112c578 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -23,26 +23,38 @@
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.Sandboxed;
 import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.StarsInput;
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.account.WatchConfig;
 import com.google.gerrit.server.account.WatchConfig.NotifyType;
+import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
 import com.google.gerrit.server.git.NotifyConfig;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.testutil.FakeEmailSender.Message;
+import com.google.inject.Inject;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
 import org.eclipse.jgit.junit.TestRepository;
 import org.junit.Test;
 
+
 @NoHttpd
 @Sandboxed
 public class ProjectWatchIT extends AbstractDaemonTest {
+  @Inject private WatchConfig.Accessor watchConfig;
+
   @Test
   public void newPatchSetsNotifyConfig() throws Exception {
     Address addr = new Address("Watcher", "watcher@example.com");
@@ -480,4 +492,34 @@
     // assert email notification
     assertThat(sender.getMessages()).isEmpty();
   }
+
+  @Test
+  public void deleteAllProjectWatches() throws Exception {
+    Map<ProjectWatchKey, Set<NotifyType>> watches = new HashMap<>();
+    watches.put(ProjectWatchKey.create(project, "*"), ImmutableSet.of(NotifyType.ALL));
+    watchConfig.upsertProjectWatches(admin.getId(), watches);
+    assertThat(watchConfig.getProjectWatches(admin.getId())).isNotEmpty();
+
+    watchConfig.deleteAllProjectWatches(admin.getId());
+    assertThat(watchConfig.getProjectWatches(admin.getId())).isEmpty();
+  }
+
+  @Test
+  public void deleteAllProjectWatchesIfWatchConfigIsTheOnlyFileInUserBranch() throws Exception {
+    // Create account that has no files in its refs/users/ branch.
+    Account.Id id = new Account.Id(db.nextAccountId());
+    Account a = new Account(id, TimeUtil.nowTs());
+    db.accounts().insert(Collections.singleton(a));
+
+    // Add a project watch so that a watch.config file in the refs/users/ branch is created.
+    Map<ProjectWatchKey, Set<NotifyType>> watches = new HashMap<>();
+    watches.put(ProjectWatchKey.create(project, "*"), ImmutableSet.of(NotifyType.ALL));
+    watchConfig.upsertProjectWatches(id, watches);
+    assertThat(watchConfig.getProjectWatches(id)).isNotEmpty();
+
+    // Delete all project watches so that the watch.config file in the refs/users/ branch is
+    // deleted.
+    watchConfig.deleteAllProjectWatches(id);
+    assertThat(watchConfig.getProjectWatches(id)).isEmpty();
+  }
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
index 77a0a5f..1ac42d1 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
@@ -26,6 +26,7 @@
 import java.net.URLClassLoader;
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
 
@@ -54,7 +55,7 @@
     }.start();
   }
 
-  public static void loadJARs(Iterable<Path> jars) {
+  public static void loadJARs(Collection<Path> jars) {
     ClassLoader cl = IoUtil.class.getClassLoader();
     if (!(cl instanceof URLClassLoader)) {
       throw noAddURL("Not loaded by URLClassLoader", null);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginServletContext.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
index a5ba2e8..53b49a4 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
@@ -39,7 +39,7 @@
     return (ServletContext)
         Proxy.newProxyInstance(
             PluginServletContext.class.getClassLoader(),
-            new Class[] {ServletContext.class, API.class},
+            new Class<?>[] {ServletContext.class, API.class},
             new Handler(plugin, contextPath));
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/BazelBuild.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/BazelBuild.java
index d2e3b58..85453fb 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/BazelBuild.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/BazelBuild.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd.raw;
 
+import static com.google.common.base.MoreObjects.firstNonNull;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.escape.Escaper;
@@ -25,7 +26,10 @@
 import java.io.InputStream;
 import java.io.InterruptedIOException;
 import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
+import java.util.Properties;
 import javax.servlet.http.HttpServletResponse;
 import org.eclipse.jgit.util.RawParseUtils;
 import org.slf4j.Logger;
@@ -117,8 +121,24 @@
     }
   }
 
-  private ProcessBuilder newBuildProcess(Label label) {
-    return new ProcessBuilder("bazel", "build", label.fullName());
+  private Properties loadBuildProperties(Path propPath) throws IOException {
+    Properties properties = new Properties();
+    try (InputStream in = Files.newInputStream(propPath)) {
+      properties.load(in);
+    } catch (NoSuchFileException e) {
+      // Ignore; will be run from PATH, with a descriptive error if it fails.
+    }
+    return properties;
+  }
+
+  private ProcessBuilder newBuildProcess(Label label) throws IOException {
+    Properties properties = loadBuildProperties(sourceRoot.resolve(".bazel_path"));
+    String bazel = firstNonNull(properties.getProperty("bazel"), "bazel");
+    ProcessBuilder proc = new ProcessBuilder(bazel, "build", label.fullName());
+    if (properties.containsKey("PATH")) {
+      proc.environment().put("PATH", properties.getProperty("PATH"));
+    }
+    return proc;
   }
 
   /** returns the root relative path to the artifact for the given label */
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 eecaf36..a7af056 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
@@ -54,7 +54,7 @@
 
 /** Main class for a JAR file to run code from "WEB-INF/lib". */
 public final class GerritLauncher {
-  private static final String pkg = "com.google.gerrit.pgm";
+  private static final String PKG = "com.google.gerrit.pgm";
   public static final String NOT_ARCHIVED = "NOT_ARCHIVED";
 
   private static ClassLoader daemonClassLoader;
@@ -173,17 +173,17 @@
     try {
       try {
         String cn = programClassName(name);
-        clazz = Class.forName(pkg + "." + cn, true, loader);
+        clazz = Class.forName(PKG + "." + cn, true, loader);
       } catch (ClassNotFoundException cnfe) {
         if (name.equals(name.toLowerCase())) {
-          clazz = Class.forName(pkg + "." + name, true, loader);
+          clazz = Class.forName(PKG + "." + name, true, loader);
         } else {
           throw cnfe;
         }
       }
     } catch (ClassNotFoundException cnfe) {
       System.err.println("fatal: unknown command " + name);
-      System.err.println("      (no " + pkg + "." + name + ")");
+      System.err.println("      (no " + PKG + "." + name + ")");
       return 1;
     }
 
@@ -200,7 +200,8 @@
       if ((main.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
         res = main.invoke(null, new Object[] {argv});
       } else {
-        res = main.invoke(clazz.getConstructor(new Class[] {}).newInstance(), new Object[] {argv});
+        res =
+            main.invoke(clazz.getConstructor(new Class<?>[] {}).newInstance(), new Object[] {argv});
       }
     } catch (InvocationTargetException ite) {
       if (ite.getCause() instanceof Exception) {
@@ -604,7 +605,7 @@
     return resolveInSourceRoot("eclipse-out");
   }
 
-  static String SOURCE_ROOT_RESOURCE = "/gerrit-launcher/workspace-root.txt";
+  static final String SOURCE_ROOT_RESOURCE = "/gerrit-launcher/workspace-root.txt";
 
   /**
    * Locate a path in the source tree.
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
index 45206c9..b80bf35 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
@@ -40,6 +40,9 @@
         .annotatedWith(Names.named("jdbc"))
         .to(JDBCInitializer.class);
     bind(DatabaseConfigInitializer.class)
+        .annotatedWith(Names.named("mariadb"))
+        .to(MariaDbInitializer.class);
+    bind(DatabaseConfigInitializer.class)
         .annotatedWith(Names.named("mysql"))
         .to(MySqlInitializer.class);
     bind(DatabaseConfigInitializer.class)
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitDatabase.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitDatabase.java
index 5565158..349ab55 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitDatabase.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitDatabase.java
@@ -85,6 +85,8 @@
 
     if (dci instanceof MySqlInitializer) {
       libraries.mysqlDriver.downloadRequired();
+    } else if (dci instanceof MariaDbInitializer) {
+      libraries.mariadbDriver.downloadRequired();
     } else if (dci instanceof OracleInitializer) {
       libraries.oracleDriver.downloadRequired();
     } else if (dci instanceof DB2Initializer) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java
index 4659ee3..e3a1d66 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java
@@ -37,6 +37,8 @@
         database.set("driver", "org.apache.derby.jdbc.EmbeddedDriver");
       } else if (url.startsWith("jdbc:h2:")) {
         database.set("driver", "org.h2.Driver");
+      } else if (url.startsWith("jdbc:mariadb:")) {
+        database.set("driver", "org.mariadb.jdbc.Driver");
       } else if (url.startsWith("jdbc:mysql:")) {
         database.set("driver", "com.mysql.jdbc.Driver");
       } else if (url.startsWith("jdbc:postgresql:")) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
index 526f172..3259f96 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
@@ -43,6 +43,7 @@
   /* final */ LibraryDownloader db2Driver;
   /* final */ LibraryDownloader db2DriverLicense;
   /* final */ LibraryDownloader hanaDriver;
+  /* final */ LibraryDownloader mariadbDriver;
   /* final */ LibraryDownloader mysqlDriver;
   /* final */ LibraryDownloader oracleDriver;
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/MariaDbInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/MariaDbInitializer.java
new file mode 100644
index 0000000..db32113
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/MariaDbInitializer.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.pgm.init;
+
+import static com.google.gerrit.pgm.init.api.InitUtil.username;
+
+import com.google.gerrit.pgm.init.api.Section;
+
+class MariaDbInitializer implements DatabaseConfigInitializer {
+
+  @Override
+  public void initConfig(Section databaseSection) {
+    final String defPort = "(mariadb default)";
+    databaseSection.string("Server hostname", "hostname", "localhost");
+    databaseSection.string("Server port", "port", defPort, true);
+    databaseSection.string("Database name", "database", "reviewdb");
+    databaseSection.string("Database username", "username", username());
+    databaseSection.password("username", "password");
+  }
+}
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
index f4564f5..26ac9d6 100644
--- a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
@@ -18,6 +18,12 @@
   sha1 = b0878056f15616989144d6114d36d3942321d0d1
   remove = mysql-connector-java-.*[.]jar
 
+[library "mariadbDriver"]
+  name = MariaDB Connector/J 1.5.9
+  url = https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/1.5.9/mariadb-java-client-1.5.9.jar
+  sha1 = 75d4d6e4cdb9a551a102e92a14c640768174e214
+  remove = mariadb-java-client-.*[.]jar
+
 [library "oracleDriver"]
   name = Oracle JDBC driver 11g Release 2 (11.2.0)
   url = file:///u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
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 e1f894f..f1c7056 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
@@ -45,7 +45,7 @@
  */
 public final class Account {
   public static final String USER_NAME_PATTERN_FIRST = "[a-zA-Z0-9]";
-  public static final String USER_NAME_PATTERN_REST = "[a-zA-Z0-9._-]";
+  public static final String USER_NAME_PATTERN_REST = "[a-zA-Z0-9._@-]";
   public static final String USER_NAME_PATTERN_LAST = "[a-zA-Z0-9]";
 
   /** Regular expression that {@link #userName} must match. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
index cbd2378..7256e8c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
@@ -52,10 +52,10 @@
   private OperatingSystemMXBeanProvider(OperatingSystemMXBean sys)
       throws ReflectiveOperationException {
     this.sys = sys;
-    getProcessCpuTime = sys.getClass().getMethod("getProcessCpuTime", new Class[] {});
+    getProcessCpuTime = sys.getClass().getMethod("getProcessCpuTime", new Class<?>[] {});
     getProcessCpuTime.setAccessible(true);
     getOpenFileDescriptorCount =
-        sys.getClass().getMethod("getOpenFileDescriptorCount", new Class[] {});
+        sys.getClass().getMethod("getOpenFileDescriptorCount", new Class<?>[] {});
     getOpenFileDescriptorCount.setAccessible(true);
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java b/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java
index 6b3a58f..4ab42f3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/Sequences.java
@@ -35,6 +35,8 @@
 @SuppressWarnings("deprecation")
 @Singleton
 public class Sequences {
+  public static final String CHANGES = "changes";
+
   private final Provider<ReviewDb> db;
   private final NotesMigration migration;
   private final RepoSequence changeSeq;
@@ -54,7 +56,7 @@
         new RepoSequence(
             repoManager,
             allProjects,
-            "changes",
+            CHANGES,
             new RepoSequence.Seed() {
               @Override
               public int get() throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalId.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalId.java
index d057f56..cd10b7b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalId.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/ExternalId.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.account;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.stream.Collectors.toSet;
 
@@ -71,7 +72,7 @@
     private static final long serialVersionUID = 1L;
 
     public static Key create(@Nullable String scheme, String id) {
-      return new AutoValue_ExternalId_Key(scheme, id);
+      return new AutoValue_ExternalId_Key(Strings.emptyToNull(scheme), id);
     }
 
     public static ExternalId.Key from(AccountExternalId.Key externalIdKey) {
@@ -158,7 +159,8 @@
 
   public static ExternalId create(
       Key key, Account.Id accountId, @Nullable String email, @Nullable String hashedPassword) {
-    return new AutoValue_ExternalId(key, accountId, email, hashedPassword);
+    return new AutoValue_ExternalId(
+        key, accountId, Strings.emptyToNull(email), Strings.emptyToNull(hashedPassword));
   }
 
   public static ExternalId createWithPassword(
@@ -174,16 +176,16 @@
   }
 
   public static ExternalId createWithEmail(
-      String scheme, String id, Account.Id accountId, String email) {
+      String scheme, String id, Account.Id accountId, @Nullable String email) {
     return createWithEmail(Key.create(scheme, id), accountId, email);
   }
 
-  public static ExternalId createWithEmail(Key key, Account.Id accountId, String email) {
-    return new AutoValue_ExternalId(key, accountId, email, null);
+  public static ExternalId createWithEmail(Key key, Account.Id accountId, @Nullable String email) {
+    return new AutoValue_ExternalId(key, accountId, Strings.emptyToNull(email), null);
   }
 
   public static ExternalId createEmail(Account.Id accountId, String email) {
-    return createWithEmail(SCHEME_MAILTO, email, accountId, email);
+    return createWithEmail(SCHEME_MAILTO, email, accountId, checkNotNull(email));
   }
 
   /**
@@ -243,7 +245,11 @@
               accountIdStr, EXTERNAL_ID_SECTION, externalIdKeyStr, ACCOUNT_ID_KEY));
     }
 
-    return new AutoValue_ExternalId(externalIdKey, new Account.Id(accountId), email, password);
+    return new AutoValue_ExternalId(
+        externalIdKey,
+        new Account.Id(accountId),
+        Strings.emptyToNull(email),
+        Strings.emptyToNull(password));
   }
 
   private static ConfigInvalidException invalidConfig(String noteId, String message) {
@@ -259,8 +265,8 @@
     return new AutoValue_ExternalId(
         ExternalId.Key.parse(externalId.getExternalId()),
         externalId.getAccountId(),
-        externalId.getEmailAddress(),
-        externalId.getPassword());
+        Strings.emptyToNull(externalId.getEmailAddress()),
+        Strings.emptyToNull(externalId.getPassword()));
   }
 
   public static Set<ExternalId> from(Collection<AccountExternalId> externalIds) {
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 3198017..d9fea44 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
@@ -994,7 +994,7 @@
           break;
 
         default:
-          reject(cmd);
+          reject(cmd, "prohibited by Gerrit: unknown command type " + cmd.getType());
           continue;
       }
 
@@ -1113,7 +1113,7 @@
             break;
 
           default:
-            reject(cmd);
+            reject(cmd, "prohibited by Gerrit: don't know how to handle config update of type " + cmd.getType());
             continue;
         }
       }
@@ -1145,7 +1145,7 @@
       validateNewCommits(ctl, cmd);
       batch.addCommand(cmd);
     } else {
-      reject(cmd);
+      reject(cmd, "prohibited by Gerrit: create access denied for " + cmd.getRefName());
     }
   }
 
@@ -1168,7 +1168,7 @@
       } else {
         errors.put(Error.UPDATE, ctl.getRefName());
       }
-      reject(cmd);
+      reject(cmd, "prohibited by Gerrit: ref update access denied");
     }
   }
 
@@ -2878,10 +2878,6 @@
     return r;
   }
 
-  private void reject(ReceiveCommand cmd) {
-    reject(cmd, "prohibited by Gerrit");
-  }
-
   private void reject(ReceiveCommand cmd, String why) {
     cmd.setResult(REJECTED_OTHER_REASON, why);
     commandProgress.update(1);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
index 9d8d1ac..536858b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -281,12 +281,7 @@
           return;
         }
 
-        // Reuse tree from parent commit unless there are contents in newTree or
-        // there is no tree for a parent commit.
-        ObjectId res =
-            newTree.getEntryCount() != 0 || srcTree == null
-                ? newTree.writeTree(inserter)
-                : srcTree.copy();
+        ObjectId res = newTree.writeTree(inserter);
         if (res.equals(srcTree) && !update.allowEmpty() && (commit.getTreeId() == null)) {
           // If there are no changes to the content, don't create the commit.
           return;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
index 0101cd7..0b097d3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -16,6 +16,8 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_SEQUENCES;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
@@ -52,6 +54,7 @@
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
 
 /**
  * Class for managing an incrementing sequence backed by a git repository.
@@ -117,7 +120,15 @@
       Retryer<RefUpdate.Result> retryer) {
     this.repoManager = checkNotNull(repoManager, "repoManager");
     this.projectName = checkNotNull(projectName, "projectName");
-    this.refName = RefNames.REFS_SEQUENCES + checkNotNull(name, "name");
+
+    checkArgument(
+        name != null
+            && !name.startsWith(REFS)
+            && !name.startsWith(REFS_SEQUENCES.substring(REFS.length())),
+        "name should be a suffix to follow \"refs/sequences/\", got: %s",
+        name);
+    this.refName = RefNames.REFS_SEQUENCES + name;
+
     this.seed = checkNotNull(seed, "seed");
 
     checkArgument(batchSize > 0, "expected batchSize > 0, got: %s", batchSize);
@@ -265,4 +276,10 @@
     ru.setForceUpdate(true); // Required for non-commitish updates.
     return ru.update(rw);
   }
+
+  public static ReceiveCommand storeNew(ObjectInserter ins, String name, int val)
+      throws IOException {
+    ObjectId newId = ins.insert(OBJ_BLOB, Integer.toString(val).getBytes(UTF_8));
+    return new ReceiveCommand(ObjectId.zeroId(), newId, RefNames.REFS_SEQUENCES + name);
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
index 8a57c73..62144ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
@@ -163,8 +163,7 @@
 
   protected final Definition<T, ? extends QueryBuilder<T>> builderDef;
 
-  @SuppressWarnings("rawtypes")
-  protected final Map<String, OperatorFactory> opFactories;
+  protected final Map<String, OperatorFactory<?, ?>> opFactories;
 
   @SuppressWarnings({"unchecked", "rawtypes"})
   protected QueryBuilder(Definition<T, ? extends QueryBuilder<T>> def) {
@@ -296,12 +295,11 @@
     throw error("Unsupported query:" + value);
   }
 
-  @SuppressWarnings("unchecked")
-  private Predicate<T>[] children(final Tree r)
+  private List<Predicate<T>> children(final Tree r)
       throws QueryParseException, IllegalArgumentException {
-    final Predicate<T>[] p = new Predicate[r.getChildCount()];
-    for (int i = 0; i < p.length; i++) {
-      p[i] = toPredicate(r.getChild(i));
+    List<Predicate<T>> p = new ArrayList<>(r.getChildCount());
+    for (int i = 0; i < r.getChildCount(); i++) {
+      p.add(toPredicate(r.getChild(i)));
     }
     return p;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 3f1d32c..9a56aa4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.schema;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_SEQUENCES;
 import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
 import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -35,28 +37,39 @@
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.Sequences;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.notedb.RepoSequence;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
 
 /** Creates the {@code All-Projects} repository and initial ACLs. */
 public class AllProjectsCreator {
   private final GitRepositoryManager mgr;
   private final AllProjectsName allProjectsName;
   private final PersonIdent serverUser;
+  private final NotesMigration notesMigration;
   private String message;
+  private int firstChangeId = ReviewDb.FIRST_CHANGE_ID;
 
   private GroupReference admin;
   private GroupReference batch;
@@ -69,10 +82,12 @@
       GitRepositoryManager mgr,
       AllProjectsName allProjectsName,
       SystemGroupBackend systemGroupBackend,
-      @GerritPersonIdent PersonIdent serverUser) {
+      @GerritPersonIdent PersonIdent serverUser,
+      NotesMigration notesMigration) {
     this.mgr = mgr;
     this.allProjectsName = allProjectsName;
     this.serverUser = serverUser;
+    this.notesMigration = notesMigration;
 
     this.anonymous = systemGroupBackend.getGroup(ANONYMOUS_USERS);
     this.registered = systemGroupBackend.getGroup(REGISTERED_USERS);
@@ -94,6 +109,12 @@
     return this;
   }
 
+  public AllProjectsCreator setFirstChangeIdForNoteDb(int id) {
+    checkArgument(id > 0, "id must be positive: %s", id);
+    firstChangeId = id;
+    return this;
+  }
+
   public void create() throws IOException, ConfigInvalidException {
     try (Repository git = mgr.openRepository(allProjectsName)) {
       initAllProjects(git);
@@ -112,8 +133,9 @@
   }
 
   private void initAllProjects(Repository git) throws IOException, ConfigInvalidException {
+    BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
     try (MetaDataUpdate md =
-        new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git)) {
+        new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git, bru)) {
       md.getCommitBuilder().setAuthor(serverUser);
       md.getCommitBuilder().setCommitter(serverUser);
       md.setMessage(
@@ -177,6 +199,8 @@
       grant(config, meta, Permission.SUBMIT, admin, owners);
 
       config.commitToNewRef(md, RefNames.REFS_CONFIG);
+      initSequences(git, bru);
+      execute(git, bru);
     }
   }
 
@@ -195,4 +219,27 @@
     c.getLabelSections().put(type.getName(), type);
     return type;
   }
+
+  private void initSequences(Repository git, BatchRefUpdate bru) throws IOException {
+    if (notesMigration.readChangeSequence()
+        && git.exactRef(REFS_SEQUENCES + Sequences.CHANGES) == null) {
+      // Can't easily reuse the inserter from MetaDataUpdate, but this shouldn't slow down site
+      // initialization unduly.
+      try (ObjectInserter ins = git.newObjectInserter()) {
+        bru.addCommand(RepoSequence.storeNew(ins, Sequences.CHANGES, firstChangeId));
+        ins.flush();
+      }
+    }
+  }
+
+  private void execute(Repository git, BatchRefUpdate bru) throws IOException {
+    try (RevWalk rw = new RevWalk(git)) {
+      bru.execute(rw, NullProgressMonitor.INSTANCE);
+    }
+    for (ReceiveCommand cmd : bru.getCommands()) {
+      if (cmd.getResult() != ReceiveCommand.Result.OK) {
+        throw new IOException("Failed to initialize " + allProjectsName + " refs:\n" + bru);
+      }
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceModule.java
index 65843d8..ee57c8b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceModule.java
@@ -25,6 +25,7 @@
     bind(DataSourceType.class).annotatedWith(Names.named("derby")).to(Derby.class);
     bind(DataSourceType.class).annotatedWith(Names.named("h2")).to(H2.class);
     bind(DataSourceType.class).annotatedWith(Names.named("jdbc")).to(JDBC.class);
+    bind(DataSourceType.class).annotatedWith(Names.named("mariadb")).to(MariaDb.class);
     bind(DataSourceType.class).annotatedWith(Names.named("mysql")).to(MySql.class);
     bind(DataSourceType.class).annotatedWith(Names.named("oracle")).to(Oracle.class);
     bind(DataSourceType.class).annotatedWith(Names.named("postgresql")).to(PostgreSQL.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/MariaDb.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/MariaDb.java
new file mode 100644
index 0000000..ed18a86
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/MariaDb.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import static com.google.gerrit.server.schema.JdbcUtil.hostname;
+import static com.google.gerrit.server.schema.JdbcUtil.port;
+
+import com.google.gerrit.server.config.ConfigSection;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import org.eclipse.jgit.lib.Config;
+
+class MariaDb extends BaseDataSourceType {
+  private final Config cfg;
+
+  @Inject
+  MariaDb(@GerritServerConfig Config cfg) {
+    super("org.mariadb.jdbc.Driver");
+    this.cfg = cfg;
+  }
+
+  @Override
+  public String getUrl() {
+    StringBuilder b = new StringBuilder();
+    ConfigSection dbs = new ConfigSection(cfg, "database");
+    b.append("jdbc:mariadb://");
+    b.append(hostname(dbs.optional("hostname")));
+    b.append(port(dbs.optional("port")));
+    b.append("/");
+    b.append(dbs.required("database"));
+    return b.toString();
+  }
+
+  @Override
+  public boolean usePool() {
+    // MariaDB has given us trouble with the connection pool,
+    // sometimes the backend disconnects and the pool winds
+    // up with a stale connection. Fortunately opening up
+    // a new MariaDB connection is usually very fast.
+    return false;
+  }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index 112fdd6..9a32365 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -30,6 +30,7 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.notedb.ConfigNotesMigration;
 import com.google.gerrit.testutil.InMemoryDatabase;
 import com.google.gerrit.testutil.InMemoryH2Type;
 import com.google.gerrit.testutil.InMemoryRepositoryManager;
@@ -108,6 +109,7 @@
                     bind(DataSourceType.class).to(InMemoryH2Type.class);
 
                     bind(SystemGroupBackend.class);
+                    install(new ConfigNotesMigration.Module());
                   }
                 })
             .getInstance(SchemaUpdater.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
index d5b1712..0a9e182 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -420,7 +420,7 @@
   private void initKeyExchanges(Config cfg) {
     List<NamedFactory<KeyExchange>> a = ServerBuilder.setUpDefaultKeyExchanges(true);
     setKeyExchangeFactories(
-        filter(cfg, "kex", (NamedFactory<KeyExchange>[]) a.toArray(new NamedFactory[a.size()])));
+        filter(cfg, "kex", (NamedFactory<KeyExchange>[]) a.toArray(new NamedFactory<?>[a.size()])));
   }
 
   private void initProviderBouncyCastle(Config cfg) {
@@ -528,14 +528,14 @@
 
     a.add(null);
     setCipherFactories(
-        filter(cfg, "cipher", (NamedFactory<Cipher>[]) a.toArray(new NamedFactory[a.size()])));
+        filter(cfg, "cipher", (NamedFactory<Cipher>[]) a.toArray(new NamedFactory<?>[a.size()])));
   }
 
   @SuppressWarnings("unchecked")
   private void initMacs(Config cfg) {
     List<NamedFactory<Mac>> m = BaseBuilder.setUpDefaultMacs(true);
     setMacFactories(
-        filter(cfg, "mac", (NamedFactory<Mac>[]) m.toArray(new NamedFactory[m.size()])));
+        filter(cfg, "mac", (NamedFactory<Mac>[]) m.toArray(new NamedFactory<?>[m.size()])));
   }
 
   @SafeVarargs
diff --git a/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
index 3ae9440..02aa1b9 100644
--- a/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
@@ -49,6 +49,10 @@
         <Set name="driverClassName">com.mysql.jdbc.Driver</Set>
         <Set name="url">jdbc:mysql://localhost/reviewdb?user=gerrit2&amp;password=secretkey</Set>
 -->
+<!--  MariaDB
+        <Set name="driverClassName">org.mariadb.jdbc.Driver</Set>
+        <Set name="url">jdbc:mariadb://localhost/reviewdb?user=gerrit2&amp;password=secretkey</Set>
+-->
 <!--  H2
         <Set name="driverClassName">org.h2.Driver</Set>
         <Set name="url">jdbc:h2:file:ReviewDb</Set>
diff --git a/lib/BUILD b/lib/BUILD
index fe1933c..c2148bf 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -264,6 +264,7 @@
         ":args4j",
         ":gson",
         ":guava",
+        ":html-types",
         ":icu4j",
         ":jsr305",
         ":protobuf",
@@ -279,6 +280,13 @@
 )
 
 java_library(
+    name = "html-types",
+    data = ["//lib:LICENSE-Apache2.0"],
+    visibility = ["//visibility:public"],
+    exports = ["@html_types//jar"],
+)
+
+java_library(
     name = "icu4j",
     data = ["//lib:LICENSE-icu4j"],
     visibility = ["//visibility:public"],
diff --git a/plugins/replication b/plugins/replication
index 305c864..7bdacab 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 305c864db28eb0c77c8499bc04c87de3f849cf3c
+Subproject commit 7bdacabfcc4c9b17b773f2783bf406c02ad738fb
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
index 1f7f85c..77d1c05 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -31,6 +31,10 @@
           return window.localStorage;
         },
       },
+      _exceededQuota: {
+        type: Boolean,
+        value: false,
+      },
     },
 
     getDraftComment: function(location) {
@@ -94,7 +98,20 @@
     },
 
     _setObject: function(key, obj) {
-      this._storage.setItem(key, JSON.stringify(obj));
+      if (this._exceededQuota) { return; }
+      try {
+        this._storage.setItem(key, JSON.stringify(obj));
+      } catch (exc) {
+        // Catch for QuotaExceededError and disable writes on local storage the
+        // first time that it occurs.
+        if (exc.code === 22) {
+          this._exceededQuota = true;
+          console.warn('Local storage quota exceeded: disabling');
+          return;
+        } else {
+          throw exc;
+        }
+      }
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
index f6c24cb..b17a9c9 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
@@ -31,19 +31,21 @@
 <script>
   suite('gr-storage tests', function() {
     var element;
-    var storage;
 
-    function cleanupStorage() {
-      // Make sure there are no entries in storage.
-      for (var key in window.localStorage) {
-        window.localStorage.removeItem(key);
-      }
+    function mockStorage(opt_quotaExceeded) {
+      return {
+        getItem: function(key) { return this[key]; },
+        removeItem: function(key) { delete this[key]; },
+        setItem: function(key, value) {
+          if (opt_quotaExceeded) { throw {code: 22}; /* Quota exceeded */ }
+          this[key] = value;
+        },
+      };
     }
 
     setup(function() {
       element = fixture('basic');
-      storage = element._storage;
-      cleanupStorage();
+      element._storage = mockStorage();
     });
 
     test('storing, retrieving and erasing drafts', function() {
@@ -68,18 +70,16 @@
 
       // Setting the draft stores it under the expected key.
       element.setDraftComment(location, 'my comment');
-      assert.isOk(storage.getItem(key));
-      assert.equal(JSON.parse(storage.getItem(key)).message, 'my comment');
-      assert.isOk(JSON.parse(storage.getItem(key)).updated);
+      assert.isOk(element._storage.getItem(key));
+      assert.equal(JSON.parse(element._storage.getItem(key)).message,
+          'my comment');
+      assert.isOk(JSON.parse(element._storage.getItem(key)).updated);
 
       // Erasing the draft removes the key.
       element.eraseDraftComment(location);
-      assert.isNotOk(storage.getItem(key));
-
-      cleanupStorage();
+      assert.isNotOk(element._storage.getItem(key));
     });
 
-
     test('automatically removes old drafts', function() {
       var changeNum = 1234;
       var patchNum = 5;
@@ -100,7 +100,7 @@
       var cleanupSpy = sinon.spy(element, '_cleanupDrafts');
 
       // Create a message with a timestamp that is a second behind the max age.
-      storage.setItem(key, JSON.stringify({
+      element._storage.setItem(key, JSON.stringify({
         message: 'old message',
         updated: Date.now() - 24 * 60 * 60 * 1000 - 1000,
       }));
@@ -110,10 +110,9 @@
 
       assert.isTrue(cleanupSpy.called);
       assert.isNotOk(draft);
-      assert.isNotOk(storage.getItem(key));
+      assert.isNotOk(element._storage.getItem(key));
 
       cleanupSpy.restore();
-      cleanupStorage();
     });
 
     test('_getDraftKey', function() {
@@ -138,5 +137,25 @@
       expectedResult = 'draft:1234:5:my_source_file.js:123:1-1-1-2';
       assert.equal(element._getDraftKey(location), expectedResult);
     });
+
+    test('exceeded quota disables storage', function() {
+      element._storage = mockStorage(true);
+      assert.isFalse(element._exceededQuota);
+
+      var changeNum = 1234;
+      var patchNum = 5;
+      var path = 'my_source_file.js';
+      var line = 123;
+      var location = {
+        changeNum: changeNum,
+        patchNum: patchNum,
+        path: path,
+        line: line,
+      };
+      var key = element._getDraftKey(location);
+      element.setDraftComment(location, 'my comment');
+      assert.isTrue(element._exceededQuota);
+      assert.isNotOk(element._storage.getItem(key));
+    });
   });
 </script>
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 7ea0989..5e8d69a 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -56,6 +56,12 @@
 def retrieve_ext_location():
   return check_output(['bazel', 'info', 'output_base']).strip()
 
+def gen_bazel_path():
+  bazel = check_output(['which', 'bazel']).strip()
+  with open(path.join(ROOT, ".bazel_path"), 'w') as fd:
+    fd.write("bazel=%s\n" % bazel)
+    fd.write("PATH=%s\n" % environ["PATH"])
+
 def _query_classpath(target):
   deps = []
   t = cp_targets[target]
@@ -258,6 +264,7 @@
   gen_project(args.project_name)
   gen_classpath(ext_location)
   gen_factorypath(ext_location)
+  gen_bazel_path()
 
   # TODO(davido): Remove this when GWT gone
   gwt_working_dir = ".gwt_work_dir"
diff --git a/tools/maven/api.sh b/tools/maven/api.sh
index 8c441fb..e72e3cb 100755
--- a/tools/maven/api.sh
+++ b/tools/maven/api.sh
@@ -16,9 +16,9 @@
 
 set -e
 
-if [[ "$#" != "1" ]] ; then
+if [[ "$#" -lt "1" ]] ; then
   cat <<EOF
-Usage: run "$0 COMMAND" from the top of your workspace,
+Usage: run "$0 COMMAND [build_args...]" from the top of your workspace,
 where COMMAND is one of
 
   install
@@ -54,12 +54,13 @@
     exit 1
     ;;
 esac
+shift
 
 if [[ "${VERBOSE:-x}" != "x" ]]; then
   set -o xtrace
 fi
 
-bazel build //tools/maven:gen_${command} || \
+bazel build //tools/maven:gen_${command} "$@" || \
   { echo "bazel failed to build gen_${command}. Use VERBOSE=1 for more info" ; exit 1 ; }
 
 ./bazel-genfiles/tools/maven/${command}.sh