MergeOp: Avoid (one instance of) scanning all refs
We were scanning all refs just to check that each patch set SHA-1 was not completely orphaned. Instead, tighten this check slightly and enforce that a patch set is present at its expected ref name. This allows us to use a limited exactRef(String...) call rather than scanning all potentially many thousands of refs. Change-Id: I5308682b111bd69ade0345c51c63ee341226c1e2
This commit is contained in:
@@ -16,12 +16,12 @@ package com.google.gerrit.server.git;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
|
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
|
||||||
import static org.eclipse.jgit.lib.RefDatabase.ALL;
|
|
||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.HashBasedTable;
|
import com.google.common.collect.HashBasedTable;
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
@@ -581,18 +581,7 @@ public class MergeOp {
|
|||||||
Collection<ChangeData> submitted) throws IntegrationException {
|
Collection<ChangeData> submitted) throws IntegrationException {
|
||||||
logDebug("Validating {} changes", submitted.size());
|
logDebug("Validating {} changes", submitted.size());
|
||||||
List<ChangeData> toSubmit = new ArrayList<>(submitted.size());
|
List<ChangeData> toSubmit = new ArrayList<>(submitted.size());
|
||||||
|
Multimap<ObjectId, PatchSet.Id> revisions = getRevisions(submitted);
|
||||||
Map<String, Ref> allRefs;
|
|
||||||
try {
|
|
||||||
allRefs = repo.getRefDatabase().getRefs(ALL);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IntegrationException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<ObjectId> tips = new HashSet<>();
|
|
||||||
for (Ref r : allRefs.values()) {
|
|
||||||
tips.add(r.getObjectId());
|
|
||||||
}
|
|
||||||
|
|
||||||
SubmitType submitType = null;
|
SubmitType submitType = null;
|
||||||
ChangeData choseSubmitTypeFrom = null;
|
ChangeData choseSubmitTypeFrom = null;
|
||||||
@@ -601,8 +590,7 @@ public class MergeOp {
|
|||||||
Change chg;
|
Change chg;
|
||||||
try {
|
try {
|
||||||
ctl = cd.changeControl();
|
ctl = cd.changeControl();
|
||||||
// Reload change in case index was stale.
|
chg = cd.change();
|
||||||
chg = cd.reloadChange();
|
|
||||||
} catch (OrmException e) {
|
} catch (OrmException e) {
|
||||||
throw new IntegrationException("Failed to validate changes", e);
|
throw new IntegrationException("Failed to validate changes", e);
|
||||||
}
|
}
|
||||||
@@ -641,18 +629,13 @@ public class MergeOp {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tips.contains(id)) {
|
if (!revisions.containsEntry(id, ps.getId())) {
|
||||||
// TODO Technically the proper way to do this test is to use a
|
|
||||||
// RevWalk on "$id --not --all" and test for an empty set. But
|
|
||||||
// that is way slower than looking for a ref directly pointing
|
|
||||||
// at the desired tip. We should always have a ref available.
|
|
||||||
//
|
|
||||||
// TODO this is actually an error, the branch is gone but we
|
// TODO this is actually an error, the branch is gone but we
|
||||||
// want to merge the issue. We can't safely do that if the
|
// want to merge the issue. We can't safely do that if the
|
||||||
// tip is not reachable.
|
// tip is not reachable.
|
||||||
//
|
//
|
||||||
logError("Revision " + idstr + " of patch set " + ps.getId()
|
logError("Revision " + idstr + " of patch set " + ps.getId()
|
||||||
+ " is not contained in any ref");
|
+ " does not match " + ps.getId().toRefName());
|
||||||
commits.put(changeId, CodeReviewCommit.revisionGone(ctl));
|
commits.put(changeId, CodeReviewCommit.revisionGone(ctl));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -705,6 +688,31 @@ public class MergeOp {
|
|||||||
return new AutoValue_MergeOp_BranchBatch(submitType, toSubmit);
|
return new AutoValue_MergeOp_BranchBatch(submitType, toSubmit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Multimap<ObjectId, PatchSet.Id> getRevisions(
|
||||||
|
Collection<ChangeData> cds) throws IntegrationException {
|
||||||
|
try {
|
||||||
|
List<String> refNames = new ArrayList<>(cds.size());
|
||||||
|
for (ChangeData cd : cds) {
|
||||||
|
// Reload change in case index was stale.
|
||||||
|
cd.reloadChange();
|
||||||
|
Change c = cd.change();
|
||||||
|
if (c != null) {
|
||||||
|
refNames.add(c.currentPatchSetId().toRefName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Multimap<ObjectId, PatchSet.Id> revisions =
|
||||||
|
HashMultimap.create(cds.size(), 1);
|
||||||
|
for (Map.Entry<String, Ref> e : repo.getRefDatabase().exactRef(
|
||||||
|
refNames.toArray(new String[refNames.size()])).entrySet()) {
|
||||||
|
revisions.put(
|
||||||
|
e.getValue().getObjectId(), PatchSet.Id.fromRef(e.getKey()));
|
||||||
|
}
|
||||||
|
return revisions;
|
||||||
|
} catch (IOException | OrmException e) {
|
||||||
|
throw new IntegrationException("Failed to validate changes", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SubmitType getSubmitType(ChangeControl ctl, PatchSet ps) {
|
private SubmitType getSubmitType(ChangeControl ctl, PatchSet ps) {
|
||||||
try {
|
try {
|
||||||
ChangeData cd = changeDataFactory.create(db, ctl);
|
ChangeData cd = changeDataFactory.create(db, ctl);
|
||||||
|
|||||||
Reference in New Issue
Block a user