Merge branch 'stable-2.15'

* stable-2.15:
  ChangeIT: Temporarily disable tests related to group matching
  Align SSH create-project with REST API
  SubmoduleOp: Fix formatting of submodule update commit message
  Fix Postgresql JDBC driver leaking memory
  Document that the build works with Python 2 or 3
  merge_jars.py: Fix for python 3 compatibility
  project.py: decode byte output from check_result
  license and doc: Add support for python3
  Bazel: Make build tool chain python 3 compatible
  ExternalIds NoteDb migration: Avoid intermediate migration state
  Make PrivateStateChanged and WorkInProgressStateChanged singletons
  BatchProgramModule: Bind GitReferenceUpdated.DISABLED
  BatchProgramModule: Don't bind event classes to null
  Document that Python 2 is required by the build
  Make event firing classes singleton
  Fix ConcurrentModificationException when posting reviews
  Revert partially "Trim multi-line arguments for task name and ssh_log"
  BaseCommand: Fix formatting of task description with arguments
  Add analytics to the list of plugins
  config-plugins: Consistently use "/+doc/" for documentation links
  config-plugins: Fix link to ref-protection plugin documentation
  config-plugins: Add readonly plugin to plugin list
  Add documentation for SshCreateCommandInterceptor
  Allow plugins to intercept ssh command creation
  GWT/Poly: Default to "Create initial commit" during project creation
  Revert "Reduce chance of deadlock in account cache"
  commit-msg: Adapt to awk behavior change on Cygwin/MSYS
  Expand docs on how to include external dependencies in core plugins
  Document how to bundle custom plugins in release.war
  Group members can't be added as reviewers if a matched username exists
  Set version to 2.13.11
  StreamEventsApiListener: Prevent NPE when account is null
  Set version to 2.11.11
  Replace links to code.google.com/p/gerrit
  Update issue tracker URL in documentation
  Update issue tracker URL in POM files
  Bump jsch to 0.54
  Update jsch to 0.1.53
  Bump Jsch to 1.52

Change-Id: Icec3dcfe53146da7d66d4d690afdd9e77ee10380
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 4d1ea05..e8c3857 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -60,7 +60,14 @@
 
 --owner::
 -o::
-	Name of the group(s) which will initially own this repository.
+	Identifier of the group(s) which will initially own this repository.
+
+        This can be:
+
+        * the UUID of the group
+        * the legacy numeric ID of the group
+        * the name of the group if it is unique
+
 	The specified group(s) must already be defined within Gerrit.
 	Several groups can be specified on the command line.
 +
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index 185feb4..c5f1457 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -119,7 +119,7 @@
 
 link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/review-strategy[
 Project] |
-link:https://gerrit.googlesource.com/plugins/review-strategy/+/master/src/main/resources/Documentation/about.md[
+link:https://gerrit.googlesource.com/plugins/review-strategy/+doc/master/src/main/resources/Documentation/about.md[
 Documentation]
 
 [[singleusergroup]]
@@ -161,6 +161,18 @@
 link:https://gerrit.googlesource.com/plugins/admin-console/+doc/master/src/main/resources/Documentation/about.md[
 Documentation]
 
+[[analytics]]
+=== analytics
+
+Plugin to extract commit and review data from Gerrit projects and
+expose aggregated metrics over REST and SSH API.
+Metrics are extracted in JSON format with one record per line, ready to be
+archived and processed with popular BigData transformation tools such
+Apache Spark or published and visualized in dashboards.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/analytics[Project] |
+link:https://gerrit.googlesource.com/plugins/analytics/+doc/master/README.md[Documentation]
+
 [[avatars-external]]
 === avatars-external
 
@@ -473,7 +485,7 @@
 appends `label('Owner-Approval', need(_))` to a provided list.
 
 link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/owners[Project] |
-link:https://gerrit.googlesource.com/plugins/owners/+/refs/heads/master/README.md[Documentation]
+link:https://gerrit.googlesource.com/plugins/owners/+doc/master/README.md[Documentation]
 
 [[project-download-commands]]
 === project-download-commands
@@ -507,6 +519,19 @@
 link:https://gerrit.googlesource.com/plugins/quota/+doc/master/src/main/resources/Documentation/config.md[
 Configuration]
 
