Support replication to local folder

Allow to specify a local folder for 'remote.<name>.url' in
'replication.config' so that the git repositories are replicated to
this local folder.

Replicating to a local folder makes e.g. sense if a network share is
mounted to which the repositories should be replicated for backup
purposes.

Change-Id: I4fb5af8efcb9ab3798707f1403cd1231854e10bc
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/Documentation/config-replication.txt b/Documentation/config-replication.txt
index 787a9be..772282e 100644
--- a/Documentation/config-replication.txt
+++ b/Documentation/config-replication.txt
@@ -14,7 +14,10 @@
 public/private key pair.  On a trusted network it is also possible to
 use replication over the insecure (but much faster) git:// protocol,
 by enabling the `receive-pack` service on the receiving system, but
-this configuration is not recommended.
+this configuration is not recommended.  It is also possible to
+specify a local path as replication target. This makes e.g. sense if
+a network share is mounted to which the repositories should be
+replicated.
 
 Enabling Replication
 --------------------
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
index d359a67..154c609 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
@@ -39,8 +39,10 @@
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.JschConfigSessionFactory;
 import org.eclipse.jgit.transport.OpenSshConfig;
 import org.eclipse.jgit.transport.RefSpec;
@@ -270,16 +272,43 @@
   }
 
   private void replicateProject(final URIish replicateURI, final String head) {
+    if (!replicateURI.isRemote()) {
+      replicateProjectLocally(replicateURI, head);
+    } else if (usingSSH(replicateURI)) {
+      replicateProjectOverSsh(replicateURI, head);
+    } else {
+      log.warn("Cannot create new project on remote site since neither the "
+          + "connection method is SSH nor the replication target is local: "
+          + replicateURI.toString());
+      return;
+    }
+  }
+
+  private void replicateProjectLocally(final URIish replicateURI,
+      final String head) {
+    try {
+      final Repository repo = new FileRepository(replicateURI.getPath());
+      try {
+        repo.create(true /* bare */);
+
+        final RefUpdate u = repo.updateRef(Constants.HEAD);
+        u.disableRefLog();
+        u.link(head);
+      } finally {
+        repo.close();
+      }
+    } catch (IOException e) {
+      log.error("Failed to replicate project locally: "
+          + replicateURI.getPath());
+    }
+  }
+
+  private void replicateProjectOverSsh(final URIish replicateURI,
+      final String head) {
     SshSessionFactory sshFactory = SshSessionFactory.getInstance();
     RemoteSession sshSession;
     String projectPath = QuotedString.BOURNE.quote(replicateURI.getPath());
 
-    if (!usingSSH(replicateURI)) {
-      log.warn("Cannot create new project on remote site since the connection "
-          + "method is not SSH: " + replicateURI.toString());
-      return;
-    }
-
     OutputStream errStream = createErrStream();
     String cmd =
         "mkdir -p " + projectPath + "&& cd " + projectPath