SubmoduleCommits: Code to create/amend gitlink commits

SubmoduleOp class does (at least) two things: keeps the map of
subscriptions for a repository and creates/amends gitlink commits.
The creation/amendment of gitlink commits doesn't need anything else
from submoduleOp and can be moved to its own class.

This makes submoduleOp smaller, simpler and more focused. The new code
is easier to unit tests. As a side benefit, GitlinkOp becomes a static
inner class.

Change-Id: I066866b264b81c70c3e3751d6e190a4b1e6828dd
This commit is contained in:
Ivan Frade
2020-08-25 15:14:14 -07:00
parent 98ad31f179
commit cfe0c7bb0a
4 changed files with 586 additions and 266 deletions

View File

@@ -0,0 +1,316 @@
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.VerboseSuperprojectUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
/** Create commit or amend existing one updating gitlinks. */
class SubmoduleCommits {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final PersonIdent myIdent;
private final VerboseSuperprojectUpdate verboseSuperProject;
private final MergeOpRepoManager orm;
private final long maxCombinedCommitMessageSize;
private final long maxCommitMessages;
private final BranchTips branchTips;
SubmoduleCommits(MergeOpRepoManager orm, PersonIdent myIdent, Config cfg, BranchTips branchTips) {
this.orm = orm;
this.myIdent = myIdent;
this.verboseSuperProject =
cfg.getEnum("submodule", null, "verboseSuperprojectUpdate", VerboseSuperprojectUpdate.TRUE);
this.maxCombinedCommitMessageSize =
cfg.getLong("submodule", "maxCombinedCommitMessageSize", 256 << 10);
this.maxCommitMessages = cfg.getLong("submodule", "maxCommitMessages", 1000);
this.branchTips = branchTips;
}
/**
* Create a separate gitlink commit
*
* @param subscriber superproject (and branch)
* @param subscriptions subprojects the superproject is subscribed to
* @return a new commit on top of subscriber with gitlinks update to the tips of the subprojects;
* empty if nothing has changed. Subproject tips are read from the cached branched tips
* (defaulting to the mergeOpRepoManager).
*/
Optional<CodeReviewCommit> composeGitlinksCommit(
BranchNameKey subscriber, List<SubmoduleSubscription> subscriptions)
throws IOException, SubmoduleConflictException {
OpenRepo or;
try {
or = orm.getRepo(subscriber.project());
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot access superproject", e);
}
CodeReviewCommit currentCommit =
branchTips
.getTip(subscriber, or)
.orElseThrow(
() ->
new SubmoduleConflictException(
"The branch was probably deleted from the subscriber repository"));
StringBuilder msgbuf = new StringBuilder();
PersonIdent author = null;
DirCache dc = readTree(or.getCodeReviewRevWalk(), currentCommit);
DirCacheEditor ed = dc.editor();
int count = 0;
for (SubmoduleSubscription s : subscriptions) {
if (count > 0) {
msgbuf.append("\n\n");
}
RevCommit newCommit = updateSubmodule(dc, ed, msgbuf, s);
count++;
if (newCommit != null) {
PersonIdent newCommitAuthor = newCommit.getAuthorIdent();
if (author == null) {
author = new PersonIdent(newCommitAuthor, myIdent.getWhen());
} else if (!author.getName().equals(newCommitAuthor.getName())
|| !author.getEmailAddress().equals(newCommitAuthor.getEmailAddress())) {
author = myIdent;
}
}
}
ed.finish();
ObjectId newTreeId = dc.writeTree(or.ins);
// Gitlinks are already in the branch, return null
if (newTreeId.equals(currentCommit.getTree())) {
return Optional.empty();
}
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(newTreeId);
commit.setParentId(currentCommit);
StringBuilder commitMsg = new StringBuilder("Update git submodules\n\n");
if (verboseSuperProject != VerboseSuperprojectUpdate.FALSE) {
commitMsg.append(msgbuf);
}
commit.setMessage(commitMsg.toString());
commit.setAuthor(author);
commit.setCommitter(myIdent);
ObjectId id = or.ins.insert(commit);
return Optional.of(or.getCodeReviewRevWalk().parseCommit(id));
}
/** Amend an existing commit with gitlink updates */
CodeReviewCommit amendGitlinksCommit(
BranchNameKey subscriber,
CodeReviewCommit currentCommit,
List<SubmoduleSubscription> subscriptions)
throws IOException, SubmoduleConflictException {
OpenRepo or;
try {
or = orm.getRepo(subscriber.project());
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot access superproject", e);
}
StringBuilder msgbuf = new StringBuilder();
DirCache dc = readTree(or.rw, currentCommit);
DirCacheEditor ed = dc.editor();
for (SubmoduleSubscription s : subscriptions) {
updateSubmodule(dc, ed, msgbuf, s);
}
ed.finish();
ObjectId newTreeId = dc.writeTree(or.ins);
// Gitlinks are already updated, just return the commit
if (newTreeId.equals(currentCommit.getTree())) {
return currentCommit;
}
or.rw.parseBody(currentCommit);
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(newTreeId);
commit.setParentIds(currentCommit.getParents());
if (verboseSuperProject != VerboseSuperprojectUpdate.FALSE) {
// TODO(czhen): handle cherrypick footer
commit.setMessage(currentCommit.getFullMessage() + "\n\n* submodules:\n" + msgbuf.toString());
} else {
commit.setMessage(currentCommit.getFullMessage());
}
commit.setAuthor(currentCommit.getAuthorIdent());
commit.setCommitter(myIdent);
ObjectId id = or.ins.insert(commit);
CodeReviewCommit newCommit = or.getCodeReviewRevWalk().parseCommit(id);
newCommit.copyFrom(currentCommit);
return newCommit;
}
private RevCommit updateSubmodule(
DirCache dc, DirCacheEditor ed, StringBuilder msgbuf, SubmoduleSubscription s)
throws SubmoduleConflictException, IOException {
logger.atFine().log("Updating gitlink for %s", s);
OpenRepo subOr;
try {
subOr = orm.getRepo(s.getSubmodule().project());
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot access submodule", e);
}
DirCacheEntry dce = dc.getEntry(s.getPath());
RevCommit oldCommit = null;
if (dce != null) {
if (!dce.getFileMode().equals(FileMode.GITLINK)) {
String errMsg =
"Requested to update gitlink "
+ s.getPath()
+ " in "
+ s.getSubmodule().project().get()
+ " but entry "
+ "doesn't have gitlink file mode.";
throw new SubmoduleConflictException(errMsg);
}
// Parse the current gitlink entry commit in the subproject repo. This is used to add a
// shortlog for this submodule to the commit message in the superproject.
//
// Even if we don't strictly speaking need that commit message, parsing the commit is a sanity
// check that the old gitlink is a commit that actually exists. If not, then there is an
// inconsistency between the superproject and subproject state, and we don't want to risk
// making things worse by updating the gitlink to something else.
try {
oldCommit = subOr.getCodeReviewRevWalk().parseCommit(dce.getObjectId());
} catch (IOException e) {
// Broken gitlink; sanity check failed. Warn and continue so the submit operation can
// proceed, it will just skip this gitlink update.
logger.atSevere().withCause(e).log("Failed to read commit %s", dce.getObjectId().name());
return null;
}
}
Optional<CodeReviewCommit> maybeNewCommit = branchTips.getTip(s.getSubmodule(), subOr);
if (!maybeNewCommit.isPresent()) {
// For whatever reason, this submodule was not updated as part of this submit batch, but the
// superproject is still subscribed to this branch. Re-read the ref to see if anything has
// changed since the last time the gitlink was updated, and roll that update into the same
// commit as all other submodule updates.
ed.add(new DeletePath(s.getPath()));
return null;
}
CodeReviewCommit newCommit = maybeNewCommit.get();
if (Objects.equals(newCommit, oldCommit)) {
// gitlink have already been updated for this submodule
return null;
}
ed.add(
new PathEdit(s.getPath()) {
@Override
public void apply(DirCacheEntry ent) {
ent.setFileMode(FileMode.GITLINK);
ent.setObjectId(newCommit.getId());
}
});
if (verboseSuperProject != VerboseSuperprojectUpdate.FALSE) {
createSubmoduleCommitMsg(msgbuf, s, subOr, newCommit, oldCommit);
}
subOr.getCodeReviewRevWalk().parseBody(newCommit);
return newCommit;
}
private void createSubmoduleCommitMsg(
StringBuilder msgbuf,
SubmoduleSubscription s,
OpenRepo subOr,
RevCommit newCommit,
RevCommit oldCommit) {
msgbuf.append("* Update ");
msgbuf.append(s.getPath());
msgbuf.append(" from branch '");
msgbuf.append(s.getSubmodule().shortName());
msgbuf.append("'");
msgbuf.append("\n to ");
msgbuf.append(newCommit.getName());
// newly created submodule gitlink, do not append whole history
if (oldCommit == null) {
return;
}
try {
subOr.rw.resetRetain(subOr.canMergeFlag);
subOr.rw.markStart(newCommit);
subOr.rw.markUninteresting(oldCommit);
int numMessages = 0;
for (Iterator<RevCommit> iter = subOr.rw.iterator(); iter.hasNext(); ) {
RevCommit c = iter.next();
subOr.rw.parseBody(c);
String message =
verboseSuperProject == VerboseSuperprojectUpdate.SUBJECT_ONLY
? c.getShortMessage()
: StringUtils.replace(c.getFullMessage(), "\n", "\n ");
String bullet = "\n - ";
String ellipsis = "\n\n[...]";
int newSize = msgbuf.length() + bullet.length() + message.length();
if (++numMessages > maxCommitMessages
|| newSize > maxCombinedCommitMessageSize
|| (iter.hasNext() && (newSize + ellipsis.length()) > maxCombinedCommitMessageSize)) {
msgbuf.append(ellipsis);
break;
}
msgbuf.append(bullet);
msgbuf.append(message);
}
} catch (IOException e) {
throw new StorageException(
"Could not perform a revwalk to create superproject commit message", e);
}
}
private static DirCache readTree(RevWalk rw, ObjectId base) throws IOException {
final DirCache dc = DirCache.newInCore();
final DirCacheBuilder b = dc.builder();
b.addTree(
new byte[0], // no prefix path
DirCacheEntry.STAGE_0, // standard stage
rw.getObjectReader(),
rw.parseTree(base));
b.finish();
return dc;
}
}

