Add support to <include> tag in repo manifest xml.
Change-Id: I32d468f92e24701ea680435bf3417e3850857303
Signed-off-by: Yuxuan 'fishy' Wang <fishywang@google.com>
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 3e5ef02..41a086f 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
@@ -571,6 +571,46 @@ public void testRemoveOverlappingBare() throws Exception {
assertTrue("The a submodule should exist", a);
}
+ @Test
+ public void testIncludeTag() throws Exception {
+ Repository localDb = createWorkRepository();
+ Repository tempDb = createWorkRepository();
+
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<include name=\"_include.xml\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(
+ tempDb, "manifest.xml", xmlContent.toString());
+
+ xmlContent = new StringBuilder();
+ 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(defaultUri)
+ .append("\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(
+ tempDb, "_include.xml", xmlContent.toString());
+
+ RepoCommand command = new RepoCommand(localDb);
+ command
+ .setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ File hello = new File(localDb.getWorkTree(), "foo/hello.txt");
+ assertTrue("submodule should be checked out", hello.exists());
+ BufferedReader reader = new BufferedReader(new FileReader(hello));
+ String content = reader.readLine();
+ reader.close();
+ assertEquals("submodule content should be as expected",
+ "master world", content);
+ }
+
private void resolveRelativeUris() {
// Find the longest common prefix ends with "/" as rootUri.
defaultUri = defaultDb.getDirectory().toURI().toString();
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
index 256dd7f..7443ad3 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
@@ -1,4 +1,6 @@
copyFileFailed=Error occurred during execution of copyfile rule.
+errorIncludeFile=Error: unable to read include file {0}
+errorIncludeNotImplemented=Error: <include> tag not supported as no callback defined.
errorNoDefault=Error: no default remote in manifest file.
errorNoDefaultFilename=Error: no default remote in manifest file {0}.
errorParsingManifestFile=Error occurred during parsing manifest file.
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 57514a2..52710d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -123,6 +123,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
private PersonIdent author;
private RemoteReader callback;
private InputStream inputStream;
+ private IncludedFileReader includedReader;
private List<Project> bareProjects;
private Git git;
@@ -163,6 +164,7 @@ public interface RemoteReader {
* @return the file content.
* @throws GitAPIException
* @throws IOException
+ * @since 3.5
*/
public byte[] readFile(String uri, String ref, String path)
throws GitAPIException, IOException;
@@ -224,6 +226,25 @@ public ObjectId sha1(String uri, String ref) throws GitAPIException {
}
}
+ /**
+ * 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;
@@ -309,7 +330,6 @@ public int compareTo(Project that) {
private static class XmlManifest extends DefaultHandler {
private final RepoCommand command;
- private final InputStream inputStream;
private final String filename;
private final String baseUrl;
private final Map<String, String> remotes;
@@ -318,12 +338,14 @@ private static class XmlManifest extends DefaultHandler {
private List<Project> projects;
private String defaultRemote;
private String defaultRevision;
+ private IncludedFileReader includedReader;
+ private int xmlInRead;
private Project currentProject;
- XmlManifest(RepoCommand command, InputStream inputStream,
+ XmlManifest(RepoCommand command, IncludedFileReader includedReader,
String filename, String baseUrl, String groups) {
this.command = command;
- this.inputStream = inputStream;
+ this.includedReader = includedReader;
this.filename = filename;
// Strip trailing /s to match repo behavior.
@@ -349,7 +371,8 @@ private static class XmlManifest extends DefaultHandler {
}
}
- void read() throws IOException {
+ void read(InputStream inputStream) throws IOException {
+ xmlInRead++;
final XMLReader xr;
try {
xr = XMLReaderFactory.createXMLReader();
@@ -395,6 +418,35 @@ public void startElement(
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");
+ 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);
+ }
}
}
@@ -411,6 +463,10 @@ public void endElement(
@Override
public void endDocument() throws SAXException {
+ xmlInRead--;
+ if (xmlInRead != 0)
+ return;
+ // Only do the following after we finished reading everything.
if (defaultRemote == null) {
if (filename != null)
throw new SAXException(MessageFormat.format(
@@ -606,6 +662,17 @@ public RepoCommand setRemoteReader(final RemoteReader callback) {
return this;
}
+ /**
+ * Set the IncludedFileReader callback.
+ *
+ * @param reader
+ * @return this command
+ */
+ public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
+ this.includedReader = reader;
+ return this;
+ }
+
@Override
public RevCommit call() throws GitAPIException {
try {
@@ -635,9 +702,9 @@ public RevCommit call() throws GitAPIException {
git = new Git(repo);
XmlManifest manifest = new XmlManifest(
- this, inputStream, path, uri, groups);
+ this, includedReader, path, uri, groups);
try {
- manifest.read();
+ manifest.read(inputStream);
} catch (IOException e) {
throw new ManifestErrorException(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
index 1f9d5d8..36b6e3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
@@ -60,6 +60,8 @@ public static RepoText get() {
// @formatter:off
/***/ public String copyFileFailed;
+ /***/ public String errorIncludeFile;
+ /***/ public String errorIncludeNotImplemented;
/***/ public String errorNoDefault;
/***/ public String errorNoDefaultFilename;
/***/ public String errorParsingManifestFile;