diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java index 72e85fa64..bfff14d9d 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.pgm; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.BufferedReader; @@ -57,17 +58,47 @@ import org.junit.Before; import org.junit.Test; public class RepoTest extends CLIRepositoryTestCase { - private Repository remoteDb; + private Repository defaultDb; + private Repository notDefaultDb; + private Repository groupADb; + private Repository groupBDb; + + private String rootUri; + private String defaultUri; + private String notDefaultUri; + private String groupAUri; + private String groupBUri; @Override @Before public void setUp() throws Exception { super.setUp(); - remoteDb = createWorkRepository(); - Git git = new Git(remoteDb); - JGitTestUtil.writeTrashFile(remoteDb, "hello.txt", "world"); + + defaultDb = createWorkRepository(); + Git git = new Git(defaultDb); + JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "world"); git.add().addFilepattern("hello.txt").call(); git.commit().setMessage("Initial commit").call(); + + notDefaultDb = createWorkRepository(); + git = new Git(notDefaultDb); + JGitTestUtil.writeTrashFile(notDefaultDb, "world.txt", "hello"); + git.add().addFilepattern("world.txt").call(); + git.commit().setMessage("Initial commit").call(); + + groupADb = createWorkRepository(); + git = new Git(groupADb); + JGitTestUtil.writeTrashFile(groupADb, "a.txt", "world"); + git.add().addFilepattern("a.txt").call(); + git.commit().setMessage("Initial commit").call(); + + groupBDb = createWorkRepository(); + git = new Git(groupBDb); + JGitTestUtil.writeTrashFile(groupBDb, "b.txt", "world"); + git.add().addFilepattern("b.txt").call(); + git.commit().setMessage("Initial commit").call(); + + resolveRelativeUris(); } @Test @@ -77,20 +108,59 @@ public class RepoTest extends CLIRepositoryTestCase { .append("") .append("") .append("") - .append("") + .append("") + .append("") + .append("") + .append("") .append(""); writeTrashFile("manifest.xml", xmlContent.toString()); StringBuilder cmd = new StringBuilder("git repo --base-uri=\"") - .append(remoteDb.getDirectory().toURI().toString()) - .append("\" \"") + .append(rootUri) + .append("\" --groups=\"all,-a\" \"") .append(db.getWorkTree().getAbsolutePath()) .append("/manifest.xml\""); execute(cmd.toString()); - File hello = new File(db.getWorkTree(), "foo/hello.txt"); - assertTrue("submodule was checked out.", hello.exists()); - BufferedReader reader = new BufferedReader(new FileReader(hello)); - String content = reader.readLine(); - reader.close(); - assertEquals("submodule content is as expected.", "world", content); + + File file = new File(db.getWorkTree(), "foo/hello.txt"); + assertFalse("\"all,-a\" doesn't have foo", file.exists()); + file = new File(db.getWorkTree(), "bar/world.txt"); + assertTrue("\"all,-a\" has bar", file.exists()); + file = new File(db.getWorkTree(), "a/a.txt"); + assertFalse("\"all,-a\" doesn't have a", file.exists()); + file = new File(db.getWorkTree(), "b/b.txt"); + assertTrue("\"all,-a\" has have b", file.exists()); + } + + private void resolveRelativeUris() { + // Find the longest common prefix ends with "/" as rootUri. + defaultUri = defaultDb.getDirectory().toURI().toString(); + notDefaultUri = notDefaultDb.getDirectory().toURI().toString(); + groupAUri = groupADb.getDirectory().toURI().toString(); + groupBUri = groupBDb.getDirectory().toURI().toString(); + int start = 0; + while (start <= defaultUri.length()) { + int newStart = defaultUri.indexOf('/', start + 1); + String prefix = defaultUri.substring(0, newStart); + if (!notDefaultUri.startsWith(prefix) || + !groupAUri.startsWith(prefix) || + !groupBUri.startsWith(prefix)) { + start++; + rootUri = defaultUri.substring(0, start); + defaultUri = defaultUri.substring(start); + notDefaultUri = notDefaultUri.substring(start); + groupAUri = groupAUri.substring(start); + groupBUri = groupBUri.substring(start); + return; + } + start = newStart; + } } } diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index 9b874c735..7d5b87fdd 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -278,6 +278,7 @@ usage_forceCheckout=when switching branches, proceed even if the index or the wo usage_forceCreateBranchEvenExists=force create branch even exists usage_forceReplacingAnExistingTag=force replacing an existing tag usage_getAndSetOptions=Get and set repository or global options +usage_groups=Restrict manifest projects to ones with specified group(s), use "-" for excluding [default|all|G1,G2,G3|G4,-G5,-G6] usage_hostnameOrIpToListenOn=hostname (or ip) to listen on usage_indexFileFormatToCreate=index file format to create usage_ignoreWhitespace=ignore all whitespace diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java index 9d18cec76..9b191e679 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java @@ -52,6 +52,9 @@ class Repo extends TextBuiltin { @Option(name = "--base-uri", aliases = { "-u" }, usage = "usage_baseUri") private String uri; + @Option(name = "--groups", aliases = { "-g" }, usage = "usage_groups") + private String groups = "default"; //$NON-NLS-1$ + @Argument(required = true, usage = "usage_pathToXml") private String path; @@ -60,6 +63,7 @@ class Repo extends TextBuiltin { new RepoCommand(db) .setURI(uri) .setPath(path) + .setGroups(groups) .call(); } } 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 66067fc9a..9fc59c578 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 @@ -43,6 +43,7 @@ package org.eclipse.jgit.gitrepo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.BufferedReader; @@ -57,16 +58,45 @@ import org.junit.Test; public class RepoCommandTest extends RepositoryTestCase { - private Repository remoteDb; + private Repository defaultDb; + private Repository notDefaultDb; + private Repository groupADb; + private Repository groupBDb; + + private String rootUri; + private String defaultUri; + private String notDefaultUri; + private String groupAUri; + private String groupBUri; public void setUp() throws Exception { super.setUp(); - remoteDb = createWorkRepository(); - Git git = new Git(remoteDb); - JGitTestUtil.writeTrashFile(remoteDb, "hello.txt", "world"); + defaultDb = createWorkRepository(); + Git git = new Git(defaultDb); + JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "world"); git.add().addFilepattern("hello.txt").call(); git.commit().setMessage("Initial commit").call(); + + notDefaultDb = createWorkRepository(); + git = new Git(notDefaultDb); + JGitTestUtil.writeTrashFile(notDefaultDb, "world.txt", "hello"); + git.add().addFilepattern("world.txt").call(); + git.commit().setMessage("Initial commit").call(); + + groupADb = createWorkRepository(); + git = new Git(groupADb); + JGitTestUtil.writeTrashFile(groupADb, "a.txt", "world"); + git.add().addFilepattern("a.txt").call(); + git.commit().setMessage("Initial commit").call(); + + groupBDb = createWorkRepository(); + git = new Git(groupBDb); + JGitTestUtil.writeTrashFile(groupBDb, "b.txt", "world"); + git.add().addFilepattern("b.txt").call(); + git.commit().setMessage("Initial commit").call(); + + resolveRelativeUris(); } @Test @@ -76,12 +106,14 @@ public class RepoCommandTest extends RepositoryTestCase { .append("") .append("") .append("") - .append("") + .append("") .append(""); writeTrashFile("manifest.xml", xmlContent.toString()); RepoCommand command = new RepoCommand(db); command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml") - .setURI(remoteDb.getDirectory().toURI().toString()) + .setURI(rootUri) .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); assertTrue("submodule was checked out", hello.exists()); @@ -90,4 +122,84 @@ public class RepoCommandTest extends RepositoryTestCase { reader.close(); assertEquals("submodule content is as expected.", "world", content); } + + @Test + public void testRepoManifestGroups() throws Exception { + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("\n") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append(""); + + // default should have foo, a & b + Repository localDb = createWorkRepository(); + JGitTestUtil.writeTrashFile(localDb, "manifest.xml", xmlContent.toString()); + RepoCommand command = new RepoCommand(localDb); + command.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri) + .call(); + File file = new File(localDb.getWorkTree(), "foo/hello.txt"); + assertTrue("default has foo", file.exists()); + file = new File(localDb.getWorkTree(), "bar/world.txt"); + assertFalse("default doesn't have bar", file.exists()); + file = new File(localDb.getWorkTree(), "a/a.txt"); + assertTrue("default has a", file.exists()); + file = new File(localDb.getWorkTree(), "b/b.txt"); + assertTrue("default has b", file.exists()); + + // all,-a should have bar & b + localDb = createWorkRepository(); + JGitTestUtil.writeTrashFile(localDb, "manifest.xml", xmlContent.toString()); + command = new RepoCommand(localDb); + command.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri) + .setGroups("all,-a") + .call(); + file = new File(localDb.getWorkTree(), "foo/hello.txt"); + assertFalse("\"all,-a\" doesn't have foo", file.exists()); + file = new File(localDb.getWorkTree(), "bar/world.txt"); + assertTrue("\"all,-a\" has bar", file.exists()); + file = new File(localDb.getWorkTree(), "a/a.txt"); + assertFalse("\"all,-a\" doesn't have a", file.exists()); + file = new File(localDb.getWorkTree(), "b/b.txt"); + assertTrue("\"all,-a\" has have b", file.exists()); + } + + private void resolveRelativeUris() { + // Find the longest common prefix ends with "/" as rootUri. + defaultUri = defaultDb.getDirectory().toURI().toString(); + notDefaultUri = notDefaultDb.getDirectory().toURI().toString(); + groupAUri = groupADb.getDirectory().toURI().toString(); + groupBUri = groupBDb.getDirectory().toURI().toString(); + int start = 0; + while (start <= defaultUri.length()) { + int newStart = defaultUri.indexOf('/', start + 1); + String prefix = defaultUri.substring(0, newStart); + if (!notDefaultUri.startsWith(prefix) || + !groupAUri.startsWith(prefix) || + !groupBUri.startsWith(prefix)) { + start++; + rootUri = defaultUri.substring(0, start); + defaultUri = defaultUri.substring(start); + notDefaultUri = notDefaultUri.substring(start); + groupAUri = groupAUri.substring(start); + groupBUri = groupBUri.substring(start); + return; + } + start = newStart; + } + } } 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 5ae134d4f..a7467c83d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -48,9 +48,12 @@ import java.net.URI; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.SubmoduleAddCommand; @@ -79,18 +82,22 @@ import org.xml.sax.helpers.XMLReaderFactory; public class RepoCommand extends GitCommand { private String path; - private String uri; + private String groups; private ProgressMonitor monitor; private static class Project { final String name; final String path; + final Set groups; - Project(String name, String path) { + Project(String name, String path, String groups) { this.name = name; this.path = path; + this.groups = new HashSet(); + if (groups != null && groups.length() > 0) + this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$ } } @@ -100,14 +107,29 @@ public class RepoCommand extends GitCommand { private final String baseUrl; private final Map remotes; private final List projects; + private final Set plusGroups; + private final Set minusGroups; private String defaultRemote; - XmlManifest(RepoCommand command, String filename, String baseUrl) { + XmlManifest(RepoCommand command, String filename, String baseUrl, String groups) { this.command = command; this.filename = filename; this.baseUrl = baseUrl; remotes = new HashMap(); projects = new ArrayList(); + plusGroups = new HashSet(); + minusGroups = new HashSet(); + if (groups == null || groups.length() == 0 || groups.equals("default")) { //$NON-NLS-1$ + // default means "all,-notdefault" + minusGroups.add("notdefault"); //$NON-NLS-1$ + } else { + for (String group : groups.split(",")) { //$NON-NLS-1$ + if (group.startsWith("-")) //$NON-NLS-1$ + minusGroups.add(group.substring(1)); + else + plusGroups.add(group); + } + } } void read() throws IOException { @@ -137,13 +159,17 @@ public class RepoCommand extends GitCommand { String localName, String qName, Attributes attributes) throws SAXException { - if ("project".equals(qName)) //$NON-NLS-1$ - projects.add(new Project(attributes.getValue("name"), attributes.getValue("path"))); //$NON-NLS-1$ //$NON-NLS-2$ - else if ("remote".equals(qName)) //$NON-NLS-1$ - remotes.put(attributes.getValue("name"), attributes.getValue("fetch")); //$NON-NLS-1$ //$NON-NLS-2$ - else if ("default".equals(qName)) //$NON-NLS-1$ + if ("project".equals(qName)) { //$NON-NLS-1$ + projects.add(new Project( //$NON-NLS-1$ + attributes.getValue("name"), //$NON-NLS-1$ + attributes.getValue("path"), //$NON-NLS-1$ + attributes.getValue("groups"))); //$NON-NLS-1$ + } else if ("remote".equals(qName)) { //$NON-NLS-1$ + remotes.put(attributes.getValue("name"), //$NON-NLS-1$ + attributes.getValue("fetch")); //$NON-NLS-1$ + } else if ("default".equals(qName)) { //$NON-NLS-1$ defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$ - else if ("copyfile".equals(qName)) { //$NON-NLS-1$ + } else if ("copyfile".equals(qName)) { //$NON-NLS-1$ // TODO(fishywang): Handle copyfile. Do nothing for now. } } @@ -162,9 +188,29 @@ public class RepoCommand extends GitCommand { throw new SAXException(e); } for (Project proj : projects) { - String url = remoteUrl + proj.name; - command.addSubmodule(url, proj.path); + if (inGroups(proj)) { + String url = remoteUrl + proj.name; + command.addSubmodule(url, proj.path); + } + } + } + + boolean inGroups(Project proj) { + for (String group : minusGroups) { + if (proj.groups.contains(group)) { + // minus groups have highest priority. + return false; + } + } + if (plusGroups.isEmpty() || plusGroups.contains("all")) { //$NON-NLS-1$ + // empty plus groups means "all" + return true; + } + for (String group : plusGroups) { + if (proj.groups.contains(group)) + return true; } + return false; } } @@ -204,6 +250,17 @@ public class RepoCommand extends GitCommand { return this; } + /** + * Set groups to sync + * + * @param groups groups separated by comma, examples: default|all|G1,-G2,-G3 + * @return this command + */ + public RepoCommand setGroups(final String groups) { + this.groups = groups; + return this; + } + /** * The progress monitor associated with the clone operation. By default, * this is set to NullProgressMonitor @@ -225,7 +282,7 @@ public class RepoCommand extends GitCommand { if (uri == null || uri.length() == 0) throw new IllegalArgumentException(JGitText.get().uriNotConfigured); - XmlManifest manifest = new XmlManifest(this, path, uri); + XmlManifest manifest = new XmlManifest(this, path, uri, groups); try { manifest.read(); } catch (IOException e) { @@ -236,11 +293,13 @@ public class RepoCommand extends GitCommand { } private void addSubmodule(String url, String name) throws SAXException { - SubmoduleAddCommand add = new SubmoduleAddCommand(repo); + SubmoduleAddCommand add = new SubmoduleAddCommand(repo) + .setPath(name) + .setURI(url); if (monitor != null) add.setProgressMonitor(monitor); try { - add.setPath(name).setURI(url).call(); + add.call(); } catch (GitAPIException e) { throw new SAXException(e); }