diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 01e3e7d52..cda3fd181 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)", org.eclipse.jgit.errors;version="[3.4.0,3.5.0)", org.eclipse.jgit.events;version="[3.4.0,3.5.0)", org.eclipse.jgit.fnmatch;version="[3.4.0,3.5.0)", + org.eclipse.jgit.gitrepo;version="[3.4.0,3.5.0)", org.eclipse.jgit.ignore;version="[3.4.0,3.5.0)", org.eclipse.jgit.internal;version="[3.4.0,3.5.0)", org.eclipse.jgit.internal.storage.dfs;version="[3.4.0,3.5.0)", 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 new file mode 100644 index 000000000..66067fc9a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.gitrepo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.Repository; +import org.junit.Test; + +public class RepoCommandTest extends RepositoryTestCase { + + private Repository remoteDb; + + public void setUp() throws Exception { + super.setUp(); + + remoteDb = createWorkRepository(); + Git git = new Git(remoteDb); + JGitTestUtil.writeTrashFile(remoteDb, "hello.txt", "world"); + git.add().addFilepattern("hello.txt").call(); + git.commit().setMessage("Initial commit").call(); + } + + @Test + public void testAddRepoManifest() throws Exception { + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("\n") + .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()) + .call(); + 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); + } +} diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties new file mode 100644 index 000000000..fb158399b --- /dev/null +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties @@ -0,0 +1,3 @@ +errorNoDefault=Error: no default remote in file {0}. +errorParsingManifestFile=Error occurred during parsing manifest file {0}. +invalidManifest=Invalid manifest. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java new file mode 100644 index 000000000..5ae134d4f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.gitrepo; + +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jgit.api.GitCommand; +import org.eclipse.jgit.api.SubmoduleAddCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.gitrepo.internal.RepoText; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.lib.Repository; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + * A class used to execute a repo command. + * + * This will parse a repo XML manifest, convert it into .gitmodules file and the + * repository config file. + * + * @see git-repo project page + * @since 3.4 + */ +public class RepoCommand extends GitCommand { + + private String path; + + private String uri; + + private ProgressMonitor monitor; + + private static class Project { + final String name; + final String path; + + Project(String name, String path) { + this.name = name; + this.path = path; + } + } + + private static class XmlManifest extends DefaultHandler { + private final RepoCommand command; + private final String filename; + private final String baseUrl; + private final Map remotes; + private final List projects; + private String defaultRemote; + + XmlManifest(RepoCommand command, String filename, String baseUrl) { + this.command = command; + this.filename = filename; + this.baseUrl = baseUrl; + remotes = new HashMap(); + projects = new ArrayList(); + } + + void read() throws IOException { + final XMLReader xr; + try { + xr = XMLReaderFactory.createXMLReader(); + } catch (SAXException e) { + throw new IOException(JGitText.get().noXMLParserAvailable); + } + xr.setContentHandler(this); + final FileInputStream in = new FileInputStream(filename); + try { + xr.parse(new InputSource(in)); + } catch (SAXException e) { + IOException error = new IOException(MessageFormat.format( + RepoText.get().errorParsingManifestFile, filename)); + error.initCause(e); + throw error; + } finally { + in.close(); + } + } + + @Override + public void startElement( + String uri, + 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$ + defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$ + else if ("copyfile".equals(qName)) { //$NON-NLS-1$ + // TODO(fishywang): Handle copyfile. Do nothing for now. + } + } + + @Override + public void endDocument() throws SAXException { + if (defaultRemote == null) { + throw new SAXException(MessageFormat.format( + RepoText.get().errorNoDefault, filename)); + } + final String remoteUrl; + try { + URI uri = new URI(String.format("%s/%s/", baseUrl, remotes.get(defaultRemote))); //$NON-NLS-1$ + remoteUrl = uri.normalize().toString(); + } catch (URISyntaxException e) { + throw new SAXException(e); + } + for (Project proj : projects) { + String url = remoteUrl + proj.name; + command.addSubmodule(url, proj.path); + } + } + } + + private static class ManifestErrorException extends GitAPIException { + ManifestErrorException(Throwable cause) { + super(RepoText.get().invalidManifest, cause); + } + } + + /** + * @param repo + */ + public RepoCommand(final Repository repo) { + super(repo); + } + + /** + * Set path to the manifest XML file + * + * @param path + * (with / as separator) + * @return this command + */ + public RepoCommand setPath(final String path) { + this.path = path; + return this; + } + + /** + * Set base URI of the pathes inside the XML + * + * @param uri + * @return this command + */ + public RepoCommand setURI(final String uri) { + this.uri = uri; + return this; + } + + /** + * The progress monitor associated with the clone operation. By default, + * this is set to NullProgressMonitor + * + * @see org.eclipse.jgit.lib.NullProgressMonitor + * @param monitor + * @return this command + */ + public RepoCommand setProgressMonitor(final ProgressMonitor monitor) { + this.monitor = monitor; + return this; + } + + @Override + public Void call() throws GitAPIException { + checkCallable(); + if (path == null || path.length() == 0) + throw new IllegalArgumentException(JGitText.get().pathNotConfigured); + if (uri == null || uri.length() == 0) + throw new IllegalArgumentException(JGitText.get().uriNotConfigured); + + XmlManifest manifest = new XmlManifest(this, path, uri); + try { + manifest.read(); + } catch (IOException e) { + throw new ManifestErrorException(e); + } + + return null; + } + + private void addSubmodule(String url, String name) throws SAXException { + SubmoduleAddCommand add = new SubmoduleAddCommand(repo); + if (monitor != null) + add.setProgressMonitor(monitor); + try { + add.setPath(name).setURI(url).call(); + } catch (GitAPIException e) { + throw new SAXException(e); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java new file mode 100644 index 000000000..519c9d156 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.gitrepo.internal; + +import org.eclipse.jgit.nls.NLS; +import org.eclipse.jgit.nls.TranslationBundle; + +/** + * Translation bundle for repo command + */ +public class RepoText extends TranslationBundle { + + /** + * @return an instance of this translation bundle + */ + public static RepoText get() { + return NLS.getBundleFor(RepoText.class); + } + + // @formatter:off + /***/ public String errorNoDefault; + /***/ public String errorParsingManifestFile; + /***/ public String invalidManifest; +}