Add method to push changes directly to given replica

This makes it possible to push changes directly to given replica
instance without sending unnecessary requests to others.

The method is intended to be used by other plugins that extend the
replication plugin.

This is a reworked version of the change that was originally done
on stable-2.16 but removed during the merge up to stable-3.1 in
change Iab801b94f.

Change-Id: Ib3e5a6c3afde834855d055b31beb43a560f8d785
Signed-off-by: Dariusz Luksza <dariusz@luksza.org>
Signed-off-by: David Pursehouse <dpursehouse@digital.ai>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
index 869f728..5fb97bd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -17,6 +17,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Queues;
+import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.events.HeadUpdatedListener;
@@ -139,15 +140,37 @@
     }
 
     for (Destination cfg : destinations.get().getAll(FilterType.ALL)) {
-      if (cfg.wouldPushProject(project) && cfg.wouldPushRef(refName)) {
-        for (URIish uri : cfg.getURIs(project, urlMatch)) {
-          replicationTasksStorage.create(
-              new ReplicateRefUpdate(project.get(), refName, uri, cfg.getRemoteConfigName()));
-          cfg.schedule(project, refName, uri, state, now);
-        }
-      } else {
-        repLog.debug("Skipping ref {} on project {}", refName, project.get());
+      pushReference(cfg, project, urlMatch, refName, state, now);
+    }
+  }
+
+  @UsedAt(UsedAt.Project.COLLABNET)
+  public void pushReference(Destination cfg, Project.NameKey project, String refName) {
+    pushReference(cfg, project, null, refName, null, true);
+  }
+
+  private void pushReference(
+      Destination cfg,
+      Project.NameKey project,
+      String urlMatch,
+      String refName,
+      ReplicationState state,
+      boolean now) {
+    boolean withoutState = state == null;
+    if (withoutState) {
+      state = new ReplicationState(new GitUpdateProcessing(dispatcher.get()));
+    }
+    if (cfg.wouldPushProject(project) && cfg.wouldPushRef(refName)) {
+      for (URIish uri : cfg.getURIs(project, urlMatch)) {
+        replicationTasksStorage.create(
+            new ReplicateRefUpdate(project.get(), refName, uri, cfg.getRemoteConfigName()));
+        cfg.schedule(project, refName, uri, state, now);
       }
+    } else {
+      repLog.debug("Skipping ref {} on project {}", refName, project.get());
+    }
+    if (withoutState) {
+      state.markAllPushTasksScheduled();
     }
   }