+[[readonly]]
+=== readonly
+
+A plugin that makes the Gerrit server read-only by rejecting git pushes,
+blocking HTTP PUT/POST/DELETE requests, and disabling SSH commands.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/readonly[
+Project] |
+link:https://gerrit.googlesource.com/plugins/readonly/+doc/master/src/main/resources/Documentation/about.md[
+Documentation] |
+link:https://gerrit.googlesource.com/plugins/readonly/+doc/master/src/main/resources/Documentation/config.md[
+Configuration]
+
 [[ref-protection]]
 === ref-protection
 
@@ -517,7 +542,7 @@
 
 link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/ref-protection[
 Project] |
-link:https://gerrit.googlesource.com/plugins/ref-protection/+/refs/heads/stable-2.11/src/main/resources/Documentation/about.md[
+link:https://gerrit.googlesource.com/plugins/ref-protection/+doc/master/src/main/resources/Documentation/about.md[
 Documentation]
 
 [[reparent]]
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index f6cdc06..3e52f16 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -7,6 +7,7 @@
 
 * A Linux or macOS system (Windows is not supported at this time)
 * A JDK for Java 8
+* Python 2 or 3
 * Node.js
 * link:https://www.bazel.io/versions/master/docs/install.html[Bazel]
 
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 90b8521..189c6c7 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2716,6 +2716,26 @@
 }
 ----
 
+[[ssh-command-interception]]
+== SSH Command Interception
+
+Gerrit provides an extension point that allows a plugin to intercept
+creation of SSH commands and override the functionality with its own
+implementation.
+
+[source, java]
+----
+import com.google.gerrit.sshd.SshCreateCommandInterceptor;
+
+class MyCommandInterceptor implements SshCreateCommandInterceptor {
+  @Override
+  public String intercept(String in) {
+    return pluginName + " mycommand";
+  }
+}
+----
+
+
 == SEE ALSO
 
 * link:js-api.html[JavaScript API]
diff --git a/Documentation/replace_macros.py b/Documentation/replace_macros.py
index 2996a98..09e2439 100755
--- a/Documentation/replace_macros.py
+++ b/Documentation/replace_macros.py
@@ -229,12 +229,16 @@
 options, _ = opts.parse_args()
 
 try:
-  out_file = open(options.out, 'w')
-  src_file = open(options.src, 'r')
+  try:
+    out_file = open(options.out, 'w', errors='ignore')
+    src_file = open(options.src, 'r', errors='ignore')
+  except TypeError:
+    out_file = open(options.out, 'w')
+    src_file = open(options.src, 'r')
   last_line = ''
   ignore_next_line = False
   last_title = ''
-  for line in src_file.xreadlines():
+  for line in src_file:
     if PAT_GERRIT.match(last_line):
       # Case of "GERRIT\n------" at the footer
       out_file.write(GERRIT_UPLINK)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
index a73d78e..02b9169 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
@@ -130,6 +130,7 @@
     addGrid(fp);
 
     emptyCommit = new CheckBox(AdminConstants.I.checkBoxEmptyCommit());
+    emptyCommit.setValue(true);
     permissionsOnly = new CheckBox(AdminConstants.I.checkBoxPermissionsOnly());
     fp.add(emptyCommit);
     fp.add(permissionsOnly);
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 77dc8ea..06efbfd 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -1161,6 +1161,14 @@
     return createGroup(name, "Administrators");
   }
 
+  protected String createGroupWithRealName(String name) throws Exception {
+    GroupInput in = new GroupInput();
+    in.name = name;
+    in.ownerId = "Administrators";
+    gApi.groups().create(in);
+    return name;
+  }
+
   protected String createGroup(String name, String owner) throws Exception {
     name = name(name);
     GroupInput in = new GroupInput();
diff --git a/java/com/google/gerrit/pgm/MigrateToNoteDb.java b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
index 2340a97..22fdb2c 100644
--- a/java/com/google/gerrit/pgm/MigrateToNoteDb.java
+++ b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.index.DummyIndexModule;
 import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
 import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
@@ -189,7 +188,6 @@
           @Override
           public void configure() {
             install(dbInjector.getInstance(BatchProgramModule.class));
-            bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
             install(new DummyIndexModule());
             factory(ChangeResource.Factory.class);
           }
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index 15a330e..7bf85dc 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.pgm.util.ThreadLimiter;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.index.IndexModule;
 import com.google.gerrit.server.index.IndexModule.IndexType;
 import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
@@ -166,7 +165,6 @@
           @Override
           protected void configure() {
             factory(ChangeResource.Factory.class);
-            bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
           }
         });
 
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 7af6c21..a7803a9 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -51,6 +51,9 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.GitReceivePackGroups;
 import com.google.gerrit.server.config.GitUploadPackGroups;
