Merge "Fix a bug in submodule topological projects"

This commit is contained in:
Dave Borowitz
2016-09-23 09:56:32 +00:00
committed by Gerrit Code Review
4 changed files with 97 additions and 57 deletions

View File

@@ -533,4 +533,56 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT
.isFalse(); .isFalse();
assertThat(hasSubmodule(subRepo, "dev", "super-project")).isFalse(); assertThat(hasSubmodule(subRepo, "dev", "super-project")).isFalse();
} }
@Test
public void testProjectNoSubscriptionWholeTopic() throws Exception {
TestRepository<?> repoA = createProjectWithPush("project-a");
TestRepository<?> repoB = createProjectWithPush("project-b");
// bootstrap the dev branch
ObjectId a0 = pushChangeTo(repoA, "dev");
// bootstrap the dev branch
ObjectId b0 = pushChangeTo(repoB, "dev");
// create a change for master branch in repo a
ObjectId aHead =
pushChangeTo(repoA, "refs/for/master", "master.txt", "content master A",
"some message in a master.txt", "same-topic");
// create a change for master branch in repo b
ObjectId bHead =
pushChangeTo(repoB, "refs/for/master", "master.txt", "content master B",
"some message in b master.txt", "same-topic");
// create a change for dev branch in repo a
repoA.reset(a0);
ObjectId aDevHead =
pushChangeTo(repoA, "refs/for/dev", "dev.txt", "content dev A",
"some message in a dev.txt", "same-topic");
// create a change for dev branch in repo b
repoB.reset(b0);
ObjectId bDevHead =
pushChangeTo(repoB, "refs/for/dev", "dev.txt", "content dev B",
"some message in b dev.txt", "same-topic");
approve(getChangeId(repoA, aHead).get());
approve(getChangeId(repoB, bHead).get());
approve(getChangeId(repoA, aDevHead).get());
approve(getChangeId(repoB, bDevHead).get());
gApi.changes().id(getChangeId(repoA, aDevHead).get()).current().submit();
assertThat(
getRemoteHead(name("project-a"), "refs/heads/master").getShortMessage())
.contains("some message in a master.txt");
assertThat(
getRemoteHead(name("project-a"), "refs/heads/dev").getShortMessage())
.contains("some message in a dev.txt");
assertThat(
getRemoteHead(name("project-b"), "refs/heads/master").getShortMessage())
.contains("some message in b master.txt");
assertThat(
getRemoteHead(name("project-b"), "refs/heads/dev").getShortMessage())
.contains("some message in b dev.txt");
}
} }

View File

