Merge changes I11366273,I256e1572
* changes: RepoCommand: Offer to set extra files in the destination repository RepoCommand: Move bare/regular superproject writing to their own classes
This commit is contained in:
commit
424c861477
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, Google Inc. and others
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
package org.eclipse.jgit.gitrepo;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.gitrepo.BareSuperprojectWriter.BareWriterConfig;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader;
|
||||||
|
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectReader;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class BareSuperprojectWriterTest extends RepositoryTestCase {
|
||||||
|
|
||||||
|
private static final String SHA1_A = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void write_setGitModulesContents() throws Exception {
|
||||||
|
try (Repository bareRepo = createBareRepository()) {
|
||||||
|
RepoProject repoProject = new RepoProject("subprojectX", "path/to",
|
||||||
|
"refs/heads/branch-x", "remote", "");
|
||||||
|
repoProject.setUrl("http://example.com/a");
|
||||||
|
|
||||||
|
RemoteReader mockRemoteReader = mock(RemoteReader.class);
|
||||||
|
when(mockRemoteReader.sha1("http://example.com/a",
|
||||||
|
"refs/heads/branch-x"))
|
||||||
|
.thenReturn(ObjectId.fromString(SHA1_A));
|
||||||
|
|
||||||
|
BareSuperprojectWriter w = new BareSuperprojectWriter(bareRepo,
|
||||||
|
null, "refs/heads/master", author, mockRemoteReader,
|
||||||
|
BareWriterConfig.getDefault(), List.of());
|
||||||
|
|
||||||
|
RevCommit commit = w.write(Arrays.asList(repoProject));
|
||||||
|
|
||||||
|
String contents = readContents(bareRepo, commit, ".gitmodules");
|
||||||
|
List<String> contentLines = Arrays
|
||||||
|
.asList(contents.split("\n"));
|
||||||
|
assertThat(contentLines.get(0),
|
||||||
|
is("[submodule \"subprojectX\"]"));
|
||||||
|
assertThat(contentLines.subList(1, contentLines.size()),
|
||||||
|
containsInAnyOrder(is("\tbranch = refs/heads/branch-x"),
|
||||||
|
is("\tpath = path/to"),
|
||||||
|
is("\turl = http://example.com/a")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void write_setExtraContents() throws Exception {
|
||||||
|
try (Repository bareRepo = createBareRepository()) {
|
||||||
|
RepoProject repoProject = new RepoProject("subprojectX", "path/to",
|
||||||
|
"refs/heads/branch-x", "remote", "");
|
||||||
|
repoProject.setUrl("http://example.com/a");
|
||||||
|
|
||||||
|
RemoteReader mockRemoteReader = mock(RemoteReader.class);
|
||||||
|
when(mockRemoteReader.sha1("http://example.com/a",
|
||||||
|
"refs/heads/branch-x"))
|
||||||
|
.thenReturn(ObjectId.fromString(SHA1_A));
|
||||||
|
|
||||||
|
BareSuperprojectWriter w = new BareSuperprojectWriter(bareRepo,
|
||||||
|
null, "refs/heads/master", author, mockRemoteReader,
|
||||||
|
BareWriterConfig.getDefault(),
|
||||||
|
List.of(new BareSuperprojectWriter.ExtraContent("x",
|
||||||
|
"extra-content")));
|
||||||
|
|
||||||
|
RevCommit commit = w.write(Arrays.asList(repoProject));
|
||||||
|
|
||||||
|
String contents = readContents(bareRepo, commit, "x");
|
||||||
|
assertThat(contents, is("extra-content"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readContents(Repository repo, RevCommit commit,
|
||||||
|
String path) throws Exception {
|
||||||
|
String idStr = commit.getId().name() + ":" + path;
|
||||||
|
ObjectId modId = repo.resolve(idStr);
|
||||||
|
try (ObjectReader reader = repo.newObjectReader()) {
|
||||||
|
return new String(
|
||||||
|
reader.open(modId).getCachedBytes(Integer.MAX_VALUE));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, Google Inc. and others
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
package org.eclipse.jgit.gitrepo;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
|
||||||
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
|
import org.eclipse.jgit.api.errors.JGitInternalException;
|
||||||
|
import org.eclipse.jgit.dircache.DirCache;
|
||||||
|
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
||||||
|
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteUnavailableException;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
|
||||||
|
import org.eclipse.jgit.gitrepo.internal.RepoText;
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.FileMode;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectInserter;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate.Result;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.util.FileUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes .gitmodules and gitlinks of parsed manifest projects into a bare
|
||||||
|
* repository.
|
||||||
|
*
|
||||||
|
* To write on a regular repository, see {@link RegularSuperprojectWriter}.
|
||||||
|
*/
|
||||||
|
class BareSuperprojectWriter {
|
||||||
|
private static final int LOCK_FAILURE_MAX_RETRIES = 5;
|
||||||
|
|
||||||
|
// Retry exponentially with delays in this range
|
||||||
|
private static final int LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS = 50;
|
||||||
|
|
||||||
|
private static final int LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS = 5000;
|
||||||
|
|
||||||
|
private final Repository repo;
|
||||||
|
|
||||||
|
private final URI targetUri;
|
||||||
|
|
||||||
|
private final String targetBranch;
|
||||||
|
|
||||||
|
private final RemoteReader callback;
|
||||||
|
|
||||||
|
private final BareWriterConfig config;
|
||||||
|
|
||||||
|
private final PersonIdent author;
|
||||||
|
|
||||||
|
private List<ExtraContent> extraContents;
|
||||||
|
|
||||||
|
static class BareWriterConfig {
|
||||||
|
boolean ignoreRemoteFailures = false;
|
||||||
|
|
||||||
|
boolean recordRemoteBranch = true;
|
||||||
|
|
||||||
|
boolean recordSubmoduleLabels = true;
|
||||||
|
|
||||||
|
boolean recordShallowSubmodules = true;
|
||||||
|
|
||||||
|
static BareWriterConfig getDefault() {
|
||||||
|
return new BareWriterConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BareWriterConfig() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ExtraContent {
|
||||||
|
final String path;
|
||||||
|
|
||||||
|
final String content;
|
||||||
|
|
||||||
|
ExtraContent(String path, String content) {
|
||||||
|
this.path = path;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BareSuperprojectWriter(Repository repo, URI targetUri,
|
||||||
|
String targetBranch,
|
||||||
|
PersonIdent author, RemoteReader callback,
|
||||||
|
BareWriterConfig config,
|
||||||
|
List<ExtraContent> extraContents) {
|
||||||
|
assert (repo.isBare());
|
||||||
|
this.repo = repo;
|
||||||
|
this.targetUri = targetUri;
|
||||||
|
this.targetBranch = targetBranch;
|
||||||
|
this.author = author;
|
||||||
|
this.callback = callback;
|
||||||
|
this.config = config;
|
||||||
|
this.extraContents = extraContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
RevCommit write(List<RepoProject> repoProjects)
|
||||||
|
throws GitAPIException {
|
||||||
|
DirCache index = DirCache.newInCore();
|
||||||
|
ObjectInserter inserter = repo.newObjectInserter();
|
||||||
|
|
||||||
|
try (RevWalk rw = new RevWalk(repo)) {
|
||||||
|
prepareIndex(repoProjects, index, inserter);
|
||||||
|
ObjectId treeId = index.writeTree(inserter);
|
||||||
|
long prevDelay = 0;
|
||||||
|
for (int i = 0; i < LOCK_FAILURE_MAX_RETRIES - 1; i++) {
|
||||||
|
try {
|
||||||
|
return commitTreeOnCurrentTip(inserter, rw, treeId);
|
||||||
|
} catch (ConcurrentRefUpdateException e) {
|
||||||
|
prevDelay = FileUtils.delay(prevDelay,
|
||||||
|
LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS,
|
||||||
|
LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS);
|
||||||
|
Thread.sleep(prevDelay);
|
||||||
|
repo.getRefDatabase().refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// In the last try, just propagate the exceptions
|
||||||
|
return commitTreeOnCurrentTip(inserter, rw, treeId);
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new ManifestErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareIndex(List<RepoProject> projects, DirCache index,
|
||||||
|
ObjectInserter inserter) throws IOException, GitAPIException {
|
||||||
|
Config cfg = new Config();
|
||||||
|
StringBuilder attributes = new StringBuilder();
|
||||||
|
DirCacheBuilder builder = index.builder();
|
||||||
|
for (RepoProject proj : projects) {
|
||||||
|
String name = proj.getName();
|
||||||
|
String path = proj.getPath();
|
||||||
|
String url = proj.getUrl();
|
||||||
|
ObjectId objectId;
|
||||||
|
if (ObjectId.isId(proj.getRevision())) {
|
||||||
|
objectId = ObjectId.fromString(proj.getRevision());
|
||||||
|
} else {
|
||||||
|
objectId = callback.sha1(url, proj.getRevision());
|
||||||
|
if (objectId == null && !config.ignoreRemoteFailures) {
|
||||||
|
throw new RemoteUnavailableException(url);
|
||||||
|
}
|
||||||
|
if (config.recordRemoteBranch) {
|
||||||
|
// "branch" field is only for non-tag references.
|
||||||
|
// Keep tags in "ref" field as hint for other tools.
|
||||||
|
String field = proj.getRevision().startsWith(R_TAGS) ? "ref" //$NON-NLS-1$
|
||||||
|
: "branch"; //$NON-NLS-1$
|
||||||
|
cfg.setString("submodule", name, field, //$NON-NLS-1$
|
||||||
|
proj.getRevision());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.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", name, "shallow", //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.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(url);
|
||||||
|
if (targetUri != null) {
|
||||||
|
submodUrl = RepoCommand.relativize(targetUri, submodUrl);
|
||||||
|
}
|
||||||
|
cfg.setString("submodule", name, "path", path); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
cfg.setString("submodule", name, "url", //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
submodUrl.toString());
|
||||||
|
|
||||||
|
// create gitlink
|
||||||
|
if (objectId != null) {
|
||||||
|
DirCacheEntry dcEntry = new DirCacheEntry(path);
|
||||||
|
dcEntry.setObjectId(objectId);
|
||||||
|
dcEntry.setFileMode(FileMode.GITLINK);
|
||||||
|
builder.add(dcEntry);
|
||||||
|
|
||||||
|
for (CopyFile copyfile : proj.getCopyFiles()) {
|
||||||
|
RemoteFile rf = callback.readFileWithMode(url,
|
||||||
|
proj.getRevision(), copyfile.src);
|
||||||
|
objectId = inserter.insert(Constants.OBJ_BLOB,
|
||||||
|
rf.getContents());
|
||||||
|
dcEntry = new DirCacheEntry(copyfile.dest);
|
||||||
|
dcEntry.setObjectId(objectId);
|
||||||
|
dcEntry.setFileMode(rf.getFileMode());
|
||||||
|
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(UTF_8));
|
||||||
|
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.
|
||||||
|
DirCacheEntry dcEntry = new DirCacheEntry(
|
||||||
|
Constants.DOT_GIT_MODULES);
|
||||||
|
ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
|
||||||
|
content.getBytes(UTF_8));
|
||||||
|
dcEntry.setObjectId(objectId);
|
||||||
|
dcEntry.setFileMode(FileMode.REGULAR_FILE);
|
||||||
|
builder.add(dcEntry);
|
||||||
|
|
||||||
|
if (config.recordSubmoduleLabels) {
|
||||||
|
// create a new DirCacheEntry for .gitattributes file.
|
||||||
|
DirCacheEntry dcEntryAttr = new DirCacheEntry(
|
||||||
|
Constants.DOT_GIT_ATTRIBUTES);
|
||||||
|
ObjectId attrId = inserter.insert(Constants.OBJ_BLOB,
|
||||||
|
attributes.toString().getBytes(UTF_8));
|
||||||
|
dcEntryAttr.setObjectId(attrId);
|
||||||
|
dcEntryAttr.setFileMode(FileMode.REGULAR_FILE);
|
||||||
|
builder.add(dcEntryAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ExtraContent ec : extraContents) {
|
||||||
|
DirCacheEntry extraDcEntry = new DirCacheEntry(ec.path);
|
||||||
|
|
||||||
|
ObjectId oid = inserter.insert(Constants.OBJ_BLOB,
|
||||||
|
ec.content.getBytes(UTF_8));
|
||||||
|
extraDcEntry.setObjectId(oid);
|
||||||
|
extraDcEntry.setFileMode(FileMode.REGULAR_FILE);
|
||||||
|
builder.add(extraDcEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RevCommit commitTreeOnCurrentTip(ObjectInserter inserter,
|
||||||
|
RevWalk rw, ObjectId treeId)
|
||||||
|
throws IOException, ConcurrentRefUpdateException {
|
||||||
|
ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$
|
||||||
|
if (headId != null
|
||||||
|
&& rw.parseCommit(headId).getTree().getId().equals(treeId)) {
|
||||||
|
// No change. Do nothing.
|
||||||
|
return rw.parseCommit(headId);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, Google Inc. and others
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
package org.eclipse.jgit.gitrepo;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.R_REMOTES;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.api.SubmoduleAddCommand;
|
||||||
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
|
||||||
|
import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
|
||||||
|
import org.eclipse.jgit.gitrepo.internal.RepoText;
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes .gitmodules and gitlinks of parsed manifest projects into a regular
|
||||||
|
* repository (using git submodule commands)
|
||||||
|
*
|
||||||
|
* To write on a bare repository, use {@link BareSuperprojectWriter}
|
||||||
|
*/
|
||||||
|
class RegularSuperprojectWriter {
|
||||||
|
|
||||||
|
private Repository repo;
|
||||||
|
|
||||||
|
private ProgressMonitor monitor;
|
||||||
|
|
||||||
|
RegularSuperprojectWriter(Repository repo, ProgressMonitor monitor) {
|
||||||
|
this.repo = repo;
|
||||||
|
this.monitor = monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
RevCommit write(List<RepoProject> repoProjects)
|
||||||
|
throws GitAPIException {
|
||||||
|
try (Git git = new Git(repo)) {
|
||||||
|
for (RepoProject proj : repoProjects) {
|
||||||
|
addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(),
|
||||||
|
proj.getRevision(), proj.getCopyFiles(),
|
||||||
|
proj.getLinkFiles(), git);
|
||||||
|
}
|
||||||
|
return git.commit().setMessage(RepoText.get().repoCommitMessage)
|
||||||
|
.call();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ManifestErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSubmodule(String name, String url, String path,
|
||||||
|
String revision, List<CopyFile> copyfiles, List<LinkFile> linkfiles,
|
||||||
|
Git git) throws GitAPIException, IOException {
|
||||||
|
assert (!repo.isBare());
|
||||||
|
assert (git != null);
|
||||||
|
if (!linkfiles.isEmpty()) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
JGitText.get().nonBareLinkFilesNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubmoduleAddCommand add = git.submoduleAdd().setName(name).setPath(path)
|
||||||
|
.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(path).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.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
|
||||||
|
if (r != null) {
|
||||||
|
return r.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,6 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.jgit.gitrepo;
|
package org.eclipse.jgit.gitrepo;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
|
|
||||||
import static org.eclipse.jgit.lib.Constants.R_REMOTES;
|
|
||||||
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -31,34 +26,21 @@
|
||||||
import org.eclipse.jgit.annotations.Nullable;
|
import org.eclipse.jgit.annotations.Nullable;
|
||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.api.GitCommand;
|
import org.eclipse.jgit.api.GitCommand;
|
||||||
import org.eclipse.jgit.api.SubmoduleAddCommand;
|
|
||||||
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
|
|
||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
import org.eclipse.jgit.api.errors.InvalidRefNameException;
|
import org.eclipse.jgit.api.errors.InvalidRefNameException;
|
||||||
import org.eclipse.jgit.api.errors.JGitInternalException;
|
import org.eclipse.jgit.gitrepo.BareSuperprojectWriter.ExtraContent;
|
||||||
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.ManifestParser.IncludedFileReader;
|
||||||
import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
|
|
||||||
import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
|
|
||||||
import org.eclipse.jgit.gitrepo.internal.RepoText;
|
import org.eclipse.jgit.gitrepo.internal.RepoText;
|
||||||
import org.eclipse.jgit.internal.JGitText;
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.FileMode;
|
import org.eclipse.jgit.lib.FileMode;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectInserter;
|
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.RefDatabase;
|
import org.eclipse.jgit.lib.RefDatabase;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
|
||||||
import org.eclipse.jgit.lib.RefUpdate.Result;
|
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.util.FileUtils;
|
import org.eclipse.jgit.util.FileUtils;
|
||||||
|
|
||||||
|
@ -80,12 +62,7 @@
|
||||||
* @since 3.4
|
* @since 3.4
|
||||||
*/
|
*/
|
||||||
public class RepoCommand extends GitCommand<RevCommit> {
|
public class RepoCommand extends GitCommand<RevCommit> {
|
||||||
private static final int LOCK_FAILURE_MAX_RETRIES = 5;
|
|
||||||
|
|
||||||
// Retry exponentially with delays in this range
|
|
||||||
private static final int LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS = 50;
|
|
||||||
|
|
||||||
private static final int LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS = 5000;
|
|
||||||
|
|
||||||
private String manifestPath;
|
private String manifestPath;
|
||||||
private String baseUri;
|
private String baseUri;
|
||||||
|
@ -93,17 +70,18 @@ public class RepoCommand extends GitCommand<RevCommit> {
|
||||||
private String groupsParam;
|
private String groupsParam;
|
||||||
private String branch;
|
private String branch;
|
||||||
private String targetBranch = Constants.HEAD;
|
private String targetBranch = Constants.HEAD;
|
||||||
private boolean recordRemoteBranch = true;
|
|
||||||
private boolean recordSubmoduleLabels = true;
|
|
||||||
private boolean recordShallowSubmodules = true;
|
|
||||||
private PersonIdent author;
|
private PersonIdent author;
|
||||||
private RemoteReader callback;
|
private RemoteReader callback;
|
||||||
private InputStream inputStream;
|
private InputStream inputStream;
|
||||||
private IncludedFileReader includedReader;
|
private IncludedFileReader includedReader;
|
||||||
private boolean ignoreRemoteFailures = false;
|
|
||||||
|
private BareSuperprojectWriter.BareWriterConfig bareWriterConfig = BareSuperprojectWriter.BareWriterConfig
|
||||||
|
.getDefault();
|
||||||
|
|
||||||
private ProgressMonitor monitor;
|
private ProgressMonitor monitor;
|
||||||
|
|
||||||
|
private final List<ExtraContent> extraContents = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback to get ref sha1 of a repository from its uri.
|
* A callback to get ref sha1 of a repository from its uri.
|
||||||
*
|
*
|
||||||
|
@ -269,14 +247,14 @@ public RemoteFile readFileWithMode(String uri, String ref, String path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
private static class ManifestErrorException extends GitAPIException {
|
static class ManifestErrorException extends GitAPIException {
|
||||||
ManifestErrorException(Throwable cause) {
|
ManifestErrorException(Throwable cause) {
|
||||||
super(RepoText.get().invalidManifest, cause);
|
super(RepoText.get().invalidManifest, cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
private static class RemoteUnavailableException extends GitAPIException {
|
static class RemoteUnavailableException extends GitAPIException {
|
||||||
RemoteUnavailableException(String uri) {
|
RemoteUnavailableException(String uri) {
|
||||||
super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri));
|
super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri));
|
||||||
}
|
}
|
||||||
|
@ -421,7 +399,7 @@ public RepoCommand setTargetBranch(String branch) {
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
public RepoCommand setRecordRemoteBranch(boolean enable) {
|
public RepoCommand setRecordRemoteBranch(boolean enable) {
|
||||||
this.recordRemoteBranch = enable;
|
this.bareWriterConfig.recordRemoteBranch = enable;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +414,7 @@ public RepoCommand setRecordRemoteBranch(boolean enable) {
|
||||||
* @since 4.4
|
* @since 4.4
|
||||||
*/
|
*/
|
||||||
public RepoCommand setRecordSubmoduleLabels(boolean enable) {
|
public RepoCommand setRecordSubmoduleLabels(boolean enable) {
|
||||||
this.recordSubmoduleLabels = enable;
|
this.bareWriterConfig.recordSubmoduleLabels = enable;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,7 +429,7 @@ public RepoCommand setRecordSubmoduleLabels(boolean enable) {
|
||||||
* @since 4.4
|
* @since 4.4
|
||||||
*/
|
*/
|
||||||
public RepoCommand setRecommendShallow(boolean enable) {
|
public RepoCommand setRecommendShallow(boolean enable) {
|
||||||
this.recordShallowSubmodules = enable;
|
this.bareWriterConfig.recordShallowSubmodules = enable;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +463,7 @@ public RepoCommand setProgressMonitor(ProgressMonitor monitor) {
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public RepoCommand setIgnoreRemoteFailures(boolean ignore) {
|
public RepoCommand setIgnoreRemoteFailures(boolean ignore) {
|
||||||
this.ignoreRemoteFailures = ignore;
|
this.bareWriterConfig.ignoreRemoteFailures = ignore;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,6 +512,22 @@ public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a file with the given content in the destination repository
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* where to create the file in the destination repository
|
||||||
|
* @param contents
|
||||||
|
* content for the create file
|
||||||
|
* @return this command
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public RepoCommand addToDestination(String path, String contents) {
|
||||||
|
this.extraContents.add(new ExtraContent(path, contents));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public RevCommit call() throws GitAPIException {
|
public RevCommit call() throws GitAPIException {
|
||||||
|
@ -570,240 +564,18 @@ public RevCommit call() throws GitAPIException {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repo.isBare()) {
|
if (repo.isBare()) {
|
||||||
if (author == null)
|
|
||||||
author = new PersonIdent(repo);
|
|
||||||
if (callback == null)
|
|
||||||
callback = new DefaultRemoteReader();
|
|
||||||
List<RepoProject> renamedProjects = renameProjects(filteredProjects);
|
List<RepoProject> renamedProjects = renameProjects(filteredProjects);
|
||||||
|
BareSuperprojectWriter writer = new BareSuperprojectWriter(repo, targetUri,
|
||||||
DirCache index = DirCache.newInCore();
|
targetBranch,
|
||||||
ObjectInserter inserter = repo.newObjectInserter();
|
author == null ? new PersonIdent(repo) : author,
|
||||||
|
callback == null ? new DefaultRemoteReader() : callback,
|
||||||
try (RevWalk rw = new RevWalk(repo)) {
|
bareWriterConfig, extraContents);
|
||||||
prepareIndex(renamedProjects, index, inserter);
|
return writer.write(renamedProjects);
|
||||||
ObjectId treeId = index.writeTree(inserter);
|
|
||||||
long prevDelay = 0;
|
|
||||||
for (int i = 0; i < LOCK_FAILURE_MAX_RETRIES - 1; i++) {
|
|
||||||
try {
|
|
||||||
return commitTreeOnCurrentTip(
|
|
||||||
inserter, rw, treeId);
|
|
||||||
} catch (ConcurrentRefUpdateException e) {
|
|
||||||
prevDelay = FileUtils.delay(prevDelay,
|
|
||||||
LOCK_FAILURE_MIN_RETRY_DELAY_MILLIS,
|
|
||||||
LOCK_FAILURE_MAX_RETRY_DELAY_MILLIS);
|
|
||||||
Thread.sleep(prevDelay);
|
|
||||||
repo.getRefDatabase().refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// In the last try, just propagate the exceptions
|
|
||||||
return commitTreeOnCurrentTip(inserter, rw, treeId);
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
throw new ManifestErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try (Git git = new Git(repo)) {
|
|
||||||
for (RepoProject proj : filteredProjects) {
|
|
||||||
addSubmodule(proj.getName(), proj.getUrl(), proj.getPath(),
|
|
||||||
proj.getRevision(), proj.getCopyFiles(),
|
|
||||||
proj.getLinkFiles(), git);
|
|
||||||
}
|
|
||||||
return git.commit().setMessage(RepoText.get().repoCommitMessage)
|
|
||||||
.call();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ManifestErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareIndex(List<RepoProject> projects, DirCache index,
|
|
||||||
ObjectInserter inserter) throws IOException, GitAPIException {
|
|
||||||
Config cfg = new Config();
|
|
||||||
StringBuilder attributes = new StringBuilder();
|
|
||||||
DirCacheBuilder builder = index.builder();
|
|
||||||
for (RepoProject proj : projects) {
|
|
||||||
String name = proj.getName();
|
|
||||||
String path = proj.getPath();
|
|
||||||
String url = proj.getUrl();
|
|
||||||
ObjectId objectId;
|
|
||||||
if (ObjectId.isId(proj.getRevision())) {
|
|
||||||
objectId = ObjectId.fromString(proj.getRevision());
|
|
||||||
} else {
|
|
||||||
objectId = callback.sha1(url, proj.getRevision());
|
|
||||||
if (objectId == null && !ignoreRemoteFailures) {
|
|
||||||
throw new RemoteUnavailableException(url);
|
|
||||||
}
|
|
||||||
if (recordRemoteBranch) {
|
|
||||||
// "branch" field is only for non-tag references.
|
|
||||||
// Keep tags in "ref" field as hint for other tools.
|
|
||||||
String field = proj.getRevision().startsWith(R_TAGS) ? "ref" //$NON-NLS-1$
|
|
||||||
: "branch"; //$NON-NLS-1$
|
|
||||||
cfg.setString("submodule", name, field, //$NON-NLS-1$
|
|
||||||
proj.getRevision());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recordShallowSubmodules
|
RegularSuperprojectWriter writer = new RegularSuperprojectWriter(repo, monitor);
|
||||||
&& proj.getRecommendShallow() != null) {
|
return writer.write(filteredProjects);
|
||||||
// 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", name, "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(url);
|
|
||||||
if (targetUri != null) {
|
|
||||||
submodUrl = relativize(targetUri, submodUrl);
|
|
||||||
}
|
|
||||||
cfg.setString("submodule", name, "path", path); //$NON-NLS-1$ //$NON-NLS-2$
|
|
||||||
cfg.setString("submodule", name, "url", //$NON-NLS-1$ //$NON-NLS-2$
|
|
||||||
submodUrl.toString());
|
|
||||||
|
|
||||||
// create gitlink
|
|
||||||
if (objectId != null) {
|
|
||||||
DirCacheEntry dcEntry = new DirCacheEntry(path);
|
|
||||||
dcEntry.setObjectId(objectId);
|
|
||||||
dcEntry.setFileMode(FileMode.GITLINK);
|
|
||||||
builder.add(dcEntry);
|
|
||||||
|
|
||||||
for (CopyFile copyfile : proj.getCopyFiles()) {
|
|
||||||
RemoteFile rf = callback.readFileWithMode(url,
|
|
||||||
proj.getRevision(), copyfile.src);
|
|
||||||
objectId = inserter.insert(Constants.OBJ_BLOB,
|
|
||||||
rf.getContents());
|
|
||||||
dcEntry = new DirCacheEntry(copyfile.dest);
|
|
||||||
dcEntry.setObjectId(objectId);
|
|
||||||
dcEntry.setFileMode(rf.getFileMode());
|
|
||||||
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(UTF_8));
|
|
||||||
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.
|
|
||||||
DirCacheEntry dcEntry = new DirCacheEntry(
|
|
||||||
Constants.DOT_GIT_MODULES);
|
|
||||||
ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
|
|
||||||
content.getBytes(UTF_8));
|
|
||||||
dcEntry.setObjectId(objectId);
|
|
||||||
dcEntry.setFileMode(FileMode.REGULAR_FILE);
|
|
||||||
builder.add(dcEntry);
|
|
||||||
|
|
||||||
if (recordSubmoduleLabels) {
|
|
||||||
// create a new DirCacheEntry for .gitattributes file.
|
|
||||||
DirCacheEntry dcEntryAttr = new DirCacheEntry(
|
|
||||||
Constants.DOT_GIT_ATTRIBUTES);
|
|
||||||
ObjectId attrId = inserter.insert(Constants.OBJ_BLOB,
|
|
||||||
attributes.toString().getBytes(UTF_8));
|
|
||||||
dcEntryAttr.setObjectId(attrId);
|
|
||||||
dcEntryAttr.setFileMode(FileMode.REGULAR_FILE);
|
|
||||||
builder.add(dcEntryAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private RevCommit commitTreeOnCurrentTip(ObjectInserter inserter,
|
|
||||||
RevWalk rw, ObjectId treeId)
|
|
||||||
throws IOException, ConcurrentRefUpdateException {
|
|
||||||
ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$
|
|
||||||
if (headId != null && rw.parseCommit(headId).getTree().getId().equals(treeId)) {
|
|
||||||
// No change. Do nothing.
|
|
||||||
return rw.parseCommit(headId);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addSubmodule(String name, String url, String path,
|
|
||||||
String revision, List<CopyFile> copyfiles, List<LinkFile> linkfiles,
|
|
||||||
Git git) throws GitAPIException, IOException {
|
|
||||||
assert (!repo.isBare());
|
|
||||||
assert (git != null);
|
|
||||||
if (!linkfiles.isEmpty()) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
JGitText.get().nonBareLinkFilesNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
SubmoduleAddCommand add = git.submoduleAdd().setName(name).setPath(path)
|
|
||||||
.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(path).call();
|
|
||||||
}
|
|
||||||
for (CopyFile copyfile : copyfiles) {
|
|
||||||
copyfile.copy();
|
|
||||||
git.add().addFilepattern(copyfile.dest).call();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -910,13 +682,4 @@ static URI relativize(URI current, URI target) {
|
||||||
return URI.create(j.toString());
|
return URI.create(j.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String findRef(String ref, Repository repo)
|
|
||||||
throws IOException {
|
|
||||||
if (!ObjectId.isId(ref)) {
|
|
||||||
Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
|
|
||||||
if (r != null)
|
|
||||||
return r.getName();
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue