* Calling {@link #setInputStream} will ignore the path set here.
*
* @param path
* (with /
as separator)
* @return this command
*/
public RepoCommand setPath(String path) {
this.manifestPath = 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 a {@link java.io.InputStream} object. * @return this command * @since 3.5 */ public RepoCommand setInputStream(InputStream inputStream) { this.inputStream = inputStream; return this; } /** * Set base URI of the paths inside the XML. This is typically the name of * the directory holding the manifest repository, eg. for * https://android.googlesource.com/platform/manifest, this should be * /platform (if you would run this on android.googlesource.com) or * https://android.googlesource.com/platform elsewhere. * * @param uri * the base URI * @return this command */ public RepoCommand setURI(String uri) { this.baseUri = uri; return this; } /** * Set the URI of the superproject (this repository), so the .gitmodules * file can specify the submodule URLs relative to the superproject. * * @param uri * the URI of the repository holding the superproject. * @return this command * @since 4.8 */ public RepoCommand setTargetURI(String uri) { // The repo name is interpreted as a directory, for example // Gerrit (http://gerrit.googlesource.com/gerrit) has a // .gitmodules referencing ../plugins/hooks, which is // on http://gerrit.googlesource.com/plugins/hooks, this.targetUri = URI.create(uri + "/"); //$NON-NLS-1$ 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.groupsParam = 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 * a branch name * @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 * branch name * @return this command * @since 4.1 */ public RepoCommand setTargetBranch(String branch) { this.targetBranch = Constants.R_HEADS + branch; return this; } /** * Set whether the branch name should be recorded in .gitmodules. *
* Submodule entries in .gitmodules can include a "branch" field * to indicate what remote branch each submodule tracks. *
* That field is used by "git submodule update --remote" to update * to the tip of the tracked branch when asked and by Gerrit to * update the superproject when a change on that branch is merged. *
* Subprojects that request a specific commit or tag will not have * a branch name recorded. *
* Not implemented for non-bare repositories. * * @param enable Whether to record the branch name * @return this command * @since 4.2 */ public RepoCommand setRecordRemoteBranch(boolean enable) { this.recordRemoteBranch = enable; return this; } /** * Set whether the labels field should be recorded as a label in * .gitattributes. *
* Not implemented for non-bare repositories. * * @param enable Whether to record the labels in the .gitattributes * @return this command * @since 4.4 */ public RepoCommand setRecordSubmoduleLabels(boolean enable) { this.recordSubmoduleLabels = enable; return this; } /** * Set whether the clone-depth field should be recorded as a shallow * recommendation in .gitmodules. *
* Not implemented for non-bare repositories.
*
* @param enable Whether to record the shallow recommendation.
* @return this command
* @since 4.4
*/
public RepoCommand setRecommendShallow(boolean enable) {
this.recordShallowSubmodules = enable;
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
* a {@link org.eclipse.jgit.lib.ProgressMonitor}
* @return this command
*/
public RepoCommand setProgressMonitor(final ProgressMonitor monitor) {
this.monitor = monitor;
return this;
}
/**
* Set whether to skip projects whose commits don't exist remotely.
*
* When set to true, we'll just skip the manifest entry and continue * on to the next one. *
* When set to false (default), we'll throw an error when remote * failures occur. *
* Not implemented for non-bare repositories. * * @param ignore Whether to ignore the remote failures. * @return this command * @since 4.3 */ public RepoCommand setIgnoreRemoteFailures(boolean ignore) { this.ignoreRemoteFailures = ignore; 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
* the author's {@link org.eclipse.jgit.lib.PersonIdent}
* @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
* a {@link org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader}
* object.
* @return this command
*/
public RepoCommand setRemoteReader(final RemoteReader callback) {
this.callback = callback;
return this;
}
/**
* Set the IncludedFileReader callback.
*
* @param reader
* a
* {@link org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader}
* object.
* @return this command
* @since 4.0
*/
public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
this.includedReader = reader;
return this;
}
/** {@inheritDoc} */
@Override
public RevCommit call() throws GitAPIException {
try {
checkCallable();
if (baseUri == null) {
baseUri = ""; //$NON-NLS-1$
}
if (inputStream == null) {
if (manifestPath == null || manifestPath.length() == 0)
throw new IllegalArgumentException(
JGitText.get().pathNotConfigured);
try {
inputStream = new FileInputStream(manifestPath);
} 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, manifestPath, branch, baseUri, groupsParam, repo);
try {
parser.read(inputStream);
for (RepoProject proj : parser.getFilteredProjects()) {
addSubmodule(proj.getUrl(),
proj.getPath(),
proj.getRevision(),
proj.getCopyFiles(),
proj.getLinkFiles(),
proj.getGroups(),
proj.getRecommendShallow());
}
} 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();
StringBuilder attributes = new StringBuilder();
for (RepoProject proj : bareProjects) {
String path = proj.getPath();
String nameUri = proj.getName();
ObjectId objectId;
if (ObjectId.isId(proj.getRevision())) {
objectId = ObjectId.fromString(proj.getRevision());
} else {
objectId = callback.sha1(nameUri, proj.getRevision());
if (objectId == null) {
if (ignoreRemoteFailures) {
continue;
}
throw new RemoteUnavailableException(nameUri);
}
if (recordRemoteBranch) {
// can be branch or tag
cfg.setString("submodule", path, "branch", //$NON-NLS-1$ //$NON-NLS-2$
proj.getRevision());
}
if (recordShallowSubmodules && proj.getRecommendShallow() != null) {
// The shallow recommendation is losing information.
// As the repo manifests stores the recommended
// depth in the 'clone-depth' field, while
// git core only uses a binary 'shallow = true/false'
// hint, we'll map any depth to 'shallow = true'
cfg.setBoolean("submodule", path, "shallow", //$NON-NLS-1$ //$NON-NLS-2$
true);
}
}
if (recordSubmoduleLabels) {
StringBuilder rec = new StringBuilder();
rec.append("/"); //$NON-NLS-1$
rec.append(path);
for (String group : proj.getGroups()) {
rec.append(" "); //$NON-NLS-1$
rec.append(group);
}
rec.append("\n"); //$NON-NLS-1$
attributes.append(rec.toString());
}
URI submodUrl = URI.create(nameUri);
if (targetUri != null) {
submodUrl = relativize(targetUri, submodUrl);
}
cfg.setString("submodule", path, "path", path); //$NON-NLS-1$ //$NON-NLS-2$
cfg.setString("submodule", path, "url", submodUrl.toString()); //$NON-NLS-1$ //$NON-NLS-2$
// create gitlink
DirCacheEntry dcEntry = new DirCacheEntry(path);
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);
}
for (LinkFile linkfile : proj.getLinkFiles()) {
String link;
if (linkfile.dest.contains("/")) { //$NON-NLS-1$
link = FileUtils.relativizeGitPath(
linkfile.dest.substring(0,
linkfile.dest.lastIndexOf('/')),
proj.getPath() + "/" + linkfile.src); //$NON-NLS-1$
} else {
link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$
}
objectId = inserter.insert(Constants.OBJ_BLOB,
link.getBytes(
Constants.CHARACTER_ENCODING));
dcEntry = new DirCacheEntry(linkfile.dest);
dcEntry.setObjectId(objectId);
dcEntry.setFileMode(FileMode.SYMLINK);
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);
if (recordSubmoduleLabels) {
// create a new DirCacheEntry for .gitattributes file.
final DirCacheEntry dcEntryAttr = new DirCacheEntry(Constants.DOT_GIT_ATTRIBUTES);
ObjectId attrId = inserter.insert(Constants.OBJ_BLOB,
attributes.toString().getBytes(Constants.CHARACTER_ENCODING));
dcEntryAttr.setObjectId(attrId);
dcEntryAttr.setFileMode(FileMode.REGULAR_FILE);
builder.add(dcEntryAttr);
}
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 path, String revision,
List