Merge changes Ia4df9808,I83e8a321,Id0e7663b,Ib809b00c,I88a6ee07

* changes:
  Commit changes generated during repo command
  Added implementation of copyfile rule.
  Added groups support to repo subcommand.
  Added the command line of jgit repo.
  Implemented first part of the repo sub-command.
This commit is contained in:
Shawn Pearce 2014-04-25 14:01:06 -04:00 committed by Gerrit Code Review @ Eclipse.org
commit d8754aa4f4
11 changed files with 942 additions and 0 deletions

View File

@ -0,0 +1,166 @@
/*
* 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.pgm;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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.lib.CLIRepositoryTestCase;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
public class RepoTest extends CLIRepositoryTestCase {
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();
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
public void testAddRepoManifest() throws Exception {
StringBuilder 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("\" groups=\"a,test\" />")
.append("<project path=\"bar\" name=\"")
.append(notDefaultUri)
.append("\" groups=\"notdefault\" />")
.append("<project path=\"a\" name=\"")
.append(groupAUri)
.append("\" groups=\"a\" />")
.append("<project path=\"b\" name=\"")
.append(groupBUri)
.append("\" groups=\"b\" />")
.append("</manifest>");
writeTrashFile("manifest.xml", xmlContent.toString());
StringBuilder cmd = new StringBuilder("git repo --base-uri=\"")
.append(rootUri)
.append("\" --groups=\"all,-a\" \"")
.append(db.getWorkTree().getAbsolutePath())
.append("/manifest.xml\"");
execute(cmd.toString());
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;
}
}
}

View File

@ -17,6 +17,7 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.3,2.0)",
org.eclipse.jgit.diff;version="[3.4.0,3.5.0)",
org.eclipse.jgit.dircache;version="[3.4.0,3.5.0)",
org.eclipse.jgit.errors;version="[3.4.0,3.5.0)",
org.eclipse.jgit.gitrepo;version="[3.4.0,3.5.0)",
org.eclipse.jgit.internal.storage.file;version="[3.4.0,3.5.0)",
org.eclipse.jgit.internal.storage.pack;version="[3.4.0,3.5.0)",
org.eclipse.jgit.lib;version="[3.4.0,3.5.0)",

View File

@ -24,6 +24,7 @@ org.eclipse.jgit.pgm.MergeBase
org.eclipse.jgit.pgm.Push
org.eclipse.jgit.pgm.ReceivePack
org.eclipse.jgit.pgm.Reflog
org.eclipse.jgit.pgm.Repo
org.eclipse.jgit.pgm.Reset
org.eclipse.jgit.pgm.RevList
org.eclipse.jgit.pgm.RevParse

View File

@ -231,6 +231,7 @@ usage_archive=zip up files from the named tree
usage_archiveFormat=archive format. Currently supported formats: 'tar', 'zip', 'tgz', 'tbz2', 'txz'
usage_archiveOutput=output file to write the archive to
usage_archivePrefix=string to prepend to each pathname in the archive
usage_baseUri=the base URI of the repo manifest file. e.g. https://android.googlesource.com/platform/
usage_blameLongRevision=show long revision
usage_blameRange=annotate only the given range
usage_blameRawTimestamp=show raw timestamp
@ -277,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
@ -300,7 +302,9 @@ usage_noRenames=disable rename detection
usage_noShowStandardNotes=Disable showing notes from the standard /refs/notes/commits branch
usage_onlyMatchAgainstAlreadyTrackedFiles=Only match <filepattern> against already tracked files in the index rather than the working tree
usage_outputFile=Output file
usage_parseRepoManifest=Parse a repo manifest file and add submodules
usage_path=path
usage_pathToXml=path to the repo manifest XML file
usage_performFsckStyleChecksOnReceive=perform fsck style checks on receive
usage_portNumberToListenOn=port number to listen on
usage_printOnlyBranchesThatContainTheCommit=print only branches that contain the commit

View File

@ -0,0 +1,69 @@
/*
* 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.pgm;
import org.eclipse.jgit.gitrepo.RepoCommand;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_parseRepoManifest")
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;
@Override
protected void run() throws Exception {
new RepoCommand(db)
.setURI(uri)
.setPath(path)
.setGroups(groups)
.call();
}
}

View File

@ -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)",

View File

@ -0,0 +1,240 @@
/*
* 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.assertFalse;
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 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();
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
public void testAddRepoManifest() throws Exception {
StringBuilder 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>");
writeTrashFile("manifest.xml", xmlContent.toString());
RepoCommand command = new RepoCommand(db);
command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
.setURI(rootUri)
.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);
}
@Test
public void testRepoManifestGroups() throws Exception {
StringBuilder 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("\" groups=\"a,test\" />")
.append("<project path=\"bar\" name=\"")
.append(notDefaultUri)
.append("\" groups=\"notdefault\" />")
.append("<project path=\"a\" name=\"")
.append(groupAUri)
.append("\" groups=\"a\" />")
.append("<project path=\"b\" name=\"")
.append(groupBUri)
.append("\" groups=\"b\" />")
.append("</manifest>");
// 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());
}
@Test
public void testRepoManifestCopyfile() throws Exception {
Repository localDb = createWorkRepository();
StringBuilder 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("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
.append("</project>")
.append("</manifest>");
JGitTestUtil.writeTrashFile(localDb, "manifest.xml", xmlContent.toString());
RepoCommand command = new RepoCommand(localDb);
command.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
.setURI(rootUri)
.call();
// The original file should exist
File hello = new File(localDb.getWorkTree(), "foo/hello.txt");
assertTrue("The original file exists", hello.exists());
BufferedReader reader = new BufferedReader(new FileReader(hello));
String content = reader.readLine();
reader.close();
assertEquals("The original file has expected content", "world", content);
// The dest file should also exist
hello = new File(localDb.getWorkTree(), "Hello");
assertTrue("The destination file exists", hello.exists());
reader = new BufferedReader(new FileReader(hello));
content = reader.readLine();
reader.close();
assertEquals("The destination file has expected content", "world", content);
}
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;
}
}
}

View File

@ -45,6 +45,9 @@ Export-Package: org.eclipse.jgit.api;version="3.4.0";
org.eclipse.jgit.events;version="3.4.0";
uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.fnmatch;version="3.4.0",
org.eclipse.jgit.gitrepo;version="3.4.0";
uses:="org.eclipse.jgit.api,
org.eclipse.jgit.lib",
org.eclipse.jgit.ignore;version="3.4.0",
org.eclipse.jgit.internal;version="3.4.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
org.eclipse.jgit.internal.storage.dfs;version="3.4.0";x-friends:="org.eclipse.jgit.test",

View File

@ -0,0 +1,5 @@
copyFileFailed=Error occurred during execution of copyfile rule.
errorNoDefault=Error: no default remote in file {0}.
errorParsingManifestFile=Error occurred during parsing manifest file {0}.
invalidManifest=Invalid manifest.
repoCommitMessage=Added repo manifest.

View File

@ -0,0 +1,385 @@
/*
* 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.FileOutputStream;
import java.io.IOException;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
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.eclipse.jgit.revwalk.RevCommit;
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 <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
* @since 3.4
*/
public class RepoCommand extends GitCommand<RevCommit> {
private String path;
private String uri;
private String groups;
private Git git;
private ProgressMonitor monitor;
private static class CopyFile {
final String src;
final String dest;
final String relativeDest;
CopyFile(Repository repo, String path, String src, String dest) {
this.src = repo.getWorkTree() + "/" + path + "/" + src; //$NON-NLS-1$ //$NON-NLS-2$
this.relativeDest = dest;
this.dest = repo.getWorkTree() + "/" + dest; //$NON-NLS-1$
}
void copy() throws IOException {
FileInputStream input = new FileInputStream(src);
try {
FileOutputStream output = new FileOutputStream(dest);
try {
FileChannel channel = input.getChannel();
output.getChannel().transferFrom(channel, 0, channel.size());
} finally {
output.close();
}
} finally {
input.close();
}
}
}
private static class Project {
final String name;
final String path;
final Set<String> groups;
final List<CopyFile> copyfiles;
Project(String name, String path, String groups) {
this.name = name;
this.path = path;
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);
}
}
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 List<Project> projects;
private final Set<String> plusGroups;
private final Set<String> minusGroups;
private String defaultRemote;
private Project currentProject;
XmlManifest(RepoCommand command, String filename, String baseUrl, String groups) {
this.command = command;
this.filename = filename;
this.baseUrl = baseUrl;
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() 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$
currentProject = 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$
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$
}
}
@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 {
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) {
if (inGroups(proj)) {
String url = remoteUrl + proj.name;
command.addSubmodule(url, proj.path);
for (CopyFile copyfile : proj.copyfiles) {
try {
copyfile.copy();
} catch (IOException e) {
throw new SAXException(
RepoText.get().copyFileFailed, e);
}
AddCommand add = command.git
.add()
.addFilepattern(copyfile.relativeDest);
try {
add.call();
} catch (GitAPIException e) {
throw new SAXException(e);
}
}
}
}
}
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;
}
}
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 <code>/</code> 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;
}
/**
* 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 <code>NullProgressMonitor</code>
*
* @see org.eclipse.jgit.lib.NullProgressMonitor
* @param monitor
* @return this command
*/
public RepoCommand setProgressMonitor(final ProgressMonitor monitor) {
this.monitor = monitor;
return this;
}
@Override
public RevCommit 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);
git = new Git(repo);
XmlManifest manifest = new XmlManifest(this, path, uri, groups);
try {
manifest.read();
} catch (IOException e) {
throw new ManifestErrorException(e);
}
return git
.commit()
.setMessage(RepoText.get().repoCommitMessage)
.call();
}
private void addSubmodule(String url, String name) throws SAXException {
SubmoduleAddCommand add = git
.submoduleAdd()
.setPath(name)
.setURI(url);
if (monitor != null)
add.setProgressMonitor(monitor);
try {
add.call();
} catch (GitAPIException e) {
throw new SAXException(e);
}
}
}

View File

@ -0,0 +1,67 @@
/*
* 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 copyFileFailed;
/***/ public String errorNoDefault;
/***/ public String errorParsingManifestFile;
/***/ public String invalidManifest;
/***/ public String repoCommitMessage;
}