+import com.google.gerrit.server.extensions.events.EventUtil;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.extensions.events.RevisionCreated;
 import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.git.SearchingChangeCacheImpl;
 import com.google.gerrit.server.git.TagCache;
@@ -169,6 +172,9 @@
     factory(SubmitRuleEvaluator.Factory.class);
 
     bind(ChangeJson.Factory.class).toProvider(Providers.<ChangeJson.Factory>of(null));
+    bind(EventUtil.class).toProvider(Providers.<EventUtil>of(null));
+    bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
+    bind(RevisionCreated.class).toInstance(RevisionCreated.DISABLED);
     bind(AccountVisibility.class).toProvider(AccountVisibilityProvider.class).in(SINGLETON);
   }
 }
diff --git a/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java b/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
index d547b8c..5958ed6 100644
--- a/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
@@ -14,12 +14,18 @@
 
 package com.google.gerrit.server.args4j;
 
+import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
+import java.util.Optional;
 import org.kohsuke.args4j.CmdLineException;
 import org.kohsuke.args4j.CmdLineParser;
 import org.kohsuke.args4j.OptionDef;
@@ -29,20 +35,50 @@
 
 public class AccountGroupUUIDHandler extends OptionHandler<AccountGroup.UUID> {
   private final GroupBackend groupBackend;
+  private final GroupControl.Factory groupControlFactory;
+  private final GroupCache groupCache;
 
   @Inject
   public AccountGroupUUIDHandler(
       final GroupBackend groupBackend,
+      final GroupControl.Factory groupControlFactory,
       @Assisted final CmdLineParser parser,
       @Assisted final OptionDef option,
-      @Assisted final Setter<AccountGroup.UUID> setter) {
+      @Assisted final Setter<AccountGroup.UUID> setter,
+      GroupCache groupCache) {
     super(parser, option, setter);
     this.groupBackend = groupBackend;
+    this.groupControlFactory = groupControlFactory;
+    this.groupCache = groupCache;
   }
 
   @Override
   public final int parseArguments(Parameters params) throws CmdLineException {
     final String n = params.getParameter(0);
+    AccountGroup.UUID uuid = new AccountGroup.UUID(n);
+    if (groupBackend.handles(uuid)) {
+      GroupDescription.Basic d = groupBackend.get(uuid);
+      if (d != null) {
+        setter.addValue(uuid);
+        return 1;
+      }
+    }
+
+    // Might be a numeric AccountGroup.Id. -> Internal group.
+    if (n.matches("^[1-9][0-9]*$")) {
+      try {
+        AccountGroup.Id groupId = AccountGroup.Id.parse(n);
+        Optional<InternalGroup> groupInternal = groupCache.get(groupId);
+        if (groupInternal.isPresent()) {
+          uuid = new InternalGroupDescription(groupInternal.get()).getGroupUUID();
+          setter.addValue(uuid);
+          return 1;
+        }
+      } catch (IllegalArgumentException e) {
+        // Ignored
+      }
+    }
+
     GroupReference group = GroupBackends.findExactSuggestion(groupBackend, n);
     if (group == null) {
       throw new CmdLineException(owner, "Group \"" + n + "\" does not exist");
diff --git a/java/com/google/gerrit/server/extensions/events/AgreementSignup.java b/java/com/google/gerrit/server/extensions/events/AgreementSignup.java
index 5f24ee4..1548c38 100644
--- a/java/com/google/gerrit/server/extensions/events/AgreementSignup.java
+++ b/java/com/google/gerrit/server/extensions/events/AgreementSignup.java
@@ -19,7 +19,9 @@
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.account.AccountState;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
+@Singleton
 public class AgreementSignup {
   private final DynamicSet<AgreementSignupListener> listeners;
   private final EventUtil util;
diff --git a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
index e51ecd0..3aeaf43 100644
--- a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
@@ -23,10 +23,12 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class AssigneeChanged {
   private static final Logger log = LoggerFactory.getLogger(AssigneeChanged.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
index 14038a4..d600240 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
@@ -29,11 +29,13 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ChangeAbandoned {
   private static final Logger log = LoggerFactory.getLogger(ChangeAbandoned.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
index 64bb3a2..04dba5c 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
@@ -29,11 +29,13 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ChangeMerged {
   private static final Logger log = LoggerFactory.getLogger(ChangeMerged.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
index 753a4f3..deee7e0 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
@@ -29,11 +29,13 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ChangeRestored {
   private static final Logger log = LoggerFactory.getLogger(ChangeRestored.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
index e57cbdd..1e91ab3 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
@@ -21,10 +21,12 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ChangeReverted {
   private static final Logger log = LoggerFactory.getLogger(ChangeReverted.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/CommentAdded.java b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
index 2757f73..ec35ea8 100644
--- a/java/com/google/gerrit/server/extensions/events/CommentAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
@@ -30,12 +30,14 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class CommentAdded {
   private static final Logger log = LoggerFactory.getLogger(CommentAdded.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/EventUtil.java b/java/com/google/gerrit/server/extensions/events/EventUtil.java
index 0b8ab33..1dd5537 100644
--- a/java/com/google/gerrit/server/extensions/events/EventUtil.java
+++ b/java/com/google/gerrit/server/extensions/events/EventUtil.java
@@ -35,6 +35,7 @@
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.EnumSet;
@@ -43,6 +44,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class EventUtil {
   private static final Logger log = LoggerFactory.getLogger(EventUtil.class);
 
@@ -65,7 +67,7 @@
 
   private final ChangeData.Factory changeDataFactory;
   private final Provider<ReviewDb> db;
-  private final ChangeJson changeJson;
+  private final ChangeJson.Factory changeJsonFactory;
 
   @Inject
   EventUtil(
@@ -74,11 +76,11 @@
       Provider<ReviewDb> db) {
     this.changeDataFactory = changeDataFactory;
     this.db = db;
-    this.changeJson = changeJsonFactory.create(CHANGE_OPTIONS);
+    this.changeJsonFactory = changeJsonFactory;
   }
 
   public ChangeInfo changeInfo(Change change) throws OrmException {
-    return changeJson.format(change);
+    return changeJsonFactory.create(CHANGE_OPTIONS).format(change);
   }
 
   public RevisionInfo revisionInfo(Project project, PatchSet ps)
@@ -91,7 +93,7 @@
       throws OrmException, PatchListNotAvailableException, GpgException, IOException,
           PermissionBackendException {
     ChangeData cd = changeDataFactory.create(db.get(), project, ps.getId().getParentKey());
-    return changeJson.getRevisionInfo(cd, ps);
+    return changeJsonFactory.create(CHANGE_OPTIONS).getRevisionInfo(cd, ps);
   }
 
   public AccountInfo accountInfo(AccountState accountState) {
diff --git a/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
index 893a488..2503ffc 100644
--- a/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
+++ b/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -21,11 +21,13 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.AccountState;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.transport.ReceiveCommand;
 
+@Singleton
 public class GitReferenceUpdated {
   public static final GitReferenceUpdated DISABLED =
       new GitReferenceUpdated() {
diff --git a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
index 9ef39bf..948ff2d 100644
--- a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
@@ -24,12 +24,14 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import java.util.Collection;
 import java.util.Set;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class HashtagsEdited {
   private static final Logger log = LoggerFactory.getLogger(HashtagsEdited.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/PluginEvent.java b/java/com/google/gerrit/server/extensions/events/PluginEvent.java
index bd643e2..8680ab1 100644
--- a/java/com/google/gerrit/server/extensions/events/PluginEvent.java
+++ b/java/com/google/gerrit/server/extensions/events/PluginEvent.java
@@ -17,7 +17,9 @@
 import com.google.gerrit.extensions.events.PluginEventListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
+@Singleton
 public class PluginEvent {
   private final DynamicSet<PluginEventListener> listeners;
 
diff --git a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
index 5d7c0d1..b30d473 100644
--- a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
@@ -23,10 +23,12 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class PrivateStateChanged {
   private static final Logger log = LoggerFactory.getLogger(PrivateStateChanged.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
index 0a9edc2..433ae06 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
@@ -30,12 +30,14 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.List;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ReviewerAdded {
   private static final Logger log = LoggerFactory.getLogger(ReviewerAdded.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
index ab21632..0037783 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
@@ -30,12 +30,14 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class ReviewerDeleted {
   private static final Logger log = LoggerFactory.getLogger(ReviewerDeleted.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
index 6eb4119..e4fa647 100644
--- a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
+++ b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
@@ -29,14 +29,27 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class RevisionCreated {
   private static final Logger log = LoggerFactory.getLogger(RevisionCreated.class);
 
+  public static final RevisionCreated DISABLED =
+      new RevisionCreated() {
+        @Override
+        public void fire(
+            Change change,
+            PatchSet patchSet,
+            AccountState uploader,
+            Timestamp when,
+            NotifyHandling notify) {}
+      };
+
   private final DynamicSet<RevisionCreatedListener> listeners;
   private final EventUtil util;
 
@@ -46,6 +59,11 @@
     this.util = util;
   }
 
+  private RevisionCreated() {
+    this.listeners = null;
+    this.util = null;
+  }
+
   public void fire(
       Change change,
       PatchSet patchSet,
diff --git a/java/com/google/gerrit/server/extensions/events/TopicEdited.java b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
index a68ad29..354137a 100644
--- a/java/com/google/gerrit/server/extensions/events/TopicEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
@@ -23,10 +23,12 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class TopicEdited {
   private static final Logger log = LoggerFactory.getLogger(TopicEdited.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
index cffd47d..bd48c32 100644
--- a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
@@ -30,12 +30,14 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class VoteDeleted {
   private static final Logger log = LoggerFactory.getLogger(VoteDeleted.class);
 
diff --git a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
index c06fdba..62312d8 100644
--- a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
@@ -23,10 +23,12 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class WorkInProgressStateChanged {
   private static final Logger log = LoggerFactory.getLogger(WorkInProgressStateChanged.class);
 
diff --git a/java/com/google/gerrit/server/git/SubmoduleOp.java b/java/com/google/gerrit/server/git/SubmoduleOp.java
index 3eb0040..56ed432 100644
--- a/java/com/google/gerrit/server/git/SubmoduleOp.java
+++ b/java/com/google/gerrit/server/git/SubmoduleOp.java
@@ -415,8 +415,13 @@
     PersonIdent author = null;
     DirCache dc = readTree(or.rw, currentCommit);
     DirCacheEditor ed = dc.editor();
+    int count = 0;
     for (SubmoduleSubscription s : targets.get(subscriber)) {
+      if (count > 0) {
+        msgbuf.append("\n\n");
+      }
       RevCommit newCommit = updateSubmodule(dc, ed, msgbuf, s);
+      count++;
       if (newCommit != null) {
         if (author == null) {
           author = newCommit.getAuthorIdent();
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index 3bf13d2..cbe1625 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -395,12 +395,11 @@
   }
 
   protected String getTaskDescription() {
-    StringBuilder m = new StringBuilder(commandName);
     String[] ta = getTrimmedArguments();
     if (ta != null) {
-      m.append(Joiner.on(" ").join(ta));
+      return commandName + " " + Joiner.on(" ").join(ta);
     }
-    return m.toString();
+    return commandName;
   }
 
   private String getTaskName() {
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 0287ceb..4cd7487 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -17,6 +17,7 @@
 import com.google.common.util.concurrent.Atomics;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.WorkQueue;
@@ -57,6 +58,7 @@
   private final ScheduledExecutorService startExecutor;
   private final ExecutorService destroyExecutor;
   private final SchemaFactory<ReviewDb> schemaFactory;
+  private final DynamicItem<SshCreateCommandInterceptor> createCommandInterceptor;
 
   @Inject
   CommandFactoryProvider(
@@ -65,11 +67,13 @@
       WorkQueue workQueue,
       SshLog l,
       SshScope s,
-      SchemaFactory<ReviewDb> sf) {
+      SchemaFactory<ReviewDb> sf,
+      DynamicItem<SshCreateCommandInterceptor> i) {
     dispatcher = d;
     log = l;
     sshScope = s;
     schemaFactory = sf;
+    createCommandInterceptor = i;
 
     int threads = cfg.getInt("sshd", "commandStartThreads", 2);
     startExecutor = workQueue.createQueue(threads, "SshCommandStart");
@@ -94,7 +98,12 @@
     return new CommandFactory() {
       @Override
       public Command createCommand(String requestCommand) {
-        return new Trampoline(requestCommand);
+        String c = requestCommand;
+        SshCreateCommandInterceptor interceptor = createCommandInterceptor.get();
+        if (interceptor != null) {
+          c = interceptor.intercept(c);
+        }
+        return new Trampoline(c);
       }
     };
   }
diff --git a/java/com/google/gerrit/sshd/SshCreateCommandInterceptor.java b/java/com/google/gerrit/sshd/SshCreateCommandInterceptor.java
new file mode 100644
index 0000000..d3ed12b
--- /dev/null
+++ b/java/com/google/gerrit/sshd/SshCreateCommandInterceptor.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.sshd;
+
+public interface SshCreateCommandInterceptor {
+
+  /**
+   * Intrcept SSH command creation
+   *
+   * @param in command name passed in to command instance creation machinery
+   * @return intercepted command name
+   */
+  String intercept(String in);
+}
diff --git a/java/com/google/gerrit/sshd/SshLog.java b/java/com/google/gerrit/sshd/SshLog.java
index 6b43d78..6465a30 100644
--- a/java/com/google/gerrit/sshd/SshLog.java
+++ b/java/com/google/gerrit/sshd/SshLog.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.sshd;
 
-import com.google.common.base.Joiner;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.TimeUtil;
@@ -283,9 +282,9 @@
       return "Command was already destroyed";
     }
     StringBuilder commandName = new StringBuilder(dcmd.getCommandName());
-    String[] trimmedArgs = dcmd.getTrimmedArguments();
-    if (trimmedArgs != null) {
-      commandName.append(Joiner.on(".").join(trimmedArgs));
+    String[] args = dcmd.getArguments();
+    for (int i = 1; i < args.length; i++) {
+      commandName.append(".").append(args[i]);
     }
     return commandName.toString();
   }
diff --git a/java/com/google/gerrit/sshd/SshModule.java b/java/com/google/gerrit/sshd/SshModule.java
index 4ad8ba3..03ed74f 100644
--- a/java/com/google/gerrit/sshd/SshModule.java
+++ b/java/com/google/gerrit/sshd/SshModule.java
@@ -19,6 +19,7 @@
 
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Splitter;
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.DynamicOptions;
@@ -100,6 +101,7 @@
         .to(SshPluginStarterCallback.class);
 
     DynamicMap.mapOf(binder(), DynamicOptions.DynamicBean.class);
+    DynamicItem.itemOf(binder(), SshCreateCommandInterceptor.class);
 
     listener().toInstance(registerInParentInjectors());
     listener().to(SshLog.class);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 9c89429..8f27de3 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -85,6 +85,7 @@
 import com.google.gerrit.extensions.api.changes.ReviewResult;
 import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.api.changes.StarsInput;
+import com.google.gerrit.extensions.api.groups.GroupApi;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.google.gerrit.extensions.api.projects.ConfigInput;
 import com.google.gerrit.extensions.client.ChangeKind;
@@ -167,6 +168,7 @@
 import org.eclipse.jgit.transport.PushResult;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 @NoHttpd
@@ -1504,6 +1506,100 @@
     assertThat(sender.getMessages()).hasSize(1);
   }
 
+  @Ignore
+  @Test
+  public void addReviewerThatIsNotPerfectMatch() throws Exception {
+    TestTimeUtil.resetWithClockStep(1, SECONDS);
+    PushOneCommit.Result r = createChange();
+    ChangeResource rsrc = parseResource(r);
+    String oldETag = rsrc.getETag();
+    Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
+
+    //create a group named "ab" with one user: testUser
+    TestAccount testUser = accountCreator.create("abcd", "abcd@test.com", "abcd");
+    String testGroup = createGroupWithRealName("ab");
+    GroupApi groupApi = gApi.groups().id(testGroup);
+    groupApi.description("test group");
+    groupApi.addMembers(user.fullName);
+
+    AddReviewerInput in = new AddReviewerInput();
+    in.reviewer = "abc";
+    gApi.changes().id(r.getChangeId()).addReviewer(in.reviewer);
+
+    List<Message> messages = sender.getMessages();
+    assertThat(messages).hasSize(1);
+    Message m = messages.get(0);
+    assertThat(m.rcpt()).containsExactly(testUser.emailAddress);
+    assertThat(m.body()).contains("Hello " + testUser.fullName + ",\n");
+    assertThat(m.body()).contains("I'd like you to do a code review.");
+    assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
+    assertMailReplyTo(m, testUser.email);
+    ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+
+    // When NoteDb is enabled adding a reviewer records that user as reviewer
+    // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
+    // approval on the change which is treated as CC when the ChangeInfo is
+    // created.
+    Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
+    assertThat(reviewers).isNotNull();
+    assertThat(reviewers).hasSize(1);
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(testUser.getId().get());
+
+    // Ensure ETag and lastUpdatedOn are updated.
+    rsrc = parseResource(r);
+    assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
+    assertThat(rsrc.getChange().getLastUpdatedOn()).isNotEqualTo(oldTs);
+  }
+
+  @Ignore
+  @Test
+  public void addGroupAsReviewersWhenANotPerfectMatchedUserExists() throws Exception {
+    TestTimeUtil.resetWithClockStep(1, SECONDS);
+    PushOneCommit.Result r = createChange();
+    ChangeResource rsrc = parseResource(r);
+    String oldETag = rsrc.getETag();
+    Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
+
+    //create a group named "us" with one user: testUser
+    TestAccount testUser = accountCreator.create("testUser", "testUser@test.com", "testUser");
+    String testGroup =
+        createGroupWithRealName(user.fullName.substring(0, user.fullName.length() / 2));
+    GroupApi groupApi = gApi.groups().id(testGroup);
+    groupApi.description("test group");
+    groupApi.addMembers(testUser.fullName);
+
+    //ensure that user "user" is not in the group
+    groupApi.removeMembers(user.fullName);
+
+    AddReviewerInput in = new AddReviewerInput();
+    in.reviewer = testGroup;
+    gApi.changes().id(r.getChangeId()).addReviewer(in.reviewer);
+
+    List<Message> messages = sender.getMessages();
+    assertThat(messages).hasSize(1);
+    Message m = messages.get(0);
+    assertThat(m.rcpt()).containsExactly(testUser.emailAddress);
+    assertThat(m.body()).contains("Hello " + testUser.fullName + ",\n");
+    assertThat(m.body()).contains("I'd like you to do a code review.");
+    assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
+    assertMailReplyTo(m, testUser.email);
+    ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+
+    // When NoteDb is enabled adding a reviewer records that user as reviewer
+    // in NoteDb. When NoteDb is disabled adding a reviewer results in a dummy 0
+    // approval on the change which is treated as CC when the ChangeInfo is
+    // created.
+    Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
+    assertThat(reviewers).isNotNull();
+    assertThat(reviewers).hasSize(1);
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(testUser.getId().get());
+
+    // Ensure ETag and lastUpdatedOn are updated.
+    rsrc = parseResource(r);
+    assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
+    assertThat(rsrc.getChange().getLastUpdatedOn()).isNotEqualTo(oldTs);
+  }
+
   @Test
   public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists() throws Exception {
     assume().that(notesMigration.readChanges()).isTrue();
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index 95b6a2a..7583830 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -313,6 +313,52 @@
     expectToHaveSubmoduleState(superRepo, "master", "sub2", sub2, "master");
     expectToHaveSubmoduleState(superRepo, "master", "sub3", sub3, "master");
 
+    String sub1HEAD =
+        sub1.git()
+            .fetch()
+            .setRemote("origin")
+            .call()
+            .getAdvertisedRef("refs/heads/master")
+            .getObjectId()
+            .name();
+
+    String sub2HEAD =
+        sub2.git()
+            .fetch()
+            .setRemote("origin")
+            .call()
+            .getAdvertisedRef("refs/heads/master")
+            .getObjectId()
+            .name();
+
+    String sub3HEAD =
+        sub3.git()
+            .fetch()
+            .setRemote("origin")
+            .call()
+            .getAdvertisedRef("refs/heads/master")
+            .getObjectId()
+            .name();
+
+    if (getSubmitType() == SubmitType.MERGE_IF_NECESSARY) {
+      expectToHaveCommitMessage(
+          superRepo,
+          "master",
+          "Update git submodules\n\n"
+              + "* Update "
+              + name("sub3")
+              + " from branch 'master'\n  to "
+              + sub3HEAD
+              + "\n\n* Update "
+              + name("sub2")
+              + " from branch 'master'\n  to "
+              + sub2HEAD
+              + "\n\n* Update "
+              + name("sub1")
+              + " from branch 'master'\n  to "
+              + sub1HEAD);
+    }
+
     superRepo
         .git()
         .fetch()
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
index 7652710..b053310 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
@@ -31,7 +31,7 @@
         value: () => {
           // Set default values for dropdowns.
           return {
-            create_empty_commit: false,
+            create_empty_commit: true,
             permissions_only: false,
           };
         },
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
index bdb5bcb..f531718 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog_test.html
@@ -49,7 +49,7 @@
     });
 
     test('default values are populated', () => {
-      assert.isFalse(element.$.initalCommit.bindValue);
+      assert.isTrue(element.$.initalCommit.bindValue);
       assert.isFalse(element.$.parentRepo.bindValue);
     });
 
diff --git a/resources/com/google/gerrit/server/tools/root/hooks/commit-msg b/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
index 03f5375..4c64559 100644
--- a/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
+++ b/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
@@ -78,6 +78,9 @@
 	# - in END, the last textLine+ block is available for footer parsing
 	$AWK '
 	BEGIN {
+		if (match(ENVIRON["OS"], "Windows")) {
+			RS="\r?\n" # Required on recent Cygwin
+		}
 		# while we start with the assumption that textLine+
 		# is a footer, the first block is not.
 		isFooter = 0
diff --git a/tools/bzl/license-map.py b/tools/bzl/license-map.py
index 1c8db72..74a84cc 100644
--- a/tools/bzl/license-map.py
+++ b/tools/bzl/license-map.py
@@ -113,8 +113,13 @@
   print()
   print("[[%s_license]]" % safename)
   print("----")
-  with open(n[2:].replace(":", "/")) as fd:
-    copyfileobj(fd, stdout)
+  filename = n[2:].replace(":", "/")
+  try:
+    with open(filename, errors='ignore') as fd:
+      copyfileobj(fd, stdout)
+  except TypeError:
+    with open(filename) as fd:
+      copyfileobj(fd, stdout)
   print()
   print("----")
   print()
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 9374d88..f054fad 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -68,7 +68,7 @@
   return check_output(_build_bazel_cmd('info', 'output_base')).strip()
 
 def gen_bazel_path():
-  bazel = check_output(['which', 'bazel']).strip()
+  bazel = check_output(['which', 'bazel']).strip().decode('UTF-8')
   with open(path.join(ROOT, ".bazel_path"), 'w') as fd:
     fd.write("bazel=%s\n" % bazel)
     fd.write("PATH=%s\n" % environ["PATH"])
diff --git a/tools/merge_jars.py b/tools/merge_jars.py
index 611b5ea..97a87c4 100755
--- a/tools/merge_jars.py
+++ b/tools/merge_jars.py
@@ -39,14 +39,12 @@
             continue
           elif n.startswith(SERVICES):
             # Concatenate all provider configuration files.
-            myfile = inzip.open(n, 'r')
-            myfile = io.TextIOWrapper(myfile, encoding='iso-8859-1', newline='')
-            services[n] += myfile.read()
+            services[n] += inzip.read(n).decode("UTF-8")
             continue
           outzip.writestr(info, inzip.read(n))
           seen.add(n)
 
-    for n, v in services.items():
+    for n, v in list(services.items()):
       outzip.writestr(n, v)
 except Exception as err:
   exit('Failed to merge jars: %s' % err)