@@ -85,16 +85,6 @@ public class ChangeSet {
return changeData; return changeData;
} }
public SetMultimap<Project.NameKey, Branch.NameKey> branchesByProject()
throws OrmException {
SetMultimap<Project.NameKey, Branch.NameKey> ret =
HashMultimap.create();
for (ChangeData cd : changeData.values()) {
ret.put(cd.change().getProject(), cd.change().getDest());
}
return ret;
}
public Multimap<Branch.NameKey, ChangeData> changesByBranch() public Multimap<Branch.NameKey, ChangeData> changesByBranch()
throws OrmException { throws OrmException {
ListMultimap<Branch.NameKey, ChangeData> ret = ListMultimap<Branch.NameKey, ChangeData> ret =

View File

@@ -456,23 +456,19 @@ public class MergeOp implements AutoCloseable {
"cannot integrate hidden changes into history"); "cannot integrate hidden changes into history");
logDebug("Beginning merge attempt on {}", cs); logDebug("Beginning merge attempt on {}", cs);
Map<Branch.NameKey, BranchBatch> toSubmit = new HashMap<>(); Map<Branch.NameKey, BranchBatch> toSubmit = new HashMap<>();
logDebug("Perform the merges");
Multimap<Project.NameKey, Branch.NameKey> br;
Multimap<Branch.NameKey, ChangeData> cbb; Multimap<Branch.NameKey, ChangeData> cbb;
try { try {
br = cs.branchesByProject();
cbb = cs.changesByBranch(); cbb = cs.changesByBranch();
} catch (OrmException e) { } catch (OrmException e) {
throw new IntegrationException("Error reading changes to submit", e); throw new IntegrationException("Error reading changes to submit", e);
} }
Set<Project.NameKey> projects = br.keySet();
Set<Branch.NameKey> branches = cbb.keySet(); Set<Branch.NameKey> branches = cbb.keySet();
openRepos(projects);
for (Branch.NameKey branch : branches) { for (Branch.NameKey branch : branches) {
OpenRepo or = orm.getRepo(branch.getParentKey()); OpenRepo or = openRepo(branch.getParentKey());
toSubmit.put(branch, validateChangeList(or, cbb.get(branch))); if (or != null) {
toSubmit.put(branch, validateChangeList(or, cbb.get(branch)));
}
} }
// Done checks that don't involve running submit strategies. // Done checks that don't involve running submit strategies.
commits.maybeFailVerbose(); commits.maybeFailVerbose();
@@ -480,12 +476,7 @@ public class MergeOp implements AutoCloseable {
try { try {
List<SubmitStrategy> strategies = getSubmitStrategies(toSubmit, List<SubmitStrategy> strategies = getSubmitStrategies(toSubmit,
submoduleOp, dryrun); submoduleOp, dryrun);
Set<Project.NameKey> allProjects = submoduleOp.getProjectsInOrder(); this.allProjects = submoduleOp.getProjectsInOrder();
// in case superproject subscription is disabled, allProjects would be null
if (allProjects == null) {
allProjects = projects;
}
this.allProjects = allProjects;
BatchUpdate.execute(orm.batchUpdates(allProjects), BatchUpdate.execute(orm.batchUpdates(allProjects),
new SubmitStrategyListener(submitInput, strategies, commits), new SubmitStrategyListener(submitInput, strategies, commits),
submissionId, dryrun); submissionId, dryrun);
@@ -521,11 +512,6 @@ public class MergeOp implements AutoCloseable {
boolean dryrun) throws IntegrationException { boolean dryrun) throws IntegrationException {
List<SubmitStrategy> strategies = new ArrayList<>(); List<SubmitStrategy> strategies = new ArrayList<>();
Set<Branch.NameKey> allBranches = submoduleOp.getBranchesInOrder(); Set<Branch.NameKey> allBranches = submoduleOp.getBranchesInOrder();
// in case superproject subscription is disabled, allBranches would be null
if (allBranches == null) {
allBranches = toSubmit.keySet();
}
for (Branch.NameKey branch : allBranches) { for (Branch.NameKey branch : allBranches) {
OpenRepo or = orm.getRepo(branch.getParentKey()); OpenRepo or = orm.getRepo(branch.getParentKey());
if (toSubmit.containsKey(branch)) { if (toSubmit.containsKey(branch)) {
@@ -747,19 +733,18 @@ public class MergeOp implements AutoCloseable {
} }
} }
private void openRepos(Collection<Project.NameKey> projects) private OpenRepo openRepo(Project.NameKey project)
throws IntegrationException { throws IntegrationException {
for (Project.NameKey project : projects) { try {
try { return orm.openRepo(project);
orm.openRepo(project); } catch (NoSuchProjectException noProject) {
} catch (NoSuchProjectException noProject) { logWarn("Project " + noProject.project() + " no longer exists, "
logWarn("Project " + noProject.project() + " no longer exists, " + "abandoning open changes");
+ "abandoning open changes"); abandonAllOpenChangeForDeletedProject(noProject.project());
abandonAllOpenChangeForDeletedProject(noProject.project()); } catch (IOException e) {
} catch (IOException e) { throw new IntegrationException("Error opening project " + project, e);
throw new IntegrationException("Error opening project " + project, e);
}
} }
return null;
} }
private void abandonAllOpenChangeForDeletedProject( private void abandonAllOpenChangeForDeletedProject(

View File

@@ -105,6 +105,7 @@ public class SubmoduleOp {
private final boolean enableSuperProjectSubscriptions; private final boolean enableSuperProjectSubscriptions;
private final Multimap<Branch.NameKey, SubmoduleSubscription> targets; private final Multimap<Branch.NameKey, SubmoduleSubscription> targets;
private final Set<Branch.NameKey> updatedBranches; private final Set<Branch.NameKey> updatedBranches;
private final Set<Branch.NameKey> affectedBranches;
private final MergeOpRepoManager orm; private final MergeOpRepoManager orm;
private final Map<Branch.NameKey, CodeReviewCommit> branchTips; private final Map<Branch.NameKey, CodeReviewCommit> branchTips;
private final Map<Branch.NameKey, GitModules> branchGitModules; private final Map<Branch.NameKey, GitModules> branchGitModules;
@@ -131,6 +132,7 @@ public class SubmoduleOp {
this.orm = orm; this.orm = orm;
this.updatedBranches = updatedBranches; this.updatedBranches = updatedBranches;
this.targets = HashMultimap.create(); this.targets = HashMultimap.create();
this.affectedBranches = new HashSet<>();
this.branchTips = new HashMap<>(); this.branchTips = new HashMap<>();
this.branchGitModules = new HashMap<>(); this.branchGitModules = new HashMap<>();
this.sortedBranches = calculateSubscriptionMap(); this.sortedBranches = calculateSubscriptionMap();
@@ -154,8 +156,11 @@ public class SubmoduleOp {
allVisited); allVisited);
} }
// Since the searchForSuperprojects will add the superprojects before one // Since the searchForSuperprojects will add all branches (related or
// submodule in sortedBranches, need reverse the order of it // unrelated) and ensure the superproject's branches get added first before
// a submodule branch. Need remove all unrelated branches and reverse
// the order.
allVisited.retainAll(affectedBranches);
reverse(allVisited); reverse(allVisited);
return ImmutableSet.copyOf(allVisited); return ImmutableSet.copyOf(allVisited);
} }
@@ -184,6 +189,8 @@ public class SubmoduleOp {
Branch.NameKey superProject = sub.getSuperProject(); Branch.NameKey superProject = sub.getSuperProject();
searchForSuperprojects(superProject, currentVisited, allVisited); searchForSuperprojects(superProject, currentVisited, allVisited);
targets.put(superProject, sub); targets.put(superProject, sub);
affectedBranches.add(superProject);
affectedBranches.add(sub.getSubmodule());
} }
} catch (IOException e) { } catch (IOException e) {
throw new SubmoduleException("Cannot find superprojects for " + current, throw new SubmoduleException("Cannot find superprojects for " + current,
@@ -549,30 +556,36 @@ public class SubmoduleOp {
public ImmutableSet<Project.NameKey> getProjectsInOrder() public ImmutableSet<Project.NameKey> getProjectsInOrder()
throws SubmoduleException { throws SubmoduleException {
if (sortedBranches == null) {
return null;
}
LinkedHashSet<Project.NameKey> projects = new LinkedHashSet<>(); LinkedHashSet<Project.NameKey> projects = new LinkedHashSet<>();
Project.NameKey prev = null; if (sortedBranches != null) {
for (Branch.NameKey branch : sortedBranches) { Project.NameKey prev = null;
Project.NameKey project = branch.getParentKey(); for (Branch.NameKey branch : sortedBranches) {
if (!project.equals(prev)) { Project.NameKey project = branch.getParentKey();
if (projects.contains(project)) { if (!project.equals(prev)) {
throw new SubmoduleException( if (projects.contains(project)) {
"Project level circular subscriptions detected: " + throw new SubmoduleException(
printCircularPath(projects, project)); "Project level circular subscriptions detected: " +
printCircularPath(projects, project));
}
projects.add(project);
} }
projects.add(project); prev = project;
} }
prev = project;
} }
for (Branch.NameKey branch : updatedBranches) {
projects.add(branch.getParentKey());
}
return ImmutableSet.copyOf(projects); return ImmutableSet.copyOf(projects);
} }
public ImmutableSet<Branch.NameKey> getBranchesInOrder() { public ImmutableSet<Branch.NameKey> getBranchesInOrder() {
return sortedBranches; LinkedHashSet<Branch.NameKey> branches = new LinkedHashSet<>();
if (sortedBranches != null) {
branches.addAll(sortedBranches);
}
branches.addAll(updatedBranches);
return ImmutableSet.copyOf(branches);
} }
public boolean hasSubscription(Branch.NameKey branch) { public boolean hasSubscription(Branch.NameKey branch) {