make repo urls relative for jiri supermanifest
Change-Id: I03f2cd61a34a6f9ac518fc2ac095c59aa0e79f21
diff --git a/src/main/java/com/googlesource/gerrit/plugins/supermanifest/JiriUpdater.java b/src/main/java/com/googlesource/gerrit/plugins/supermanifest/JiriUpdater.java
index 3e92349..7d8426e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/supermanifest/JiriUpdater.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/supermanifest/JiriUpdater.java
@@ -6,6 +6,7 @@
import java.io.IOException;
import java.net.URI;
import java.text.MessageFormat;
+import java.util.StringJoiner;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -13,6 +14,7 @@
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -47,7 +49,11 @@
}
private void updateSubmodules(
- Repository repo, String targetRef, JiriProjects projects, GerritRemoteReader reader)
+ Repository repo,
+ String targetRef,
+ URI targetURI,
+ JiriProjects projects,
+ GerritRemoteReader reader)
throws IOException, GitAPIException {
DirCache index = DirCache.newInCore();
DirCacheBuilder builder = index.builder();
@@ -95,9 +101,20 @@
}
}
- nameUri = URI.create(nameUri).toString();
+ URI submodUrl = URI.create(nameUri);
+
+ //check if repo exists locally then relativize its URL
+ try {
+ String repoName = submodUrl.getPath();
+ while (repoName.startsWith("/")) {
+ repoName = repoName.substring(1);
+ }
+ reader.openRepository(repoName);
+ submodUrl = relativize(targetURI, URI.create(repoName));
+ } catch (RepositoryNotFoundException e) {
+ }
cfg.setString("submodule", path, "path", path);
- cfg.setString("submodule", path, "url", nameUri);
+ cfg.setString("submodule", path, "url", submodUrl.toString());
// create gitlink
DirCacheEntry dcEntry = new DirCacheEntry(path);
@@ -155,6 +172,73 @@
}
}
+ private static final String SLASH = "/";
+ /*
+ * Copied from https://github.com/eclipse/jgit/blob/e9fb111182b55cc82c530d82f13176c7a85cd958/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java#L729
+ */
+ static URI relativize(URI current, URI target) {
+ // We only handle bare paths for now.
+ if (!target.toString().equals(target.getPath())) {
+ return target;
+ }
+ if (!current.toString().equals(current.getPath())) {
+ return target;
+ }
+
+ String cur = current.normalize().getPath();
+ String dest = target.normalize().getPath();
+
+ if (cur.startsWith(SLASH) != dest.startsWith(SLASH)) {
+ return target;
+ }
+
+ while (cur.startsWith(SLASH)) {
+ cur = cur.substring(1);
+ }
+ while (dest.startsWith(SLASH)) {
+ dest = dest.substring(1);
+ }
+
+ if (cur.indexOf('/') == -1 || dest.indexOf('/') == -1) {
+ // Avoid having to special-casing in the next two ifs.
+ String prefix = "prefix/";
+ cur = prefix + cur;
+ dest = prefix + dest;
+ }
+
+ if (!cur.endsWith(SLASH)) {
+ // The current file doesn't matter.
+ int lastSlash = cur.lastIndexOf('/');
+ cur = cur.substring(0, lastSlash);
+ }
+ String destFile = "";
+ if (!dest.endsWith(SLASH)) {
+ // We always have to provide the destination file.
+ int lastSlash = dest.lastIndexOf('/');
+ destFile = dest.substring(lastSlash + 1, dest.length());
+ dest = dest.substring(0, dest.lastIndexOf('/'));
+ }
+
+ String[] cs = cur.split(SLASH);
+ String[] ds = dest.split(SLASH);
+
+ int common = 0;
+ while (common < cs.length && common < ds.length && cs[common].equals(ds[common])) {
+ common++;
+ }
+
+ StringJoiner j = new StringJoiner(SLASH);
+ for (int i = common; i < cs.length; i++) {
+ j.add("..");
+ }
+ for (int i = common; i < ds.length; i++) {
+ j.add(ds[i]);
+ }
+
+ j.add(destFile);
+ return URI.create(j.toString());
+ }
+
@Override
public void update(GerritRemoteReader reader, ConfigEntry c, String srcRef)
throws IOException, GitAPIException, ConfigInvalidException {
@@ -162,6 +246,7 @@
Repository destRepo = reader.openRepository(c.getDestRepoKey().toString());
JiriProjects projects = JiriManifestParser.getProjects(srcRepo, srcRef, c.getXmlPath());
String targetRef = c.getDestBranch().equals("*") ? srcRef : REFS_HEADS + c.getDestBranch();
- updateSubmodules(destRepo, targetRef, projects, reader);
+ updateSubmodules(
+ destRepo, targetRef, URI.create(c.getDestRepoKey().toString() + "/"), projects, reader);
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/supermanifest/JiriSuperManifestIT.java b/src/test/java/com/googlesource/gerrit/plugins/supermanifest/JiriSuperManifestIT.java
index 8556150..18d9d6d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/supermanifest/JiriSuperManifestIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/supermanifest/JiriSuperManifestIT.java
@@ -15,6 +15,7 @@
package com.googlesource.gerrit.plugins.supermanifest;
import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.fail;
import com.google.gerrit.acceptance.GitUtil;
@@ -24,10 +25,14 @@
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.reviewdb.client.Project.NameKey;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
+import java.net.URI;
import java.util.Arrays;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.BlobBasedConfig;
+import org.eclipse.jgit.lib.Config;
import org.junit.Test;
@TestPlugin(
@@ -270,6 +275,79 @@
}
@Test
+ public void relativeFetch() throws Exception {
+ // Test that first party gerrit repos are represented by relative URLs in supermanifest and
+ // external repos by their absolute URLs.
+ setupTestRepos("platform/project");
+
+ String realPrefix = testRepoKeys[0].get().split("/")[0];
+
+ Project.NameKey manifestKey = createProject(realPrefix + "/manifest");
+ TestRepository<InMemoryRepository> manifestRepo = cloneProject(manifestKey, admin);
+
+ Project.NameKey superKey = createProject("superproject");
+ pushConfig(
+ "[superproject \""
+ + superKey.get()
+ + ":refs/heads/destbranch\"]\n"
+ + " srcRepo = "
+ + manifestKey.get()
+ + "\n"
+ + " srcRef = refs/heads/srcbranch\n"
+ + " srcPath = default\n"
+ + " toolType = jiri\n");
+
+ // XML change will trigger commit to superproject.
+ String xml =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<manifest>\n<projects>\n"
+ + "<project name=\""
+ + testRepoKeys[0].get()
+ + "\" remote=\""
+ + canonicalWebUrl.get()
+ + testRepoKeys[0].get()
+ + "\" path=\"project1\" />\n"
+ + "<project name=\"external\""
+ + " remote=\"https://external/repo\""
+ + " revision=\"c438d02cdf08a08fe29550cb11cb6ae8190919f1\""
+ + " path=\"project2\" />\n"
+ + "</projects>\n</manifest>\n";
+
+ pushFactory
+ .create(db, admin.getIdent(), manifestRepo, "Subject", "default", xml)
+ .to("refs/heads/srcbranch")
+ .assertOkStatus();
+
+ BranchApi branch = gApi.projects().name(superKey.get()).branch("refs/heads/destbranch");
+ assertThat(branch.file("project1").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
+ assertThat(branch.file("project2").getContentType()).isEqualTo("x-git/gitlink; charset=UTF-8");
+
+ Config base = new Config();
+ BlobBasedConfig cfg =
+ new BlobBasedConfig(base, branch.file(".gitmodules").asString().getBytes(UTF_8));
+
+ String subUrl = cfg.getString("submodule", "project1", "url");
+
+ // URL is valid.
+ URI.create(subUrl);
+
+ // The suburl must be interpreted as relative to the parent project as a directory, i.e.
+ // to go from superproject/ to platform/project0, you have to do ../platform/project0
+
+ // URL is clean.
+ assertThat(subUrl).isEqualTo("../" + realPrefix + "/project0");
+
+ subUrl = cfg.getString("submodule", "project2", "url");
+
+ // URL is valid.
+ URI.create(subUrl);
+
+ // The suburl must be absolute as this is external repo
+
+ assertThat(subUrl).isEqualTo("https://external/repo");
+ }
+
+ @Test
public void manifestIncludesOtherManifest() throws Exception {
setupTestRepos("project");
@@ -388,4 +466,31 @@
// all fine.
}
}
+
+ /*
+ * Copied test from https://github.com/eclipse/jgit/blob/e9fb111182b55cc82c530d82f13176c7a85cd958/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java#L1105
+ */
+ void testRelative(String a, String b, String want) {
+ String got = JiriUpdater.relativize(URI.create(a), URI.create(b)).toString();
+
+ if (!got.equals(want)) {
+ fail(String.format("relative('%s', '%s') = '%s', want '%s'", a, b, got, want));
+ }
+ }
+
+ @Test
+ public void relative() {
+ testRelative("a/b/", "a/", "../");
+ // Normalization:
+ testRelative("a/p/..//b/", "a/", "../");
+ testRelative("a/b", "a/", "");
+ testRelative("a/", "a/b/", "b/");
+ testRelative("a/", "a/b", "b");
+ testRelative("/a/b/c", "/b/c", "../../b/c");
+ testRelative("/abc", "bcd", "bcd");
+ testRelative("abc", "def", "def");
+ testRelative("abc", "/bcd", "/bcd");
+ testRelative("http://a", "a/b", "a/b");
+ testRelative("http://base.com/a/", "http://child.com/a/b", "http://child.com/a/b");
+ }
}