SubmoduleOp: Submit different Submodules in one commit
Before this commit there was just one `SubmoduleOp.update()` method to do it all. Now this is split up into two parts: * The submodule subscription update, which inspects each branch for possible changes in submodule subscriptions during each change of the branch tip. * The update of superprojects, which will be performed once per merge or push operation. The the internal data structures which are passed around, are easier to follow now by passing a collection of SubmoduleSubscriptions into the updateGitlinks method. Change-Id: I021272b75939707ec22764568d27e3f6413899ad
This commit is contained in:
@@ -78,7 +78,6 @@ import com.google.gerrit.server.git.MergeUtil;
|
||||
import com.google.gerrit.server.git.NotesBranchUtil;
|
||||
import com.google.gerrit.server.git.ReceivePackInitializer;
|
||||
import com.google.gerrit.server.git.SignedPushModule;
|
||||
import com.google.gerrit.server.git.SubmoduleOp;
|
||||
import com.google.gerrit.server.git.TagCache;
|
||||
import com.google.gerrit.server.git.TransferConfig;
|
||||
import com.google.gerrit.server.git.validators.CommitValidationListener;
|
||||
@@ -232,7 +231,6 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
bind(Boolean.class).annotatedWith(DisableReverseDnsLookup.class)
|
||||
.toProvider(DisableReverseDnsLookupProvider.class).in(SINGLETON);
|
||||
|
||||
factory(SubmoduleOp.Factory.class);
|
||||
bind(PatchSetInfoFactory.class);
|
||||
bind(IdentifiedUser.GenericFactory.class).in(SINGLETON);
|
||||
bind(ChangeControl.GenericFactory.class);
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.common.ChangeHooks;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.SubmitRecord;
|
||||
@@ -152,7 +153,7 @@ public class MergeOp {
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final Submit submit;
|
||||
private final SubmitStrategyFactory submitStrategyFactory;
|
||||
private final SubmoduleOp.Factory subOpFactory;
|
||||
private final Provider<SubmoduleOp> subOpProvider;
|
||||
private final TagCache tagCache;
|
||||
private final WorkQueue workQueue;
|
||||
|
||||
@@ -194,7 +195,7 @@ public class MergeOp {
|
||||
SchemaFactory<ReviewDb> schemaFactory,
|
||||
Submit submit,
|
||||
SubmitStrategyFactory submitStrategyFactory,
|
||||
SubmoduleOp.Factory subOpFactory,
|
||||
Provider<SubmoduleOp> subOpProvider,
|
||||
TagCache tagCache,
|
||||
WorkQueue workQueue) {
|
||||
this.accountCache = accountCache;
|
||||
@@ -217,7 +218,7 @@ public class MergeOp {
|
||||
this.schemaFactory = schemaFactory;
|
||||
this.submit = submit;
|
||||
this.submitStrategyFactory = submitStrategyFactory;
|
||||
this.subOpFactory = subOpFactory;
|
||||
this.subOpProvider = subOpProvider;
|
||||
this.tagCache = tagCache;
|
||||
this.workQueue = workQueue;
|
||||
commits = new HashMap<>();
|
||||
@@ -492,6 +493,7 @@ public class MergeOp {
|
||||
closeRepository();
|
||||
}
|
||||
logDebug("Write out the new branch tips");
|
||||
SubmoduleOp subOp = subOpProvider.get();
|
||||
for (Project.NameKey project : cs.projects()) {
|
||||
openRepository(project);
|
||||
for (Branch.NameKey branch : cs.branchesByProject().get(project)) {
|
||||
@@ -503,8 +505,7 @@ public class MergeOp {
|
||||
ListMultimap<SubmitType, Change> submitting = toSubmit.get(branch);
|
||||
for (SubmitType submitType : submitting.keySet()) {
|
||||
updateChangeStatus(submitting.get(submitType), branch, false);
|
||||
updateSubscriptions(branch, submitting.get(submitType),
|
||||
getBranchTip(branch));
|
||||
updateSubmoduleSubscriptions(subOp, branch, getBranchTip(branch));
|
||||
}
|
||||
if (update != null) {
|
||||
fireRefUpdated(branch, update);
|
||||
@@ -512,6 +513,7 @@ public class MergeOp {
|
||||
}
|
||||
closeRepository();
|
||||
}
|
||||
updateSuperProjects(subOp, cs.branches());
|
||||
checkState(pendingRefUpdates.isEmpty(), "programmer error: "
|
||||
+ "pending ref update list not emptied");
|
||||
} catch (NoSuchProjectException noProject) {
|
||||
@@ -985,26 +987,32 @@ public class MergeOp {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSubscriptions(Branch.NameKey destBranch,
|
||||
List<Change> submitted, CodeReviewCommit branchTip) {
|
||||
private void updateSubmoduleSubscriptions(SubmoduleOp subOp,
|
||||
Branch.NameKey destBranch, CodeReviewCommit branchTip) {
|
||||
MergeTip mergeTip = mergeTips.get(destBranch);
|
||||
if (mergeTip != null
|
||||
&& (branchTip == null || branchTip != mergeTip.getCurrentTip())) {
|
||||
logDebug("Updating submodule subscriptions for {} changes",
|
||||
submitted.size());
|
||||
SubmoduleOp subOp =
|
||||
subOpFactory.create(destBranch, mergeTip.getCurrentTip(), rw, repo,
|
||||
destProject.getProject(), submitted, commits,
|
||||
getAccount(mergeTip.getCurrentTip()));
|
||||
logDebug("Updating submodule subscriptions for branch {}", destBranch);
|
||||
try {
|
||||
subOp.update();
|
||||
subOp.updateSubmoduleSubscriptions(db, destBranch);
|
||||
} catch (SubmoduleException e) {
|
||||
logError("The gitLinks were not updated according to the subscriptions",
|
||||
e);
|
||||
logError("The submodule subscriptions were not updated according"
|
||||
+ "to the .gitmodules files", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSuperProjects(SubmoduleOp subOp,
|
||||
Set<Branch.NameKey> branches) {
|
||||
logDebug("Updating superprojects");
|
||||
try {
|
||||
subOp.updateSuperProjects(db, branches);
|
||||
} catch (SubmoduleException e) {
|
||||
logError("The gitlinks were not updated according to the "
|
||||
+ "subscriptions", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ChangeMessage message(Change c, String body) {
|
||||
String uuid;
|
||||
try {
|
||||
|
||||
@@ -325,7 +325,7 @@ public class ReceiveCommits {
|
||||
private SetMultimap<ObjectId, Ref> refsById;
|
||||
private Map<String, Ref> allRefs;
|
||||
|
||||
private final SubmoduleOp.Factory subOpFactory;
|
||||
private final Provider<SubmoduleOp> subOpProvider;
|
||||
private final Provider<Submit> submitProvider;
|
||||
private final Provider<MergeOp> mergeOpProvider;
|
||||
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
|
||||
@@ -375,7 +375,7 @@ public class ReceiveCommits {
|
||||
ReceiveConfig config,
|
||||
@Assisted final ProjectControl projectControl,
|
||||
@Assisted final Repository repo,
|
||||
final SubmoduleOp.Factory subOpFactory,
|
||||
final Provider<SubmoduleOp> subOpProvider,
|
||||
final Provider<Submit> submitProvider,
|
||||
final Provider<MergeOp> mergeOpProvider,
|
||||
final ChangeKindCache changeKindCache,
|
||||
@@ -422,7 +422,7 @@ public class ReceiveCommits {
|
||||
this.rp = new ReceivePack(repo);
|
||||
this.rejectCommits = BanCommit.loadRejectCommitsMap(repo, rp.getRevWalk());
|
||||
|
||||
this.subOpFactory = subOpFactory;
|
||||
this.subOpProvider = subOpProvider;
|
||||
this.submitProvider = submitProvider;
|
||||
this.mergeOpProvider = mergeOpProvider;
|
||||
this.pluginConfigEntries = pluginConfigEntries;
|
||||
@@ -598,6 +598,7 @@ public class ReceiveCommits {
|
||||
rp.sendMessage(COMMAND_REJECTION_MESSAGE_FOOTER);
|
||||
}
|
||||
|
||||
Set<Branch.NameKey> branches = Sets.newHashSet();
|
||||
for (final ReceiveCommand c : commands) {
|
||||
if (c.getResult() == OK) {
|
||||
if (c.getType() == ReceiveCommand.Type.UPDATE) { // aka fast-forward
|
||||
@@ -613,6 +614,8 @@ public class ReceiveCommits {
|
||||
case UPDATE:
|
||||
case UPDATE_NONFASTFORWARD:
|
||||
autoCloseChanges(c);
|
||||
branches.add(new Branch.NameKey(project.getNameKey(),
|
||||
c.getRefName()));
|
||||
break;
|
||||
|
||||
case DELETE:
|
||||
@@ -651,6 +654,16 @@ public class ReceiveCommits {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update superproject gitlinks if required.
|
||||
SubmoduleOp op = subOpProvider.get();
|
||||
try {
|
||||
op.updateSubmoduleSubscriptions(db, branches);
|
||||
op.updateSuperProjects(db, branches);
|
||||
} catch (SubmoduleException e) {
|
||||
log.error("Can't update submodule subscriptions "
|
||||
+ "or update the superprojects", e);
|
||||
}
|
||||
|
||||
closeProgress.end();
|
||||
commandProgress.end();
|
||||
progress.end();
|
||||
@@ -2592,19 +2605,10 @@ public class ReceiveCommits {
|
||||
closeProgress.update(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Update superproject gitlinks if required.
|
||||
subOpFactory.create(
|
||||
branch, newTip, rw, repo, project,
|
||||
new ArrayList<Change>(),
|
||||
new HashMap<Change.Id, CodeReviewCommit>(),
|
||||
currentUser.getAccount()).update();
|
||||
} catch (RestApiException e) {
|
||||
log.error("Can't insert patchset", e);
|
||||
} catch (IOException | OrmException e) {
|
||||
log.error("Can't scan for changes to close", e);
|
||||
} catch (SubmoduleException e) {
|
||||
log.error("Can't complete git links check", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,15 +14,13 @@
|
||||
|
||||
package com.google.gerrit.server.git;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.common.ChangeHooks;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
@@ -30,14 +28,13 @@ import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.util.SubmoduleSectionParser;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
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.errors.ConfigInvalidException;
|
||||
@@ -62,67 +59,34 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SubmoduleOp {
|
||||
public interface Factory {
|
||||
SubmoduleOp create(Branch.NameKey destBranch, RevCommit mergeTip,
|
||||
RevWalk rw, Repository db, Project destProject, List<Change> submitted,
|
||||
Map<Change.Id, CodeReviewCommit> commits, Account account);
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SubmoduleOp.class);
|
||||
private static final String GIT_MODULES = ".gitmodules";
|
||||
|
||||
private final Branch.NameKey destBranch;
|
||||
private RevCommit mergeTip;
|
||||
private RevWalk rw;
|
||||
private final Provider<String> urlProvider;
|
||||
private ReviewDb schema;
|
||||
private Repository db;
|
||||
private Project destProject;
|
||||
private List<Change> submitted;
|
||||
private final Map<Change.Id, CodeReviewCommit> commits;
|
||||
private final PersonIdent myIdent;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final GitReferenceUpdated gitRefUpdated;
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final Set<Branch.NameKey> updatedSubscribers;
|
||||
private final Account account;
|
||||
private final ChangeHooks changeHooks;
|
||||
private final SubmoduleSectionParser.Factory subSecParserFactory;
|
||||
|
||||
@Inject
|
||||
public SubmoduleOp(@Assisted Branch.NameKey destBranch,
|
||||
@Assisted RevCommit mergeTip,
|
||||
@Assisted RevWalk rw,
|
||||
public SubmoduleOp(
|
||||
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
|
||||
SchemaFactory<ReviewDb> sf,
|
||||
@Assisted Repository db,
|
||||
@Assisted Project destProject,
|
||||
@Assisted List<Change> submitted,
|
||||
@Assisted Map<Change.Id,
|
||||
CodeReviewCommit> commits,
|
||||
@GerritPersonIdent PersonIdent myIdent,
|
||||
GitRepositoryManager repoManager,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
@Nullable @Assisted Account account,
|
||||
@Nullable Account account,
|
||||
ChangeHooks changeHooks,
|
||||
SubmoduleSectionParser.Factory subSecParserFactory) {
|
||||
this.destBranch = destBranch;
|
||||
this.mergeTip = mergeTip;
|
||||
this.rw = rw;
|
||||
this.urlProvider = urlProvider;
|
||||
this.schemaFactory = sf;
|
||||
this.db = db;
|
||||
this.destProject = destProject;
|
||||
this.submitted = submitted;
|
||||
this.commits = commits;
|
||||
this.myIdent = myIdent;
|
||||
this.repoManager = repoManager;
|
||||
this.gitRefUpdated = gitRefUpdated;
|
||||
@@ -133,49 +97,42 @@ public class SubmoduleOp {
|
||||
updatedSubscribers = new HashSet<>();
|
||||
}
|
||||
|
||||
public void update() throws SubmoduleException {
|
||||
try {
|
||||
schema = schemaFactory.open();
|
||||
|
||||
updateSubmoduleSubscriptions();
|
||||
updateSuperProjects(destBranch, rw, mergeTip.getId().toObjectId(), null);
|
||||
} catch (OrmException | IOException e) {
|
||||
throw new SubmoduleException("Cannot open database", e);
|
||||
} finally {
|
||||
if (schema != null) {
|
||||
schema.close();
|
||||
schema = null;
|
||||
}
|
||||
void updateSubmoduleSubscriptions(ReviewDb db, Set<Branch.NameKey> branches)
|
||||
throws SubmoduleException {
|
||||
for (Branch.NameKey branch : branches) {
|
||||
updateSubmoduleSubscriptions(db, branch);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSubmoduleSubscriptions() throws SubmoduleException {
|
||||
void updateSubmoduleSubscriptions(ReviewDb db, Branch.NameKey destBranch)
|
||||
throws SubmoduleException {
|
||||
if (urlProvider.get() == null) {
|
||||
logAndThrowSubmoduleException("Cannot establish canonical web url used to access gerrit."
|
||||
+ " It should be provided in gerrit.config file.");
|
||||
logAndThrowSubmoduleException("Cannot establish canonical web url used "
|
||||
+ "to access gerrit. It should be provided in gerrit.config file.");
|
||||
}
|
||||
try (Repository repo = repoManager.openRepository(
|
||||
destBranch.getParentKey());
|
||||
RevWalk rw = new RevWalk(repo)) {
|
||||
|
||||
ObjectId id = repo.resolve(destBranch.get());
|
||||
RevCommit commit = rw.parseCommit(id);
|
||||
|
||||
try {
|
||||
Set<SubmoduleSubscription> oldSubscriptions =
|
||||
Sets.newHashSet(schema.submoduleSubscriptions()
|
||||
Sets.newHashSet(db.submoduleSubscriptions()
|
||||
.bySuperProject(destBranch));
|
||||
|
||||
Set<SubmoduleSubscription> newSubscriptions;
|
||||
TreeWalk tw = TreeWalk.forPath(db, GIT_MODULES, mergeTip.getTree());
|
||||
TreeWalk tw = TreeWalk.forPath(repo, GIT_MODULES, commit.getTree());
|
||||
if (tw != null
|
||||
&& (FileMode.REGULAR_FILE.equals(tw.getRawMode(0)) ||
|
||||
FileMode.EXECUTABLE_FILE.equals(tw.getRawMode(0)))) {
|
||||
BlobBasedConfig bbc =
|
||||
new BlobBasedConfig(null, db, mergeTip, GIT_MODULES);
|
||||
new BlobBasedConfig(null, repo, commit, GIT_MODULES);
|
||||
|
||||
String thisServer = new URI(urlProvider.get()).getHost();
|
||||
|
||||
Branch.NameKey target =
|
||||
new Branch.NameKey(new Project.NameKey(destProject.getName()),
|
||||
destBranch.get());
|
||||
|
||||
newSubscriptions = subSecParserFactory.create(bbc, thisServer, target)
|
||||
.parseAllSections();
|
||||
newSubscriptions = subSecParserFactory.create(bbc, thisServer,
|
||||
destBranch).parseAllSections();
|
||||
} else {
|
||||
newSubscriptions = Collections.emptySet();
|
||||
}
|
||||
@@ -191,10 +148,10 @@ public class SubmoduleOp {
|
||||
newSubscriptions.removeAll(alreadySubscribeds);
|
||||
|
||||
if (!oldSubscriptions.isEmpty()) {
|
||||
schema.submoduleSubscriptions().delete(oldSubscriptions);
|
||||
db.submoduleSubscriptions().delete(oldSubscriptions);
|
||||
}
|
||||
if (!newSubscriptions.isEmpty()) {
|
||||
schema.submoduleSubscriptions().insert(newSubscriptions);
|
||||
db.submoduleSubscriptions().insert(newSubscriptions);
|
||||
}
|
||||
|
||||
} catch (OrmException e) {
|
||||
@@ -216,73 +173,31 @@ public class SubmoduleOp {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSuperProjects(final Branch.NameKey updatedBranch, RevWalk myRw,
|
||||
final ObjectId mergedCommit, final String msg) throws SubmoduleException,
|
||||
IOException {
|
||||
protected void updateSuperProjects(ReviewDb db,
|
||||
Set<Branch.NameKey> updatedBranches) throws SubmoduleException {
|
||||
try {
|
||||
final List<SubmoduleSubscription> subscribers =
|
||||
schema.submoduleSubscriptions().bySubmodule(updatedBranch).toList();
|
||||
// These (repo/branch) will be updated later with all the given
|
||||
// individual submodule subscriptions
|
||||
Multimap<Branch.NameKey, SubmoduleSubscription> targets =
|
||||
HashMultimap.create();
|
||||
|
||||
if (!subscribers.isEmpty()) {
|
||||
// Initialize the message buffer
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (msg != null) {
|
||||
sb.append(msg);
|
||||
} else {
|
||||
// The first updatedBranch on a cascade event of automatic
|
||||
// updates of repos is added to updatedSubscribers set so
|
||||
// if we face a situation having
|
||||
// submodule-a(master)-->super(master)-->submodule-a(master),
|
||||
// it will be detected we have a circular subscription
|
||||
// when updateSuperProjects is called having as updatedBranch
|
||||
// the super(master) value.
|
||||
updatedSubscribers.add(updatedBranch);
|
||||
|
||||
for (final Change chg : submitted) {
|
||||
final CodeReviewCommit c = commits.get(chg.getId());
|
||||
if (c != null
|
||||
&& (c.getStatusCode() == CommitMergeStatus.CLEAN_MERGE
|
||||
|| c.getStatusCode() == CommitMergeStatus.CLEAN_PICK
|
||||
|| c.getStatusCode() == CommitMergeStatus.CLEAN_REBASE)) {
|
||||
myRw.parseBody(c);
|
||||
sb.append("\n")
|
||||
.append(c.getFullMessage());
|
||||
}
|
||||
}
|
||||
for (Branch.NameKey updatedBranch : updatedBranches) {
|
||||
for (SubmoduleSubscription sub : db.submoduleSubscriptions()
|
||||
.bySubmodule(updatedBranch)) {
|
||||
targets.put(sub.getSuperProject(), sub);
|
||||
}
|
||||
|
||||
// update subscribers of this module
|
||||
List<SubmoduleSubscription> incorrectSubscriptions = Lists.newLinkedList();
|
||||
for (final SubmoduleSubscription s : subscribers) {
|
||||
try {
|
||||
if (!updatedSubscribers.add(s.getSuperProject())) {
|
||||
log.error("Possible circular subscription involving " + s);
|
||||
} else {
|
||||
|
||||
Map<Branch.NameKey, ObjectId> modules = new HashMap<>(1);
|
||||
modules.put(updatedBranch, mergedCommit);
|
||||
|
||||
Map<Branch.NameKey, String> paths = new HashMap<>(1);
|
||||
paths.put(updatedBranch, s.getPath());
|
||||
updateGitlinks(s.getSuperProject(), myRw, modules, paths, sb.toString());
|
||||
}
|
||||
} catch (SubmoduleException e) {
|
||||
log.warn("Cannot update gitlinks for " + s + " due to " + e.getMessage());
|
||||
incorrectSubscriptions.add(s);
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot update gitlinks for " + s, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!incorrectSubscriptions.isEmpty()) {
|
||||
try {
|
||||
schema.submoduleSubscriptions().delete(incorrectSubscriptions);
|
||||
log.info("Deleted incorrect submodule subscription(s) "
|
||||
+ incorrectSubscriptions);
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot delete submodule subscription(s) "
|
||||
+ incorrectSubscriptions, e);
|
||||
}
|
||||
updatedSubscribers.addAll(updatedBranches);
|
||||
// Update subscribers.
|
||||
for (Branch.NameKey dest : targets.keySet()) {
|
||||
try {
|
||||
if (!updatedSubscribers.add(dest)) {
|
||||
log.error("Possible circular subscription involving " + dest);
|
||||
} else {
|
||||
updateGitlinks(db, dest, targets.get(dest));
|
||||
}
|
||||
} catch (SubmoduleException e) {
|
||||
log.warn("Cannot update gitlinks for " + dest, e);
|
||||
}
|
||||
}
|
||||
} catch (OrmException e) {
|
||||
@@ -290,78 +205,109 @@ public class SubmoduleOp {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGitlinks(final Branch.NameKey subscriber, RevWalk myRw,
|
||||
final Map<Branch.NameKey, ObjectId> modules,
|
||||
final Map<Branch.NameKey, String> paths, final String msg)
|
||||
throws SubmoduleException {
|
||||
/**
|
||||
* Update the submodules in one branch of one repository.
|
||||
*
|
||||
* @param subscriber the branch of the repository which should be changed.
|
||||
* @param updates submodule updates which should be updated to.
|
||||
* @throws SubmoduleException
|
||||
*/
|
||||
private void updateGitlinks(ReviewDb db, Branch.NameKey subscriber,
|
||||
Collection<SubmoduleSubscription> updates) throws SubmoduleException {
|
||||
PersonIdent author = null;
|
||||
|
||||
final StringBuilder msgbuf = new StringBuilder("Updated git submodules\n");
|
||||
Repository pdb = null;
|
||||
RevWalk recRw = null;
|
||||
|
||||
StringBuilder msgbuf = new StringBuilder("Updated git submodules\n\n");
|
||||
try {
|
||||
boolean sameAuthorForAll = true;
|
||||
|
||||
for (final Map.Entry<Branch.NameKey, ObjectId> me : modules.entrySet()) {
|
||||
RevCommit c = myRw.parseCommit(me.getValue());
|
||||
if (c == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
msgbuf.append("\nProject: ");
|
||||
msgbuf.append(me.getKey().getParentKey().get());
|
||||
msgbuf.append(" ").append(me.getValue().getName());
|
||||
msgbuf.append("\n");
|
||||
if (modules.size() == 1) {
|
||||
if (!Strings.isNullOrEmpty(msg)) {
|
||||
msgbuf.append(msg);
|
||||
} else {
|
||||
msgbuf.append("\n");
|
||||
msgbuf.append(c.getFullMessage());
|
||||
}
|
||||
} else {
|
||||
msgbuf.append(c.getShortMessage());
|
||||
}
|
||||
msgbuf.append("\n");
|
||||
|
||||
if (author == null) {
|
||||
author = c.getAuthorIdent();
|
||||
} else if (!author.equals(c.getAuthorIdent())) {
|
||||
sameAuthorForAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sameAuthorForAll || author == null) {
|
||||
author = myIdent;
|
||||
}
|
||||
|
||||
pdb = repoManager.openRepository(subscriber.getParentKey());
|
||||
if (pdb.getRef(subscriber.get()) == null) {
|
||||
throw new SubmoduleException(
|
||||
"The branch was probably deleted from the subscriber repository");
|
||||
}
|
||||
|
||||
final ObjectId currentCommitId =
|
||||
pdb.getRef(subscriber.get()).getObjectId();
|
||||
|
||||
DirCache dc = readTree(pdb, pdb.getRef(subscriber.get()));
|
||||
DirCacheEditor ed = dc.editor();
|
||||
for (final Map.Entry<Branch.NameKey, ObjectId> me : modules.entrySet()) {
|
||||
ed.add(new PathEdit(paths.get(me.getKey())) {
|
||||
@Override
|
||||
public void apply(DirCacheEntry ent) {
|
||||
ent.setFileMode(FileMode.GITLINK);
|
||||
ent.setObjectId(me.getValue().copy());
|
||||
|
||||
for (SubmoduleSubscription s : updates) {
|
||||
try (Repository subrepo = repoManager.openRepository(
|
||||
s.getSubmodule().getParentKey());
|
||||
RevWalk rw = CodeReviewCommit.newRevWalk(subrepo)) {
|
||||
Ref ref = subrepo.getRefDatabase().exactRef(s.getSubmodule().get());
|
||||
if (ref == null) {
|
||||
ed.add(new DeletePath(s.getPath()));
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
final ObjectId updateTo = ref.getObjectId();
|
||||
RevCommit newCommit = rw.parseCommit(updateTo);
|
||||
|
||||
if (author == null) {
|
||||
author = newCommit.getAuthorIdent();
|
||||
} else if (!author.equals(newCommit.getAuthorIdent())) {
|
||||
sameAuthorForAll = false;
|
||||
}
|
||||
|
||||
DirCacheEntry dce = dc.getEntry(s.getPath());
|
||||
ObjectId oldId = null;
|
||||
if (dce != null) {
|
||||
if (!dce.getFileMode().equals(FileMode.GITLINK)) {
|
||||
log.error("Requested to update gitlink " + s.getPath() + " in "
|
||||
+ s.getSubmodule().getParentKey().get() + " but entry "
|
||||
+ "doesn't have gitlink file mode.");
|
||||
continue;
|
||||
}
|
||||
oldId = dce.getObjectId();
|
||||
} else {
|
||||
// This submodule did not exist before. We do not want to add
|
||||
// the full submodule history to the commit message, so omit it.
|
||||
oldId = updateTo;
|
||||
}
|
||||
|
||||
ed.add(new PathEdit(s.getPath()) {
|
||||
@Override
|
||||
public void apply(DirCacheEntry ent) {
|
||||
ent.setFileMode(FileMode.GITLINK);
|
||||
ent.setObjectId(updateTo);
|
||||
}
|
||||
});
|
||||
|
||||
msgbuf.append("Project: " + s.getSubmodule().getParentKey().get());
|
||||
msgbuf.append(" " + s.getSubmodule().getShortName());
|
||||
msgbuf.append(" " + updateTo.getName());
|
||||
msgbuf.append("\n\n");
|
||||
|
||||
try {
|
||||
rw.markStart(newCommit);
|
||||
|
||||
if (oldId != null) {
|
||||
rw.markUninteresting(rw.parseCommit(oldId));
|
||||
}
|
||||
for (RevCommit c : rw) {
|
||||
msgbuf.append(c.getFullMessage() + "\n\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logAndThrowSubmoduleException("Could not perform a revwalk to "
|
||||
+ "create superproject commit message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
ed.finish();
|
||||
|
||||
if (!sameAuthorForAll || author == null) {
|
||||
author = myIdent;
|
||||
}
|
||||
|
||||
ObjectInserter oi = pdb.newObjectInserter();
|
||||
ObjectId tree = dc.writeTree(oi);
|
||||
|
||||
final CommitBuilder commit = new CommitBuilder();
|
||||
ObjectId currentCommitId =
|
||||
pdb.getRef(subscriber.get()).getObjectId();
|
||||
|
||||
CommitBuilder commit = new CommitBuilder();
|
||||
commit.setTreeId(tree);
|
||||
commit.setParentIds(new ObjectId[] {currentCommitId});
|
||||
commit.setAuthor(author);
|
||||
@@ -390,14 +336,12 @@ public class SubmoduleOp {
|
||||
default:
|
||||
throw new IOException(rfu.getResult().name());
|
||||
}
|
||||
|
||||
recRw = new RevWalk(pdb);
|
||||
|
||||
// Recursive call: update subscribers of the subscriber
|
||||
updateSuperProjects(subscriber, recRw, commitId, msgbuf.toString());
|
||||
updateSuperProjects(db, Sets.newHashSet(subscriber));
|
||||
} catch (IOException e) {
|
||||
throw new SubmoduleException("Cannot update gitlinks for "
|
||||
+ subscriber.get(), e);
|
||||
throw new SubmoduleException("Cannot update gitlinks for "
|
||||
+ subscriber.get(), e);
|
||||
} finally {
|
||||
if (recRw != null) {
|
||||
recRw.close();
|
||||
|
||||
Reference in New Issue
Block a user