map = Git
.lsRemoteRepository()
.setRemote(uri)
.callAsMap();
Ref r = RefDatabase.findRef(map, ref);
return r != null ? r.getObjectId() : null;
}
public byte[] readFile(String uri, String ref, String path)
throws GitAPIException, IOException {
File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
Repository repo = Git
.cloneRepository()
.setBare(true)
.setDirectory(dir)
.setURI(uri)
.call()
.getRepository();
try {
return readFileFromRepo(repo, ref, path);
} finally {
repo.close();
FileUtils.delete(dir, FileUtils.RECURSIVE);
}
}
/**
* Read a file from the repository
*
* @param repo
* The repository containing the file
* @param ref
* The ref (branch/tag/etc.) to read
* @param path
* The relative path (inside the repo) to the file to read
* @return the file's content
* @throws GitAPIException
* @throws IOException
* @since 3.5
*/
protected byte[] readFileFromRepo(Repository repo,
String ref, String path) throws GitAPIException, IOException {
try (ObjectReader reader = repo.newObjectReader()) {
ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$
return reader.open(oid).getBytes(Integer.MAX_VALUE);
}
}
}
@SuppressWarnings("serial")
private static class ManifestErrorException extends GitAPIException {
ManifestErrorException(Throwable cause) {
super(RepoText.get().invalidManifest, cause);
}
}
@SuppressWarnings("serial")
private static class RemoteUnavailableException extends GitAPIException {
RemoteUnavailableException(String uri) {
super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri));
}
}
/**
* @param repo
*/
public RepoCommand(Repository repo) {
super(repo);
}
/**
* Set path to the manifest XML file.
*
* Calling {@link #setInputStream} will ignore the path set here.
*
* @param path
* (with /
as separator)
* @return this command
*/
public RepoCommand setPath(String path) {
this.path = path;
return this;
}
/**
* Set the input stream to the manifest XML.
*
* Setting inputStream will ignore the path set. It will be closed in
* {@link #call}.
*
* @param inputStream
* @return this command
* @since 3.5
*/
public RepoCommand setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
return this;
}
/**
* Set base URI of the pathes inside the XML
*
* @param uri
* @return this command
*/
public RepoCommand setURI(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(String groups) {
this.groups = groups;
return this;
}
/**
* Set default branch.
*
* This is generally the name of the branch the manifest file was in. If
* there's no default revision (branch) specified in manifest and no
* revision specified in project, this branch will be used.
*
* @param branch
* @return this command
*/
public RepoCommand setBranch(String branch) {
this.branch = branch;
return this;
}
/**
* Set target branch.
*
* This is the target branch of the super project to be updated. If not set,
* default is HEAD.
*
* For non-bare repositories, HEAD will always be used and this will be
* ignored.
*
* @param branch
* @return this command
* @since 4.1
*/
public RepoCommand setTargetBranch(String branch) {
this.targetBranch = Constants.R_HEADS + branch;
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;
}
/**
* Set the author/committer for the bare repository commit.
*
* For non-bare repositories, the current user will be used and this will be
* ignored.
*
* @param author
* @return this command
*/
public RepoCommand setAuthor(final PersonIdent author) {
this.author = author;
return this;
}
/**
* Set the GetHeadFromUri callback.
*
* This is only used in bare repositories.
*
* @param callback
* @return this command
*/
public RepoCommand setRemoteReader(final RemoteReader callback) {
this.callback = callback;
return this;
}
/**
* Set the IncludedFileReader callback.
*
* @param reader
* @return this command
* @since 4.0
*/
public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
this.includedReader = reader;
return this;
}
@Override
public RevCommit call() throws GitAPIException {
try {
checkCallable();
if (uri == null || uri.length() == 0)
throw new IllegalArgumentException(
JGitText.get().uriNotConfigured);
if (inputStream == null) {
if (path == null || path.length() == 0)
throw new IllegalArgumentException(
JGitText.get().pathNotConfigured);
try {
inputStream = new FileInputStream(path);
} catch (IOException e) {
throw new IllegalArgumentException(
JGitText.get().pathNotConfigured);
}
}
if (repo.isBare()) {
bareProjects = new ArrayList();
if (author == null)
author = new PersonIdent(repo);
if (callback == null)
callback = new DefaultRemoteReader();
} else
git = new Git(repo);
ManifestParser parser = new ManifestParser(
includedReader, path, branch, uri, groups, repo);
try {
parser.read(inputStream);
for (RepoProject proj : parser.getFilteredProjects()) {
addSubmodule(proj.getUrl(),
proj.getPath(),
proj.getRevision(),
proj.getCopyFiles());
}
} catch (GitAPIException | IOException e) {
throw new ManifestErrorException(e);
}
} finally {
try {
if (inputStream != null)
inputStream.close();
} catch (IOException e) {
// Just ignore it, it's not important.
}
}
if (repo.isBare()) {
DirCache index = DirCache.newInCore();
DirCacheBuilder builder = index.builder();
ObjectInserter inserter = repo.newObjectInserter();
try (RevWalk rw = new RevWalk(repo)) {
Config cfg = new Config();
for (RepoProject proj : bareProjects) {
String name = proj.getPath();
String nameUri = proj.getName();
cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
// create gitlink
DirCacheEntry dcEntry = new DirCacheEntry(name);
ObjectId objectId;
if (ObjectId.isId(proj.getRevision()))
objectId = ObjectId.fromString(proj.getRevision());
else {
objectId = callback.sha1(nameUri, proj.getRevision());
}
if (objectId == null)
throw new RemoteUnavailableException(nameUri);
dcEntry.setObjectId(objectId);
dcEntry.setFileMode(FileMode.GITLINK);
builder.add(dcEntry);
for (CopyFile copyfile : proj.getCopyFiles()) {
byte[] src = callback.readFile(
nameUri, proj.getRevision(), copyfile.src);
objectId = inserter.insert(Constants.OBJ_BLOB, src);
dcEntry = new DirCacheEntry(copyfile.dest);
dcEntry.setObjectId(objectId);
dcEntry.setFileMode(FileMode.REGULAR_FILE);
builder.add(dcEntry);
}
}
String content = cfg.toText();
// create a new DirCacheEntry for .gitmodules file.
final DirCacheEntry dcEntry = new DirCacheEntry(Constants.DOT_GIT_MODULES);
ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
content.getBytes(Constants.CHARACTER_ENCODING));
dcEntry.setObjectId(objectId);
dcEntry.setFileMode(FileMode.REGULAR_FILE);
builder.add(dcEntry);
builder.finish();
ObjectId treeId = index.writeTree(inserter);
// Create a Commit object, populate it and write it
ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(treeId);
if (headId != null)
commit.setParentIds(headId);
commit.setAuthor(author);
commit.setCommitter(author);
commit.setMessage(RepoText.get().repoCommitMessage);
ObjectId commitId = inserter.insert(commit);
inserter.flush();
RefUpdate ru = repo.updateRef(targetBranch);
ru.setNewObjectId(commitId);
ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId());
Result rc = ru.update(rw);
switch (rc) {
case NEW:
case FORCED:
case FAST_FORWARD:
// Successful. Do nothing.
break;
case REJECTED:
case LOCK_FAILURE:
throw new ConcurrentRefUpdateException(
MessageFormat.format(
JGitText.get().cannotLock, targetBranch),
ru.getRef(),
rc);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed,
targetBranch, commitId.name(), rc));
}
return rw.parseCommit(commitId);
} catch (IOException e) {
throw new ManifestErrorException(e);
}
} else {
return git
.commit()
.setMessage(RepoText.get().repoCommitMessage)
.call();
}
}
private void addSubmodule(String url, String name, String revision,
List copyfiles) throws GitAPIException, IOException {
if (repo.isBare()) {
RepoProject proj = new RepoProject(url, name, revision, null, null);
proj.addCopyFiles(copyfiles);
bareProjects.add(proj);
} else {
SubmoduleAddCommand add = git
.submoduleAdd()
.setPath(name)
.setURI(url);
if (monitor != null)
add.setProgressMonitor(monitor);
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();
}
}
}
private static String findRef(String ref, Repository repo)
throws IOException {
if (!ObjectId.isId(ref)) {
Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
if (r != null)
return r.getName();
}
return ref;
}
}