--- /dev/null
+/*
+ * Copyright (C) 2015, 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.assertTrue;
+
+import java.io.StringBufferInputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+
+public class ManifestParserTest {
+
+ @Test
+ public void testManifestParser() throws Exception {
+ String baseUrl = "https://git.google.com/";
+ StringBuilder xmlContent = new StringBuilder();
+ Set<String> results = new HashSet<String>();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append("foo")
+ .append("\" groups=\"a,test\" />")
+ .append("<project path=\"bar\" name=\"")
+ .append("bar")
+ .append("\" groups=\"notdefault\" />")
+ .append("<project path=\"foo/a\" name=\"")
+ .append("a")
+ .append("\" groups=\"a\" />")
+ .append("<project path=\"b\" name=\"")
+ .append("b")
+ .append("\" groups=\"b\" />")
+ .append("</manifest>");
+
+ ManifestParser parser = new ManifestParser(
+ null, null, "master", baseUrl, null, null);
+ parser.read(new StringBufferInputStream(xmlContent.toString()));
+ // Unfiltered projects should have them all.
+ results.clear();
+ results.add("foo");
+ results.add("bar");
+ results.add("foo/a");
+ results.add("b");
+ for (RepoProject proj : parser.getProjects()) {
+ String msg = String.format(
+ "project \"%s\" should be included in unfiltered projects",
+ proj.path);
+ assertTrue(msg, results.contains(proj.path));
+ results.remove(proj.path);
+ }
+ assertTrue(
+ "Unfiltered projects shouldn't contain any unexpected results",
+ results.isEmpty());
+ // Filtered projects should have foo & b
+ results.clear();
+ results.add("foo");
+ results.add("b");
+ for (RepoProject proj : parser.getFilteredProjects()) {
+ String msg = String.format(
+ "project \"%s\" should be included in filtered projects",
+ proj.path);
+ assertTrue(msg, results.contains(proj.path));
+ results.remove(proj.path);
+ }
+ assertTrue(
+ "Filtered projects shouldn't contain any unexpected results",
+ results.isEmpty());
+ }
+}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
+import java.util.HashSet;
+import java.util.Set;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.junit.JGitTestUtil;
--- /dev/null
+/*
+ * Copyright (C) 2015, 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.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+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;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
+import org.eclipse.jgit.gitrepo.internal.RepoText;
+import org.eclipse.jgit.internal.JGitText;
+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;
+
+/**
+ * Repo XML manifest parser.
+ *
+ * @see <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
+ * @since 4.0
+ */
+public class ManifestParser extends DefaultHandler {
+ private final String filename;
+ private final String baseUrl;
+ private final String defaultBranch;
+ private final Repository rootRepo;
+ private final Map<String, String> remotes;
+ private final Set<String> plusGroups;
+ private final Set<String> minusGroups;
+ private List<RepoProject> projects;
+ private List<RepoProject> filteredProjects;
+ private String defaultRemote;
+ private String defaultRevision;
+ private IncludedFileReader includedReader;
+ private int xmlInRead;
+ private RepoProject currentProject;
+
+ /**
+ * A callback to read included xml files.
+ */
+ public interface IncludedFileReader {
+ /**
+ * Read a file from the same base dir of the manifest xml file.
+ *
+ * @param path
+ * The relative path to the file to read
+ * @return the {@code InputStream} of the file.
+ * @throws GitAPIException
+ * @throws IOException
+ */
+ public InputStream readIncludeFile(String path)
+ throws GitAPIException, IOException;
+ }
+
+ /**
+ * @param includedReader
+ * @param filename
+ * @param defaultBranch
+ * @param baseUrl
+ * @param groups
+ * @param rootRepo
+ */
+ public ManifestParser(IncludedFileReader includedReader, String filename,
+ String defaultBranch, String baseUrl, String groups,
+ Repository rootRepo) {
+ this.includedReader = includedReader;
+ this.filename = filename;
+ this.defaultBranch = defaultBranch;
+ this.rootRepo = rootRepo;
+
+ // Strip trailing /s to match repo behavior.
+ int lastIndex = baseUrl.length() - 1;
+ while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
+ lastIndex--;
+ this.baseUrl = baseUrl.substring(0, lastIndex + 1);
+
+ plusGroups = new HashSet<String>();
+ minusGroups = new HashSet<String>();
+ 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);
+ }
+ }
+
+ remotes = new HashMap<String, String>();
+ projects = new ArrayList<RepoProject>();
+ filteredProjects = new ArrayList<RepoProject>();
+ }
+
+ /**
+ * Read the xml file.
+ *
+ * @param inputStream
+ */
+ public void read(InputStream inputStream) throws IOException {
+ xmlInRead++;
+ final XMLReader xr;
+ try {
+ xr = XMLReaderFactory.createXMLReader();
+ } catch (SAXException e) {
+ throw new IOException(JGitText.get().noXMLParserAvailable);
+ }
+ xr.setContentHandler(this);
+ try {
+ xr.parse(new InputSource(inputStream));
+ } catch (SAXException e) {
+ IOException error = new IOException(
+ RepoText.get().errorParsingManifestFile);
+ error.initCause(e);
+ throw error;
+ }
+ }
+
+ @Override
+ public void startElement(
+ String uri,
+ String localName,
+ String qName,
+ Attributes attributes) throws SAXException {
+ if ("project".equals(qName)) { //$NON-NLS-1$
+ currentProject = new RepoProject(
+ attributes.getValue("name"), //$NON-NLS-1$
+ attributes.getValue("path"), //$NON-NLS-1$
+ attributes.getValue("revision"), //$NON-NLS-1$
+ attributes.getValue("remote"), //$NON-NLS-1$
+ attributes.getValue("groups")); //$NON-NLS-1$
+ } else if ("remote".equals(qName)) { //$NON-NLS-1$
+ String alias = attributes.getValue("alias"); //$NON-NLS-1$
+ String fetch = attributes.getValue("fetch"); //$NON-NLS-1$
+ remotes.put(attributes.getValue("name"), fetch); //$NON-NLS-1$
+ if (alias != null)
+ remotes.put(alias, fetch);
+ } else if ("default".equals(qName)) { //$NON-NLS-1$
+ defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
+ defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
+ if (defaultRevision == null)
+ defaultRevision = defaultBranch;
+ } else if ("copyfile".equals(qName)) { //$NON-NLS-1$
+ if (currentProject == null)
+ throw new SAXException(RepoText.get().invalidManifest);
+ currentProject.addCopyFile(new CopyFile(
+ rootRepo,
+ currentProject.path,
+ attributes.getValue("src"), //$NON-NLS-1$
+ attributes.getValue("dest"))); //$NON-NLS-1$
+ } else if ("include".equals(qName)) { //$NON-NLS-1$
+ String name = attributes.getValue("name"); //$NON-NLS-1$
+ InputStream is = null;
+ if (includedReader != null) {
+ try {
+ is = includedReader.readIncludeFile(name);
+ } catch (Exception e) {
+ throw new SAXException(MessageFormat.format(
+ RepoText.get().errorIncludeFile, name), e);
+ }
+ } else if (filename != null) {
+ int index = filename.lastIndexOf('/');
+ String path = filename.substring(0, index + 1) + name;
+ try {
+ is = new FileInputStream(path);
+ } catch (IOException e) {
+ throw new SAXException(MessageFormat.format(
+ RepoText.get().errorIncludeFile, path), e);
+ }
+ }
+ if (is == null) {
+ throw new SAXException(
+ RepoText.get().errorIncludeNotImplemented);
+ }
+ try {
+ read(is);
+ } catch (IOException e) {
+ throw new SAXException(e);
+ }
+ }
+ }
+
+ @Override
+ public void endElement(
+ String uri,
+ String localName,
+ String qName) throws SAXException {
+ if ("project".equals(qName)) { //$NON-NLS-1$
+ projects.add(currentProject);
+ currentProject = null;
+ }
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ xmlInRead--;
+ if (xmlInRead != 0)
+ return;
+
+ // Only do the following after we finished reading everything.
+ Map<String, String> remoteUrls = new HashMap<String, String>();
+ URI baseUri;
+ try {
+ baseUri = new URI(baseUrl);
+ } catch (URISyntaxException e) {
+ throw new SAXException(e);
+ }
+ for (RepoProject proj : projects) {
+ String remote = proj.remote;
+ if (remote == null) {
+ if (defaultRemote == null) {
+ if (filename != null)
+ throw new SAXException(MessageFormat.format(
+ RepoText.get().errorNoDefaultFilename,
+ filename));
+ else
+ throw new SAXException(
+ RepoText.get().errorNoDefault);
+ }
+ remote = defaultRemote;
+ }
+ String remoteUrl = remoteUrls.get(remote);
+ if (remoteUrl == null) {
+ remoteUrl = baseUri.resolve(remotes.get(remote)).toString();
+ if (!remoteUrl.endsWith("/")) //$NON-NLS-1$
+ remoteUrl = remoteUrl + "/"; //$NON-NLS-1$
+ remoteUrls.put(remote, remoteUrl);
+ }
+ proj.setUrl(remoteUrl + proj.name)
+ .setDefaultRevision(defaultRevision);
+ }
+
+ filteredProjects.addAll(projects);
+ removeNotInGroup();
+ removeOverlaps();
+ }
+
+ /**
+ * Getter for projects.
+ */
+ public List<RepoProject> getProjects() {
+ return projects;
+ }
+
+ /**
+ * Getter for filterdProjects.
+ */
+ public List<RepoProject> getFilteredProjects() {
+ return filteredProjects;
+ }
+
+ /** Remove projects that are not in our desired groups. */
+ void removeNotInGroup() {
+ Iterator<RepoProject> iter = filteredProjects.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(filteredProjects);
+ Iterator<RepoProject> iter = filteredProjects.iterator();
+ if (!iter.hasNext())
+ return;
+ RepoProject last = iter.next();
+ while (iter.hasNext()) {
+ RepoProject p = iter.next();
+ if (last.isAncestorOf(p))
+ iter.remove();
+ else
+ last = p;
+ }
+ }
+
+ boolean inGroups(RepoProject 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;
+ }
+}
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
-import java.net.URISyntaxException;
-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;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader;
+import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.FileUtils;
-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.
private InputStream inputStream;
private IncludedFileReader includedReader;
- private List<Project> bareProjects;
+ private List<RepoProject> bareProjects;
private Git git;
private ProgressMonitor monitor;
}
}
- /**
- * A callback to read included xml files.
- *
- * @since 3.5
- */
- public interface IncludedFileReader {
- /**
- * Read a file from the same base dir of the manifest xml file.
- *
- * @param path
- * The relative path to the file to read
- * @return the {@code InputStream} of the file.
- * @throws GitAPIException
- * @throws IOException
- */
- public InputStream readIncludeFile(String path)
- throws GitAPIException, IOException;
- }
-
- private static class CopyFile {
- final Repository repo;
- final String path;
- final String src;
- final String dest;
-
- CopyFile(Repository repo, String path, String src, String dest) {
- this.repo = repo;
- this.path = path;
- this.src = src;
- this.dest = dest;
- }
-
- void copy() throws IOException {
- File srcFile = new File(repo.getWorkTree(),
- path + "/" + src); //$NON-NLS-1$
- File destFile = new File(repo.getWorkTree(), dest);
- FileInputStream input = new FileInputStream(srcFile);
- try {
- FileOutputStream output = new FileOutputStream(destFile);
- try {
- FileChannel channel = input.getChannel();
- output.getChannel().transferFrom(channel, 0, channel.size());
- } finally {
- output.close();
- }
- } finally {
- input.close();
- }
- }
- }
-
- private static class Project implements Comparable<Project> {
- final String name;
- final String path;
- final String revision;
- final String remote;
- final Set<String> groups;
- final List<CopyFile> copyfiles;
-
- Project(String name, String path, String revision,
- String remote, String groups) {
- this.name = name;
- if (path != null)
- this.path = path;
- else
- this.path = name;
- this.revision = revision;
- this.remote = remote;
- this.groups = new HashSet<String>();
- if (groups != null && groups.length() > 0)
- this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
- copyfiles = new ArrayList<CopyFile>();
- }
-
- 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 hashCode() {
- return this.getPathWithSlash().hashCode();
- }
-
- public int compareTo(Project that) {
- return this.getPathWithSlash().compareTo(that.getPathWithSlash());
- }
- }
-
- private static class XmlManifest extends DefaultHandler {
- private final RepoCommand command;
- private final String filename;
- private final String baseUrl;
- private final Map<String, String> remotes;
- private final Set<String> plusGroups;
- private final Set<String> minusGroups;
- private List<Project> projects;
- private String defaultRemote;
- private String defaultRevision;
- private IncludedFileReader includedReader;
- private int xmlInRead;
- private Project currentProject;
-
- XmlManifest(RepoCommand command, IncludedFileReader includedReader,
- String filename, String baseUrl, String groups) {
- this.command = command;
- this.includedReader = includedReader;
- this.filename = filename;
-
- // Strip trailing /s to match repo behavior.
- int lastIndex = baseUrl.length() - 1;
- while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
- lastIndex--;
- this.baseUrl = baseUrl.substring(0, lastIndex + 1);
-
- remotes = new HashMap<String, String>();
- projects = new ArrayList<Project>();
- plusGroups = new HashSet<String>();
- minusGroups = new HashSet<String>();
- 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(InputStream inputStream) throws IOException {
- xmlInRead++;
- final XMLReader xr;
- try {
- xr = XMLReaderFactory.createXMLReader();
- } catch (SAXException e) {
- throw new IOException(JGitText.get().noXMLParserAvailable);
- }
- xr.setContentHandler(this);
- try {
- xr.parse(new InputSource(inputStream));
- } catch (SAXException e) {
- IOException error = new IOException(
- RepoText.get().errorParsingManifestFile);
- error.initCause(e);
- throw error;
- }
- }
-
- @Override
- public void startElement(
- String uri,
- String localName,
- String qName,
- Attributes attributes) throws SAXException {
- if ("project".equals(qName)) { //$NON-NLS-1$
- currentProject = new Project(
- attributes.getValue("name"), //$NON-NLS-1$
- attributes.getValue("path"), //$NON-NLS-1$
- attributes.getValue("revision"), //$NON-NLS-1$
- attributes.getValue("remote"), //$NON-NLS-1$
- attributes.getValue("groups")); //$NON-NLS-1$
- } else if ("remote".equals(qName)) { //$NON-NLS-1$
- String alias = attributes.getValue("alias"); //$NON-NLS-1$
- String fetch = attributes.getValue("fetch"); //$NON-NLS-1$
- remotes.put(attributes.getValue("name"), fetch); //$NON-NLS-1$
- if (alias != null)
- remotes.put(alias, fetch);
- } else if ("default".equals(qName)) { //$NON-NLS-1$
- defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
- defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
- if (defaultRevision == null)
- defaultRevision = command.branch;
- } else if ("copyfile".equals(qName)) { //$NON-NLS-1$
- if (currentProject == null)
- throw new SAXException(RepoText.get().invalidManifest);
- currentProject.addCopyFile(new CopyFile(
- command.repo,
- currentProject.path,
- attributes.getValue("src"), //$NON-NLS-1$
- attributes.getValue("dest"))); //$NON-NLS-1$
- } else if ("include".equals(qName)) { //$NON-NLS-1$
- String name = attributes.getValue("name"); //$NON-NLS-1$
- InputStream is = null;
- if (includedReader != null) {
- try {
- is = includedReader.readIncludeFile(name);
- } catch (Exception e) {
- throw new SAXException(MessageFormat.format(
- RepoText.get().errorIncludeFile, name), e);
- }
- } else if (filename != null) {
- int index = filename.lastIndexOf('/');
- String path = filename.substring(0, index + 1) + name;
- try {
- is = new FileInputStream(path);
- } catch (IOException e) {
- throw new SAXException(MessageFormat.format(
- RepoText.get().errorIncludeFile, path), e);
- }
- }
- if (is == null) {
- throw new SAXException(
- RepoText.get().errorIncludeNotImplemented);
- }
- try {
- read(is);
- } catch (IOException e) {
- throw new SAXException(e);
- }
- }
- }
-
- @Override
- public void endElement(
- String uri,
- String localName,
- String qName) throws SAXException {
- if ("project".equals(qName)) { //$NON-NLS-1$
- projects.add(currentProject);
- currentProject = null;
- }
- }
-
- @Override
- public void endDocument() throws SAXException {
- xmlInRead--;
- if (xmlInRead != 0)
- return;
-
- // Only do the following after we finished reading everything.
- removeNotInGroup();
- removeOverlaps();
-
- Map<String, String> remoteUrls = new HashMap<String, String>();
- URI baseUri;
- try {
- baseUri = new URI(baseUrl);
- } catch (URISyntaxException e) {
- throw new SAXException(e);
- }
- for (Project proj : projects) {
- String remote = proj.remote;
- if (remote == null) {
- if (defaultRemote == null) {
- if (filename != null)
- throw new SAXException(MessageFormat.format(
- RepoText.get().errorNoDefaultFilename,
- filename));
- else
- throw new SAXException(
- RepoText.get().errorNoDefault);
- }
- remote = defaultRemote;
- }
- String remoteUrl = remoteUrls.get(remote);
- if (remoteUrl == null) {
- remoteUrl = baseUri.resolve(remotes.get(remote)).toString();
- if (!remoteUrl.endsWith("/")) //$NON-NLS-1$
- remoteUrl = remoteUrl + "/"; //$NON-NLS-1$
- remoteUrls.put(remote, remoteUrl);
- }
-
- 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<Project> 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<Project> 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;
- }
- }
-
- 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;
- }
- }
-
@SuppressWarnings("serial")
private static class ManifestErrorException extends GitAPIException {
ManifestErrorException(Throwable cause) {
}
if (repo.isBare()) {
- bareProjects = new ArrayList<Project>();
+ bareProjects = new ArrayList<RepoProject>();
if (author == null)
author = new PersonIdent(repo);
if (callback == null)
} else
git = new Git(repo);
- XmlManifest manifest = new XmlManifest(
- this, includedReader, path, uri, groups);
+ ManifestParser parser = new ManifestParser(
+ includedReader, path, branch, uri, groups, repo);
try {
- manifest.read(inputStream);
- } catch (IOException e) {
+ parser.read(inputStream);
+ for (RepoProject proj : parser.getFilteredProjects()) {
+ addSubmodule(proj.url,
+ proj.path,
+ proj.getRevision(),
+ proj.copyfiles);
+ }
+ } catch (GitAPIException | IOException e) {
throw new ManifestErrorException(e);
}
} finally {
ObjectInserter inserter = repo.newObjectInserter();
try (RevWalk rw = new RevWalk(repo)) {
Config cfg = new Config();
- for (Project proj : bareProjects) {
+ for (RepoProject proj : bareProjects) {
String name = proj.path;
String nameUri = proj.name;
cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
}
private void addSubmodule(String url, String name, String revision,
- List<CopyFile> copyfiles) throws SAXException {
+ List<CopyFile> copyfiles) throws GitAPIException, IOException {
if (repo.isBare()) {
- Project proj = new Project(url, name, revision, null, null);
+ RepoProject proj = new RepoProject(url, name, revision, null, null);
proj.copyfiles.addAll(copyfiles);
bareProjects.add(proj);
} else {
if (monitor != null)
add.setProgressMonitor(monitor);
- try {
- Repository subRepo = add.call();
- if (revision != null) {
- try (Git sub = new Git(subRepo)) {
- sub.checkout().setName(findRef(revision, subRepo))
- .call();
- }
- subRepo.close();
- git.add().addFilepattern(name).call();
- }
- for (CopyFile copyfile : copyfiles) {
- copyfile.copy();
- git.add().addFilepattern(copyfile.dest).call();
+ Repository subRepo = add.call();
+ if (revision != null) {
+ try (Git sub = new Git(subRepo)) {
+ sub.checkout().setName(findRef(revision, subRepo))
+ .call();
}
- } catch (GitAPIException e) {
- throw new SAXException(e);
- } catch (IOException e) {
- throw new SAXException(e);
+ subRepo.close();
+ git.add().addFilepattern(name).call();
+ }
+ for (CopyFile copyfile : copyfiles) {
+ copyfile.copy();
+ git.add().addFilepattern(copyfile.dest).call();
}
}
}
--- /dev/null
+/*
+ * Copyright (C) 2015, 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.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * The representation of a repo sub project.
+ *
+ * @see <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
+ * @since 4.0
+ */
+public class RepoProject implements Comparable<RepoProject> {
+ final String name;
+ final String path;
+ final String revision;
+ final String remote;
+ final Set<String> groups;
+ final List<CopyFile> copyfiles;
+ String url;
+ String defaultRevision;
+
+ /**
+ * The representation of a copy file configuration.
+ */
+ public static class CopyFile {
+ final Repository repo;
+ final String path;
+ final String src;
+ final String dest;
+
+ /**
+ * @param repo
+ * @param path
+ * the path of the project containing this copyfile config.
+ * @param src
+ * @param dest
+ */
+ public CopyFile(Repository repo, String path, String src, String dest) {
+ this.repo = repo;
+ this.path = path;
+ this.src = src;
+ this.dest = dest;
+ }
+
+ /**
+ * Do the copy file action.
+ */
+ public void copy() throws IOException {
+ File srcFile = new File(repo.getWorkTree(),
+ path + "/" + src); //$NON-NLS-1$
+ File destFile = new File(repo.getWorkTree(), dest);
+ FileInputStream input = new FileInputStream(srcFile);
+ try {
+ FileOutputStream output = new FileOutputStream(destFile);
+ try {
+ FileChannel channel = input.getChannel();
+ output.getChannel().transferFrom(channel, 0, channel.size());
+ } finally {
+ output.close();
+ }
+ } finally {
+ input.close();
+ }
+ }
+ }
+
+ /**
+ * @param name
+ * @param path
+ * @param revision
+ * @param remote
+ * @param groups
+ */
+ public RepoProject(String name, String path, String revision,
+ String remote, String groups) {
+ this.name = name;
+ if (path != null)
+ this.path = path;
+ else
+ this.path = name;
+ this.revision = revision;
+ this.remote = remote;
+ this.groups = new HashSet<String>();
+ if (groups != null && groups.length() > 0)
+ this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
+ copyfiles = new ArrayList<CopyFile>();
+ }
+
+ /**
+ * Set the url of the sub repo.
+ *
+ * @param url
+ * @return this for chaining.
+ */
+ public RepoProject setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Set the default revision for the sub repo.
+ *
+ * @param defaultRevision
+ * @return this for chaining.
+ */
+ public RepoProject setDefaultRevision(String defaultRevision) {
+ this.defaultRevision = defaultRevision;
+ return this;
+ }
+
+ /**
+ * Get the revision of the sub repo.
+ *
+ * @return revision if set, or default revision.
+ */
+ public String getRevision() {
+ return revision == null ? defaultRevision : revision;
+ }
+
+ /**
+ * Add a copy file configuration.
+ *
+ * @param copyfile
+ */
+ public void addCopyFile(CopyFile copyfile) {
+ copyfiles.add(copyfile);
+ }
+
+ String getPathWithSlash() {
+ if (path.endsWith("/")) //$NON-NLS-1$
+ return path;
+ else
+ return path + "/"; //$NON-NLS-1$
+ }
+
+ /**
+ * Check if this sub repo is the ancestor of another sub repo.
+ */
+ public boolean isAncestorOf(RepoProject that) {
+ return that.getPathWithSlash().startsWith(this.getPathWithSlash());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof RepoProject) {
+ RepoProject that = (RepoProject) o;
+ return this.getPathWithSlash().equals(that.getPathWithSlash());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.getPathWithSlash().hashCode();
+ }
+
+ @Override
+ public int compareTo(RepoProject that) {
+ return this.getPathWithSlash().compareTo(that.getPathWithSlash());
+ }
+}
+