Merge "superproject subscriptions: Separate checks and execution"

This commit is contained in:
Dave Borowitz
2016-05-19 20:04:36 +00:00
committed by Gerrit Code Review
3 changed files with 108 additions and 27 deletions

View File

@@ -262,16 +262,15 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"subscribed-to-project", "refs/heads/master");
pushChangeTo(subRepo, "master");
pushChangeTo(superRepo, "master");
createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
createSubmoduleSubscription(subRepo, "master", "super-project", "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
pushChangeTo(subRepo, "master");
pushChangeTo(superRepo, "master");
expectToHaveSubmoduleState(superRepo, "master",
"subscribed-to-project", subHEAD);
assertThat(hasSubmodule(subRepo, "master", "super-project")).isFalse();
assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
}

View File

@@ -92,6 +92,71 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
"subscribed-to-project", subRepoId);
}
@Test
public void testSubscriptionUpdateIncludingChangeInSuperproject() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");
TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowSubmoduleSubscription("subscribed-to-project", "refs/heads/master",
"super-project", "refs/heads/master");
createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
ObjectId subHEAD = subRepo.branch("HEAD").commit().insertChangeId()
.message("some change")
.add("a.txt", "a contents ")
.create();
subRepo.git().push().setRemote("origin").setRefSpecs(
new RefSpec("HEAD:refs/heads/master")).call();
RevCommit c = subRepo.getRevWalk().parseCommit(subHEAD);
RevCommit c1 = subRepo.branch("HEAD").commit().insertChangeId()
.message("first change")
.add("asdf", "asdf\n")
.create();
subRepo.git().push().setRemote("origin")
.setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo")))
.call();
subRepo.reset(c.getId());
RevCommit c2 = subRepo.branch("HEAD").commit().insertChangeId()
.message("qwerty")
.add("qwerty", "qwerty")
.create();
RevCommit c3 = subRepo.branch("HEAD").commit().insertChangeId()
.message("qwerty followup")
.add("qwerty", "qwerty\nqwerty\n")
.create();
subRepo.git().push().setRemote("origin")
.setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo")))
.call();
RevCommit c4 = superRepo.branch("HEAD").commit().insertChangeId()
.message("new change on superproject")
.add("foo", "bar")
.create();
superRepo.git().push().setRemote("origin")
.setRefSpecs(new RefSpec("HEAD:refs/for/master/" + name("topic-foo")))
.call();
String id1 = getChangeId(subRepo, c1).get();
String id2 = getChangeId(subRepo, c2).get();
String id3 = getChangeId(subRepo, c3).get();
String id4 = getChangeId(superRepo, c4).get();
gApi.changes().id(id1).current().review(ReviewInput.approve());
gApi.changes().id(id2).current().review(ReviewInput.approve());
gApi.changes().id(id3).current().review(ReviewInput.approve());
gApi.changes().id(id4).current().review(ReviewInput.approve());
gApi.changes().id(id1).current().submit();
ObjectId subRepoId = subRepo.git().fetch().setRemote("origin").call()
.getAdvertisedRef("refs/heads/master").getObjectId();
expectToHaveSubmoduleState(superRepo, "master",
"subscribed-to-project", subRepoId);
}
@Test
public void testUpdateManySubmodules() throws Exception {
TestRepository<?> superRepo = createProjectWithPush("super-project");

View File

@@ -32,6 +32,9 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.SubmoduleSectionParser;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import org.eclipse.jgit.dircache.DirCache;
@@ -71,7 +74,6 @@ public class SubmoduleOp {
private final PersonIdent myIdent;
private final GitReferenceUpdated gitRefUpdated;
private final ProjectCache projectCache;
private final Set<Branch.NameKey> updatedSubscribers;
private final Account account;
private final ChangeHooks changeHooks;
private final boolean verboseSuperProject;
@@ -97,7 +99,6 @@ public class SubmoduleOp {
"verboseSuperprojectUpdate", true);
this.enableSuperProjectSubscriptions = cfg.getBoolean("submodule",
"enableSuperProjectSubscriptions", true);
updatedSubscribers = new HashSet<>();
}
public Collection<Branch.NameKey> getDestinationBranches(Branch.NameKey src,
@@ -165,32 +166,50 @@ public class SubmoduleOp {
}
this.updateId = updateId;
logDebug("Updating superprojects");
// These (repo/branch) will be updated later with all the given
// individual submodule subscriptions
Multimap<Branch.NameKey, SubmoduleSubscription> targets =
HashMultimap.create();
try {
for (Branch.NameKey updatedBranch : updatedBranches) {
for (SubmoduleSubscription sub :
superProjectSubscriptionsForSubmoduleBranch(updatedBranch, orm)) {
targets.put(sub.getSuperProject(), sub);
for (Branch.NameKey updatedBranch : updatedBranches) {
logDebug("Now processing " + updatedBranch);
Set<Branch.NameKey> checkedTargets = new HashSet<>();
Set<Branch.NameKey> targetsToProcess = new HashSet<>();
targetsToProcess.add(updatedBranch);
while (!targetsToProcess.isEmpty()) {
Set<Branch.NameKey> newTargets = new HashSet<>();
for (Branch.NameKey b : targetsToProcess) {
try {
Collection<SubmoduleSubscription> subs =
superProjectSubscriptionsForSubmoduleBranch(b, orm);
for (SubmoduleSubscription sub : subs) {
Branch.NameKey dst = sub.getSuperProject();
targets.put(dst, sub);
newTargets.add(dst);
}
} catch (IOException e) {
throw new SubmoduleException("Cannot find superprojects for " + b, e);
}
}
logDebug("adding to done " + targetsToProcess);
checkedTargets.addAll(targetsToProcess);
logDebug("completely done with " + checkedTargets);
Set<Branch.NameKey> intersection = new HashSet<>(checkedTargets);
intersection.retainAll(newTargets);
if (!intersection.isEmpty()) {
throw new SubmoduleException("Possible circular subscription involving " + updatedBranch);
}
targetsToProcess = newTargets;
}
} catch (IOException e) {
throw new SubmoduleException("Could not calculate all superprojects");
}
updatedSubscribers.addAll(updatedBranches);
// Update subscribers.
for (Branch.NameKey dest : targets.keySet()) {
for (Branch.NameKey dst : targets.keySet()) {
try {
if (!updatedSubscribers.add(dest)) {
log.error("Possible circular subscription involving " + dest);
} else {
updateGitlinks(db, dest, targets.get(dest), orm);
}
updateGitlinks(dst, targets.get(dst), orm);
} catch (SubmoduleException e) {
log.warn("Cannot update gitlinks for " + dest, e);
throw new SubmoduleException("Cannot update gitlinks for " + dst, e);
}
}
}
@@ -202,7 +221,7 @@ public class SubmoduleOp {
* @param updates submodule updates which should be updated to.
* @throws SubmoduleException
*/
private void updateGitlinks(ReviewDb db, Branch.NameKey subscriber,
private void updateGitlinks(Branch.NameKey subscriber,
Collection<SubmoduleSubscription> updates, MergeOpRepoManager orm)
throws SubmoduleException {
PersonIdent author = null;
@@ -340,8 +359,6 @@ public class SubmoduleOp {
default:
throw new IOException(rfu.getResult().name());
}
// Recursive call: update subscribers of the subscriber
updateSuperProjects(db, Sets.newHashSet(subscriber), updateId, orm);
} catch (IOException e) {
throw new SubmoduleException("Cannot update gitlinks for "
+ subscriber.get(), e);