Merge branch 'stable-2.9' into stable-2.10

* stable-2.9:
  Prevent creating repos on extra servers

Conflicts:
	src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java

Change-Id: I5934d118a72dd249b17b7f381aa3081ef58aa09d
diff --git a/.gitignore b/.gitignore
index fc3c0ff..9c143f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,6 @@
 /.settings/org.eclipse.m2e.core.prefs
 /.idea
 replication.iml
+/.buckd
+/buck-cache
+/buck-out
diff --git a/BUCK b/BUCK
index 6048a93..2e8a623 100644
--- a/BUCK
+++ b/BUCK
@@ -9,8 +9,9 @@
     'Gerrit-Module: com.googlesource.gerrit.plugins.replication.ReplicationModule',
     'Gerrit-SshModule: com.googlesource.gerrit.plugins.replication.SshModule'
   ],
-  compile_deps = [
+  provided_deps = [
     '//lib/commons:io',
+    '//lib/log:log4j'
   ],
 )
 
@@ -18,13 +19,13 @@
   name = 'replication_tests',
   srcs = glob(['src/test/java/**/*.java']),
   deps = [
-    ':replication__plugin__compile',
+    ':replication__plugin',
     '//gerrit-common:server',
     '//gerrit-reviewdb:server',
     '//gerrit-server:server',
-    '//lib:easymock',
     '//lib:gwtorm',
     '//lib:junit',
+    '//lib/easymock:easymock',
     '//lib/jgit:jgit',
   ],
 )
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 a2b66d1..83b97c6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
@@ -13,13 +13,11 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.replication;
 
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.PluginUser;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.WorkQueue;
-import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Singleton;
@@ -44,14 +42,13 @@
   private final SitePaths site;
   private final RemoteSiteUser.Factory remoteSiteUserFactory;
   private final PluginUser pluginUser;
