Merge "Preserve topic when cherry-picking"
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 3cc353b..b0cf0cb 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -33,7 +33,7 @@
org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
@@ -48,12 +48,12 @@
org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 0c13b23..9bcbdf4 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -820,19 +820,22 @@
[[changeMerge.threadPoolSize]]changeMerge.threadPoolSize::
+
-Maximum size of the thread pool in which the mergeability flag of open
-changes is updated.
+_Deprecated:_ Formerly used to control thread pool size for background
+mergeability checks. These checks were moved to the indexing threadpool,
+so this value is now used for
+link:#index.batchThreads[index.batchThreads], only if that value is not
+provided.
+
-Default is 1.
+This option may be removed in a future version.
[[changeMerge.interactiveThreadPoolSize]]changeMerge.interactiveThreadPoolSize::
+
-Maximum size of the thread pool in which the mergeability flag of open
-changes is updated, when processing interactive user requests (e.g.
-pushes to refs/for/*). Set to 0 or negative to share the pool for
-background mergeability checks.
+_Deprecated:_ Formerly used to control thread pool size for interactive
+mergeability checks. These checks were moved to the indexing threadpool,
+so this value is now used for link:#index.threads[index.threads], only
+if that value is not provided.
+
-Default is 1.
+This option may be removed in a future version.
[[commentlink]]
=== Section commentlink
@@ -2036,9 +2039,20 @@
[[index.threads]]index.threads::
+
-Determines the number of threads to use for indexing.
+Number of threads to use for indexing in normal interactive operations.
+
-Defaults to 1 if not set, or set to a negative value.
+Defaults to 1 if not set, or set to a negative value (unless
+link:#changeMerge.interactiveThreadPoolSize[changeMerge.interactiveThreadPoolSize]
+is iset).
+
+[[index.batchThreads]]index.batchThreads::
++
+Number of threads to use for indexing in background operations, such as
+online schema upgrades.
++
+If not set or set to a negative value, defaults to using the same
+thread pool as interactive operations (unless
+link:#changeMerge.threadPoolSize[changeMerge.threadPoolSize] is set).
==== Lucene configuration
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ReindexIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ReindexIT.java
index 8733611..faace57 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ReindexIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/pgm/ReindexIT.java
@@ -43,18 +43,10 @@
@Test
public void reindexEmptySite() throws Exception {
initSite();
- runGerrit("reindex", "-d", sitePath.getPath(),
+ runGerrit("reindex", "-d", sitePath.toString(),
"--show-stack-trace");
}
- @Test
- public void reindexEmptySiteWithRecheckMergeable() throws Exception {
- initSite();
- runGerrit("reindex", "-d", sitePath.getPath(),
- "--show-stack-trace",
- "--recheck-mergeable");
- }
-
private void initSite() throws Exception {
runGerrit("init", "-d", sitePath.getPath(),
"--batch", "--no-auto-start", "--skip-plugins", "--show-stack-trace");
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
index 448ce86..1b50a47 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -45,6 +45,7 @@
protected SubmitType submitType;
protected SubmitTypeRecord submitTypeRecord;
protected boolean canSubmit;
+ protected boolean mergeable;
protected List<ChangeMessage> messages;
protected PatchSet.Id currentPatchSetId;
protected PatchSetDetail currentDetail;
@@ -274,4 +275,12 @@
public boolean canEdit() {
return canEdit;
}
+
+ public void setMergeable(boolean m) {
+ mergeable = m;
+ }
+
+ public boolean isMergeable() {
+ return mergeable;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java
index f2c97c1..3ad90d9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java
@@ -91,6 +91,7 @@
r.setChange(toChange(info));
r.setStarred(info.starred());
r.setPatchSets(toPatchSets(info));
+ r.setMergeable(info.mergeable());
r.setMessages(toMessages(info));
r.setAccounts(users(info));
r.setCurrentPatchSetId(new PatchSet.Id(info.legacy_id(), rev._number()));
@@ -224,7 +225,6 @@
c.setStatus(info.status());
c.setCurrentPatchSet(p);
c.setLastUpdatedOn(info.updated());
- c.setMergeable(info.mergeable());
return c;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
index 8fe7d56..5f5cf8f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
@@ -125,7 +125,7 @@
if (Gerrit.getConfig().getNewFeatures()
&& (status.equals(Change.Status.NEW) || status.equals(Change.Status.DRAFT))) {
table.getRowFormatter().setVisible(R_MERGE_TEST, true);
- table.setText(R_MERGE_TEST, 1, chg.isMergeable() ? Util.C
+ table.setText(R_MERGE_TEST, 1, changeDetail.isMergeable() ? Util.C
.changeInfoBlockCanMergeYes() : Util.C.changeInfoBlockCanMergeNo());
} else {
table.getRowFormatter().setVisible(R_MERGE_TEST, false);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java
index 6c7423c..3afa208 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java
@@ -17,6 +17,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/** Helper class to handle calculations involving line gaps. */
class LineMapper {
@@ -193,6 +194,11 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(this);
+ }
+
+ @Override
public String toString() {
return line + " " + aligned;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index 4180c70..63ec4ae 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -36,12 +36,12 @@
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.MergeabilityChecker;
import com.google.gerrit.server.change.PostReviewers;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.CreateChangeSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ProjectCache;
@@ -79,7 +79,7 @@
private final IdentifiedUser user;
private final PatchSetInfoFactory patchSetInfoFactory;
private final Provider<PostReviewers> reviewersProvider;
- private final MergeabilityChecker mergeabilityChecker;
+ private final ChangeIndexer indexer;
private final ChangeHooks hooks;
private final CreateChangeSender.Factory createChangeSenderFactory;
private final ProjectCache projectCache;
@@ -91,7 +91,8 @@
MetaDataUpdate.User metaDataUpdateFactory, ReviewDb db,
IdentifiedUser user, PatchSetInfoFactory patchSetInfoFactory,
Provider<PostReviewers> reviewersProvider,
- MergeabilityChecker mergeabilityChecker, ChangeHooks hooks,
+ ChangeIndexer indexer,
+ ChangeHooks hooks,
CreateChangeSender.Factory createChangeSenderFactory,
ProjectCache projectCache,
AllProjectsNameProvider allProjects,
@@ -110,7 +111,7 @@
this.user = user;
this.patchSetInfoFactory = patchSetInfoFactory;
this.reviewersProvider = reviewersProvider;
- this.mergeabilityChecker = mergeabilityChecker;
+ this.indexer = indexer;
this.hooks = hooks;
this.createChangeSenderFactory = createChangeSenderFactory;
this.projectCache = projectCache;
@@ -155,7 +156,7 @@
} finally {
db.rollback();
}
- mergeabilityChecker.newCheck().addChange(change).reindex().run();
+ indexer.index(db, change);
hooks.doPatchsetCreatedHook(change, ps, db);
try {
CreateChangeSender cm =
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 9550055..676bc71 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -15,6 +15,7 @@
package com.google.gerrit.lucene;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
import static com.google.gerrit.server.index.IndexRewriteImpl.CLOSED_STATUSES;
import static com.google.gerrit.server.index.IndexRewriteImpl.OPEN_STATUSES;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -119,8 +120,10 @@
private static final String CHANGE_FIELD = ChangeField.CHANGE.getName();
private static final String DELETED_FIELD = ChangeField.DELETED.getName();
private static final String ID_FIELD = ChangeField.LEGACY_ID.getName();
+ private static final String MERGEABLE_FIELD = ChangeField.MERGEABLE.getName();
private static final ImmutableSet<String> FIELDS = ImmutableSet.of(
- ADDED_FIELD, APPROVAL_FIELD, CHANGE_FIELD, DELETED_FIELD, ID_FIELD);
+ ADDED_FIELD, APPROVAL_FIELD, CHANGE_FIELD, DELETED_FIELD, ID_FIELD,
+ MERGEABLE_FIELD);
private static final Map<String, String> CUSTOM_CHAR_MAPPING = ImmutableMap.of(
"_", " ", ".", " ");
@@ -138,6 +141,8 @@
Version lucene47 = Version.LUCENE_47;
@SuppressWarnings("deprecation")
Version lucene48 = Version.LUCENE_48;
+ @SuppressWarnings("deprecation")
+ Version lucene410 = Version.LUCENE_4_10_0;
for (Map.Entry<Integer, Schema<ChangeData>> e
: ChangeSchemas.ALL.entrySet()) {
if (e.getKey() <= 3) {
@@ -150,8 +155,10 @@
versions.put(e.getValue(), lucene47);
} else if (e.getKey() <= 11) {
versions.put(e.getValue(), lucene48);
+ } else if (e.getKey() <= 13) {
+ versions.put(e.getValue(), lucene410);
} else {
- versions.put(e.getValue(), Version.LUCENE_4_10_0);
+ versions.put(e.getValue(), Version.LUCENE_4_10_1);
}
}
LUCENE_VERSIONS = versions.build();
@@ -223,7 +230,7 @@
LuceneChangeIndex(
@GerritServerConfig Config cfg,
SitePaths sitePaths,
- @IndexExecutor ListeningExecutorService executor,
+ @IndexExecutor(INTERACTIVE) ListeningExecutorService executor,
Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
FillArgs fillArgs,
@@ -480,6 +487,14 @@
deleted.numericValue().intValue());
}
+ // Mergeable.
+ String mergeable = doc.get(MERGEABLE_FIELD);
+ if ("1".equals(mergeable)) {
+ cd.setMergeable(true);
+ } else if ("0".equals(mergeable)) {
+ cd.setMergeable(false);
+ }
+
return cd;
}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java
index d3dc963..99bf7e0 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/OnlineReindexer.java
@@ -17,9 +17,9 @@
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
-import com.google.gerrit.server.index.ChangeBatchIndexer;
import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.IndexCollection;
+import com.google.gerrit.server.index.SiteIndexer;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -39,14 +39,14 @@
}
private final IndexCollection indexes;
- private final ChangeBatchIndexer batchIndexer;
+ private final SiteIndexer batchIndexer;
private final ProjectCache projectCache;
private final int version;
@Inject
OnlineReindexer(
IndexCollection indexes,
- ChangeBatchIndexer batchIndexer,
+ SiteIndexer batchIndexer,
ProjectCache projectCache,
@Assisted int version) {
this.indexes = indexes;
@@ -76,8 +76,8 @@
"not an active write schema version: %s", version);
log.info("Starting online reindex from schema version {} to {}",
version(indexes.getSearchIndex()), version(index));
- ChangeBatchIndexer.Result result = batchIndexer.indexAll(
- index, projectCache.all(), -1, -1, null, null);
+ SiteIndexer.Result result =
+ batchIndexer.indexAll(index, projectCache.all());
if (!result.success()) {
log.error("Online reindex of schema version {} failed", version(index));
return;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index cca6700..361fe98 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -44,7 +44,6 @@
import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.server.account.InternalAccountDirectory;
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
-import com.google.gerrit.server.change.MergeabilityChecksExecutorModule;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.AuthConfigModule;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
@@ -317,7 +316,6 @@
modules.add(new WorkQueue.Module());
modules.add(new ChangeHookRunner.Module());
modules.add(new ReceiveCommitsExecutorModule());
- modules.add(new MergeabilityChecksExecutorModule());
modules.add(new IntraLineWorkerPool.Module());
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new InternalAccountDirectory.Module());
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 cf4fdbf..67f4cb2 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
@@ -21,37 +21,23 @@
import com.google.gerrit.common.Die;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lucene.LuceneIndexModule;
-import com.google.gerrit.pgm.util.BatchGitModule;
import com.google.gerrit.pgm.util.BatchProgramModule;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.pgm.util.ThreadLimiter;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.MergeabilityChecker;
-import com.google.gerrit.server.change.MergeabilityChecksExecutor;
-import com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority;
-import com.google.gerrit.server.change.PatchSetInserter;
-import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.MergeUtil;
-import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.index.ChangeBatchIndexer;
import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.ChangeSchemas;
import com.google.gerrit.server.index.IndexCollection;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
-import com.google.gerrit.server.mail.ReplacePatchSetSender;
-import com.google.gerrit.server.notedb.NoteDbModule;
+import com.google.gerrit.server.index.SiteIndexer;
import com.google.gerrit.solr.SolrIndexModule;
-import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
-import com.google.inject.Provides;
-import com.google.inject.Singleton;
-import com.google.inject.util.Providers;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ProgressMonitor;
@@ -74,9 +60,6 @@
@Option(name = "--output", usage = "Prefix for output; path for local disk index, or prefix for remote index")
private String outputBase;
- @Option(name = "--recheck-mergeable", usage = "Recheck mergeable flag on all changes")
- private boolean recheckMergeable;
-
@Option(name = "--verbose", usage = "Output debug information for each change")
private boolean verbose;
@@ -144,18 +127,6 @@
}
modules.add(changeIndexModule);
modules.add(dbInjector.getInstance(BatchProgramModule.class));
- modules.add(new AbstractModule() {
- @Override
- protected void configure() {
- if (recheckMergeable) {
- install(new MergeabilityModule());
- } else {
- bind(MergeabilityChecker.class)
- .toProvider(Providers.<MergeabilityChecker> of(null));
- }
- }
- });
-
return dbInjector.createChildInjector(modules);
}
@@ -168,36 +139,6 @@
}
}
- private static class MergeabilityModule extends FactoryModule {
- @Override
- public void configure() {
- factory(PatchSetInserter.Factory.class);
- bind(ReplacePatchSetSender.Factory.class).toProvider(
- Providers.<ReplacePatchSetSender.Factory>of(null));
-
- factory(MergeUtil.Factory.class);
- install(new NoteDbModule());
- install(new BatchGitModule());
- }
-
- @Provides
- @Singleton
- @MergeabilityChecksExecutor(Priority.BACKGROUND)
- public WorkQueue.Executor createMergeabilityChecksExecutor(
- WorkQueue queues) {
- return queues.createQueue(1, "MergeabilityChecks");
- }
-
- @Provides
- @Singleton
- @MergeabilityChecksExecutor(Priority.INTERACTIVE)
- public WorkQueue.Executor createInteractiveMergeabilityChecksExecutor(
- @MergeabilityChecksExecutor(Priority.BACKGROUND)
- WorkQueue.Executor bg) {
- return bg;
- }
- }
-
private int indexAll() throws Exception {
ReviewDb db = sysInjector.getInstance(ReviewDb.class);
ProgressMonitor pm = new TextProgressMonitor();
@@ -217,11 +158,12 @@
}
pm.endTask();
- ChangeBatchIndexer batchIndexer =
- sysInjector.getInstance(ChangeBatchIndexer.class);
- ChangeBatchIndexer.Result result = batchIndexer.indexAll(
- index, projects, projects.size(), changeCount, System.err,
- verbose ? System.out : NullOutputStream.INSTANCE);
+ SiteIndexer batchIndexer =
+ sysInjector.getInstance(SiteIndexer.class);
+ SiteIndexer.Result result = batchIndexer.setNumChanges(changeCount)
+ .setProgressOut(System.err)
+ .setVerboseOut(verbose ? System.out : NullOutputStream.INSTANCE)
+ .indexAll(index, projects);
int n = result.doneCount() + result.failedCount();
double t = result.elapsed(TimeUnit.MILLISECONDS) / 1000d;
System.out.format("Reindexed %d changes in %.01fs (%.01f/s)\n", n, t, n/t);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 8f9f1f4..91ffa91 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -30,14 +30,19 @@
import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
import com.google.gerrit.server.change.ChangeKindCacheImpl;
+import com.google.gerrit.server.change.MergeabilityCache;
+import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
import com.google.gerrit.server.config.DisableReverseDnsLookup;
import com.google.gerrit.server.config.DisableReverseDnsLookupProvider;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.git.ChangeCache;
+import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.group.GroupModule;
+import com.google.gerrit.server.mail.ReplacePatchSetSender;
+import com.google.gerrit.server.notedb.NoteDbModule;
import com.google.gerrit.server.patch.PatchListCacheImpl;
import com.google.gerrit.server.project.AccessControlModule;
import com.google.gerrit.server.project.CommentLinkInfo;
@@ -87,10 +92,16 @@
.toProvider(DisableReverseDnsLookupProvider.class).in(SINGLETON);
bind(IdentifiedUser.class)
.toProvider(Providers.<IdentifiedUser> of(null));
+ bind(ReplacePatchSetSender.Factory.class).toProvider(
+ Providers.<ReplacePatchSetSender.Factory>of(null));
bind(CurrentUser.class).to(IdentifiedUser.class);
+ factory(MergeUtil.Factory.class);
+ factory(PatchSetInserter.Factory.class);
install(new AccessControlModule());
+ install(new BatchGitModule());
install(new DefaultCacheFactory.Module());
install(new GroupModule());
+ install(new NoteDbModule());
install(new PrologModule());
install(AccountByEmailCacheImpl.module());
install(AccountCacheImpl.module());
@@ -100,6 +111,7 @@
install(SectionSortCache.module());
install(ChangeKindCacheImpl.module());
install(ChangeCache.module());
+ install(MergeabilityCache.module());
install(TagCache.module());
factory(CapabilityControl.Factory.class);
factory(ChangeData.Factory.class);
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
index b443c4d..575f4ac 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -51,6 +51,18 @@
</includes>
</fileSet>
+ <fileSet filtered="true">
+ <directory></directory>
+ <include>.buckconfig</include>
+ <include>BUCK</include>
+ <include>VERSION</include>
+ <include>lib/gerrit/BUCK</include>
+ <include>lib/gwt/BUCK</include>
+ <excludes>
+ <exclude>**/*.java</exclude>
+ </excludes>
+ </fileSet>
+
<fileSet>
<directory></directory>
<includes>
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.buckconfig b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.buckconfig
new file mode 100644
index 0000000..1044c12
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.buckconfig
@@ -0,0 +1,14 @@
+[alias]
+ ${pluginName} = //:${pluginName}
+ plugin = //:${pluginName}
+
+[java]
+ src_roots = java, resources
+
+[project]
+ ignore = .git
+
+[cache]
+ mode = dir
+ dir = buck-out/cache
+
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.gitignore b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.gitignore
index 80d6257..43838b0 100644
--- a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.gitignore
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/.gitignore
@@ -1,3 +1,7 @@
+/.buckversion
+/.buckd
+/buck-out
+/bucklets
/target
/.classpath
/.project
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/BUCK b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/BUCK
new file mode 100644
index 0000000..b19312c
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/BUCK
@@ -0,0 +1,23 @@
+include_defs('//bucklets/gerrit_plugin.bucklet')
+
+gerrit_plugin(
+ name = '${pluginName}',
+ srcs = glob(['src/main/java/**/*.java']),
+ resources = glob(['src/main/**/*']),
+ gwt_module = '${package}.HelloPlugin',
+ manifest_entries = [
+ 'Gerrit-PluginName: ${pluginName}',
+ 'Gerrit-ApiType: plugin',
+ 'Gerrit-ApiVersion: ${gerritApiVersion}',
+ 'Gerrit-Module: ${package}.Module',
+ 'Gerrit-SshModule: ${package}.SshModule',
+ 'Gerrit-HttpModule: ${package}.HttpModule',
+ ],
+)
+
+# this is required for bucklets/tools/eclipse/project.py to work
+java_library(
+ name = 'classpath',
+ deps = [':${pluginName}__plugin'],
+)
+
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/VERSION b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/VERSION
new file mode 100644
index 0000000..8bbb460
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/VERSION
@@ -0,0 +1,5 @@
+# Used by BUCK to include "Implementation-Version" in plugin Manifest.
+# If this file doesn't exist the output of 'git describe' is used
+# instead.
+PLUGIN_VERSION = '${version}'
+
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/lib/gerrit/BUCK b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/lib/gerrit/BUCK
new file mode 100644
index 0000000..0a0d8b9
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/lib/gerrit/BUCK
@@ -0,0 +1,20 @@
+include_defs('//bucklets/maven_jar.bucklet')
+
+VER = '${gerritApiVersion}'
+REPO = MAVEN_LOCAL
+
+maven_jar(
+ name = 'plugin-api',
+ id = 'com.google.gerrit:gerrit-plugin-api:' + VER,
+ attach_source = False,
+ repository = REPO,
+ license = 'Apache2.0',
+)
+
+maven_jar(
+ name = 'gwtui-api',
+ id = 'com.google.gerrit:gerrit-plugin-gwtui:' + VER,
+ attach_source = False,
+ repository = REPO,
+ license = 'Apache2.0',
+)
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/lib/gwt/BUCK b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/lib/gwt/BUCK
new file mode 100644
index 0000000..511a8ec
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/lib/gwt/BUCK
@@ -0,0 +1,32 @@
+include_defs('//bucklets/maven_jar.bucklet')
+
+VERSION = '${Gwt-Version}'
+
+maven_jar(
+ name = 'user',
+ id = 'com.google.gwt:gwt-user:' + VERSION,
+ license = 'Apache2.0',
+ attach_source = False,
+)
+
+maven_jar(
+ name = 'dev',
+ id = 'com.google.gwt:gwt-dev:' + VERSION,
+ license = 'Apache2.0',
+ deps = [
+ ':javax-validation',
+ ':javax-validation_src',
+ ],
+ attach_source = False,
+ exclude = ['org/eclipse/jetty/*'],
+)
+
+maven_jar(
+ name = 'javax-validation',
+ id = 'javax.validation:validation-api:1.0.0.GA',
+ bin_sha1 = 'b6bd7f9d78f6fdaa3c37dae18a4bd298915f328e',
+ src_sha1 = '7a561191db2203550fbfa40d534d4997624cd369',
+ license = 'Apache2.0',
+ visibility = [],
+)
+
diff --git a/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/build.md b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/build.md
new file mode 100644
index 0000000..4c56ed6
--- /dev/null
+++ b/gerrit-plugin-gwt-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/build.md
@@ -0,0 +1,77 @@
+Build
+=====
+
+This plugin can be built with Buck or Maven.
+
+Buck
+----
+
+Two build modes are supported: Standalone and in Gerrit tree.
+The standalone build mode is recommended, as this mode doesn't require
+the Gerrit tree to exist locally.
+
+
+
+Clone bucklets library:
+
+```
+ git clone https://gerrit.googlesource.com/bucklets
+
+```
+and link it to @PLUGIN@ plugin directory:
+
+```
+ cd @PLUGIN@ && ln -s ../bucklets .
+```
+
+Add link to the .buckversion file:
+
+```
+ cd @PLUGIN@ && ln -s bucklets/buckversion .buckversion
+```
+
+To build the plugin, issue the following command:
+
+
+```
+ buck build plugin
+```
+
+The output is created in
+
+```
+ buck-out/gen/@PLUGIN@.jar
+```
+
+
+Clone or link this plugin to the plugins directory of Gerrit's source
+tree, and issue the command:
+
+```
+ buck build plugins/@PLUGIN@
+```
+
+The output is created in
+
+```
+ buck-out/gen/plugins/@PLUGIN@/@PLUGIN@.jar
+```
+
+This project can be imported into the Eclipse IDE:
+
+```
+ ./tools/eclipse/project.py
+```
+
+Maven
+-----
+
+Note that the Maven build is provided for compatibility reasons, but
+it is considered to be deprecated and will be removed in a future
+version of this plugin.
+
+To build with Maven, run
+
+```
+mvn clean package
+```
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
index 94d2f64..594f974 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
@@ -432,17 +432,6 @@
@Column(id = 14, notNull = false)
protected String topic;
- /**
- * Null if the change has never been tested.
- * Empty if it has been tested but against a branch that does
- * not exist.
- */
- @Column(id = 15, notNull = false)
- protected RevId lastSha1MergeTested;
-
- @Column(id = 16)
- protected boolean mergeable;
-
protected Change() {
}
@@ -455,7 +444,6 @@
owner = ownedBy;
dest = forBranch;
setStatus(Status.NEW);
- setLastSha1MergeTested(null);
}
public Change(Change other) {
@@ -472,8 +460,6 @@
currentPatchSetId = other.currentPatchSetId;
subject = other.subject;
topic = other.topic;
- mergeable = other.mergeable;
- lastSha1MergeTested = other.lastSha1MergeTested;
}
/** Legacy 32 bit integer identity for a change. */
@@ -564,20 +550,4 @@
public void setTopic(String topic) {
this.topic = topic;
}
-
- public RevId getLastSha1MergeTested() {
- return lastSha1MergeTested;
- }
-
- public void setLastSha1MergeTested(RevId lastSha1MergeTested) {
- this.lastSha1MergeTested = lastSha1MergeTested;
- }
-
- public boolean isMergeable() {
- return mergeable;
- }
-
- public void setMergeable(boolean mergeable) {
- this.mergeable = mergeable;
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
index 4f19c8e..28e7a34 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.auth.AuthException;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.CreateChangeSender;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -68,7 +69,7 @@
private final ChangeHooks hooks;
private final ApprovalsUtil approvalsUtil;
private final ChangeMessagesUtil cmUtil;
- private final MergeabilityChecker mergeabilityChecker;
+ private final ChangeIndexer indexer;
private final CreateChangeSender.Factory createChangeSenderFactory;
private final HashtagsUtil hashtagsUtil;
private final AccountCache accountCache;
@@ -95,7 +96,7 @@
ChangeHooks hooks,
ApprovalsUtil approvalsUtil,
ChangeMessagesUtil cmUtil,
- MergeabilityChecker mergeabilityChecker,
+ ChangeIndexer indexer,
CreateChangeSender.Factory createChangeSenderFactory,
HashtagsUtil hashtagsUtil,
AccountCache accountCache,
@@ -108,7 +109,7 @@
this.hooks = hooks;
this.approvalsUtil = approvalsUtil;
this.cmUtil = cmUtil;
- this.mergeabilityChecker = mergeabilityChecker;
+ this.indexer = indexer;
this.createChangeSenderFactory = createChangeSenderFactory;
this.hashtagsUtil = hashtagsUtil;
this.accountCache = accountCache;
@@ -221,10 +222,7 @@
}
}
- CheckedFuture<?, IOException> f = mergeabilityChecker.newCheck()
- .addChange(change)
- .reindex()
- .runAsync();
+ CheckedFuture<?, IOException> f = indexer.indexAsync(change.getId());
if(!messageIsForChange()) {
commitMessageNotForChange();
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 66f1388..55f65d5 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
@@ -273,7 +273,10 @@
out.topic = in.getTopic();
out.hashtags = ctl.getNotes().load().getHashtags();
out.changeId = in.getKey().get();
- out.mergeable = isMergeable(in);
+ // TODO(dborowitz): This gets the submit type, so we could include that in
+ // the response and avoid making a request to /submit_type from the UI.
+ out.mergeable = in.getStatus() == Change.Status.MERGED
+ ? null : cd.isMergeable();
ChangedLines changedLines = cd.changedLines();
if (changedLines != null) {
out.insertions = changedLines.insertions;
@@ -344,14 +347,6 @@
return out;
}
- private Boolean isMergeable(Change c) {
- if (c.getStatus() == Change.Status.MERGED
- || c.getLastSha1MergeTested() == null) {
- return null;
- }
- return c.isMergeable();
- }
-
private List<SubmitRecord> submitRecords(ChangeData cd) throws OrmException {
if (cd.getSubmitRecords() != null) {
return cd.getSubmitRecords();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCache.java
new file mode 100644
index 0000000..7589ea5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCache.java
@@ -0,0 +1,322 @@
+// 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.
+// 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.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.Weigher;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.Sets;
+import com.google.gerrit.extensions.common.SubmitType;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.git.CodeReviewCommit;
+import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+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.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+@Singleton
+public class MergeabilityCache {
+ private static final Logger log =
+ LoggerFactory.getLogger(MergeabilityCache.class);
+
+ private static final String CACHE_NAME = "mergeability";
+
+ public static final BiMap<SubmitType, Character> SUBMIT_TYPES = ImmutableBiMap.of(
+ SubmitType.FAST_FORWARD_ONLY, 'F',
+ SubmitType.MERGE_IF_NECESSARY, 'M',
+ SubmitType.REBASE_IF_NECESSARY, 'R',
+ SubmitType.MERGE_ALWAYS, 'A',
+ SubmitType.CHERRY_PICK, 'C');
+
+ static {
+ checkState(SUBMIT_TYPES.size() == SubmitType.values().length,
+ "SubmitType <-> char BiMap needs updating");
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static Key bindingKey() {
+ return Key.get(new TypeLiteral<LoadingCache<EntryKey, Boolean>>() {},
+ Names.named(CACHE_NAME));
+ }
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ persist(CACHE_NAME, EntryKey.class, Boolean.class)
+ .maximumWeight(1 << 20)
+ .weigher(MergeabilityWeigher.class)
+ .loader(Loader.class);
+ bind(MergeabilityCache.class);
+ }
+ };
+ }
+
+ public static ObjectId toId(Ref ref) {
+ return ref != null && ref.getObjectId() != null
+ ? ref.getObjectId()
+ : ObjectId.zeroId();
+ }
+
+ public static class EntryKey implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private ObjectId commit;
+ private ObjectId into;
+ private SubmitType submitType;
+ private String mergeStrategy;
+
+ // Only used for loading, not stored.
+ private transient LoadHelper load;
+
+ public EntryKey(ObjectId commit, ObjectId into, SubmitType submitType,
+ String mergeStrategy) {
+ this.commit = checkNotNull(commit, "commit");
+ this.into = checkNotNull(into, "into");
+ this.submitType = checkNotNull(submitType, "submitType");
+ this.mergeStrategy = checkNotNull(mergeStrategy, "mergeStrategy");
+ }
+
+ private EntryKey(ObjectId commit, ObjectId into, SubmitType submitType,
+ String mergeStrategy, Branch.NameKey dest, Repository repo,
+ ReviewDb db) {
+ this(commit, into, submitType, mergeStrategy);
+ load = new LoadHelper(dest, repo, db);
+ }
+
+ public ObjectId getCommit() {
+ return commit;
+ }
+
+ public ObjectId getInto() {
+ return into;
+ }
+
+ public SubmitType getSubmitType() {
+ return submitType;
+ }
+
+ public String getMergeStrategy() {
+ return mergeStrategy;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof EntryKey) {
+ EntryKey k = (EntryKey) o;
+ return commit.equals(k.commit)
+ && into.equals(k.into)
+ && submitType == k.submitType
+ && mergeStrategy.equals(k.mergeStrategy);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(commit, into, submitType, mergeStrategy);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("commit", commit.name())
+ .add("into", into.name())
+ .addValue(submitType)
+ .addValue(mergeStrategy)
+ .toString();
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ writeNotNull(out, commit);
+ writeNotNull(out, into);
+ Character c = SUBMIT_TYPES.get(submitType);
+ if (c == null) {
+ throw new IOException("Invalid submit type: " + submitType);
+ }
+ out.writeChar(c);
+ writeString(out, mergeStrategy);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException {
+ commit = readNotNull(in);
+ into = readNotNull(in);
+ char t = in.readChar();
+ submitType = SUBMIT_TYPES.inverse().get(t);
+ if (submitType == null) {
+ throw new IOException("Invalid submit type code: " + t);
+ }
+ mergeStrategy = readString(in);
+ }
+ }
+
+ private static class LoadHelper {
+ private final Branch.NameKey dest;
+ private final Repository repo;
+ private final ReviewDb db;
+
+ private LoadHelper(Branch.NameKey dest, Repository repo, ReviewDb db) {
+ this.dest = checkNotNull(dest, "dest");
+ this.repo = checkNotNull(repo, "repo");
+ this.db = checkNotNull(db, "db");
+ }
+ }
+
+ @Singleton
+ public static class Loader extends CacheLoader<EntryKey, Boolean> {
+ private final SubmitStrategyFactory submitStrategyFactory;
+
+ @Inject
+ Loader(SubmitStrategyFactory submitStrategyFactory) {
+ this.submitStrategyFactory = submitStrategyFactory;
+ }
+
+ @Override
+ public Boolean load(EntryKey key)
+ throws NoSuchProjectException, MergeException, IOException {
+ checkArgument(key.load != null, "Key cannot be loaded: %s", key);
+ if (key.into.equals(ObjectId.zeroId())) {
+ return true; // Assume yes on new branch.
+ }
+ try {
+ Map<String, Ref> refs = key.load.repo.getAllRefs();
+ RevWalk rw = CodeReviewCommit.newRevWalk(key.load.repo);
+ try {
+ RevFlag canMerge = rw.newFlag("CAN_MERGE");
+ CodeReviewCommit rev = parse(rw, key.commit);
+ rev.add(canMerge);
+ CodeReviewCommit tip = parse(rw, key.into);
+ Set<RevCommit> accepted = alreadyAccepted(rw, refs.values());
+ accepted.add(tip);
+ accepted.addAll(Arrays.asList(rev.getParents()));
+ return submitStrategyFactory.create(
+ key.submitType,
+ key.load.db,
+ key.load.repo,
+ rw,
+ null /*inserter*/,
+ canMerge,
+ accepted,
+ key.load.dest).dryRun(tip, rev);
+ } finally {
+ rw.release();
+ }
+ } finally {
+ key.load = null;
+ }
+ }
+
+ private static Set<RevCommit> alreadyAccepted(RevWalk rw,
+ Collection<Ref> refs) throws MissingObjectException, IOException {
+ Set<RevCommit> accepted = Sets.newHashSet();
+ for (Ref r : refs) {
+ if (r.getName().startsWith(Constants.R_HEADS)
+ || r.getName().startsWith(Constants.R_TAGS)) {
+ try {
+ accepted.add(rw.parseCommit(r.getObjectId()));
+ } catch (IncorrectObjectTypeException nonCommit) {
+ // Not a commit? Skip over it.
+ }
+ }
+ }
+ return accepted;
+ }
+
+ private static CodeReviewCommit parse(RevWalk rw, ObjectId id)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return (CodeReviewCommit) rw.parseCommit(id);
+ }
+ }
+
+ public static class MergeabilityWeigher
+ implements Weigher<EntryKey, Boolean> {
+ @Override
+ public int weigh(EntryKey k, Boolean v) {
+ return 16 + 2 * (16 + 20) + 3 * 8 // Size of EntryKey, 64-bit JVM.
+ + 8; // Size of Boolean.
+ }
+ }
+
+ private final LoadingCache<EntryKey, Boolean> cache;
+
+ @Inject
+ MergeabilityCache(@Named(CACHE_NAME) LoadingCache<EntryKey, Boolean> cache) {
+ this.cache = cache;
+ }
+
+ public boolean get(ObjectId commit, Ref intoRef, SubmitType submitType,
+ String mergeStrategy, Branch.NameKey dest, Repository repo, ReviewDb db) {
+ ObjectId into = intoRef != null ? intoRef.getObjectId() : ObjectId.zeroId();
+ EntryKey key =
+ new EntryKey(commit, into, submitType, mergeStrategy, dest, repo, db);
+ try {
+ return cache.get(key);
+ } catch (ExecutionException e) {
+ log.error(String.format("Error checking mergeability of %s into %s (%s)",
+ key.commit.name(), key.into.name(), key.submitType.name()),
+ e.getCause());
+ return false;
+ }
+ }
+
+ public boolean getIfPresent(ObjectId commit, Ref intoRef,
+ SubmitType submitType, String mergeStrategy) {
+ return cache.getIfPresent(new EntryKey(
+ commit, toId(intoRef), submitType, mergeStrategy, null, null, null));
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCheckQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCheckQueue.java
deleted file mode 100644
index ae44ea2..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCheckQueue.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.inject.Singleton;
-
-import java.util.Collection;
-import java.util.Set;
-
-@Singleton
-class MergeabilityCheckQueue {
- private final Set<Change.Id> pending = Sets.newHashSet();
- private final Set<Change.Id> forcePending = Sets.newHashSet();
-
- synchronized Set<Change> addAll(Collection<Change> changes, boolean force) {
- Set<Change> r = Sets.newLinkedHashSetWithExpectedSize(changes.size());
- for (Change c : changes) {
- if (force ? forcePending.add(c.getId()) : pending.add(c.getId())) {
- r.add(c);
- }
- }
- return r;
- }
-
- synchronized void updatingMergeabilityFlag(Change change, boolean force) {
- if (force) {
- forcePending.remove(change.getId());
- }
- pending.remove(change.getId());
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java
deleted file mode 100644
index 270f718..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java
+++ /dev/null
@@ -1,366 +0,0 @@
-// 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 com.google.common.base.Function;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority;
-import com.google.gerrit.server.git.MetaDataUpdate;
-import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.git.WorkQueue.Executor;
-import com.google.gerrit.server.index.ChangeIndexer;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.util.RequestContext;
-import com.google.gerrit.server.util.ThreadLocalRequestContext;
-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.ProvisionException;
-
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-
-public class MergeabilityChecker implements GitReferenceUpdatedListener {
- private static final Logger log = LoggerFactory
- .getLogger(MergeabilityChecker.class);
-
- private static final Function<Exception, IOException> MAPPER =
- new Function<Exception, IOException>() {
- @Override
- public IOException apply(Exception in) {
- if (in instanceof IOException) {
- return (IOException) in;
- } else if (in instanceof ExecutionException
- && in.getCause() instanceof IOException) {
- return (IOException) in.getCause();
- } else {
- return new IOException(in);
- }
- }
- };
-
- public class Check {
- private List<Change> changes;
- private List<Branch.NameKey> branches;
- private List<Project.NameKey> projects;
- private boolean force;
- private boolean reindex;
- private boolean interactive;
-
- private Check() {
- changes = Lists.newArrayListWithExpectedSize(1);
- branches = Lists.newArrayListWithExpectedSize(1);
- projects = Lists.newArrayListWithExpectedSize(1);
- interactive = true;
- }
-
- public Check addChange(Change change) {
- changes.add(change);
- return this;
- }
-
- public Check addBranch(Branch.NameKey branch) {
- branches.add(branch);
- interactive = false;
- return this;
- }
-
- public Check addProject(Project.NameKey project) {
- projects.add(project);
- interactive = false;
- return this;
- }
-
- /** Force reindexing regardless of whether mergeable flag was modified. */
- public Check reindex() {
- reindex = true;
- return this;
- }
-
- /** Force mergeability check even if change is not stale. */
- private Check force() {
- force = true;
- return this;
- }
-
- private ListeningExecutorService getExecutor() {
- return interactive ? interactiveExecutor : backgroundExecutor;
- }
-
- public CheckedFuture<?, IOException> runAsync() {
- final ListeningExecutorService executor = getExecutor();
- ListenableFuture<List<Change>> getChanges;
- if (branches.isEmpty() && projects.isEmpty()) {
- getChanges = Futures.immediateFuture(changes);
- } else {
- getChanges = executor.submit(
- new Callable<List<Change>>() {
- @Override
- public List<Change> call() throws OrmException {
- return getChanges();
- }
- });
- }
-
- return Futures.makeChecked(Futures.transform(getChanges,
- new AsyncFunction<List<Change>, List<Object>>() {
- @Override
- public ListenableFuture<List<Object>> apply(List<Change> changes) {
- List<ListenableFuture<?>> result =
- Lists.newArrayListWithCapacity(changes.size());
- for (final Change c : changes) {
- // Don't try to guess whether Mergeable will reindex; just turn
- // off reindexing in that code path and do it explicitly below.
- ListenableFuture<Void> b =
- executor.submit(new Task(c, force, !reindex));
- if (reindex) {
- result.add(Futures.transform(
- b, new AsyncFunction<Void, Void>() {
- @SuppressWarnings("unchecked")
- @Override
- public ListenableFuture<Void> apply(Void o) {
- return (ListenableFuture<Void>)
- indexer.indexAsync(c.getId());
- }
- }));
- } else {
- result.add(b);
- }
- }
- return Futures.allAsList(result);
- }
- }), MAPPER);
- }
-
- public void run() throws IOException {
- try {
- runAsync().checkedGet();
- } catch (Exception e) {
- Throwables.propagateIfPossible(e, IOException.class);
- throw MAPPER.apply(e);
- }
- }
-
- private List<Change> getChanges() throws OrmException {
- ReviewDb db = schemaFactory.open();
- try {
- List<Change> results = Lists.newArrayList();
- results.addAll(changes);
- for (Project.NameKey p : projects) {
- Iterables.addAll(results, db.changes().byProjectOpenAll(p));
- }
- for (Branch.NameKey b : branches) {
- Iterables.addAll(results, db.changes().byBranchOpenAll(b));
- }
- return results;
- } catch (OrmException e) {
- log.error("Failed to fetch changes for mergeability check", e);
- throw e;
- } finally {
- db.close();
- }
- }
- }
-
- private final ThreadLocalRequestContext tl;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final IdentifiedUser.GenericFactory identifiedUserFactory;
- private final ChangeControl.GenericFactory changeControlFactory;
- private final Provider<Mergeable> mergeable;
- private final ChangeIndexer indexer;
- private final ListeningExecutorService backgroundExecutor;
- private final ListeningExecutorService interactiveExecutor;
- private final MergeabilityCheckQueue mergeabilityCheckQueue;
- private final MetaDataUpdate.Server metaDataUpdateFactory;
-
- @Inject
- public MergeabilityChecker(ThreadLocalRequestContext tl,
- SchemaFactory<ReviewDb> schemaFactory,
- IdentifiedUser.GenericFactory identifiedUserFactory,
- ChangeControl.GenericFactory changeControlFactory,
- Provider<Mergeable> mergeable, ChangeIndexer indexer,
- @MergeabilityChecksExecutor(Priority.BACKGROUND)
- Executor backgroundExecutor,
- @MergeabilityChecksExecutor(Priority.INTERACTIVE)
- Executor interactiveExecutor,
- MergeabilityCheckQueue mergeabilityCheckQueue,
- MetaDataUpdate.Server metaDataUpdateFactory) {
- this.tl = tl;
- this.schemaFactory = schemaFactory;
- this.identifiedUserFactory = identifiedUserFactory;
- this.changeControlFactory = changeControlFactory;
- this.mergeable = mergeable;
- this.indexer = indexer;
- this.backgroundExecutor =
- MoreExecutors.listeningDecorator(backgroundExecutor);
- this.interactiveExecutor =
- MoreExecutors.listeningDecorator(interactiveExecutor);
- this.mergeabilityCheckQueue = mergeabilityCheckQueue;
- this.metaDataUpdateFactory = metaDataUpdateFactory;
- }
-
- public Check newCheck() {
- return new Check();
- }
-
- @Override
- public void onGitReferenceUpdated(GitReferenceUpdatedListener.Event event) {
- String ref = event.getRefName();
- if (ref.startsWith(Constants.R_HEADS) || ref.equals(RefNames.REFS_CONFIG)) {
- Branch.NameKey branch = new Branch.NameKey(
- new Project.NameKey(event.getProjectName()), ref);
- newCheck().addBranch(branch).runAsync();
- }
- if (ref.equals(RefNames.REFS_CONFIG)) {
- Project.NameKey p = new Project.NameKey(event.getProjectName());
- try {
- ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId());
- ProjectConfig newCfg = parseConfig(p, event.getNewObjectId());
- if (recheckMerges(oldCfg, newCfg)) {
- newCheck().addProject(p).force().runAsync();
- }
- } catch (ConfigInvalidException | IOException e) {
- String msg = "Failed to update mergeability flags for project " + p.get()
- + " on update of " + RefNames.REFS_CONFIG;
- log.error(msg, e);
- throw new RuntimeException(msg, e);
- }
- }
- }
-
- private boolean recheckMerges(ProjectConfig oldCfg, ProjectConfig newCfg) {
- if (oldCfg == null || newCfg == null) {
- return true;
- }
- return !oldCfg.getProject().getSubmitType().equals(newCfg.getProject().getSubmitType())
- || oldCfg.getProject().getUseContentMerge() != newCfg.getProject().getUseContentMerge()
- || (oldCfg.getRulesId() == null
- ? newCfg.getRulesId() != null
- : !oldCfg.getRulesId().equals(newCfg.getRulesId()));
- }
-
- private ProjectConfig parseConfig(Project.NameKey p, String idStr)
- throws IOException, ConfigInvalidException, RepositoryNotFoundException {
- ObjectId id = ObjectId.fromString(idStr);
- if (ObjectId.zeroId().equals(id)) {
- return null;
- }
- return ProjectConfig.read(metaDataUpdateFactory.create(p), id);
- }
-
- private class Task implements Callable<Void> {
- private final Change change;
- private final boolean force;
- private final boolean reindex;
-
- private ReviewDb reviewDb;
-
- Task(Change change, boolean force, boolean reindex) {
- this.change = change;
- this.force = force;
- this.reindex = reindex;
- }
-
- @Override
- public Void call() throws Exception {
- mergeabilityCheckQueue.updatingMergeabilityFlag(change, force);
-
- RequestContext context = new RequestContext() {
- @Override
- public CurrentUser getCurrentUser() {
- return identifiedUserFactory.create(change.getOwner());
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- if (reviewDb == null) {
- try {
- reviewDb = schemaFactory.open();
- } catch (OrmException e) {
- throw new ProvisionException("Cannot open ReviewDb", e);
- }
- }
- return reviewDb;
- }
- };
- }
- };
- RequestContext old = tl.setContext(context);
- ReviewDb db = context.getReviewDbProvider().get();
- try {
- PatchSet ps = db.patchSets().get(change.currentPatchSetId());
- if (ps == null) {
- // Cannot compute mergeability if current patch set is missing.
- return null;
- }
-
- Mergeable m = mergeable.get();
- m.setForce(force);
- m.setReindex(reindex);
-
- ChangeControl control =
- changeControlFactory.controlFor(change, context.getCurrentUser());
- m.apply(new RevisionResource(new ChangeResource(control), ps));
- return null;
- } catch (ResourceConflictException e) {
- // change is closed
- return null;
- } catch (Exception e) {
- log.error(String.format(
- "cannot update mergeability flag of change %d in project %s after update of %s",
- change.getId().get(),
- change.getDest().getParentKey(), change.getDest().get()), e);
- throw e;
- } finally {
- tl.setContext(old);
- if (reviewDb != null) {
- reviewDb.close();
- reviewDb = null;
- }
- }
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java
deleted file mode 100644
index 632e6ac..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.gerrit.server.git.WorkQueue.Executor;
-import com.google.inject.BindingAnnotation;
-
-import java.lang.annotation.Retention;
-
-/**
- * Marker on the global {@link Executor} used by
- * {@link MergeabilityChecker}.
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface MergeabilityChecksExecutor {
- public enum Priority {
- BACKGROUND, INTERACTIVE;
- }
-
- Priority value();
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java
deleted file mode 100644
index e5bcabe..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.WorkQueue;
-import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.lib.Config;
-
-/** Module providing the {@link MergeabilityChecksExecutor}. */
-public class MergeabilityChecksExecutorModule extends AbstractModule {
- @Override
- protected void configure() {
- }
-
- @Provides
- @Singleton
- @MergeabilityChecksExecutor(Priority.BACKGROUND)
- public WorkQueue.Executor createMergeabilityChecksExecutor(
- @GerritServerConfig Config config,
- WorkQueue queues) {
- int poolSize = config.getInt("changeMerge", null, "threadPoolSize", 1);
- return queues.createQueue(poolSize, "MergeabilityChecks-Background");
- }
-
- @Provides
- @Singleton
- @MergeabilityChecksExecutor(Priority.INTERACTIVE)
- public WorkQueue.Executor createMergeabilityChecksExecutor(
- @GerritServerConfig Config config,
- WorkQueue queues,
- @MergeabilityChecksExecutor(Priority.BACKGROUND)
- WorkQueue.Executor backgroundExecutor) {
- int poolSize =
- config.getInt("changeMerge", null, "interactiveThreadPoolSize", 1);
- if (poolSize <= 0) {
- return backgroundExecutor;
- }
- return queues.createQueue(poolSize, "MergeabilityChecks-Interactive");
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
index e5800e3..ca83a20 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.change;
-import com.google.common.collect.Sets;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.extensions.common.SubmitType;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -23,45 +22,32 @@
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.git.BranchOrderSection;
-import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MergeException;
-import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
+import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.index.ChangeIndexer;
-import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.Objects;
public class Mergeable implements RestReadView<RevisionResource> {
private static final Logger log = LoggerFactory.getLogger(Mergeable.class);
@@ -82,34 +68,31 @@
this.force = force;
}
- public void setReindex(boolean reindex) {
- this.reindex = reindex;
- }
-
private final GitRepositoryManager gitManager;
private final ProjectCache projectCache;
+ private final MergeUtil.Factory mergeUtilFactory;
private final ChangeData.Factory changeDataFactory;
- private final SubmitStrategyFactory submitStrategyFactory;
private final Provider<ReviewDb> db;
private final ChangeIndexer indexer;
+ private final MergeabilityCache cache;
private boolean force;
- private boolean reindex;
@Inject
Mergeable(GitRepositoryManager gitManager,
ProjectCache projectCache,
+ MergeUtil.Factory mergeUtilFactory,
ChangeData.Factory changeDataFactory,
- SubmitStrategyFactory submitStrategyFactory,
Provider<ReviewDb> db,
- ChangeIndexer indexer) {
+ ChangeIndexer indexer,
+ MergeabilityCache cache) {
this.gitManager = gitManager;
this.projectCache = projectCache;
+ this.mergeUtilFactory = mergeUtilFactory;
this.changeDataFactory = changeDataFactory;
- this.submitStrategyFactory = submitStrategyFactory;
this.db = db;
this.indexer = indexer;
- reindex = true;
+ this.cache = cache;
}
@Override
@@ -134,29 +117,39 @@
throw new OrmException("Submit type rule failed: " + rec);
}
result.submitType = rec.type;
- result.mergeable = change.isMergeable();
Repository git = gitManager.openRepository(change.getProject());
try {
- Map<String, Ref> refs = git.getRefDatabase().getRefs(RefDatabase.ALL);
- Ref ref = refs.get(change.getDest().get());
- if (force || isStale(change, ref)) {
- result.mergeable =
- refresh(change, ps, result.submitType, git, refs, ref);
+ ObjectId commit = toId(ps);
+ if (commit == null) {
+ result.mergeable = false;
+ return result;
+ }
+
+ Ref ref = git.getRef(change.getDest().get());
+ ProjectState projectState = projectCache.get(change.getProject());
+ String strategy = mergeUtilFactory.create(projectState)
+ .mergeStrategyName();
+ Boolean old =
+ cache.getIfPresent(commit, ref, result.submitType, strategy);
+
+ if (force || old == null) {
+ result.mergeable = refresh(change, commit, ref, result.submitType,
+ strategy, git, old);
}
if (otherBranches) {
result.mergeableInto = new ArrayList<>();
- BranchOrderSection branchOrder =
- projectCache.get(change.getProject()).getBranchOrderSection();
+ BranchOrderSection branchOrder = projectState.getBranchOrderSection();
if (branchOrder != null) {
int prefixLen = Constants.R_HEADS.length();
for (String n : branchOrder.getMoreStable(ref.getName())) {
- Ref other = refs.get(n);
+ Ref other = git.getRef(n);
if (other == null) {
continue;
}
- if (isMergeable(change, ps, SubmitType.CHERRY_PICK, git, refs, other)) {
+ if (cache.get(commit, other, SubmitType.CHERRY_PICK, strategy,
+ change.getDest(), git, db.get())) {
result.mergeableInto.add(other.getName().substring(prefixLen));
}
}
@@ -168,121 +161,25 @@
return result;
}
- private static boolean isStale(Change change, Ref ref) {
- return change.getLastSha1MergeTested() == null
- || !toRevId(ref).equals(change.getLastSha1MergeTested());
+ private static ObjectId toId(PatchSet ps) {
+ try {
+ return ObjectId.fromString(ps.getRevision().get());
+ } catch (IllegalArgumentException e) {
+ log.error("Invalid revision on patch set " + ps);
+ return null;
+ }
}
- private static RevId toRevId(Ref ref) {
- return new RevId(ref != null && ref.getObjectId() != null
- ? ref.getObjectId().name()
- : "");
- }
-
- private boolean refresh(Change change,
- final PatchSet ps,
- SubmitType type,
- Repository git,
- Map<String, Ref> refs,
- final Ref ref) throws IOException, OrmException {
-
- final boolean mergeable = isMergeable(change, ps, type, git, refs, ref);
-
- Change c = db.get().changes().atomicUpdate(
- change.getId(),
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change c) {
- if (c.getStatus().isOpen()
- && ps.getId().equals(c.currentPatchSetId())) {
- c.setMergeable(mergeable);
- c.setLastSha1MergeTested(toRevId(ref));
- return c;
- } else {
- return null;
- }
- }
- });
- if (reindex && c != null) {
- indexer.index(db.get(), c);
+ private boolean refresh(final Change change, ObjectId commit,
+ final Ref ref, SubmitType type, String strategy, Repository git,
+ Boolean old) throws OrmException, IOException {
+ final boolean mergeable =
+ cache.get(commit, ref, type, strategy, change.getDest(), git, db.get());
+ if (!Objects.equals(mergeable, old)) {
+ // TODO(dborowitz): Include cache info in ETag somehow instead.
+ ChangeUtil.bumpRowVersionNotLastUpdatedOn(change.getId(), db.get());
+ indexer.index(db.get(), change);
}
return mergeable;
}
-
- private boolean isMergeable(Change change,
- final PatchSet ps,
- SubmitType type,
- Repository git,
- Map<String, Ref> refs,
- final Ref ref) {
- RevWalk rw = new RevWalk(git) {
- @Override
- protected CodeReviewCommit createCommit(AnyObjectId id) {
- return new CodeReviewCommit(id);
- }
- };
- try {
- ObjectId id;
- try {
- id = ObjectId.fromString(ps.getRevision().get());
- } catch (IllegalArgumentException e) {
- log.error(String.format(
- "Invalid revision on patch set %d of %d",
- ps.getId().get(),
- change.getId().get()));
- return false;
- }
-
- RevFlag canMerge = rw.newFlag("CAN_MERGE");
- CodeReviewCommit rev = parse(rw, id);
- rev.add(canMerge);
-
- final boolean mergeable;
- if (ref == null || ref.getObjectId() == null) {
- mergeable = true; // Assume yes on new branch.
- } else {
- CodeReviewCommit tip = parse(rw, ref.getObjectId());
- Set<RevCommit> accepted = alreadyAccepted(rw, refs.values());
- accepted.add(tip);
- accepted.addAll(Arrays.asList(rev.getParents()));
- mergeable = submitStrategyFactory.create(
- type,
- db.get(),
- git,
- rw,
- null /*inserter*/,
- canMerge,
- accepted,
- change.getDest()).dryRun(tip, rev);
- }
- return mergeable;
- } catch (MergeException | IOException | NoSuchProjectException e) {
- log.error(String.format(
- "Cannot merge test change %d", change.getId().get()), e);
- return false;
- } finally {
- rw.release();
- }
- }
-
- private static Set<RevCommit> alreadyAccepted(RevWalk rw, Collection<Ref> refs)
- throws MissingObjectException, IOException {
- Set<RevCommit> accepted = Sets.newHashSet();
- for (Ref r : refs) {
- if (r.getName().startsWith(Constants.R_HEADS)
- || r.getName().startsWith(Constants.R_TAGS)) {
- try {
- accepted.add(rw.parseCommit(r.getObjectId()));
- } catch (IncorrectObjectTypeException nonCommit) {
- // Not a commit? Skip over it.
- }
- }
- }
- return accepted;
- }
-
- private static CodeReviewCommit parse(RevWalk rw, ObjectId id)
- throws MissingObjectException, IncorrectObjectTypeException, IOException {
- return (CodeReviewCommit) rw.parseCommit(id);
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index 7322f30..bc2a46e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -37,6 +37,7 @@
import com.google.gerrit.server.git.BanCommit;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidators;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.ReviewerState;
@@ -89,7 +90,7 @@
private final ChangeControl.GenericFactory ctlFactory;
private final GitReferenceUpdated gitRefUpdated;
private final CommitValidators.Factory commitValidatorsFactory;
- private final MergeabilityChecker mergeabilityChecker;
+ private final ChangeIndexer indexer;
private final ReplacePatchSetSender.Factory replacePatchSetFactory;
private final ApprovalsUtil approvalsUtil;
private final ApprovalCopier approvalCopier;
@@ -122,7 +123,7 @@
PatchSetInfoFactory patchSetInfoFactory,
GitReferenceUpdated gitRefUpdated,
CommitValidators.Factory commitValidatorsFactory,
- MergeabilityChecker mergeabilityChecker,
+ ChangeIndexer indexer,
ReplacePatchSetSender.Factory replacePatchSetFactory,
@Assisted Repository git,
@Assisted RevWalk revWalk,
@@ -141,7 +142,7 @@
this.patchSetInfoFactory = patchSetInfoFactory;
this.gitRefUpdated = gitRefUpdated;
this.commitValidatorsFactory = commitValidatorsFactory;
- this.mergeabilityChecker = mergeabilityChecker;
+ this.indexer = indexer;
this.replacePatchSetFactory = replacePatchSetFactory;
this.git = git;
@@ -268,7 +269,6 @@
if (change.getStatus() != Change.Status.DRAFT) {
change.setStatus(Change.Status.NEW);
}
- change.setLastSha1MergeTested(null);
change.setCurrentPatchSet(patchSetInfoFactory.get(commit,
patchSet.getId()));
ChangeUtil.updated(change);
@@ -316,10 +316,7 @@
} finally {
db.rollback();
}
- mergeabilityChecker.newCheck()
- .addChange(updatedChange)
- .reindex()
- .run();
+ indexer.index(db, c);
if (runHooks) {
hooks.doPatchsetCreatedHook(updatedChange, patchSet, db);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
index 88e2137..1e0fb2a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Restore.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.ReplyToChangeSender;
import com.google.gerrit.server.mail.RestoredSender;
import com.google.gerrit.server.notedb.ChangeUpdate;
@@ -54,7 +55,7 @@
private final RestoredSender.Factory restoredSenderFactory;
private final Provider<ReviewDb> dbProvider;
private final ChangeJson json;
- private final MergeabilityChecker mergeabilityChecker;
+ private final ChangeIndexer indexer;
private final ChangeMessagesUtil cmUtil;
private final ChangeUpdate.Factory updateFactory;
@@ -63,14 +64,14 @@
RestoredSender.Factory restoredSenderFactory,
Provider<ReviewDb> dbProvider,
ChangeJson json,
- MergeabilityChecker mergeabilityChecker,
+ ChangeIndexer indexer,
ChangeMessagesUtil cmUtil,
ChangeUpdate.Factory updateFactory) {
this.hooks = hooks;
this.restoredSenderFactory = restoredSenderFactory;
this.dbProvider = dbProvider;
this.json = json;
- this.mergeabilityChecker = mergeabilityChecker;
+ this.indexer = indexer;
this.cmUtil = cmUtil;
this.updateFactory = updateFactory;
}
@@ -121,10 +122,7 @@
}
update.commit();
- CheckedFuture<?, IOException> f = mergeabilityChecker.newCheck()
- .addChange(change)
- .reindex()
- .runAsync();
+ CheckedFuture<?, IOException> f = indexer.indexAsync(change.getId());
try {
ReplyToChangeSender cm = restoredSenderFactory.create(change);
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 8e06229..8d6cef2 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
@@ -71,7 +71,7 @@
import com.google.gerrit.server.avatar.AvatarProvider;
import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.change.ChangeKindCacheImpl;
-import com.google.gerrit.server.change.MergeabilityChecker;
+import com.google.gerrit.server.change.MergeabilityCache;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.ChangeCache;
@@ -94,6 +94,7 @@
import com.google.gerrit.server.git.validators.UploadValidationListener;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.group.GroupModule;
+import com.google.gerrit.server.index.ReindexAfterUpdate;
import com.google.gerrit.server.mail.AddReviewerSender;
import com.google.gerrit.server.mail.CreateChangeSender;
import com.google.gerrit.server.mail.EmailModule;
@@ -166,6 +167,7 @@
install(ConflictsCacheImpl.module());
install(GroupCacheImpl.module());
install(GroupIncludeCacheImpl.module());
+ install(MergeabilityCache.module());
install(PatchListCacheImpl.module());
install(ProjectCacheImpl.module());
install(SectionSortCache.module());
@@ -266,7 +268,7 @@
DynamicSet.setOf(binder(), HeadUpdatedListener.class);
DynamicSet.setOf(binder(), UsageDataPublishedListener.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ChangeCache.class);
- DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(MergeabilityChecker.class);
+ DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterUpdate.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
.to(ProjectConfigEntry.UpdateChecker.class);
DynamicSet.setOf(binder(), ChangeListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
index 9e48e81..848d460 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -96,5 +96,11 @@
public String getNewObjectId() {
return newObjectId;
}
+
+ @Override
+ public String toString() {
+ return String.format("%s[%s,%s: %s -> %s]", getClass().getSimpleName(),
+ projectName, ref, oldObjectId, newObjectId);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index 1c49bc6..7d603b1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -966,10 +966,6 @@
@Override
public Change update(Change c) {
c.setStatus(Change.Status.MERGED);
- // It could be possible that the change being merged
- // has never had its mergeability tested. So we insure
- // merged changes has mergeable field true.
- c.setMergeable(true);
if (!merged.equals(c.currentPatchSetId())) {
// Uncool; the patch set changed after we merged it.
// Go back to the patch set that was actually merged.
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 5824c6f..9eb9945 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
@@ -634,8 +634,11 @@
public ThreeWayMerger newThreeWayMerger(final Repository repo,
final ObjectInserter inserter) {
- return newThreeWayMerger(repo, inserter,
- mergeStrategyName(useContentMerge, useRecursiveMerge));
+ return newThreeWayMerger(repo, inserter, mergeStrategyName());
+ }
+
+ public String mergeStrategyName() {
+ return mergeStrategyName(useContentMerge, useRecursiveMerge);
}
public static String mergeStrategyName(boolean useContentMerge,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 49080f7..b7c7679 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -82,7 +82,6 @@
import com.google.gerrit.server.change.ChangeKind;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.change.ChangesCollection;
-import com.google.gerrit.server.change.MergeabilityChecker;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.config.AllProjectsName;
@@ -300,7 +299,6 @@
private final ListeningExecutorService changeUpdateExector;
private final RequestScopePropagator requestScopePropagator;
private final ChangeIndexer indexer;
- private final MergeabilityChecker mergeabilityChecker;
private final SshInfo sshInfo;
private final AllProjectsName allProjectsName;
private final ReceiveConfig receiveConfig;
@@ -370,7 +368,6 @@
@ChangeUpdateExecutor ListeningExecutorService changeUpdateExector,
final RequestScopePropagator requestScopePropagator,
final ChangeIndexer indexer,
- final MergeabilityChecker mergeabilityChecker,
final SshInfo sshInfo,
final AllProjectsName allProjectsName,
ReceiveConfig config,
@@ -410,7 +407,6 @@
this.changeUpdateExector = changeUpdateExector;
this.requestScopePropagator = requestScopePropagator;
this.indexer = indexer;
- this.mergeabilityChecker = mergeabilityChecker;
this.sshInfo = sshInfo;
this.allProjectsName = allProjectsName;
this.receiveConfig = config;
@@ -2122,7 +2118,6 @@
} else {
change.setStatus(Change.Status.NEW);
}
- change.setLastSha1MergeTested(null);
change.setCurrentPatchSet(info);
final List<String> idList = newCommit.getFooterLines(CHANGE_ID);
@@ -2159,10 +2154,7 @@
if (cmd.getResult() == NOT_ATTEMPTED) {
cmd.execute(rp);
}
- CheckedFuture<?, IOException> f = mergeabilityChecker.newCheck()
- .addChange(change)
- .reindex()
- .runAsync();
+ CheckedFuture<?, IOException> f = indexer.indexAsync(change.getId());
workQueue.getDefaultQueue()
.submit(requestScopePropagator.wrap(new Runnable() {
@Override
@@ -2393,6 +2385,9 @@
closeChange(cmd, PatchSet.Id.fromRef(ref.getName()), c);
closeProgress.update(1);
if (closedChange != null) {
+ if (byKey == null) {
+ byKey = openChangesByKey(branch);
+ }
byKey.remove(closedChange);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index 01db36f..2e2959c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -441,13 +441,25 @@
};
/** Whether the change is mergeable. */
- public static final FieldDef<ChangeData, String> MERGEABLE =
+ @Deprecated
+ public static final FieldDef<ChangeData, String> LEGACY_MERGEABLE =
new FieldDef.Single<ChangeData, String>(
ChangeQueryBuilder.FIELD_MERGEABLE, FieldType.EXACT, false) {
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().isMergeable() ? "1" : null;
+ return input.isMergeable() ? "1" : null;
+ }
+ };
+
+ /** Whether the change is mergeable. */
+ public static final FieldDef<ChangeData, String> MERGEABLE =
+ new FieldDef.Single<ChangeData, String>(
+ "mergeable2", FieldType.EXACT, true) {
+ @Override
+ public String get(ChangeData input, FillArgs args)
+ throws OrmException {
+ return input.isMergeable() ? "1" : "0";
}
};
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
index 437f559..e235379 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndexer.java
@@ -55,8 +55,9 @@
LoggerFactory.getLogger(ChangeIndexer.class);
public interface Factory {
- ChangeIndexer create(ChangeIndex index);
- ChangeIndexer create(IndexCollection indexes);
+ ChangeIndexer create(ListeningExecutorService executor, ChangeIndex index);
+ ChangeIndexer create(ListeningExecutorService executor,
+ IndexCollection indexes);
}
private static final Function<Exception, IOException> MAPPER =
@@ -82,10 +83,10 @@
private final ListeningExecutorService executor;
@AssistedInject
- ChangeIndexer(@IndexExecutor ListeningExecutorService executor,
- SchemaFactory<ReviewDb> schemaFactory,
+ ChangeIndexer(SchemaFactory<ReviewDb> schemaFactory,
ChangeData.Factory changeDataFactory,
ThreadLocalRequestContext context,
+ @Assisted ListeningExecutorService executor,
@Assisted ChangeIndex index) {
this.executor = executor;
this.schemaFactory = schemaFactory;
@@ -96,10 +97,10 @@
}
@AssistedInject
- ChangeIndexer(@IndexExecutor ListeningExecutorService executor,
- SchemaFactory<ReviewDb> schemaFactory,
+ ChangeIndexer(SchemaFactory<ReviewDb> schemaFactory,
ChangeData.Factory changeDataFactory,
ThreadLocalRequestContext context,
+ @Assisted ListeningExecutorService executor,
@Assisted IndexCollection indexes) {
this.executor = executor;
this.schemaFactory = schemaFactory;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
index 031e741..4b7850a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -118,7 +118,7 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
- ChangeField.MERGEABLE);
+ ChangeField.LEGACY_MERGEABLE);
// For upgrade to Lucene 4.6.0 index format only.
static final Schema<ChangeData> V6 = release(V5.getFields().values());
@@ -145,7 +145,7 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
- ChangeField.MERGEABLE);
+ ChangeField.LEGACY_MERGEABLE);
@SuppressWarnings("deprecation")
static final Schema<ChangeData> V8 = release(
@@ -168,7 +168,7 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
- ChangeField.MERGEABLE);
+ ChangeField.LEGACY_MERGEABLE);
@SuppressWarnings("deprecation")
static final Schema<ChangeData> V9 = release(
@@ -192,8 +192,9 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
- ChangeField.MERGEABLE);
+ ChangeField.LEGACY_MERGEABLE);
+ @SuppressWarnings("deprecation")
static final Schema<ChangeData> V10 = release(
ChangeField.LEGACY_ID,
ChangeField.ID,
@@ -215,8 +216,9 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
- ChangeField.MERGEABLE);
+ ChangeField.LEGACY_MERGEABLE);
+ @SuppressWarnings("deprecation")
static final Schema<ChangeData> V11 = release(
ChangeField.LEGACY_ID,
ChangeField.ID,
@@ -238,7 +240,7 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
- ChangeField.MERGEABLE,
+ ChangeField.LEGACY_MERGEABLE,
ChangeField.ADDED,
ChangeField.DELETED,
ChangeField.DELTA);
@@ -246,6 +248,7 @@
// For upgrade to Lucene 4.10.0 index format only.
static final Schema<ChangeData> V12 = release(V11.getFields().values());
+ @SuppressWarnings("deprecation")
static final Schema<ChangeData> V13 = release(
ChangeField.LEGACY_ID,
ChangeField.ID,
@@ -267,6 +270,33 @@
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
+ ChangeField.LEGACY_MERGEABLE,
+ ChangeField.ADDED,
+ ChangeField.DELETED,
+ ChangeField.DELTA,
+ ChangeField.HASHTAG);
+
+ static final Schema<ChangeData> V14 = release(
+ ChangeField.LEGACY_ID,
+ ChangeField.ID,
+ ChangeField.STATUS,
+ ChangeField.PROJECT,
+ ChangeField.PROJECTS,
+ ChangeField.REF,
+ ChangeField.TOPIC,
+ ChangeField.UPDATED,
+ ChangeField.FILE_PART,
+ ChangeField.PATH,
+ ChangeField.OWNER,
+ ChangeField.REVIEWER,
+ ChangeField.COMMIT,
+ ChangeField.TR,
+ ChangeField.LABEL,
+ ChangeField.REVIEWED,
+ ChangeField.COMMIT_MESSAGE,
+ ChangeField.COMMENT,
+ ChangeField.CHANGE,
+ ChangeField.APPROVAL,
ChangeField.MERGEABLE,
ChangeField.ADDED,
ChangeField.DELETED,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexExecutor.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexExecutor.java
index 0a96d1d..eb97fdc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexExecutor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexExecutor.java
@@ -17,6 +17,7 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.server.git.QueueProvider.QueueType;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
@@ -28,4 +29,5 @@
@Retention(RUNTIME)
@BindingAnnotation
public @interface IndexExecutor {
+ QueueType value();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index 3aeeef2..41df287 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.index;
+import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
+import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
+
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.lifecycle.LifecycleModule;
@@ -21,7 +24,6 @@
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.query.change.BasicChangeRewrites;
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
-import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
@@ -48,16 +50,20 @@
}
private final int threads;
- private final ListeningExecutorService indexExecutor;
+ private final ListeningExecutorService interactiveExecutor;
+ private final ListeningExecutorService batchExecutor;
public IndexModule(int threads) {
this.threads = threads;
- this.indexExecutor = null;
+ this.interactiveExecutor = null;
+ this.batchExecutor = null;
}
- public IndexModule(ListeningExecutorService indexExecutor) {
+ public IndexModule(ListeningExecutorService interactiveExecutor,
+ ListeningExecutorService batchExecutor) {
this.threads = -1;
- this.indexExecutor = indexExecutor;
+ this.interactiveExecutor = interactiveExecutor;
+ this.batchExecutor = batchExecutor;
}
@Override
@@ -67,49 +73,61 @@
bind(IndexCollection.class);
listener().to(IndexCollection.class);
factory(ChangeIndexer.Factory.class);
-
- if (indexExecutor != null) {
- bind(ListeningExecutorService.class)
- .annotatedWith(IndexExecutor.class)
- .toInstance(indexExecutor);
- } else {
- install(new IndexExecutorModule(threads));
- }
}
@Provides
+ @Singleton
ChangeIndexer getChangeIndexer(
+ @IndexExecutor(INTERACTIVE) ListeningExecutorService executor,
ChangeIndexer.Factory factory,
IndexCollection indexes) {
- return factory.create(indexes);
+ // Bind default indexer to interactive executor; callers who need a
+ // different executor can use the factory directly.
+ return factory.create(executor, indexes);
}
- private static class IndexExecutorModule extends AbstractModule {
- private final int threads;
-
- private IndexExecutorModule(int threads) {
- this.threads = threads;
+ @Provides
+ @Singleton
+ @IndexExecutor(INTERACTIVE)
+ ListeningExecutorService getInteractiveIndexExecutor(
+ @GerritServerConfig Config config,
+ WorkQueue workQueue) {
+ if (interactiveExecutor != null) {
+ return interactiveExecutor;
}
-
- @Override
- public void configure() {
+ int threads = this.threads;
+ if (threads <= 0) {
+ threads = config.getInt("index", null, "threads", 0);
}
-
- @Provides
- @Singleton
- @IndexExecutor
- ListeningExecutorService getIndexExecutor(
- @GerritServerConfig Config config,
- WorkQueue workQueue) {
- int threads = this.threads;
- if (threads <= 0) {
- threads = config.getInt("index", null, "threads", 0);
- }
- if (threads <= 0) {
- return MoreExecutors.newDirectExecutorService();
- }
- return MoreExecutors.listeningDecorator(
- workQueue.createQueue(threads, "index"));
+ if (threads <= 0) {
+ threads =
+ config.getInt("changeMerge", null, "interactiveThreadPoolSize", 0);
}
+ if (threads <= 0) {
+ return MoreExecutors.newDirectExecutorService();
+ }
+ return MoreExecutors.listeningDecorator(
+ workQueue.createQueue(threads, "Index-Interactive"));
+ }
+
+ @Provides
+ @Singleton
+ @IndexExecutor(BATCH)
+ ListeningExecutorService getBatchIndexExecutor(
+ @IndexExecutor(INTERACTIVE) ListeningExecutorService interactive,
+ @GerritServerConfig Config config,
+ WorkQueue workQueue) {
+ if (batchExecutor != null) {
+ return batchExecutor;
+ }
+ int threads = config.getInt("index", null, "batchThreads", 0);
+ if (threads <= 0) {
+ threads = config.getInt("changeMerge", null, "threadPoolSize", 0);
+ }
+ if (threads <= 0) {
+ return interactive;
+ }
+ return MoreExecutors.listeningDecorator(
+ workQueue.createQueue(threads, "Index-Batch"));
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ReindexAfterUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ReindexAfterUpdate.java
new file mode 100644
index 0000000..13d37fc
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ReindexAfterUpdate.java
@@ -0,0 +1,164 @@
+// 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.
+// 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.index;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+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.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.git.QueueProvider.QueueType;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+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.util.Providers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
+ private static final Logger log = LoggerFactory
+ .getLogger(ReindexAfterUpdate.class);
+
+ private final ThreadLocalRequestContext tl;
+ private final SchemaFactory<ReviewDb> schemaFactory;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final ChangeIndexer.Factory indexerFactory;
+ private final IndexCollection indexes;
+ private final ListeningExecutorService executor;
+
+ @Inject
+ ReindexAfterUpdate(
+ ThreadLocalRequestContext tl,
+ SchemaFactory<ReviewDb> schemaFactory,
+ IdentifiedUser.GenericFactory userFactory,
+ ChangeIndexer.Factory indexerFactory,
+ IndexCollection indexes,
+ @IndexExecutor(QueueType.BATCH) ListeningExecutorService executor) {
+ this.tl = tl;
+ this.schemaFactory = schemaFactory;
+ this.userFactory = userFactory;
+ this.indexerFactory = indexerFactory;
+ this.indexes = indexes;
+ this.executor = executor;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(final Event event) {
+ Futures.transform(
+ executor.submit(new GetChanges(event)),
+ new AsyncFunction<List<Change>, List<Void>>() {
+ @Override
+ public ListenableFuture<List<Void>> apply(List<Change> changes) {
+ List<ListenableFuture<Void>> result =
+ Lists.newArrayListWithCapacity(changes.size());
+ for (Change c : changes) {
+ result.add(executor.submit(new Index(event, c)));
+ }
+ return Futures.allAsList(result);
+ }
+ });
+ }
+
+ private abstract class Task<V> implements Callable<V> {
+ protected ReviewDb db;
+ protected Event event;
+
+ protected Task(Event event) {
+ this.event = event;
+ }
+
+ @Override
+ public final V call() throws Exception {
+ try {
+ db = schemaFactory.open();
+ return impl();
+ } catch (Exception e) {
+ log.error("Failed to reindex changes after " + event, e);
+ throw e;
+ } finally {
+ if (db != null) {
+ db.close();
+ }
+ }
+ }
+
+ protected abstract V impl() throws Exception;
+ }
+
+ private class GetChanges extends Task<List<Change>> {
+ private GetChanges(Event event) {
+ super(event);
+ }
+
+ @Override
+ protected List<Change> impl() throws OrmException {
+ String ref = event.getRefName();
+ Project.NameKey project = new Project.NameKey(event.getProjectName());
+ if (ref.equals(RefNames.REFS_CONFIG)) {
+ return db.changes().byProjectOpenAll(project).toList();
+ } else {
+ return db.changes().byBranchOpenAll(new Branch.NameKey(project, ref))
+ .toList();
+ }
+ }
+ }
+
+ private class Index extends Task<Void> {
+ private final Change change;
+
+ Index(Event event, Change change) {
+ super(event);
+ this.change = change;
+ }
+
+ @Override
+ protected Void impl() throws IOException {
+ RequestContext context = new RequestContext() {
+ @Override
+ public CurrentUser getCurrentUser() {
+ return userFactory.create(change.getOwner());
+ }
+
+ @Override
+ public Provider<ReviewDb> getReviewDbProvider() {
+ return Providers.of(db);
+ }
+ };
+ RequestContext old = tl.setContext(context);
+ try {
+ indexerFactory.create(executor, indexes).index(db, change);
+ return null;
+ } finally {
+ tl.setContext(old);
+ }
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/SiteIndexer.java
similarity index 90%
rename from gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/index/SiteIndexer.java
index 9a992cf..8b029dd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/SiteIndexer.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.index;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
import static org.eclipse.jgit.lib.RefDatabase.ALL;
import com.google.common.base.Stopwatch;
@@ -27,11 +29,9 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.MergeabilityChecker;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
@@ -65,6 +65,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -75,9 +76,9 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-public class ChangeBatchIndexer {
+public class SiteIndexer {
private static final Logger log =
- LoggerFactory.getLogger(ChangeBatchIndexer.class);
+ LoggerFactory.getLogger(SiteIndexer.class);
public static class Result {
private final long elapsedNanos;
@@ -114,40 +115,52 @@
private final GitRepositoryManager repoManager;
private final ListeningExecutorService executor;
private final ChangeIndexer.Factory indexerFactory;
- private final MergeabilityChecker mergeabilityChecker;
private final ThreeWayMergeStrategy mergeStrategy;
+ private int numChanges = -1;
+ private OutputStream progressOut = NullOutputStream.INSTANCE;
+ private PrintWriter verboseWriter =
+ new PrintWriter(NullOutputStream.INSTANCE);
+
@Inject
- ChangeBatchIndexer(SchemaFactory<ReviewDb> schemaFactory,
+ SiteIndexer(SchemaFactory<ReviewDb> schemaFactory,
ChangeData.Factory changeDataFactory,
GitRepositoryManager repoManager,
- @IndexExecutor ListeningExecutorService executor,
+ @IndexExecutor(BATCH) ListeningExecutorService executor,
ChangeIndexer.Factory indexerFactory,
- @GerritServerConfig Config config,
- @Nullable MergeabilityChecker mergeabilityChecker) {
+ @GerritServerConfig Config config) {
this.schemaFactory = schemaFactory;
this.changeDataFactory = changeDataFactory;
this.repoManager = repoManager;
this.executor = executor;
this.indexerFactory = indexerFactory;
- this.mergeabilityChecker = mergeabilityChecker;
this.mergeStrategy = MergeUtil.getMergeStrategy(config);
}
- public Result indexAll(ChangeIndex index, Iterable<Project.NameKey> projects,
- int numProjects, int numChanges, OutputStream progressOut,
- OutputStream verboseOut) {
- if (progressOut == null) {
- progressOut = NullOutputStream.INSTANCE;
- }
- PrintWriter verboseWriter = verboseOut != null ? new PrintWriter(verboseOut)
- : null;
+ public SiteIndexer setNumChanges(int num) {
+ numChanges = num;
+ return this;
+ }
+ public SiteIndexer setProgressOut(OutputStream out) {
+ progressOut = checkNotNull(out);
+ return this;
+ }
+
+ public SiteIndexer setVerboseOut(OutputStream out) {
+ verboseWriter = new PrintWriter(checkNotNull(out));
+ return this;
+ }
+
+ public Result indexAll(ChangeIndex index,
+ Iterable<Project.NameKey> projects) {
Stopwatch sw = Stopwatch.createStarted();
final MultiProgressMonitor mpm =
new MultiProgressMonitor(progressOut, "Reindexing changes");
final Task projTask = mpm.beginSubTask("projects",
- numProjects >= 0 ? numProjects : MultiProgressMonitor.UNKNOWN);
+ (projects instanceof Collection)
+ ? ((Collection<?>) projects).size()
+ : MultiProgressMonitor.UNKNOWN);
final Task doneTask = mpm.beginSubTask(null,
numChanges >= 0 ? numChanges : MultiProgressMonitor.UNKNOWN);
final Task failedTask = mpm.beginSubTask("failed", MultiProgressMonitor.UNKNOWN);
@@ -156,11 +169,8 @@
final AtomicBoolean ok = new AtomicBoolean(true);
for (final Project.NameKey project : projects) {
- if (!updateMergeable(project)) {
- ok.set(false);
- }
final ListenableFuture<?> future = executor.submit(reindexProject(
- indexerFactory.create(index), project, doneTask, failedTask,
+ indexerFactory.create(executor, index), project, doneTask, failedTask,
verboseWriter));
futures.add(future);
future.addListener(new Runnable() {
@@ -214,18 +224,6 @@
return new Result(sw, ok.get(), doneTask.getCount(), failedTask.getCount());
}
- private boolean updateMergeable(Project.NameKey project) {
- if (mergeabilityChecker != null) {
- try {
- mergeabilityChecker.newCheck().addProject(project).run();
- } catch (IOException e) {
- log.error("Error in mergeability checker", e);
- return false;
- }
- }
- return true;
- }
-
private Callable<Void> reindexProject(final ChangeIndexer indexer,
final Project.NameKey project, final Task done, final Task failed,
final PrintWriter verboseWriter) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
index 5f2ffcb..b587791 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -121,7 +121,7 @@
try {
patchList = getPatchList();
} catch (PatchListNotAvailableException e) {
- patchList = null;
+ log.error("Failed to get patch list", e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
index 4c5ca27..87afc33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BasicChangeRewrites.java
@@ -26,11 +26,11 @@
public class BasicChangeRewrites extends QueryRewriter<ChangeData> {
private static final ChangeQueryBuilder BUILDER = new ChangeQueryBuilder(
- new ChangeQueryBuilder.Arguments( //
- new InvalidProvider<ReviewDb>(), //
- new InvalidProvider<ChangeQueryRewriter>(), //
- null, null, null, null, null, null, null, //
- null, null, null, null, null, null, null, null, null, null, null),
+ new ChangeQueryBuilder.Arguments(
+ new InvalidProvider<ReviewDb>(),
+ new InvalidProvider<ChangeQueryRewriter>(),
+ null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null),
null);
private static final QueryRewriter.Definition<ChangeData, BasicChangeRewrites> mydef =
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 9707733..d45a58e 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
@@ -23,6 +23,7 @@
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
@@ -36,7 +37,9 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.change.MergeabilityCache;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerState;
@@ -46,6 +49,8 @@
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.assistedinject.Assisted;
@@ -55,6 +60,7 @@
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.FooterLine;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -154,8 +160,8 @@
* @return instance for testing.
*/
static ChangeData createForTest(Change.Id id, int currentPatchSetId) {
- ChangeData cd = new ChangeData(null, null, null, null, null,
- null, null, null, null, null, id);
+ ChangeData cd = new ChangeData(null, null, null, null, null, null, null,
+ null, null, null, null, null, null, id);
cd.currentPatchSet = new PatchSet(new PatchSet.Id(id, currentPatchSetId));
return cd;
}
@@ -164,12 +170,15 @@
private final GitRepositoryManager repoManager;
private final ChangeControl.GenericFactory changeControlFactory;
private final IdentifiedUser.GenericFactory userFactory;
+ private final ProjectCache projectCache;
+ private final MergeUtil.Factory mergeUtilFactory;
private final ChangeNotes.Factory notesFactory;
private final ApprovalsUtil approvalsUtil;
private final ChangeMessagesUtil cmUtil;
private final PatchLineCommentsUtil plcUtil;
private final PatchListCache patchListCache;
private final NotesMigration notesMigration;
+ private final MergeabilityCache mergeabilityCache;
private final Change.Id legacyId;
private ChangeDataSource returnedBySource;
private Change change;
@@ -187,30 +196,37 @@
private List<ChangeMessage> messages;
private List<SubmitRecord> submitRecords;
private ChangedLines changedLines;
+ private Boolean mergeable;
@AssistedInject
private ChangeData(
GitRepositoryManager repoManager,
ChangeControl.GenericFactory changeControlFactory,
IdentifiedUser.GenericFactory userFactory,
+ ProjectCache projectCache,
+ MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
ChangeMessagesUtil cmUtil,
PatchLineCommentsUtil plcUtil,
PatchListCache patchListCache,
NotesMigration notesMigration,
+ MergeabilityCache mergeabilityCache,
@Assisted ReviewDb db,
@Assisted Change.Id id) {
this.db = db;
this.repoManager = repoManager;
this.changeControlFactory = changeControlFactory;
this.userFactory = userFactory;
+ this.projectCache = projectCache;
+ this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
this.approvalsUtil = approvalsUtil;
this.cmUtil = cmUtil;
this.plcUtil = plcUtil;
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
+ this.mergeabilityCache = mergeabilityCache;
legacyId = id;
}
@@ -219,24 +235,30 @@
GitRepositoryManager repoManager,
ChangeControl.GenericFactory changeControlFactory,
IdentifiedUser.GenericFactory userFactory,
+ ProjectCache projectCache,
+ MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
ChangeMessagesUtil cmUtil,
PatchLineCommentsUtil plcUtil,
PatchListCache patchListCache,
NotesMigration notesMigration,
+ MergeabilityCache mergeabilityCache,
@Assisted ReviewDb db,
@Assisted Change c) {
this.db = db;
this.repoManager = repoManager;
this.changeControlFactory = changeControlFactory;
this.userFactory = userFactory;
+ this.projectCache = projectCache;
+ this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
this.approvalsUtil = approvalsUtil;
this.cmUtil = cmUtil;
this.plcUtil = plcUtil;
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
+ this.mergeabilityCache = mergeabilityCache;
legacyId = c.getId();
change = c;
}
@@ -246,24 +268,30 @@
GitRepositoryManager repoManager,
ChangeControl.GenericFactory changeControlFactory,
IdentifiedUser.GenericFactory userFactory,
+ ProjectCache projectCache,
+ MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
ChangeMessagesUtil cmUtil,
PatchLineCommentsUtil plcUtil,
PatchListCache patchListCache,
NotesMigration notesMigration,
+ MergeabilityCache mergeabilityCache,
@Assisted ReviewDb db,
@Assisted ChangeControl c) {
this.db = db;
this.repoManager = repoManager;
this.changeControlFactory = changeControlFactory;
this.userFactory = userFactory;
+ this.projectCache = projectCache;
+ this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
this.approvalsUtil = approvalsUtil;
this.cmUtil = cmUtil;
this.plcUtil = plcUtil;
this.patchListCache = patchListCache;
this.notesMigration = notesMigration;
+ this.mergeabilityCache = mergeabilityCache;
legacyId = c.getChange().getId();
change = c.getChange();
changeControl = c;
@@ -555,6 +583,45 @@
return submitRecords;
}
+ public void setMergeable(boolean mergeable) {
+ this.mergeable = mergeable;
+ }
+
+ public boolean isMergeable() throws OrmException {
+ if (mergeable == null) {
+ Change c = change();
+ if (c.getStatus() == Change.Status.MERGED) {
+ mergeable = true;
+ } else {
+ PatchSet ps = currentPatchSet();
+ Repository repo = null;
+ try {
+ repo = repoManager.openRepository(c.getProject());
+ Ref ref = repo.getRef(c.getDest().get());
+ SubmitTypeRecord rec = new SubmitRuleEvaluator(this)
+ .getSubmitType();
+ if (rec.status != SubmitTypeRecord.Status.OK) {
+ throw new OrmException(
+ "Error in mergeability check: " + rec.errorMessage);
+ }
+ String mergeStrategy = mergeUtilFactory
+ .create(projectCache.get(c.getProject()))
+ .mergeStrategyName();
+ mergeable = mergeabilityCache.get(
+ ObjectId.fromString(ps.getRevision().get()),
+ ref, rec.type, mergeStrategy, c.getDest(), repo, db);
+ } catch (IOException e) {
+ throw new OrmException(e);
+ } finally {
+ if (repo != null) {
+ repo.close();
+ }
+ }
+ }
+ }
+ return mergeable;
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(this).addValue(getId()).toString();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 64ba2e9..5c39df8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -37,6 +37,7 @@
import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
import com.google.gerrit.server.index.ChangeField;
import com.google.gerrit.server.index.ChangeIndex;
+import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.IndexCollection;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.patch.PatchListCache;
@@ -146,6 +147,7 @@
final CapabilityControl.Factory capabilityControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory;
final ChangeData.Factory changeDataFactory;
+ final FieldDef.FillArgs fillArgs;
final PatchLineCommentsUtil plcUtil;
final AccountResolver accountResolver;
final GroupBackend groupBackend;
@@ -169,6 +171,7 @@
CapabilityControl.Factory capabilityControlFactory,
ChangeControl.GenericFactory changeControlGenericFactory,
ChangeData.Factory changeDataFactory,
+ FieldDef.FillArgs fillArgs,
PatchLineCommentsUtil plcUtil,
AccountResolver accountResolver,
GroupBackend groupBackend,
@@ -189,6 +192,7 @@
this.capabilityControlFactory = capabilityControlFactory;
this.changeControlGenericFactory = changeControlGenericFactory;
this.changeDataFactory = changeDataFactory;
+ this.fillArgs = fillArgs;
this.plcUtil = plcUtil;
this.accountResolver = accountResolver;
this.groupBackend = groupBackend;
@@ -326,7 +330,7 @@
}
if ("mergeable".equalsIgnoreCase(value)) {
- return new IsMergeablePredicate();
+ return new IsMergeablePredicate(schema(args.indexes), args.fillArgs);
}
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java
index 787b90e..6ef4ab6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergeablePredicate.java
@@ -14,20 +14,39 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.reviewdb.client.Change;
+import static com.google.gerrit.server.index.ChangeField.MERGEABLE;
+
import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.FieldDef.FillArgs;
import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.index.Schema;
import com.google.gwtorm.server.OrmException;
class IsMergeablePredicate extends IndexPredicate<ChangeData> {
- IsMergeablePredicate() {
- super(ChangeField.MERGEABLE, "1");
+ @SuppressWarnings("deprecation")
+ static FieldDef<ChangeData, ?> mergeableField(Schema<ChangeData> schema) {
+ if (schema == null) {
+ return ChangeField.LEGACY_MERGEABLE;
+ }
+ FieldDef<ChangeData, ?> f = schema.getFields().get(MERGEABLE.getName());
+ if (f != null) {
+ return f;
+ }
+ return schema.getFields().get(ChangeField.LEGACY_MERGEABLE.getName());
+ }
+
+ private final FillArgs args;
+
+ IsMergeablePredicate(Schema<ChangeData> schema,
+ FillArgs args) {
+ super(mergeableField(schema), "1");
+ this.args = args;
}
@Override
public boolean match(ChangeData object) throws OrmException {
- Change c = object.change();
- return c != null && c.isMergeable();
+ return getValue().equals(getField().get(object, args));
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 44f0798..470fb14 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_99> C = Schema_99.class;
+ public static final Class<Schema_100> C = Schema_100.class;
public static class Module extends AbstractModule {
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_100.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_100.java
new file mode 100644
index 0000000..0902194
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_100.java
@@ -0,0 +1,27 @@
+// 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.
+// 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.schema;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class Schema_100 extends SchemaVersion {
+ @Inject
+ Schema_100(Provider<Schema_99> prior) {
+ super(prior);
+ }
+
+ // No database migration; merges are rechecked on reindex.
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_59.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_59.java
index d90ecc1..1fdc182 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_59.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_59.java
@@ -14,29 +14,14 @@
package com.google.gerrit.server.schema;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.util.List;
-
public class Schema_59 extends SchemaVersion {
@Inject
Schema_59(Provider<Schema_58> prior) {
super(prior);
}
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException,
- SQLException {
- List<Change> allChanges = db.changes().all().toList();
- for (Change change : allChanges) {
- change.setMergeable(true);
- change.setLastSha1MergeTested(null);
- }
- db.changes().update(allChanges);
- }
-}
\ No newline at end of file
+ // Don't migrate columns; they are removed in Schema_100.
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
index 7090f0d..c6425f5 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeQueryBuilder.java
@@ -26,8 +26,8 @@
new FakeQueryBuilder.Definition<>(
FakeQueryBuilder.class),
new ChangeQueryBuilder.Arguments(null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, indexes, null,
- null, null, null),
+ null, null, null, null, null, null, null, null, null, null, indexes,
+ null, null, null, null),
null);
}
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 a44666b..9e58bea 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
@@ -40,6 +40,7 @@
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.change.MergeabilityCache;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
@@ -50,6 +51,7 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.patch.PatchListCache;
@@ -58,6 +60,7 @@
import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.inject.Guice;
import com.google.inject.Injector;
+import com.google.inject.Provider;
import com.google.inject.util.Providers;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -253,18 +256,20 @@
};
Injector injector = Guice.createInjector(new FactoryModule() {
+ @SuppressWarnings({"rawtypes", "unchecked"})
@Override
protected void configure() {
+ Provider nullProvider = Providers.of(null);
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(
new Config());
- bind(ReviewDb.class).toProvider(Providers.<ReviewDb> of(null));
+ bind(ReviewDb.class).toProvider(nullProvider);
bind(GitRepositoryManager.class).toInstance(repoManager);
- bind(PatchListCache.class)
- .toProvider(Providers.<PatchListCache> of(null));
+ bind(PatchListCache.class).toProvider(nullProvider);
factory(CapabilityControl.Factory.class);
factory(ChangeControl.AssistedFactory.class);
factory(ChangeData.Factory.class);
+ factory(MergeUtil.Factory.class);
bind(ProjectCache.class).toInstance(projectCache);
bind(AccountCache.class).toInstance(new FakeAccountCache());
bind(GroupBackend.class).to(SystemGroupBackend.class);
@@ -275,6 +280,7 @@
bind(String.class).annotatedWith(AnonymousCowardName.class)
.toProvider(AnonymousCowardNameProvider.class);
bind(ChangeKindCache.class).to(ChangeKindCacheImpl.NoCache.class);
+ bind(MergeabilityCache.bindingKey()).toProvider(nullProvider);
}
});
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
index 5e9858c..0053098 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -26,7 +26,6 @@
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
-import com.google.gerrit.server.change.MergeabilityChecksExecutorModule;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.AllUsersName;
@@ -168,7 +167,6 @@
install(new DefaultCacheFactory.Module());
install(new SmtpEmailSender.Module());
install(new SignedTokenEmailTokenVerifier.Module());
- install(new MergeabilityChecksExecutorModule());
IndexType indexType = null;
try {
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index aba9013..253299d 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -27,7 +27,6 @@
import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.server.account.InternalAccountDirectory;
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
-import com.google.gerrit.server.change.MergeabilityChecksExecutorModule;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.AuthConfigModule;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
@@ -278,7 +277,6 @@
modules.add(new WorkQueue.Module());
modules.add(new ChangeHookRunner.Module());
modules.add(new ReceiveCommitsExecutorModule());
- modules.add(new MergeabilityChecksExecutorModule());
modules.add(new IntraLineWorkerPool.Module());
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new InternalAccountDirectory.Module());
diff --git a/lib/asciidoctor/java/DocIndexer.java b/lib/asciidoctor/java/DocIndexer.java
index 4736b0b..27bf30a 100644
--- a/lib/asciidoctor/java/DocIndexer.java
+++ b/lib/asciidoctor/java/DocIndexer.java
@@ -51,7 +51,7 @@
import java.util.zip.ZipOutputStream;
public class DocIndexer {
- private static final Version LUCENE_VERSION = Version.LUCENE_4_10_0;
+ private static final Version LUCENE_VERSION = Version.LUCENE_4_10_1;
private static final Pattern SECTION_HEADER = Pattern.compile("^=+ (.*)");
@Option(name = "-o", usage = "output JAR file")
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK
index dd148b1..df14528 100644
--- a/lib/lucene/BUCK
+++ b/lib/lucene/BUCK
@@ -1,11 +1,11 @@
include_defs('//lib/maven.defs')
-VERSION = '4.10.0'
+VERSION = '4.10.1'
maven_jar(
name = 'core',
id = 'org.apache.lucene:lucene-core:' + VERSION,
- sha1 = 'a4ceea9a80e81fe84e81fe4fccce9e9930dc703a',
+ sha1 = '4ff28101d9de465b7f3cf59d7bc2892c1c118b4b',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@@ -16,7 +16,7 @@
maven_jar(
name = 'analyzers-common',
id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION,
- sha1 = '912962d436d9851dc90091e48251c802d3b65941',
+ sha1 = '6491c6019c32e7c4f7674f238d5beaa84d3108a6',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@@ -27,6 +27,6 @@
maven_jar(
name = 'query-parser',
id = 'org.apache.lucene:lucene-queryparser:' + VERSION,
- sha1 = '7a00eb4b97a6cb7a5a29957b62c7f002dd71b7ff',
+ sha1 = '0174ffd89d5289037ae24759f38111285b98636d',
license = 'Apache2.0',
)