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 void testReplaceManifestBare() throws Exception { 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.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 @@ * 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 @@ void copy() throws IOException { } } - private static class Project { + private static class Project implements Comparable { final String name; final String path; final String revision; @@ -269,6 +276,31 @@ private static class Project { 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 @@ private static class XmlManifest extends DefaultHandler { 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 void endDocument() throws SAXException { } 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; } }