View File

@@ -27,7 +27,6 @@ import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.VerboseSuperprojectUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
@@ -42,44 +41,39 @@ import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
public class SubmoduleOp {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/** Only used for branches without code review changes */
public class GitlinkOp implements RepoOnlyOp {
public static class GitlinkOp implements RepoOnlyOp {
private final BranchNameKey branch;
private final BranchTips currentBranchTips;
private final SubmoduleCommits commitHelper;
private final List<SubmoduleSubscription> branchTargets;
GitlinkOp(BranchNameKey branch, BranchTips branchTips) {
GitlinkOp(
BranchNameKey branch,
BranchTips branchTips,
SubmoduleCommits commitHelper,
List<SubmoduleSubscription> branchTargets) {
this.branch = branch;
this.currentBranchTips = branchTips;
this.commitHelper = commitHelper;
this.branchTargets = branchTargets;
}
@Override
public void updateRepo(RepoContext ctx) throws Exception {
CodeReviewCommit c = composeGitlinksCommit(branch);
if (c != null) {
Optional<CodeReviewCommit> commit = commitHelper.composeGitlinksCommit(branch, branchTargets);
if (commit.isPresent()) {
CodeReviewCommit c = commit.get();
ctx.addRefUpdate(c.getParent(0), c, branch.branch());
currentBranchTips.put(branch, c);
}
@@ -116,28 +110,21 @@ public class SubmoduleOp {
}
}
private final PersonIdent myIdent;
private final VerboseSuperprojectUpdate verboseSuperProject;
private final long maxCombinedCommitMessageSize;
private final long maxCommitMessages;
private final MergeOpRepoManager orm;
private final SubscriptionGraph subscriptionGraph;
private final BranchTips branchTips = new BranchTips();
private final SubmoduleCommits submoduleCommits;
private SubmoduleOp(
PersonIdent myIdent,
Config cfg,
MergeOpRepoManager orm,
SubscriptionGraph subscriptionGraph) {
this.myIdent = myIdent;
this.verboseSuperProject =
cfg.getEnum("submodule", null, "verboseSuperprojectUpdate", VerboseSuperprojectUpdate.TRUE);
this.maxCombinedCommitMessageSize =
cfg.getLong("submodule", "maxCombinedCommitMessageSize", 256 << 10);
this.maxCommitMessages = cfg.getLong("submodule", "maxCommitMessages", 1000);
this.orm = orm;
this.subscriptionGraph = subscriptionGraph;
this.submoduleCommits = new SubmoduleCommits(orm, myIdent, cfg, branchTips);
}
@UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
@@ -170,243 +157,9 @@ public class SubmoduleOp {
}
}
/** Create a separate gitlink commit */
private CodeReviewCommit composeGitlinksCommit(BranchNameKey subscriber)
throws IOException, SubmoduleConflictException {
OpenRepo or;
try {
or = orm.getRepo(subscriber.project());
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot access superproject", e);
}
CodeReviewCommit currentCommit =
branchTips
.getTip(subscriber, or)
.orElseThrow(
() ->
new SubmoduleConflictException(
"The branch was probably deleted from the subscriber repository"));
StringBuilder msgbuf = new StringBuilder();
PersonIdent author = null;
DirCache dc = readTree(or.rw, currentCommit);
DirCacheEditor ed = dc.editor();
int count = 0;
List<SubmoduleSubscription> subscriptions =
subscriptionGraph.getSubscriptions(subscriber).stream()
.sorted(comparing(SubmoduleSubscription::getPath))
.collect(toList());
for (SubmoduleSubscription s : subscriptions) {
if (count > 0) {
msgbuf.append("\n\n");
}
RevCommit newCommit = updateSubmodule(dc, ed, msgbuf, s);
count++;
if (newCommit != null) {
PersonIdent newCommitAuthor = newCommit.getAuthorIdent();
if (author == null) {
author = new PersonIdent(newCommitAuthor, myIdent.getWhen());
} else if (!author.getName().equals(newCommitAuthor.getName())
|| !author.getEmailAddress().equals(newCommitAuthor.getEmailAddress())) {
author = myIdent;
}
}
}
ed.finish();
ObjectId newTreeId = dc.writeTree(or.ins);
// Gitlinks are already in the branch, return null
if (newTreeId.equals(currentCommit.getTree())) {
return null;
}
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(newTreeId);
commit.setParentId(currentCommit);
StringBuilder commitMsg = new StringBuilder("Update git submodules\n\n");
if (verboseSuperProject != VerboseSuperprojectUpdate.FALSE) {
commitMsg.append(msgbuf);
}
commit.setMessage(commitMsg.toString());
commit.setAuthor(author);
commit.setCommitter(myIdent);
ObjectId id = or.ins.insert(commit);
return or.rw.parseCommit(id);
}
/** Amend an existing commit with gitlink updates */
CodeReviewCommit amendGitlinksCommit(BranchNameKey subscriber, CodeReviewCommit currentCommit)
throws IOException, SubmoduleConflictException {
OpenRepo or;
try {
or = orm.getRepo(subscriber.project());
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot access superproject", e);
}
StringBuilder msgbuf = new StringBuilder();
DirCache dc = readTree(or.rw, currentCommit);
DirCacheEditor ed = dc.editor();
for (SubmoduleSubscription s : subscriptionGraph.getSubscriptions(subscriber)) {
updateSubmodule(dc, ed, msgbuf, s);
}
ed.finish();
ObjectId newTreeId = dc.writeTree(or.ins);
// Gitlinks are already updated, just return the commit
if (newTreeId.equals(currentCommit.getTree())) {
return currentCommit;
}
or.rw.parseBody(currentCommit);
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(newTreeId);
commit.setParentIds(currentCommit.getParents());
if (verboseSuperProject != VerboseSuperprojectUpdate.FALSE) {
// TODO(czhen): handle cherrypick footer
commit.setMessage(currentCommit.getFullMessage() + "\n\n* submodules:\n" + msgbuf.toString());
} else {
commit.setMessage(currentCommit.getFullMessage());
}
commit.setAuthor(currentCommit.getAuthorIdent());
commit.setCommitter(myIdent);
ObjectId id = or.ins.insert(commit);
CodeReviewCommit newCommit = or.rw.parseCommit(id);
newCommit.copyFrom(currentCommit);
return newCommit;
}
private RevCommit updateSubmodule(
DirCache dc, DirCacheEditor ed, StringBuilder msgbuf, SubmoduleSubscription s)
CodeReviewCommit amendGitlinksCommit(BranchNameKey branch, CodeReviewCommit commit)
throws SubmoduleConflictException, IOException {
logger.atFine().log("Updating gitlink for %s", s);
OpenRepo subOr;
try {
subOr = orm.getRepo(s.getSubmodule().project());
} catch (NoSuchProjectException | IOException e) {
throw new StorageException("Cannot access submodule", e);
}
DirCacheEntry dce = dc.getEntry(s.getPath());
RevCommit oldCommit = null;
if (dce != null) {
if (!dce.getFileMode().equals(FileMode.GITLINK)) {
String errMsg =
"Requested to update gitlink "
+ s.getPath()
+ " in "
+ s.getSubmodule().project().get()
+ " but entry "
+ "doesn't have gitlink file mode.";
throw new SubmoduleConflictException(errMsg);
}
// Parse the current gitlink entry commit in the subproject repo. This is used to add a
// shortlog for this submodule to the commit message in the superproject.
//
// Even if we don't strictly speaking need that commit message, parsing the commit is a sanity
// check that the old gitlink is a commit that actually exists. If not, then there is an
// inconsistency between the superproject and subproject state, and we don't want to risk
// making things worse by updating the gitlink to something else.
try {
oldCommit = subOr.rw.parseCommit(dce.getObjectId());
} catch (IOException e) {
// Broken gitlink; sanity check failed. Warn and continue so the submit operation can
// proceed, it will just skip this gitlink update.
logger.atSevere().withCause(e).log("Failed to read commit %s", dce.getObjectId().name());
return null;
}
}
Optional<CodeReviewCommit> maybeNewCommit = branchTips.getTip(s.getSubmodule(), subOr);
if (!maybeNewCommit.isPresent()) {
// This submodule branch is neither in the submit set nor in the repository itself
ed.add(new DeletePath(s.getPath()));
return null;
}
CodeReviewCommit newCommit = maybeNewCommit.get();
if (Objects.equals(newCommit, oldCommit)) {
// gitlink have already been updated for this submodule
return null;
}
ed.add(
new PathEdit(s.getPath()) {
@Override
public void apply(DirCacheEntry ent) {
ent.setFileMode(FileMode.GITLINK);
ent.setObjectId(newCommit.getId());
}
});
if (verboseSuperProject != VerboseSuperprojectUpdate.FALSE) {
createSubmoduleCommitMsg(msgbuf, s, subOr, newCommit, oldCommit);
}
subOr.rw.parseBody(newCommit);
return newCommit;
}
private void createSubmoduleCommitMsg(
StringBuilder msgbuf,
SubmoduleSubscription s,
OpenRepo subOr,
RevCommit newCommit,
RevCommit oldCommit) {
msgbuf.append("* Update ");
msgbuf.append(s.getPath());
msgbuf.append(" from branch '");
msgbuf.append(s.getSubmodule().shortName());
msgbuf.append("'");
msgbuf.append("\n to ");
msgbuf.append(newCommit.getName());
// newly created submodule gitlink, do not append whole history
if (oldCommit == null) {
return;
}
try {
subOr.rw.resetRetain(subOr.canMergeFlag);
subOr.rw.markStart(newCommit);
subOr.rw.markUninteresting(oldCommit);
int numMessages = 0;
for (Iterator<RevCommit> iter = subOr.rw.iterator(); iter.hasNext(); ) {
RevCommit c = iter.next();
subOr.rw.parseBody(c);
String message =
verboseSuperProject == VerboseSuperprojectUpdate.SUBJECT_ONLY
? c.getShortMessage()
: StringUtils.replace(c.getFullMessage(), "\n", "\n ");
String bullet = "\n - ";
String ellipsis = "\n\n[...]";
int newSize = msgbuf.length() + bullet.length() + message.length();
if (++numMessages > maxCommitMessages
|| newSize > maxCombinedCommitMessageSize
|| (iter.hasNext() && (newSize + ellipsis.length()) > maxCombinedCommitMessageSize)) {
msgbuf.append(ellipsis);
break;
}
msgbuf.append(bullet);
msgbuf.append(message);
}
} catch (IOException e) {
throw new StorageException(
"Could not perform a revwalk to create superproject commit message", e);
}
}
private static DirCache readTree(RevWalk rw, ObjectId base) throws IOException {
final DirCache dc = DirCache.newInCore();
final DirCacheBuilder b = dc.builder();
b.addTree(
new byte[0], // no prefix path
DirCacheEntry.STAGE_0, // standard stage
rw.getObjectReader(),
rw.parseTree(base));
b.finish();
return dc;
return submoduleCommits.amendGitlinksCommit(branch, commit, getSubscriptions(branch));
}
ImmutableSet<Project.NameKey> getProjectsInOrder() throws SubmoduleConflictException {
@@ -469,6 +222,12 @@ public class SubmoduleOp {
}
void addOp(BatchUpdate bu, BranchNameKey branch) {
bu.addRepoOnlyOp(new GitlinkOp(branch, branchTips));
bu.addRepoOnlyOp(new GitlinkOp(branch, branchTips, submoduleCommits, getSubscriptions(branch)));
}
private List<SubmoduleSubscription> getSubscriptions(BranchNameKey branch) {
return subscriptionGraph.getSubscriptions(branch).stream()
.sorted(comparing(SubmoduleSubscription::getPath))
.collect(toList());
}
}