Normalize paths as much as possible

Beginning slashes and a trailing ".git" suffix should be ignored, and
not included when canonicalizing project names.  Also, let UNC paths
go through untouched, and attempt to remove duplicate path elements in
a project reference.

Change-Id: Icc6b5cffc9d1bab99e6eb2e7c7e7f9965cf46e8a
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ManifestParser.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ManifestParser.java
index acaa3ee..1f5fb4b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ManifestParser.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ManifestParser.java
@@ -113,6 +113,9 @@
   }
 
   public void addRemote(String name, String fetch) {
+    if (fetch != null && fetch.endsWith("/")) {
+      fetch = fetch.substring(0, fetch.length()-1);
+    }
     remotes.put(name, fetch);
   }
 
@@ -155,6 +158,15 @@
       this.remote = remote;
       this.name = name;
       this.revision = revision;
+      if (this.name != null && this.name.endsWith("/")) {
+        this.name = this.name.substring(0, this.name.length()-1);
+      }
+      if (this.name != null && this.name.endsWith(".git")) {
+        this.name = this.name.substring(0, this.name.length()-4);
+      }
+      if (this.name != null && this.name.startsWith("/")) {
+        this.name = this.name.substring(1);
+      }
     }
 
     public String getRemote() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java
index 6e184a8..5c1165b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/RefUpdateHandlerImpl.java
@@ -81,7 +81,8 @@
     if (event.isDelete() && event.getRefName().startsWith(Constants.R_HEADS)
         || event.getRefName().startsWith(Constants.R_TAGS)) {
       // Ref was deleted... clean up any references
-      Ref ref = Ref.fetchByRef(getCanonicalProject(event.getProjectName()), event.getRefName());
+      Ref ref = Ref.fetchByRef(getCanonicalProject(event.getProjectName()),
+          event.getRefName());
       if (ref != null) {
         ref.delete();
       }
@@ -91,12 +92,12 @@
             event.getRefName());
       }
     } else if (event.getRefName().startsWith(Constants.R_TAGS)) {
-      Ref updatedRef = new Ref(getCanonicalProject(event.getProjectName()), event.getRefName(),
-          event.getNewObjectId());
+      Ref updatedRef = new Ref(getCanonicalProject(event.getProjectName()),
+          event.getRefName(), event.getNewObjectId());
       updatedRef.save();
     } else if (event.getRefName().startsWith(Constants.R_HEADS)) {
-      Ref updatedRef = new Ref(getCanonicalProject(event.getProjectName()), event.getRefName(),
-          event.getNewObjectId());
+      Ref updatedRef = new Ref(getCanonicalProject(event.getProjectName()),
+          event.getRefName(), event.getNewObjectId());
       updatedRef.save();
       Project.NameKey nameKey = new Project.NameKey(event.getProjectName());
       try {
@@ -256,9 +257,10 @@
           if (modulesUrl == null && modulesConfig != null) {
             for (String key : modulesConfig
                 .getSubsections(ConfigConstants.CONFIG_SUBMODULE_SECTION)) {
-              if (sw.getPath().equals(modulesConfig.getString(
-                  ConfigConstants.CONFIG_SUBMODULE_SECTION, key,
-                  ConfigConstants.CONFIG_KEY_PATH))) {
+              if (sw.getPath()
+                  .equals(modulesConfig.getString(
+                      ConfigConstants.CONFIG_SUBMODULE_SECTION, key,
+                      ConfigConstants.CONFIG_KEY_PATH))) {
                 modulesUrl = modulesConfig.getString(
                     ConfigConstants.CONFIG_SUBMODULE_SECTION, key,
                     ConfigConstants.CONFIG_KEY_URL);
@@ -267,7 +269,8 @@
             }
           }
           if (modulesUrl != null) {
-            submodules.put(normalizePath(event.getProjectName(), modulesUrl, false),
+            submodules.put(
+                normalizePath(event.getProjectName(), modulesUrl, false),
                 sw.getObjectId().name());
           } else {
             log.warn(String.format(
@@ -324,7 +327,21 @@
     String originalProject =
         isManifest ? project.substring(0, project.lastIndexOf(":")) : project;
 
+    // Strip trailing slashes and .git suffix
+    if (destination.endsWith("/")) {
+      destination = destination.substring(0, destination.length() - 1);
+    }
+
+    if (destination.endsWith(".git")) {
+      destination = destination.substring(0, destination.length() - 4);
+    }
+
     // Handle relative and absolute paths on the same server
+    if (destination.startsWith("//")) {
+      // UNC path; let this pass through unaltered.
+      // This should be rather uncommon, though.
+      return destination;
+    }
     if (destination.startsWith("/")) {
       if (serverName != null) {
         destination = serverName + destination;
@@ -350,7 +367,9 @@
       // Replace the protocol with a known scheme, to avoid angering URL
       destination = destination.replaceFirst("^[^:]+://", "");
       URL url = new URL("https://" + destination);
-      destination = url.getHost() + url.getPath();
+      destination = url.getHost();
+      Path path = Paths.get(url.getPath()).normalize();
+      destination += path.toString();
     } catch (MalformedURLException e) {
       log.warn("Could not parse destination as URL: " + destination);
     }