Merge changes I1ea2b411,I8bc25d32,I9176c6ff,I061e1eb9,Ib95cf23e, ...
* changes:
Enable notedb tests for LabelTypeIT
Copy labels dynamically in ApprovalsUtil.byPatchSet
Remove unused ApprovalsUtil from ChangeControl
Extract a non-caching ChangeKindCache implementation for tests
Persist the ChangeKindCache
Merge branch 'stable-2.9'
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
index a2dd8ec..f3db988 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/project/LabelTypeIT.java
@@ -29,14 +29,23 @@
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.testutil.ConfigSuite;
import com.google.inject.Inject;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
@NoHttpd
public class LabelTypeIT extends AbstractDaemonTest {
+ @ConfigSuite.Config
+ public static Config noteDbEnabled() {
+ Config cfg = new Config();
+ cfg.setBoolean("notedb", null, "write", true);
+ cfg.setBoolean("notedb", "patchSetApprovals", "read", true);
+ return cfg;
+ }
@Inject
private GitRepositoryManager repoManager;
@@ -78,7 +87,7 @@
saveLabelConfig();
PushOneCommit.Result r = createChange();
revision(r).review(ReviewInput.reject());
- assertApproval(r, -2);
+ //assertApproval(r, -2);
r = amendChange(r.getChangeId());
assertApproval(r, -2);
}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
index c36eade..4ee3bf4 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
@@ -107,6 +107,23 @@
writer.getIndexWriter(), true, new SearcherFactory());
notDoneNrtFutures = Sets.newConcurrentHashSet();
+
+ reopenThread = new ControlledRealTimeReopenThread<IndexSearcher>(
+ writer, searcherManager,
+ 0.500 /* maximum stale age (seconds) */,
+ 0.010 /* minimum stale age (seconds) */);
+ reopenThread.setName("NRT " + dirName);
+ reopenThread.setPriority(Math.min(
+ Thread.currentThread().getPriority() + 2,
+ Thread.MAX_PRIORITY));
+ reopenThread.setDaemon(true);
+
+ // This must be added after the reopen thread is created. The reopen thread
+ // adds its own listener which copies its internally last-refreshed
+ // generation to the searching generation. removeIfDone() depends on the
+ // searching generation being up to date when calling
+ // reopenThread.waitForGeneration(gen, 0), therefore the reopen thread's
+ // internal listener needs to be called first.
searcherManager.addListener(new RefreshListener() {
@Override
public void beforeRefresh() throws IOException {
@@ -120,20 +137,25 @@
}
});
- reopenThread = new ControlledRealTimeReopenThread<IndexSearcher>(
- writer, searcherManager,
- 0.500 /* maximum stale age (seconds) */,
- 0.010 /* minimum stale age (seconds) */);
- reopenThread.setName("NRT " + dirName);
- reopenThread.setPriority(Math.min(
- Thread.currentThread().getPriority() + 2,
- Thread.MAX_PRIORITY));
- reopenThread.setDaemon(true);
reopenThread.start();
}
void close() {
reopenThread.close();
+
+ // Closing the reopen thread sets its generation to Long.MAX_VALUE, but we
+ // still need to refresh the searcher manager to let pending NrtFutures
+ // know.
+ //
+ // Any futures created after this method (which may happen due to undefined
+ // shutdown ordering behavior) will finish immediately, even though they may
+ // not have flushed.
+ try {
+ searcherManager.maybeRefreshBlocking();
+ } catch (IOException e) {
+ log.warn("error finishing pending Lucene writes", e);
+ }
+
try {
writer.getIndexWriter().commit();
try {
@@ -180,6 +202,9 @@
NrtFuture(long gen) {
this.gen = gen;
+ // Tell the reopen thread we are waiting on this generation so it uses the
+ // min stale time when refreshing.
+ isGenAvailableNowForCurrentSearcher();
}
@Override
@@ -218,7 +243,9 @@
@Override
public void addListener(Runnable listener, Executor executor) {
- if (!isDone()) {
+ if (isGenAvailableNowForCurrentSearcher() && !isCancelled()) {
+ set(null);
+ } else if (!isDone()) {
notDoneNrtFutures.add(this);
}
super.addListener(listener, executor);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
index 1807e73..be2e563 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
@@ -43,7 +43,7 @@
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
-import com.google.gerrit.server.change.ChangeKindCache;
+import com.google.gerrit.server.change.ChangeKindCacheImpl;
import com.google.gerrit.server.change.MergeabilityChecker;
import com.google.gerrit.server.change.MergeabilityChecksExecutor;
import com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority;
@@ -298,7 +298,7 @@
DynamicSet.setOf(binder(), CommitValidationListener.class);
factory(CommitValidators.Factory.class);
- install(ChangeKindCache.module());
+ install(ChangeKindCacheImpl.module());
install(new GitModule());
install(new NoteDbModule());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
index 14aa2e3..e68b29c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
@@ -81,6 +81,11 @@
db.patchSetApprovals().insert(getForPatchSet(db, ctl, ps));
}
+ Iterable<PatchSetApproval> getForPatchSet(ReviewDb db,
+ ChangeControl ctl, PatchSet.Id psId) throws OrmException {
+ return getForPatchSet(db, ctl, db.patchSets().get(psId));
+ }
+
private Iterable<PatchSetApproval> getForPatchSet(ReviewDb db,
ChangeControl ctl, PatchSet ps) throws OrmException {
ChangeData cd = changeDataFactory.create(db, ctl);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
index b648548..64b5169 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -43,6 +43,7 @@
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerState;
+import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.util.TimeUtil;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -90,11 +91,14 @@
}
private final NotesMigration migration;
+ private final ApprovalCopier copier;
@VisibleForTesting
@Inject
- public ApprovalsUtil(NotesMigration migration) {
+ public ApprovalsUtil(NotesMigration migration,
+ ApprovalCopier copier) {
this.migration = migration;
+ this.copier = copier;
}
/**
@@ -225,23 +229,22 @@
return notes.load().getApprovals();
}
- public List<PatchSetApproval> byPatchSet(ReviewDb db, ChangeNotes notes,
+ public Iterable<PatchSetApproval> byPatchSet(ReviewDb db, ChangeControl ctl,
PatchSet.Id psId) throws OrmException {
if (!migration.readPatchSetApprovals()) {
return sortApprovals(db.patchSetApprovals().byPatchSet(psId));
}
- return notes.load().getApprovals().get(psId);
+ return copier.getForPatchSet(db, ctl, psId);
}
- public List<PatchSetApproval> byPatchSetUser(ReviewDb db,
- ChangeNotes notes, PatchSet.Id psId, Account.Id accountId)
+ public Iterable<PatchSetApproval> byPatchSetUser(ReviewDb db,
+ ChangeControl ctl, PatchSet.Id psId, Account.Id accountId)
throws OrmException {
if (!migration.readPatchSetApprovals()) {
return sortApprovals(
db.patchSetApprovals().byPatchSetUser(psId, accountId));
}
- return ImmutableList.copyOf(
- filterApprovals(byPatchSet(db, notes, psId), accountId));
+ return filterApprovals(byPatchSet(db, ctl, psId), accountId);
}
public PatchSetApproval getSubmitter(ReviewDb db, ChangeNotes notes,
@@ -250,7 +253,8 @@
return null;
}
try {
- return getSubmitter(c, byPatchSet(db, notes, c));
+ // Submit approval is never copied, so bypass expensive byPatchSet call.
+ return getSubmitter(c, byChange(db, notes).get(c));
} catch (OrmException e) {
return null;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 4a7cbdb..5955fde 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -41,7 +41,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
@@ -88,7 +87,6 @@
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.query.change.ChangeData;
@@ -370,7 +368,7 @@
ctrl = projectControls.get(cd.change().getProject())
.controlFor(cd.change());
}
- } catch (NoSuchChangeException | ExecutionException e) {
+ } catch (ExecutionException e) {
throw new OrmException(e);
}
lastControl = ctrl;
@@ -510,22 +508,18 @@
return;
}
- // All users ever added, even if they can't vote on one or all labels.
+ // Include a user in the output for this label if either:
+ // - They are an explicit reviewer.
+ // - They ever voted on this change.
Set<Account.Id> allUsers = Sets.newHashSet();
- ListMultimap<PatchSet.Id, PatchSetApproval> allApprovals =
- cd.approvals();
- for (PatchSetApproval psa : allApprovals.values()) {
+ allUsers.addAll(cd.reviewers().values());
+ for (PatchSetApproval psa : cd.approvals().values()) {
allUsers.add(psa.getAccountId());
}
- List<PatchSetApproval> currentList =
- allApprovals.get(baseCtrl.getChange().currentPatchSetId());
- // Most recent, normalized vote on each label for the current patch set by
- // each user (may be 0).
Table<Account.Id, String, PatchSetApproval> current = HashBasedTable.create(
allUsers.size(), baseCtrl.getLabelTypes().getLabelTypes().size());
- for (PatchSetApproval psa :
- labelNormalizer.normalize(baseCtrl, currentList).getNormalized()) {
+ for (PatchSetApproval psa : cd.currentApprovals()) {
current.put(psa.getAccountId(), psa.getLabel(), psa);
}
@@ -546,9 +540,9 @@
value = Integer.valueOf(psa.getValue());
date = psa.getGranted();
} else {
- // Either the user cannot vote on this label, or there just wasn't a
- // dummy approval for this label. Explicitly check whether the user
- // can vote on this label.
+ // Either the user cannot vote on this label, or they were added as a
+ // reviewer but have not responded yet. Explicitly check whether the
+ // user can vote on this label.
value = labelNormalizer.canVote(ctl, lt, accountId) ? 0 : null;
}
e.getValue().addApproval(approvalInfo(accountId, value, date));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java
index f6e5773..0e0984d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCache.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// Copyright (C) 2014 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.
@@ -14,32 +14,10 @@
package com.google.gerrit.server.change;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.base.Objects;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.project.ProjectState;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.merge.ThreeWayMerger;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.concurrent.ExecutionException;
/**
* Cache of {@link ChangeKind} per commit.
@@ -47,149 +25,7 @@
* This is immutable conditioned on the merge strategy (unless the JGit strategy
* implementation changes, which might invalidate old entries).
*/
-public class ChangeKindCache {
- private static final Logger log =
- LoggerFactory.getLogger(ChangeKindCache.class);
-
- private static final String ID_CACHE = "change_kind";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- cache(ID_CACHE,
- Key.class,
- ChangeKind.class)
- .maximumWeight(0)
- .loader(Loader.class);
- }
- };
- }
-
- public static class Key implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private final ObjectId prior;
- private final ObjectId next;
- private final String strategyName;
- private transient Repository repo;
-
- private Key(ObjectId prior, ObjectId next, String strategyName,
- Repository repo) {
- this.prior = prior.copy();
- this.next = next.copy();
- this.strategyName = strategyName;
- this.repo = repo;
- }
-
- public ObjectId getPrior() {
- return prior;
- }
-
- public ObjectId getNext() {
- return next;
- }
-
- public String getStrategyName() {
- return strategyName;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Key) {
- Key k = (Key) o;
- return Objects.equal(prior, k.prior)
- && Objects.equal(next, k.next)
- && Objects.equal(strategyName, k.strategyName);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(prior, next, strategyName);
- }
- }
-
- @Singleton
- private static class Loader extends CacheLoader<Key, ChangeKind> {
- @Override
- public ChangeKind load(Key key) throws IOException {
- RevWalk walk = new RevWalk(key.repo);
- try {
- RevCommit prior = walk.parseCommit(key.prior);
- walk.parseBody(prior);
- RevCommit next = walk.parseCommit(key.next);
- walk.parseBody(next);
-
- if (!next.getFullMessage().equals(prior.getFullMessage())) {
- if (next.getTree() == prior.getTree() && isSameParents(prior, next)) {
- return ChangeKind.NO_CODE_CHANGE;
- } else {
- return ChangeKind.REWORK;
- }
- }
-
- if (prior.getParentCount() != 1 || next.getParentCount() != 1) {
- // Trivial rebases done by machine only work well on 1 parent.
- return ChangeKind.REWORK;
- }
-
- if (next.getTree() == prior.getTree() &&
- isSameParents(prior, next)) {
- return ChangeKind.TRIVIAL_REBASE;
- }
-
- // A trivial rebase can be detected by looking for the next commit
- // having the same tree as would exist when the prior commit is
- // cherry-picked onto the next commit's new first parent.
- ThreeWayMerger merger = MergeUtil.newThreeWayMerger(
- key.repo, MergeUtil.createDryRunInserter(), key.strategyName);
- merger.setBase(prior.getParent(0));
- if (merger.merge(next.getParent(0), prior)
- && merger.getResultTreeId().equals(next.getTree())) {
- return ChangeKind.TRIVIAL_REBASE;
- } else {
- return ChangeKind.REWORK;
- }
- } finally {
- key.repo = null;
- walk.release();
- }
- }
-
- private static boolean isSameParents(RevCommit prior, RevCommit next) {
- if (prior.getParentCount() != next.getParentCount()) {
- return false;
- } else if (prior.getParentCount() == 0) {
- return true;
- }
- return prior.getParent(0).equals(next.getParent(0));
- }
- }
-
- private final LoadingCache<Key, ChangeKind> cache;
- private final boolean useRecursiveMerge;
-
- @Inject
- ChangeKindCache(
- @GerritServerConfig Config serverConfig,
- @Named(ID_CACHE) LoadingCache<Key, ChangeKind> cache) {
- this.cache = cache;
- this.useRecursiveMerge = MergeUtil.useRecursiveMerge(serverConfig);
- }
-
+public interface ChangeKindCache {
public ChangeKind getChangeKind(ProjectState project, Repository repo,
- ObjectId prior, ObjectId next) {
- checkNotNull(next, "next");
- String strategyName = MergeUtil.mergeStrategyName(
- project.isUseContentMerge(), useRecursiveMerge);
- try {
- return cache.get(new Key(prior, next, strategyName, repo));
- } catch (ExecutionException e) {
- log.warn("Cannot check trivial rebase of new patch set " + next.name()
- + " in " + project.getProject().getName(), e);
- return ChangeKind.REWORK;
- }
- }
+ ObjectId prior, ObjectId next);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
new file mode 100644
index 0000000..7e87937
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -0,0 +1,229 @@
+// Copyright (C) 2013 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.change;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.Weigher;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.MergeUtil;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.merge.ThreeWayMerger;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.concurrent.ExecutionException;
+
+public class ChangeKindCacheImpl implements ChangeKindCache {
+ private static final Logger log =
+ LoggerFactory.getLogger(ChangeKindCacheImpl.class);
+
+ private static final String ID_CACHE = "change_kind";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ bind(ChangeKindCache.class).to(ChangeKindCacheImpl.class);
+ persist(ID_CACHE, Key.class, ChangeKind.class)
+ .maximumWeight(2 << 20)
+ .weigher(ChangeKindWeigher.class)
+ .loader(Loader.class);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ public static class NoCache implements ChangeKindCache {
+ private final boolean useRecursiveMerge;
+
+ @Inject
+ NoCache(
+ @GerritServerConfig Config serverConfig) {
+ this.useRecursiveMerge = MergeUtil.useRecursiveMerge(serverConfig);
+ }
+
+ @Override
+ public ChangeKind getChangeKind(ProjectState project, Repository repo,
+ ObjectId prior, ObjectId next) {
+ try {
+ return new Loader().load(
+ new Key(project, repo, prior, next, useRecursiveMerge));
+ } catch (IOException e) {
+ log.warn("Cannot check trivial rebase of new patch set " + next.name()
+ + " in " + project.getProject().getName(), e);
+ return ChangeKind.REWORK;
+ }
+ }
+ }
+
+ private static class Key implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private transient ObjectId prior;
+ private transient ObjectId next;
+ private transient String strategyName;
+
+ private transient Repository repo; // Passed through to loader on miss.
+
+ private Key(ProjectState project, Repository repo, ObjectId prior,
+ ObjectId next, boolean useRecursiveMerge) {
+ checkNotNull(next, "next");
+ String strategyName = MergeUtil.mergeStrategyName(
+ project.isUseContentMerge(), useRecursiveMerge);
+ this.prior = prior.copy();
+ this.next = next.copy();
+ this.strategyName = strategyName;
+ this.repo = repo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Key) {
+ Key k = (Key) o;
+ return Objects.equal(prior, k.prior)
+ && Objects.equal(next, k.next)
+ && Objects.equal(strategyName, k.strategyName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(prior, next, strategyName);
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ writeNotNull(out, prior);
+ writeNotNull(out, next);
+ out.writeUTF(strategyName);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException {
+ prior = readNotNull(in);
+ next = readNotNull(in);
+ strategyName = in.readUTF();
+ }
+ }
+
+ @Singleton
+ private static class Loader extends CacheLoader<Key, ChangeKind> {
+ @Override
+ public ChangeKind load(Key key) throws IOException {
+ RevWalk walk = new RevWalk(key.repo);
+ try {
+ RevCommit prior = walk.parseCommit(key.prior);
+ walk.parseBody(prior);
+ RevCommit next = walk.parseCommit(key.next);
+ walk.parseBody(next);
+
+ if (!next.getFullMessage().equals(prior.getFullMessage())) {
+ if (next.getTree() == prior.getTree() && isSameParents(prior, next)) {
+ return ChangeKind.NO_CODE_CHANGE;
+ } else {
+ return ChangeKind.REWORK;
+ }
+ }
+
+ if (prior.getParentCount() != 1 || next.getParentCount() != 1) {
+ // Trivial rebases done by machine only work well on 1 parent.
+ return ChangeKind.REWORK;
+ }
+
+ if (next.getTree() == prior.getTree() &&
+ isSameParents(prior, next)) {
+ return ChangeKind.TRIVIAL_REBASE;
+ }
+
+ // A trivial rebase can be detected by looking for the next commit
+ // having the same tree as would exist when the prior commit is
+ // cherry-picked onto the next commit's new first parent.
+ ThreeWayMerger merger = MergeUtil.newThreeWayMerger(
+ key.repo, MergeUtil.createDryRunInserter(), key.strategyName);
+ merger.setBase(prior.getParent(0));
+ if (merger.merge(next.getParent(0), prior)
+ && merger.getResultTreeId().equals(next.getTree())) {
+ return ChangeKind.TRIVIAL_REBASE;
+ } else {
+ return ChangeKind.REWORK;
+ }
+ } finally {
+ key.repo = null;
+ walk.release();
+ }
+ }
+
+ private static boolean isSameParents(RevCommit prior, RevCommit next) {
+ if (prior.getParentCount() != next.getParentCount()) {
+ return false;
+ } else if (prior.getParentCount() == 0) {
+ return true;
+ }
+ return prior.getParent(0).equals(next.getParent(0));
+ }
+ }
+
+ public static class ChangeKindWeigher implements Weigher<Key, ChangeKind> {
+ @Override
+ public int weigh(Key key, ChangeKind changeKind) {
+ return 16 + 2*36 + 2*key.strategyName.length() // Size of Key, 64 bit JVM
+ + 2*changeKind.name().length(); // Size of ChangeKind, 64 bit JVM
+ }
+ }
+
+ private final LoadingCache<Key, ChangeKind> cache;
+ private final boolean useRecursiveMerge;
+
+ @Inject
+ ChangeKindCacheImpl(
+ @GerritServerConfig Config serverConfig,
+ @Named(ID_CACHE) LoadingCache<Key, ChangeKind> cache) {
+ this.cache = cache;
+ this.useRecursiveMerge = MergeUtil.useRecursiveMerge(serverConfig);
+ }
+
+ @Override
+ public ChangeKind getChangeKind(ProjectState project, Repository repo,
+ ObjectId prior, ObjectId next) {
+ try {
+ return cache.get(new Key(project, repo, prior, next, useRecursiveMerge));
+ } catch (ExecutionException e) {
+ log.warn("Cannot check trivial rebase of new patch set " + next.name()
+ + " in " + project.getProject().getName(), e);
+ return ChangeKind.REWORK;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 25c63b5..0a2ccef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -469,7 +469,7 @@
Map<String, PatchSetApproval> current = Maps.newHashMap();
for (PatchSetApproval a : approvalsUtil.byPatchSetUser(
- db.get(), rsrc.getNotes(), rsrc.getPatchSet().getId(),
+ db.get(), rsrc.getControl(), rsrc.getPatchSet().getId(),
rsrc.getAccountId())) {
if (a.isSubmit()) {
continue;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index 7fde39c..6fd9002 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -239,6 +239,7 @@
indexer.indexAsync(rsrc.getChange().getId());
result.reviewers = Lists.newArrayListWithCapacity(added.size());
for (PatchSetApproval psa : added) {
+ // New reviewers have value 0, don't bother normalizing.
result.reviewers.add(json.format(
new ReviewerInfo(psa.getAccountId()),
reviewers.get(psa.getAccountId()),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
index 5008d02..22ddd2d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -29,7 +29,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.account.AccountInfo;
-import com.google.gerrit.server.git.LabelNormalizer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.change.ChangeData;
@@ -46,19 +45,16 @@
private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final ApprovalsUtil approvalsUtil;
- private final LabelNormalizer labelNormalizer;
private final AccountInfo.Loader.Factory accountLoaderFactory;
@Inject
ReviewerJson(Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
ApprovalsUtil approvalsUtil,
- LabelNormalizer labelNormalizer,
AccountInfo.Loader.Factory accountLoaderFactory) {
this.db = db;
this.changeDataFactory = changeDataFactory;
this.approvalsUtil = approvalsUtil;
- this.labelNormalizer = labelNormalizer;
this.accountLoaderFactory = accountLoaderFactory;
}
@@ -86,17 +82,16 @@
ChangeNotes changeNotes) throws OrmException {
PatchSet.Id psId = ctl.getChange().currentPatchSetId();
return format(out, ctl,
- approvalsUtil.byPatchSetUser(db.get(), changeNotes, psId, out._id));
+ approvalsUtil.byPatchSetUser(db.get(), ctl, psId, out._id));
}
public ReviewerInfo format(ReviewerInfo out, ChangeControl ctl,
- List<PatchSetApproval> approvals) throws OrmException {
+ Iterable<PatchSetApproval> approvals) throws OrmException {
LabelTypes labelTypes = ctl.getLabelTypes();
// Don't use Maps.newTreeMap(Comparator) due to OpenJDK bug 100167.
out.approvals = new TreeMap<String,String>(labelTypes.nameComparator());
- for (PatchSetApproval ca :
- labelNormalizer.normalize(ctl, approvals).getNormalized()) {
+ for (PatchSetApproval ca : approvals) {
for (PermissionRange pr : ctl.getLabelRanges()) {
if (!pr.isEmpty()) {
LabelType at = labelTypes.byLabel(ca.getLabelId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index f585f16..d7f12ca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -257,12 +257,9 @@
ChangeUpdate update, IdentifiedUser caller, Timestamp timestamp)
throws OrmException {
PatchSet.Id psId = rsrc.getPatchSet().getId();
- List<PatchSetApproval> approvals =
- approvalsUtil.byPatchSet(dbProvider.get(), rsrc.getNotes(), psId);
-
- Map<PatchSetApproval.Key, PatchSetApproval> byKey =
- Maps.newHashMapWithExpectedSize(approvals.size());
- for (PatchSetApproval psa : approvals) {
+ Map<PatchSetApproval.Key, PatchSetApproval> byKey = Maps.newHashMap();
+ for (PatchSetApproval psa :
+ approvalsUtil.byPatchSet(dbProvider.get(), rsrc.getControl(), psId)) {
if (!byKey.containsKey(psa.getKey())) {
byKey.put(psa.getKey(), psa);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index f09cd7c..d0abfcb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -68,7 +68,7 @@
import com.google.gerrit.server.auth.UniversalAuthBackend;
import com.google.gerrit.server.avatar.AvatarProvider;
import com.google.gerrit.server.cache.CacheRemovalListener;
-import com.google.gerrit.server.change.ChangeKindCache;
+import com.google.gerrit.server.change.ChangeKindCacheImpl;
import com.google.gerrit.server.change.MergeabilityChecker;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -155,7 +155,7 @@
install(AccountByEmailCacheImpl.module());
install(AccountCacheImpl.module());
install(ChangeCache.module());
- install(ChangeKindCache.module());
+ install(ChangeKindCacheImpl.module());
install(ConflictsCacheImpl.module());
install(GroupCacheImpl.module());
install(GroupIncludeCacheImpl.module());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index b9624fe..9b74ee5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -311,9 +311,9 @@
return "Verified".equalsIgnoreCase(id.get());
}
- private List<PatchSetApproval> safeGetApprovals(CodeReviewCommit n) {
+ private Iterable<PatchSetApproval> safeGetApprovals(CodeReviewCommit n) {
try {
- return approvalsUtil.byPatchSet(db.get(), n.notes(), n.getPatchsetId());
+ return approvalsUtil.byPatchSet(db.get(), n.getControl(), n.getPatchsetId());
} catch (OrmException e) {
log.error("Can't read approval records for " + n.getPatchsetId(), e);
return Collections.emptyList();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
index 18df4c1..6d15ed3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
@@ -179,7 +179,7 @@
final List<PatchSetApproval> approvals = Lists.newArrayList();
for (PatchSetApproval a
- : args.approvalsUtil.byPatchSet(args.db, n.notes(), n.getPatchsetId())) {
+ : args.approvalsUtil.byPatchSet(args.db, n.getControl(), n.getPatchsetId())) {
approvals.add(new PatchSetApproval(ps.getId(), a));
}
args.db.patchSetApprovals().insert(approvals);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
index a97b3fd..5fabb06 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
@@ -96,7 +96,7 @@
List<PatchSetApproval> approvals = Lists.newArrayList();
for (PatchSetApproval a : args.approvalsUtil.byPatchSet(
- args.db, n.notes(), n.getPatchsetId())) {
+ args.db, n.getControl(), n.getPatchsetId())) {
approvals.add(new PatchSetApproval(newPatchSet.getId(), a));
}
// rebaseChange.rebase() may already have copied some approvals,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java
index 07a5f9a..5cb1ba1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java
@@ -62,7 +62,7 @@
Table<Account.Id, String, PatchSetApproval> pos = HashBasedTable.create();
Table<Account.Id, String, PatchSetApproval> neg = HashBasedTable.create();
for (PatchSetApproval ca : args.approvalsUtil.byPatchSet(
- args.db.get(), changeData.notes(), patchSet.getId())) {
+ args.db.get(), changeData.changeControl(), patchSet.getId())) {
LabelType lt = labelTypes.byLabel(ca.getLabelId());
if (lt == null) {
continue;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java
index c9ac8de..5321046 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NotesMigration.java
@@ -14,8 +14,6 @@
package com.google.gerrit.server.notedb;
-import static com.google.common.base.Preconditions.checkArgument;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -36,7 +34,7 @@
static NotesMigration allEnabled() {
Config cfg = new Config();
cfg.setBoolean("notedb", null, "write", true);
- //cfg.setBoolean("notedb", "patchSetApprovals", "read", true);
+ cfg.setBoolean("notedb", "patchSetApprovals", "read", true);
return new NotesMigration(cfg);
}
@@ -48,8 +46,6 @@
write = cfg.getBoolean("notedb", null, "write", false);
readPatchSetApprovals =
cfg.getBoolean("notedb", "patchSetApprovals", "read", false);
- checkArgument(!readPatchSetApprovals,
- "notedb.readPatchSetApprovals not yet supported");
}
public boolean write() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index a2d553e..377a768 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -29,7 +29,6 @@
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -189,29 +188,25 @@
}
}
- private final ApprovalsUtil approvalsUtil;
private final ChangeData.Factory changeDataFactory;
private final RefControl refControl;
private final ChangeNotes notes;
@AssistedInject
ChangeControl(
- ApprovalsUtil approvalsUtil,
ChangeData.Factory changeDataFactory,
ChangeNotes.Factory notesFactory,
@Assisted RefControl refControl,
@Assisted Change change) {
- this(approvalsUtil, changeDataFactory, refControl,
+ this(changeDataFactory, refControl,
notesFactory.create(change));
}
@AssistedInject
ChangeControl(
- ApprovalsUtil approvalsUtil,
ChangeData.Factory changeDataFactory,
@Assisted RefControl refControl,
@Assisted ChangeNotes notes) {
- this.approvalsUtil = approvalsUtil;
this.changeDataFactory = changeDataFactory;
this.refControl = refControl;
this.notes = notes;
@@ -221,7 +216,7 @@
if (getCurrentUser().equals(who)) {
return this;
}
- return new ChangeControl(approvalsUtil, changeDataFactory,
+ return new ChangeControl(changeDataFactory,
getRefControl().forUser(who), notes);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 333c343..2f0bf33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -343,12 +343,15 @@
return changeControl != null;
}
- public ChangeControl changeControl() throws NoSuchChangeException,
- OrmException {
+ public ChangeControl changeControl() throws OrmException {
if (changeControl == null) {
Change c = change();
- changeControl =
- changeControlFactory.controlFor(c, userFactory.create(c.getOwner()));
+ try {
+ changeControl =
+ changeControlFactory.controlFor(c, userFactory.create(c.getOwner()));
+ } catch (NoSuchChangeException e) {
+ throw new OrmException(e);
+ }
}
return changeControl;
}
@@ -394,11 +397,9 @@
Change c = change();
if (c == null) {
currentApprovals = Collections.emptyList();
- } else if (allApprovals != null) {
- return allApprovals.get(c.currentPatchSetId());
} else {
- currentApprovals = approvalsUtil.byPatchSet(
- db, notes(), c.currentPatchSetId());
+ currentApprovals = ImmutableList.copyOf(approvalsUtil.byPatchSet(
+ db, changeControl(), c.currentPatchSetId()));
}
}
return currentApprovals;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
index e81c061..ba33b97 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -38,6 +38,8 @@
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.ListGroupMembership;
+import com.google.gerrit.server.change.ChangeKindCache;
+import com.google.gerrit.server.change.ChangeKindCacheImpl;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
@@ -222,6 +224,7 @@
.toProvider(CanonicalWebUrlProvider.class);
bind(String.class).annotatedWith(AnonymousCowardName.class)
.toProvider(AnonymousCowardNameProvider.class);
+ bind(ChangeKindCache.class).to(ChangeKindCacheImpl.NoCache.class);
}
});
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index b544447..6170241 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit b544447649d9ee3b3f78a6a1a7f839cb6a361292
+Subproject commit 61702414c046dd6b811c9137b765f9db422f83db