-  private final SchemaFactory<ReviewDb> db;
   private final GitRepositoryManager gitRepositoryManager;
   private final GroupBackend groupBackend;
   private final WorkQueue workQueue;
 
   @Inject
   public AutoReloadConfigDecorator(Injector injector, SitePaths site,
-      RemoteSiteUser.Factory ruf, PluginUser pu, SchemaFactory<ReviewDb> db,
+      RemoteSiteUser.Factory ruf, PluginUser pu,
       GitRepositoryManager grm, GroupBackend gb,
       WorkQueue workQueue) throws ConfigInvalidException,
       IOException {
@@ -59,7 +56,6 @@
     this.site = site;
     this.remoteSiteUserFactory = ruf;
     this.pluginUser = pu;
-    this.db = db;
     this.gitRepositoryManager = grm;
     this.groupBackend = gb;
     this.currentConfig = loadConfig();
@@ -70,7 +66,7 @@
   private ReplicationFileBasedConfig loadConfig()
       throws ConfigInvalidException, IOException {
     return new ReplicationFileBasedConfig(injector, site,
-        remoteSiteUserFactory, pluginUser, db, gitRepositoryManager,
+        remoteSiteUserFactory, pluginUser, gitRepositoryManager,
         groupBackend);
   }
 
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 feff6c0..ef659f2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -38,7 +38,6 @@
 import com.google.gerrit.server.project.PerRequestProjectControlCache;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.util.RequestContext;
-import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
 import com.google.inject.Provides;
@@ -65,8 +64,9 @@
 import java.util.concurrent.TimeUnit;
 
 class Destination {
-  private static final Logger log = ReplicationQueue.log;
-  private static final ReplicationStateLogger stateLog = new ReplicationStateLogger(log);
+  private static final Logger repLog = ReplicationQueue.repLog;
+  private static final ReplicationStateLogger stateLog =
+      new ReplicationStateLogger(repLog);
 
   private final int poolThreads;
   private final String poolName;
@@ -97,7 +97,6 @@
   Destination(final Injector injector,
       final RemoteConfig rc,
       final Config cfg,
-      final SchemaFactory<ReviewDb> db,
       final RemoteSiteUser.Factory replicationUserFactory,
       final PluginUser pluginUser,
       final GitRepositoryManager gitRepositoryManager,
@@ -130,7 +129,7 @@
         if (g != null) {
           builder.add(g.getUUID());
         } else {
-          log.warn(String.format(
+          repLog.warn(String.format(
               "Group \"%s\" not recognized, removing from authGroup", name));
         }
       }
@@ -220,6 +219,7 @@
 
   void schedule(final Project.NameKey project, final String ref,
       final URIish uri, ReplicationState state) {
+    repLog.info("scheduling replication {}:{} => {}", project, ref, uri);
     if (!isVisible(project, state)) {
       return;
     }
@@ -265,13 +265,15 @@
       e.addRef(ref);
       state.increasePushTaskCount(project.get(), ref);
       e.addState(ref, state);
+      repLog.info("scheduled {}:{} => {} to run after {}s", project, ref,
+          e, delay);
     }
   }
 
   /**
    * It schedules again a PushOp instance.
    * <p>
-   * If the reason for rescheduling is to avoid a collison
+   * If the reason for rescheduling is to avoid a collision
    * with an in-flight push to the same URI, we don't
    * mark the operation as "retrying," and we schedule
    * using the replication delay, rather than the retry
@@ -482,8 +484,9 @@
         } else if (remoteNameStyle.equals("basenameOnly")) {
           name = FilenameUtils.getBaseName(name);
         } else if (!remoteNameStyle.equals("slash")) {
-            ReplicationQueue.log.debug(String.format(
-                "Unknown remoteNameStyle: %s, falling back to slash", remoteNameStyle));
+          repLog.debug(String.format(
+              "Unknown remoteNameStyle: %s, falling back to slash",
+              remoteNameStyle));
         }
         String replacedPath = ReplicationQueue.replaceName(uri.getPath(), name,
             isSingleProjectMatch());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java
index 165ef03..720da54 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushAll.java
@@ -21,15 +21,12 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 class PushAll implements Runnable {
-  private static final Logger log = LoggerFactory.getLogger(PushAll.class);
-  private static final ReplicationStateLogger stateLog = new ReplicationStateLogger(log);
+  private static final ReplicationStateLogger stateLog =
+      new ReplicationStateLogger(ReplicationQueue.repLog);
 
   interface Factory {
     PushAll create(String urlMatch, ReplicationState state);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
index 876a4d5..b699e0d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.replication;
 
+import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
 import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Lists;
@@ -33,6 +34,7 @@
 import com.google.gerrit.server.git.VisibleRefFilter;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.util.IdGenerator;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
@@ -59,6 +61,7 @@
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
 import org.slf4j.Logger;
+import org.slf4j.MDC;
 
 import java.io.IOException;
 import java.util.Collection;
@@ -68,6 +71,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A push to remote operation started by {@link GitReferenceUpdatedListener}.
@@ -76,9 +80,11 @@
  * take that lock to ensure they are working with a current view of the object.
  */
 class PushOne implements ProjectRunnable {
-  private static final Logger log = ReplicationQueue.log;
-  private static final ReplicationStateLogger stateLog = new ReplicationStateLogger(log);
+  private static final Logger repLog = ReplicationQueue.repLog;
+  private static final ReplicationStateLogger stateLog =
+      new ReplicationStateLogger(repLog);
   static final String ALL_REFS = "..all..";
+  static final String ID_MDC_KEY = "pushOneId";
 
   interface Factory {
     PushOne create(Project.NameKey d, URIish u);
@@ -106,6 +112,7 @@
       LinkedListMultimap.create();
   private final int maxLockRetries;
   private int lockRetryCount;
+  private final int id;
 
   @Inject
   PushOne(final GitRepositoryManager grm,
@@ -117,6 +124,7 @@
       final PerThreadRequestScope.Scoper ts,
       final ChangeCache cc,
       final ReplicationQueue rq,
+      final IdGenerator ig,
       @Assisted final Project.NameKey d,
       @Assisted final URIish u) {
     gitManager = grm;
@@ -132,6 +140,7 @@
     uri = u;
     lockRetryCount = 0;
     maxLockRetries = pool.getLockErrorMaxRetries();
+    id = ig.next();
   }
 
   @Override
@@ -151,10 +160,12 @@
 
   @Override
   public String toString() {
-    if (retryCount == 0) {
-      return "push " + uri;
+    String print = "[" + IdGenerator.format(id) + "] push " + uri;
+
+    if (retryCount > 0) {
+      print = "(retry " + retryCount + ") " + print;
     }
-    return "(retry " + retryCount + ") " + "push " + uri;
+    return print;
   }
 
   boolean isRetrying() {
@@ -182,8 +193,10 @@
     if (ALL_REFS.equals(ref)) {
       delta.clear();
       pushAllRefs = true;
+      repLog.trace("Added all refs for replication to " + uri);
     } else if (!pushAllRefs) {
       delta.add(ref);
+      repLog.trace("Added ref " + ref + " for replication to " + uri);
     }
   }
 
@@ -257,18 +270,23 @@
     // we start replication (instead a new instance, with the same URI, is
     // created and scheduled for a future point in time.)
     //
+    MDC.put(ID_MDC_KEY, IdGenerator.format(id));
     if (!pool.requestRunway(this)) {
       if (!canceled) {
-        log.info("Rescheduling replication to " + uri +
-                 " to avoid collision with an in-flight push.");
+        repLog.info("Rescheduling replication to " + uri
+            + " to avoid collision with an in-flight push.");
         pool.reschedule(this, Destination.RetryReason.COLLISION);
       }
       return;
     }
 
+    Stopwatch stopwatch = Stopwatch.createStarted();
+    repLog.info("Replication to " + uri + " started...");
     try {
       git = gitManager.openRepository(projectName);
       runImpl();
+      repLog.info("Replication to " + uri + " completed in "
+          + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
     } catch (RepositoryNotFoundException e) {
       stateLog.error("Cannot replicate " + projectName
           + "; Local repository error: "
@@ -282,9 +300,8 @@
       if (msg.contains("access denied")) {
         createRepository();
       } else {
-        log.error("Cannot replicate " + projectName
-            + "; Remote repository error: "
-            + msg);
+        repLog.error("Cannot replicate " + projectName
+            + "; Remote repository error: " + msg);
       }
 
     } catch (NoRemoteRepositoryException e) {
@@ -296,33 +313,29 @@
       Throwable cause = e.getCause();
       if (cause instanceof JSchException
           && cause.getMessage().startsWith("UnknownHostKey:")) {
-        log.error("Cannot replicate to " + uri + ": " + cause.getMessage());
+        repLog.error("Cannot replicate to " + uri + ": " + cause.getMessage());
       } else if (e instanceof LockFailureException) {
         lockRetryCount++;
         // The LockFailureException message contains both URI and reason
         // for this failure.
-        log.error("Cannot replicate to " + e.getMessage());
+        repLog.error("Cannot replicate to " + e.getMessage());
 
         // The remote push operation should be retried.
         if (lockRetryCount <= maxLockRetries) {
           pool.reschedule(this, Destination.RetryReason.TRANSPORT_ERROR);
         } else {
-          log.error("Giving up after " + lockRetryCount
+          repLog.error("Giving up after " + lockRetryCount
               + " of this error during replication to " + e.getMessage());
         }
       } else {
-        log.error("Cannot replicate to " + uri, e);
+        repLog.error("Cannot replicate to " + uri, e);
         // The remote push operation should be retried.
         pool.reschedule(this, Destination.RetryReason.TRANSPORT_ERROR);
       }
     } catch (IOException e) {
       stateLog.error("Cannot replicate to " + uri, e, getStatesAsArray());
-    } catch (RuntimeException e) {
+    } catch (RuntimeException | Error e) {
       stateLog.error("Unexpected error during replication to " + uri, e, getStatesAsArray());
-
-    } catch (Error e) {
-      stateLog.error("Unexpected error during replication to " + uri, e, getStatesAsArray());
-
     } finally {
       if (git != null) {
         git.close();
@@ -348,7 +361,7 @@
               }
             };
         replicationQueue.onNewProjectCreated(event);
-        log.warn("Missing repository created; retry replication to " + uri);
+        repLog.warn("Missing repository created; retry replication to " + uri);
         pool.reschedule(this, Destination.RetryReason.REPOSITORY_MISSING);
       } catch (IOException ioe) {
         stateLog.error("Cannot replicate to " + uri + "; failed to create missing repository",
@@ -368,7 +381,7 @@
       try {
         tn.close();
       } catch (Throwable e2) {
-        log.warn("Unexpected error while closing " + uri, e2);
+        repLog.warn("Unexpected error while closing " + uri, e2);
       }
     }
 
@@ -389,7 +402,7 @@
       return new PushResult();
     }
 
-    log.info("Push to " + uri + " references: " + todo);
+    repLog.info("Push to " + uri + " references: " + todo);
 
     return tn.push(NullProgressMonitor.INSTANCE, todo);
   }
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 dd8f4f5..57d07b8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
@@ -15,13 +15,11 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.PluginUser;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.WorkQueue;
-import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Singleton;
@@ -50,7 +48,6 @@
   private boolean replicateAllOnPluginStart;
   private boolean defaultForceUpdate;
   private Injector injector;
-  private final SchemaFactory<ReviewDb> database;
   private final RemoteSiteUser.Factory replicationUserFactory;
   private final PluginUser pluginUser;
   private final GitRepositoryManager gitRepositoryManager;
@@ -60,13 +57,12 @@
   @Inject
   public ReplicationFileBasedConfig(final Injector injector, final SitePaths site,
       final RemoteSiteUser.Factory ruf, final PluginUser pu,
-      final SchemaFactory<ReviewDb> db, final GitRepositoryManager grm,
+      final GitRepositoryManager grm,
       final GroupBackend gb) throws ConfigInvalidException, IOException {
     this.cfgPath = new File(site.etc_dir, "replication.config");
     this.injector = injector;
     this.replicationUserFactory = ruf;
     this.pluginUser = pu;
-    this.database = db;
     this.gitRepositoryManager = grm;
     this.groupBackend = gb;
     this.config = new FileBasedConfig(cfgPath, FS.DETECTED);
@@ -127,7 +123,7 @@
       }
 
       Destination destination =
-          new Destination(injector, c, config, database, replicationUserFactory,
+          new Destination(injector, c, config, replicationUserFactory,
               pluginUser, gitRepositoryManager, groupBackend);
 
       if (!destination.isSingleProjectMatch()) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationLogFile.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationLogFile.java
new file mode 100644
index 0000000..aeb0519
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationLogFile.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2014 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.googlesource.gerrit.plugins.replication;
+
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.util.SystemLog;
+import com.google.inject.Inject;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+
+public class ReplicationLogFile implements LifecycleListener {
+
+  private final SystemLog systemLog;
+
+  @Inject
+  public ReplicationLogFile(final SystemLog systemLog) {
+    this.systemLog = systemLog;
+  }
+
+  @Override
+  public void start() {
+    Logger replicationLogger =
+        LogManager.getLogger(ReplicationQueue.REPLICATION_LOG_NAME);
+    replicationLogger.removeAllAppenders();
+    replicationLogger.addAppender(systemLog.createAsyncAppender(
+        replicationLogger.getName(), new PatternLayout("[%d] [%X{"
+            + PushOne.ID_MDC_KEY + "}] %m%n")));
+    replicationLogger.setAdditivity(false);
+  }
+
+  @Override
+  public void stop() {
+    LogManager.getLogger(ReplicationQueue.repLog.getName()).removeAllAppenders();
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java
index 8aa3248..6e2ffdc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java
@@ -47,6 +47,8 @@
     bind(LifecycleListener.class)
       .annotatedWith(UniqueAnnotations.create())
       .to(OnStartStop.class);
+    bind(LifecycleListener.class).annotatedWith(UniqueAnnotations.create()).to(
+        ReplicationLogFile.class);
     bind(CredentialsFactory.class).to(
         AutoReloadSecureCredentialsFactoryDecorator.class).in(Scopes.SINGLETON);
     bind(CapabilityDefinition.class)
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 223e597..8be6b21 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -60,8 +60,10 @@
     NewProjectCreatedListener,
     ProjectDeletedListener,
     HeadUpdatedListener {
-  static final Logger log = LoggerFactory.getLogger(ReplicationQueue.class);
-  private static final ReplicationStateLogger stateLog = new ReplicationStateLogger(log);
+  static final String REPLICATION_LOG_NAME = "replication_log";
+  static final Logger repLog = LoggerFactory.getLogger(REPLICATION_LOG_NAME);
+  private static final ReplicationStateLogger stateLog =
+      new ReplicationStateLogger(repLog);
 
   static String replaceName(String in, String name, boolean keyIsOptional) {
     String key = "${name}";
@@ -102,16 +104,16 @@
     running = false;
     int discarded = config.shutdown();
     if (discarded > 0) {
-      log.warn(String.format(
-          "Cancelled %d replication events during shutdown",
-          discarded));
+      repLog.warn(String.format(
+          "Cancelled %d replication events during shutdown", discarded));
     }
   }
 
   void scheduleFullSync(final Project.NameKey project, final String urlMatch,
       ReplicationState state) {
     if (!running) {
-      stateLog.warn("Replication plugin did not finish startup before event", state);
+      stateLog.warn("Replication plugin did not finish startup before event",
+          state);
       return;
     }
 
@@ -170,7 +172,7 @@
       return Collections.emptySet();
     }
     if (!running) {
-      log.error("Replication plugin did not finish startup before event");
+      repLog.error("Replication plugin did not finish startup before event");
       return Collections.emptySet();
     }
 
@@ -195,20 +197,23 @@
         try {
           uri = new URIish(url);
         } catch (URISyntaxException e) {
-          log.warn(String.format("adminURL '%s' is invalid: %s", url, e.getMessage()));
+          repLog.warn(String.format("adminURL '%s' is invalid: %s", url,
+              e.getMessage()));
           continue;
         }
 
         String path = replaceName(uri.getPath(), projectName.get(),
             config.isSingleProjectMatch());
         if (path == null) {
-          log.warn(String.format("adminURL %s does not contain ${name}", uri));
+          repLog.warn(String
+              .format("adminURL %s does not contain ${name}", uri));
           continue;
         }
 
         uri = uri.setPath(path);
         if (!isSSH(uri)) {
-          log.warn(String.format("adminURL '%s' is invalid: only SSH is supported", uri));
+          repLog.warn(String.format(
+              "adminURL '%s' is invalid: only SSH is supported", uri));
           continue;
         }
 
@@ -228,12 +233,12 @@
   private void createProject(URIish replicateURI, String head) {
     if (!replicateURI.isRemote()) {
       createLocally(replicateURI, head);
-      log.info("Created local repository: " + replicateURI);
+      repLog.info("Created local repository: " + replicateURI);
     } else if (isSSH(replicateURI)) {
       createRemoteSsh(replicateURI, head);
-      log.info("Created remote repository: " + replicateURI);
+      repLog.info("Created remote repository: " + replicateURI);
     } else {
-      log.warn(String.format("Cannot create new project on remote site %s."
+      repLog.warn(String.format("Cannot create new project on remote site %s."
           + " Only local paths and SSH URLs are supported"
           + " for remote repository creation", replicateURI));
     }
@@ -254,9 +259,8 @@
         repo.close();
       }
     } catch (IOException e) {
-      log.error(String.format(
-          "Error creating local repository %s:\n",
-          uri.getPath()), e);
+      repLog.error(String.format(
+          "Error creating local repository %s:\n", uri.getPath()), e);
     }
   }
 
@@ -272,7 +276,7 @@
     try {
       executeRemoteSsh(uri, cmd, errStream);
     } catch (IOException e) {
-      log.error(String.format(
+      repLog.error(String.format(
              "Error creating remote repository at %s:\n"
           + "  Exception: %s\n"
           + "  Command: %s\n"
@@ -284,12 +288,12 @@
   private void deleteProject(URIish replicateURI) {
     if (!replicateURI.isRemote()) {
       deleteLocally(replicateURI);
-      log.info("Deleted local repository: " + replicateURI);
+      repLog.info("Deleted local repository: " + replicateURI);
     } else if (isSSH(replicateURI)) {
       deleteRemoteSsh(replicateURI);
-      log.info("Deleted remote repository: " + replicateURI);
+      repLog.info("Deleted remote repository: " + replicateURI);
     } else {
-      log.warn(String.format("Cannot delete project on remote site %s."
+      repLog.warn(String.format("Cannot delete project on remote site %s."
           + " Only local paths and SSH URLs are supported"
           + " for remote repository deletion", replicateURI));
     }
@@ -299,7 +303,7 @@
     try {
       recursivelyDelete(new File(uri.getPath()));
     } catch (IOException e) {
-      log.error(String.format(
+      repLog.error(String.format(
           "Error deleting local repository %s:\n",
           uri.getPath()), e);
     }
@@ -330,7 +334,7 @@
     try {
       executeRemoteSsh(uri, cmd, errStream);
     } catch (IOException e) {
-      log.error(String.format(
+      repLog.error(String.format(
              "Error deleting remote repository at %s:\n"
           + "  Exception: %s\n"
           + "  Command: %s\n"
@@ -345,9 +349,10 @@
     } else if (isSSH(replicateURI)) {
       updateHeadRemoteSsh(replicateURI, newHead);
     } else {
-      log.warn(String.format("Cannot update HEAD of project on remote site %s."
-          + " Only local paths and SSH URLs are supported"
-          + " for remote HEAD update.", replicateURI));
+      repLog.warn(String.format(
+          "Cannot update HEAD of project on remote site %s."
+              + " Only local paths and SSH URLs are supported"
+              + " for remote HEAD update.", replicateURI));
     }
   }
 
@@ -359,7 +364,7 @@
     try {
       executeRemoteSsh(uri, cmd, errStream);
     } catch (IOException e) {
-      log.error(String.format(
+      repLog.error(String.format(
              "Error updating HEAD of remote repository at %s to %s:\n"
           + "  Exception: %s\n"
           + "  Command: %s\n"
@@ -380,7 +385,9 @@
         repo.close();
       }
     } catch (IOException e) {
-      log.error(String.format("Failed to update HEAD of repository %s to %s", uri.getPath(), newHead), e);
+      repLog.error(
+          String.format("Failed to update HEAD of repository %s to %s",
+              uri.getPath(), newHead), e);
     }
   }