Merge branch 'stable-2.12'

* stable-2.12:
  Revert "Replicate HEAD reference when replicating a project"
  Fetch parent groups of the authGroups
  Replicate HEAD reference when replicating a project
  Revert "Remove obsolete remote.NAME.timeout from config documentation"
  Add logging of cancelled replication events

Change-Id: I159cba10662e7aaa6cd8d2f7b121e99e87ac24ce
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
index 2c946a5..2743549 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
@@ -16,6 +16,7 @@
 import com.google.gerrit.common.FileUtil;
 import com.google.gerrit.server.PluginUser;
 import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupIncludeCache;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.WorkQueue;
@@ -45,13 +46,15 @@
   private final GroupBackend groupBackend;
   private final WorkQueue workQueue;
   private final ReplicationStateListener stateLog;
+  private final GroupIncludeCache groupIncludeCache;
 
   @Inject
   public AutoReloadConfigDecorator(Injector injector, SitePaths site,
       RemoteSiteUser.Factory ruf, PluginUser pu,
       GitRepositoryManager grm, GroupBackend gb,
       WorkQueue workQueue,
-      ReplicationStateListener stateLog) throws ConfigInvalidException,
+      ReplicationStateListener stateLog,
+      GroupIncludeCache groupIncludeCache) throws ConfigInvalidException,
       IOException {
     this.injector = injector;
     this.site = site;
@@ -59,6 +62,7 @@
     this.pluginUser = pu;
     this.gitRepositoryManager = grm;
     this.groupBackend = gb;
+    this.groupIncludeCache = groupIncludeCache;
     this.currentConfig = loadConfig();
     this.currentConfigTs = getLastModified(currentConfig);
     this.workQueue = workQueue;
@@ -71,9 +75,9 @@
 
   private ReplicationFileBasedConfig loadConfig()
       throws ConfigInvalidException, IOException {
-    return new ReplicationFileBasedConfig(injector, site,
-        remoteSiteUserFactory, pluginUser, gitRepositoryManager,
-        groupBackend, stateLog);
+    return new ReplicationFileBasedConfig(injector, site, remoteSiteUserFactory,
+        pluginUser, gitRepositoryManager, groupBackend, stateLog,
+        groupIncludeCache);
   }
 
   private synchronized boolean isAutoReload() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
index b1a46b8..120c94c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -17,6 +17,7 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.extensions.config.FactoryModule;
@@ -28,6 +29,7 @@
 import com.google.gerrit.server.PluginUser;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
+import com.google.gerrit.server.account.GroupIncludeCache;
 import com.google.gerrit.server.account.ListGroupMembership;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
@@ -103,7 +105,8 @@
       final PluginUser pluginUser,
       final GitRepositoryManager gitRepositoryManager,
       final GroupBackend groupBackend,
-      final ReplicationStateListener stateLog) {
+      final ReplicationStateListener stateLog,
+      final GroupIncludeCache groupIncludeCache) {
     remote = rc;
     gitManager = gitRepositoryManager;
     this.stateLog = stateLog;
@@ -136,6 +139,7 @@
         GroupReference g = GroupBackends.findExactSuggestion(groupBackend, name);
         if (g != null) {
           builder.add(g.getUUID());
+          addRecursiveParents(g.getUUID(), builder, groupIncludeCache);
         } else {
           repLog.warn(String.format(
               "Group \"%s\" not recognized, removing from authGroup", name));
@@ -188,6 +192,17 @@
     threadScoper = child.getInstance(PerThreadRequestScope.Scoper.class);
   }
 
+  private void addRecursiveParents(AccountGroup.UUID g,
+      Builder<AccountGroup.UUID> builder, GroupIncludeCache groupIncludeCache) {
+    for (AccountGroup.UUID p : groupIncludeCache.parentGroupsOf(g)) {
+      if (builder.build().contains(p)) {
+        continue;
+      }
+      builder.add(p);
+      addRecursiveParents(p, builder, groupIncludeCache);
+    }
+  }
+
   void start(WorkQueue workQueue) {
     pool = workQueue.createQueue(poolThreads, poolName);
   }
@@ -195,6 +210,9 @@
   int shutdown() {
     int cnt = 0;
     if (pool != null) {
+      for (Runnable r : pool.getQueue()) {
+        repLog.warn(String.format("Cancelling replication event %s", r));
+      }
       cnt = pool.shutdownNow().size();
       pool.unregisterWorkQueue();
       pool = null;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
index 1486f1b..f7dc733 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Lists;
 import com.google.gerrit.server.PluginUser;
 import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupIncludeCache;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.WorkQueue;
@@ -56,14 +57,17 @@
   private final GroupBackend groupBackend;
   private final FileBasedConfig config;
   private final ReplicationStateListener stateLog;
+  private final GroupIncludeCache groupIncludeCache;
 
   @Inject
   public ReplicationFileBasedConfig(final Injector injector, final SitePaths site,
       final RemoteSiteUser.Factory ruf, final PluginUser pu,
       final GitRepositoryManager grm,
       final GroupBackend gb,
-      final ReplicationStateListener stateLog) throws ConfigInvalidException, IOException {
+      final ReplicationStateListener stateLog,
+      final GroupIncludeCache groupIncludeCache) throws ConfigInvalidException, IOException {
     this.cfgPath = site.etc_dir.resolve("replication.config");
+    this.groupIncludeCache = groupIncludeCache;
     this.injector = injector;
     this.replicationUserFactory = ruf;
     this.pluginUser = pu;
@@ -162,7 +166,7 @@
 
       Destination destination =
           new Destination(injector, c, config, replicationUserFactory,
-              pluginUser, gitRepositoryManager, groupBackend, stateLog);
+              pluginUser, gitRepositoryManager, groupBackend, stateLog, groupIncludeCache);
 
       if (!destination.isSingleProjectMatch()) {
         for (URIish u : c.getURIs()) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 54251ce..a528565 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -182,6 +182,19 @@
 
 [2]: #example_file
 
+remote.NAME.timeout
+:	Number of seconds to wait for a network read or write to
+	complete before giving up and declaring the remote side is not
+	responding.  If 0, there is no timeout, and the push client
+	waits indefinitely.
+
+	A timeout should be large enough to mostly transfer the
+	objects to the other side.  1 second may be too small for
+	larger projects, especially over a WAN link, while 10-30
+	seconds is a much more reasonable timeout value.
+
+	Defaults to 0 seconds, wait indefinitely.
+
 remote.NAME.replicationDelay
 :	Time to wait before scheduling a remote push operation. Setting
 	the delay to 0 effectively disables the delay, causing the push