From a9b9c5b324256e3e1f9c3267a81bf8bd1dbbbec4 Mon Sep 17 00:00:00 2001 From: Yuxuan 'fishy' Wang Date: Tue, 29 Jul 2014 15:43:04 -0700 Subject: [PATCH] Remove overlapping submodules from repo manifest. Apparently repo allows projects overlapping, e.g. one project's path is "foo" and another project's path is "foo/bar". This is not supported in git submodule. At JGit repo side we'll skip all the submodules that are in subdirectories of other submodules, and on repo side we'll make them submodules to resolve this problem. Change-Id: I6820c4ef400c530a36150b1228706adfcc43ef64 Signed-off-by: Yuxuan 'fishy' Wang --- .../eclipse/jgit/gitrepo/RepoCommandTest.java | 59 +++++++++++++++ .../org/eclipse/jgit/gitrepo/RepoCommand.java | 74 ++++++++++++++++--- 2 files changed, 124 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java index 15a7ef93c..3e5ef0273 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java @@ -512,6 +512,65 @@ public class RepoCommandTest extends RepositoryTestCase { assertFalse("The foo submodule shouldn't exist", foo); } + @Test + public void testRemoveOverlappingBare() throws Exception { + Repository remoteDb = createBareRepository(); + Repository tempDb = createWorkRepository(); + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("\n") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append(""); + JGitTestUtil.writeTrashFile( + tempDb, "manifest.xml", xmlContent.toString()); + RepoCommand command = new RepoCommand(remoteDb); + command + .setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri) + .call(); + // Clone it + File directory = createTempDirectory("testRemoveOverlappingBare"); + Repository localDb = Git + .cloneRepository() + .setDirectory(directory) + .setURI(remoteDb.getDirectory().toURI().toString()) + .call() + .getRepository(); + // The .gitmodules file should have 'submodule "foo"' and shouldn't have + // 'submodule "foo/bar"' lines. + File dotmodules = new File(localDb.getWorkTree(), + Constants.DOT_GIT_MODULES); + BufferedReader reader = new BufferedReader(new FileReader(dotmodules)); + boolean foo = false; + boolean foobar = false; + boolean a = false; + while (true) { + String line = reader.readLine(); + if (line == null) + break; + if (line.contains("submodule \"foo\"")) + foo = true; + if (line.contains("submodule \"foo/bar\"")) + foobar = true; + if (line.contains("submodule \"a\"")) + a = true; + } + reader.close(); + assertTrue("The foo submodule should exist", foo); + assertFalse("The foo/bar submodule shouldn't exist", foobar); + assertTrue("The a submodule should exist", a); + } + private void resolveRelativeUris() { // Find the longest common prefix ends with "/" as rootUri. defaultUri = defaultDb.getDirectory().toURI().toString(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java index c47ff0d74..95ab1aa59 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -53,8 +53,10 @@ import java.nio.channels.FileChannel; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -104,6 +106,11 @@ import org.xml.sax.helpers.XMLReaderFactory; * If called against a bare repository, it will replace all the existing content * of the repository with the contents populated from the manifest. * + * repo manifest allows projects overlapping, e.g. one project's path is + * "foo" and another project's path is "foo/bar". This won't + * work in git submodule, so we'll skip all the sub projects + * ("foo/bar" in the example) while converting. + * * @see git-repo project page * @since 3.4 */ @@ -249,7 +256,7 @@ public class RepoCommand extends GitCommand { } } - private static class Project { + private static class Project implements Comparable { final String name; final String path; final String revision; @@ -269,6 +276,31 @@ public class RepoCommand extends GitCommand { void addCopyFile(CopyFile copyfile) { copyfiles.add(copyfile); } + + String getPathWithSlash() { + if (path.endsWith("/")) //$NON-NLS-1$ + return path; + else + return path + "/"; //$NON-NLS-1$ + } + + boolean isAncestorOf(Project that) { + return that.getPathWithSlash().startsWith(this.getPathWithSlash()); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Project) { + Project that = (Project) o; + return this.getPathWithSlash().equals(that.getPathWithSlash()); + } + return false; + } + + @Override + public int compareTo(Project that) { + return this.getPathWithSlash().compareTo(that.getPathWithSlash()); + } } private static class XmlManifest extends DefaultHandler { @@ -277,9 +309,9 @@ public class RepoCommand extends GitCommand { private final String filename; private final String baseUrl; private final Map remotes; - private final List projects; private final Set plusGroups; private final Set minusGroups; + private List projects; private String defaultRemote; private String defaultRevision; private Project currentProject; @@ -389,14 +421,38 @@ public class RepoCommand extends GitCommand { } catch (URISyntaxException e) { throw new SAXException(e); } + removeNotInGroup(); + removeOverlaps(); for (Project proj : projects) { - if (inGroups(proj)) { - command.addSubmodule(remoteUrl + proj.name, - proj.path, - proj.revision == null - ? defaultRevision : proj.revision, - proj.copyfiles); - } + command.addSubmodule(remoteUrl + proj.name, + proj.path, + proj.revision == null + ? defaultRevision : proj.revision, + proj.copyfiles); + } + } + + /** Remove projects that are not in our desired groups. */ + void removeNotInGroup() { + Iterator iter = projects.iterator(); + while (iter.hasNext()) + if (!inGroups(iter.next())) + iter.remove(); + } + + /** Remove projects that sits in a subdirectory of any other project. */ + void removeOverlaps() { + Collections.sort(projects); + Iterator iter = projects.iterator(); + if (!iter.hasNext()) + return; + Project last = iter.next(); + while (iter.hasNext()) { + Project p = iter.next(); + if (last.isAncestorOf(p)) + iter.remove(); + else + last = p; } }