Merge changes Ie5d1b547,Ib623ce65,Ie1ab3bfa,I66ed2bd8,Ib320b0ed, ...

* changes:
  MergeOp: Use factories of the subscription components
  MergeOp: Pass the specific objects, instead of submoduleOp
  SubmoduleOp: Move subscription graph instantiation to its own factory
  UpdateOrderCalculator: Move update order calculation out of submoduleOp
  SubmoduleCommits: Define its own guice factory
  GitlinkOp: Move it to its own class and factory
This commit is contained in:
Alice Kober-Sotzek
2020-09-22 07:51:17 +00:00
committed by Gerrit Code Review
9 changed files with 306 additions and 138 deletions

View File

@@ -187,9 +187,11 @@ import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.RulesCache;
import com.google.gerrit.server.rules.SubmitRule;
import com.google.gerrit.server.ssh.SshAddressesModule;
import com.google.gerrit.server.submit.ConfiguredSubscriptionGraphFactory;
import com.google.gerrit.server.submit.GitModules;
import com.google.gerrit.server.submit.MergeSuperSetComputation;
import com.google.gerrit.server.submit.SubmitStrategy;
import com.google.gerrit.server.submit.SubscriptionGraph;
import com.google.gerrit.server.tools.ToolsCatalog;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.util.IdGenerator;
@@ -453,6 +455,7 @@ public class GerritGlobalModule extends FactoryModule {
factory(VersionedAuthorizedKeys.Factory.class);
bind(AccountManager.class);
bind(SubscriptionGraph.Factory.class).to(ConfiguredSubscriptionGraphFactory.class);
bind(new TypeLiteral<List<CommentLinkInfo>>() {}).toProvider(CommentLinkProvider.class);
DynamicSet.bind(binder(), GerritConfigListener.class).to(CommentLinkProvider.class);

View File

@@ -0,0 +1,54 @@
// Copyright (C) 2020 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.submit;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
/**
* Wrap a (@link {@link SubscriptionGraph.Factory} to honor the gerrit configuration.
*
* <p>If superproject subscriptions are disabled in the conf, return an empty graph.
*/
public class ConfiguredSubscriptionGraphFactory implements SubscriptionGraph.Factory {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final SubscriptionGraph.Factory subscriptionGraphFactory;
private final Config cfg;
@Inject
ConfiguredSubscriptionGraphFactory(
@VanillaSubscriptionGraph SubscriptionGraph.Factory subscriptionGraphFactory,
@GerritServerConfig Config cfg) {
this.subscriptionGraphFactory = subscriptionGraphFactory;
this.cfg = cfg;
}
@Override
public SubscriptionGraph compute(Set<BranchNameKey> updatedBranches, MergeOpRepoManager orm)
throws SubmoduleConflictException {
if (cfg.getBoolean("submodule", "enableSuperProjectSubscriptions", true)) {
return subscriptionGraphFactory.compute(updatedBranches, orm);
} else {
logger.atFine().log("Updating superprojects disabled");
return SubscriptionGraph.createEmptyGraph(ImmutableSet.copyOf(updatedBranches));
}
}
}

View File

@@ -0,0 +1,64 @@
// Copyright (C) 2020 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.submit;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.update.RepoOnlyOp;
import java.util.Collection;
import java.util.Optional;
/** Only used for branches without code review changes */
public class GitlinkOp implements RepoOnlyOp {
static class Factory {
private SubmoduleCommits submoduleCommits;
private SubscriptionGraph subscriptionGraph;
Factory(SubmoduleCommits submoduleCommits, SubscriptionGraph subscriptionGraph) {
this.submoduleCommits = submoduleCommits;
this.subscriptionGraph = subscriptionGraph;
}
GitlinkOp create(BranchNameKey branch) {
return new GitlinkOp(branch, submoduleCommits, subscriptionGraph.getSubscriptions(branch));
}
}
private final BranchNameKey branch;
private final SubmoduleCommits commitHelper;
private final Collection<SubmoduleSubscription> branchTargets;
GitlinkOp(
BranchNameKey branch,
SubmoduleCommits commitHelper,
Collection<SubmoduleSubscription> branchTargets) {
this.branch = branch;
this.commitHelper = commitHelper;
this.branchTargets = branchTargets;
}
@Override
public void updateRepo(RepoContext ctx) throws Exception {
Optional<CodeReviewCommit> commit = commitHelper.composeGitlinksCommit(branch, branchTargets);
if (commit.isPresent()) {
CodeReviewCommit c = commit.get();
ctx.addRefUpdate(c.getParent(0), c, branch.branch());
commitHelper.addBranchTip(branch, c);
}
}
}

View File

@@ -226,7 +226,9 @@ public class MergeOp implements AutoCloseable {
private final MergeValidators.Factory mergeValidatorsFactory;
private final Provider<InternalChangeQuery> queryProvider;
private final SubmitStrategyFactory submitStrategyFactory;
private final SubscriptionGraph.Factory subscriptionGraphFactory;
private final SubmoduleOp.Factory subOpFactory;
private final SubmoduleCommits.Factory submoduleCommitsFactory;
private final Provider<MergeOpRepoManager> ormProvider;
private final NotifyResolver notifyResolver;
private final RetryHelper retryHelper;
@@ -256,6 +258,8 @@ public class MergeOp implements AutoCloseable {
MergeValidators.Factory mergeValidatorsFactory,
Provider<InternalChangeQuery> queryProvider,
SubmitStrategyFactory submitStrategyFactory,
SubmoduleCommits.Factory submoduleCommitsFactory,
SubscriptionGraph.Factory subscriptionGraphFactory,
SubmoduleOp.Factory subOpFactory,
Provider<MergeOpRepoManager> ormProvider,
NotifyResolver notifyResolver,
@@ -269,6 +273,8 @@ public class MergeOp implements AutoCloseable {
this.mergeValidatorsFactory = mergeValidatorsFactory;
this.queryProvider = queryProvider;
this.submitStrategyFactory = submitStrategyFactory;
this.submoduleCommitsFactory = submoduleCommitsFactory;
this.subscriptionGraphFactory = subscriptionGraphFactory;
this.subOpFactory = subOpFactory;
this.ormProvider = ormProvider;
this.notifyResolver = notifyResolver;
@@ -605,9 +611,13 @@ public class MergeOp implements AutoCloseable {
commitStatus.maybeFailVerbose();
try {
SubmoduleOp submoduleOp = subOpFactory.create(branches, orm);
List<SubmitStrategy> strategies = getSubmitStrategies(toSubmit, submoduleOp, dryrun);
this.allProjects = submoduleOp.getProjectsInOrder();
SubscriptionGraph subscriptionGraph = subscriptionGraphFactory.compute(branches, orm);
SubmoduleCommits submoduleCommits = submoduleCommitsFactory.create(orm);
UpdateOrderCalculator updateOrderCalculator = new UpdateOrderCalculator(subscriptionGraph);
List<SubmitStrategy> strategies =
getSubmitStrategies(
toSubmit, updateOrderCalculator, submoduleCommits, subscriptionGraph, dryrun);
this.allProjects = updateOrderCalculator.getProjectsInOrder();
try {
BatchUpdate.execute(
orm.batchUpdates(allProjects),
@@ -658,12 +668,19 @@ public class MergeOp implements AutoCloseable {
}
private List<SubmitStrategy> getSubmitStrategies(
Map<BranchNameKey, BranchBatch> toSubmit, SubmoduleOp submoduleOp, boolean dryrun)
Map<BranchNameKey, BranchBatch> toSubmit,
UpdateOrderCalculator updateOrderCalculator,
SubmoduleCommits submoduleCommits,
SubscriptionGraph subscriptionGraph,
boolean dryrun)
throws IntegrationConflictException, NoSuchProjectException, IOException {
List<SubmitStrategy> strategies = new ArrayList<>();
Set<BranchNameKey> allBranches = submoduleOp.getBranchesInOrder();
Set<BranchNameKey> allBranches = updateOrderCalculator.getBranchesInOrder();
Set<CodeReviewCommit> allCommits =
toSubmit.values().stream().map(BranchBatch::commits).flatMap(Set::stream).collect(toSet());
GitlinkOp.Factory gitlinkOpFactory = new GitlinkOp.Factory(submoduleCommits, subscriptionGraph);
for (BranchNameKey branch : allBranches) {
OpenRepo or = orm.getRepo(branch.project());
if (toSubmit.containsKey(branch)) {
@@ -688,19 +705,19 @@ public class MergeOp implements AutoCloseable {
commitStatus,
submissionId,
submitInput,
submoduleOp.getSubmoduleCommits(),
submoduleOp.getSubscriptionGraph(),
submoduleCommits,
subscriptionGraph,
dryrun);
strategies.add(strategy);
strategy.addOps(or.getUpdate(), commitsToSubmit);
if (submitting.submitType().equals(SubmitType.FAST_FORWARD_ONLY)
&& submoduleOp.getSubscriptionGraph().hasSubscription(branch)) {
submoduleOp.addOp(or.getUpdate(), branch);
&& subscriptionGraph.hasSubscription(branch)) {
or.getUpdate().addRepoOnlyOp(gitlinkOpFactory.create(branch));
}
} else {
// no open change for this branch
// add submodule triggered op into BatchUpdate
submoduleOp.addOp(or.getUpdate(), branch);
or.getUpdate().addRepoOnlyOp(gitlinkOpFactory.create(branch));
}
}
return strategies;

View File

@@ -21,10 +21,15 @@ import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.VerboseSuperprojectUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
@@ -58,6 +63,22 @@ class SubmoduleCommits {
private final long maxCommitMessages;
private final BranchTips branchTips = new BranchTips();
@Singleton
public static class Factory {
private final Provider<PersonIdent> serverIdent;
private final Config cfg;
@Inject
Factory(@GerritPersonIdent Provider<PersonIdent> serverIdent, @GerritServerConfig Config cfg) {
this.serverIdent = serverIdent;
this.cfg = cfg;
}
public SubmoduleCommits create(MergeOpRepoManager orm) {
return new SubmoduleCommits(orm, serverIdent.get(), cfg);
}
}
SubmoduleCommits(MergeOpRepoManager orm, PersonIdent myIdent, Config cfg) {
this.orm = orm;
this.myIdent = myIdent;

View File

@@ -15,116 +15,59 @@
package com.google.gerrit.server.submit;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.SubmoduleSubscription;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateListener;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.update.RepoOnlyOp;
import com.google.gerrit.server.update.UpdateException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
public class SubmoduleOp {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/** Only used for branches without code review changes */
public static class GitlinkOp implements RepoOnlyOp {
private final BranchNameKey branch;
private final SubmoduleCommits commitHelper;
private final Collection<SubmoduleSubscription> branchTargets;
GitlinkOp(
BranchNameKey branch,
SubmoduleCommits commitHelper,
Collection<SubmoduleSubscription> branchTargets) {
this.branch = branch;
this.commitHelper = commitHelper;
this.branchTargets = branchTargets;
}
@Override
public void updateRepo(RepoContext ctx) throws Exception {
Optional<CodeReviewCommit> commit = commitHelper.composeGitlinksCommit(branch, branchTargets);
if (commit.isPresent()) {
CodeReviewCommit c = commit.get();
ctx.addRefUpdate(c.getParent(0), c, branch.branch());
commitHelper.addBranchTip(branch, c);
}
}
}
@Singleton
public static class Factory {
private final SubscriptionGraph.Factory subscriptionGraphFactory;
private final Provider<PersonIdent> serverIdent;
private final Config cfg;
private final SubmoduleCommits.Factory submoduleCommitsFactory;
@Inject
Factory(
SubscriptionGraph.Factory subscriptionGraphFactory,
@GerritPersonIdent Provider<PersonIdent> serverIdent,
@GerritServerConfig Config cfg) {
SubmoduleCommits.Factory submoduleCommitsFactory) {
this.subscriptionGraphFactory = subscriptionGraphFactory;
this.serverIdent = serverIdent;
this.cfg = cfg;
this.submoduleCommitsFactory = submoduleCommitsFactory;
}
public SubmoduleOp create(Set<BranchNameKey> updatedBranches, MergeOpRepoManager orm)
throws SubmoduleConflictException {
SubscriptionGraph subscriptionGraph;
if (cfg.getBoolean("submodule", "enableSuperProjectSubscriptions", true)) {
subscriptionGraph = subscriptionGraphFactory.compute(updatedBranches, orm);
} else {
logger.atFine().log("Updating superprojects disabled");
subscriptionGraph =
SubscriptionGraph.createEmptyGraph(ImmutableSet.copyOf(updatedBranches));
}
return new SubmoduleOp(serverIdent.get(), cfg, orm, subscriptionGraph);
return new SubmoduleOp(
orm,
subscriptionGraphFactory.compute(updatedBranches, orm),
submoduleCommitsFactory.create(orm));
}
}
private final MergeOpRepoManager orm;
private final SubscriptionGraph subscriptionGraph;
private final SubmoduleCommits submoduleCommits;
private final UpdateOrderCalculator updateOrderCalculator;
private SubmoduleOp(
PersonIdent myIdent,
Config cfg,
MergeOpRepoManager orm,
SubscriptionGraph subscriptionGraph) {
SubscriptionGraph subscriptionGraph,
SubmoduleCommits submoduleCommits) {
this.orm = orm;
this.subscriptionGraph = subscriptionGraph;
this.submoduleCommits = new SubmoduleCommits(orm, myIdent, cfg);
}
// TODO(ifrade): subscription graph should be instantiated somewhere else and passed to
// SubmoduleOp
SubscriptionGraph getSubscriptionGraph() {
return subscriptionGraph;
}
SubmoduleCommits getSubmoduleCommits() {
return submoduleCommits;
this.submoduleCommits = submoduleCommits;
this.updateOrderCalculator = new UpdateOrderCalculator(subscriptionGraph);
}
@UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
@@ -133,13 +76,15 @@ public class SubmoduleOp {
}
public void updateSuperProjects() throws RestApiException {
ImmutableSet<Project.NameKey> projects = getProjectsInOrder();
ImmutableSet<Project.NameKey> projects = updateOrderCalculator.getProjectsInOrder();
if (projects == null) {
return;
}
LinkedHashSet<Project.NameKey> superProjects = new LinkedHashSet<>();
try {
GitlinkOp.Factory gitlinkOpFactory =
new GitlinkOp.Factory(submoduleCommits, subscriptionGraph);
for (Project.NameKey project : projects) {
// only need superprojects
if (subscriptionGraph.isAffectedSuperProject(project)) {
@@ -147,7 +92,7 @@ public class SubmoduleOp {
// get a new BatchUpdate for the super project
OpenRepo or = orm.getRepo(project);
for (BranchNameKey branch : subscriptionGraph.getAffectedSuperBranches(project)) {
addOp(or.getUpdate(), branch);
or.getUpdate().addRepoOnlyOp(gitlinkOpFactory.create(branch));
}
}
}
@@ -156,60 +101,4 @@ public class SubmoduleOp {
throw new StorageException("Cannot update gitlinks", e);
}
}
ImmutableSet<Project.NameKey> getProjectsInOrder() throws SubmoduleConflictException {
LinkedHashSet<Project.NameKey> projects = new LinkedHashSet<>();
for (Project.NameKey project : subscriptionGraph.getAffectedSuperProjects()) {
addAllSubmoduleProjects(project, new LinkedHashSet<>(), projects);
}
for (BranchNameKey branch : subscriptionGraph.getUpdatedBranches()) {
projects.add(branch.project());
}
return ImmutableSet.copyOf(projects);
}
private void addAllSubmoduleProjects(
Project.NameKey project,
LinkedHashSet<Project.NameKey> current,
LinkedHashSet<Project.NameKey> projects)
throws SubmoduleConflictException {
if (current.contains(project)) {
throw new SubmoduleConflictException(
"Project level circular subscriptions detected: "
+ CircularPathFinder.printCircularPath(current, project));
}
if (projects.contains(project)) {
return;
}
current.add(project);
Set<Project.NameKey> subprojects = new HashSet<>();
for (BranchNameKey branch : subscriptionGraph.getAffectedSuperBranches(project)) {
Collection<SubmoduleSubscription> subscriptions = subscriptionGraph.getSubscriptions(branch);
for (SubmoduleSubscription s : subscriptions) {
subprojects.add(s.getSubmodule().project());
}
}
for (Project.NameKey p : subprojects) {
addAllSubmoduleProjects(p, current, projects);
}
current.remove(project);
projects.add(project);
}
ImmutableSet<BranchNameKey> getBranchesInOrder() {
LinkedHashSet<BranchNameKey> branches = new LinkedHashSet<>();
branches.addAll(subscriptionGraph.getSortedSuperprojectAndSubmoduleBranches());
branches.addAll(subscriptionGraph.getUpdatedBranches());
return ImmutableSet.copyOf(branches);
}
void addOp(BatchUpdate bu, BranchNameKey branch) {
bu.addRepoOnlyOp(
new GitlinkOp(branch, submoduleCommits, subscriptionGraph.getSubscriptions(branch)));
}
}

View File

@@ -159,7 +159,7 @@ public class SubscriptionGraph {
public static class Module extends AbstractModule {
@Override
protected void configure() {
bind(Factory.class).to(DefaultFactory.class);
bind(Factory.class).annotatedWith(VanillaSubscriptionGraph.class).to(DefaultFactory.class);
}
}

View File

@@ -0,0 +1,91 @@
// Copyright (C) 2020 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.submit;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.SubmoduleSubscription;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Sorts the projects or branches affected by the update.
*
* <p>The subscription graph contains all branches (and projects) affected by the update, but the
* updates must be executed in the right order, so no superproject reference is updated before its
* target.
*/
class UpdateOrderCalculator {
private final SubscriptionGraph subscriptionGraph;
UpdateOrderCalculator(SubscriptionGraph subscriptionGraph) {
this.subscriptionGraph = subscriptionGraph;
}
ImmutableSet<Project.NameKey> getProjectsInOrder() throws SubmoduleConflictException {
LinkedHashSet<Project.NameKey> projects = new LinkedHashSet<>();
for (Project.NameKey project : subscriptionGraph.getAffectedSuperProjects()) {
addAllSubmoduleProjects(project, new LinkedHashSet<>(), projects);
}
for (BranchNameKey branch : subscriptionGraph.getUpdatedBranches()) {
projects.add(branch.project());
}
return ImmutableSet.copyOf(projects);
}
private void addAllSubmoduleProjects(
Project.NameKey project,
LinkedHashSet<Project.NameKey> current,
LinkedHashSet<Project.NameKey> projects)
throws SubmoduleConflictException {
if (current.contains(project)) {
throw new SubmoduleConflictException(
"Project level circular subscriptions detected: "
+ CircularPathFinder.printCircularPath(current, project));
}
if (projects.contains(project)) {
return;
}
current.add(project);
Set<Project.NameKey> subprojects = new HashSet<>();
for (BranchNameKey branch : subscriptionGraph.getAffectedSuperBranches(project)) {
Collection<SubmoduleSubscription> subscriptions = subscriptionGraph.getSubscriptions(branch);
for (SubmoduleSubscription s : subscriptions) {
subprojects.add(s.getSubmodule().project());
}
}
for (Project.NameKey p : subprojects) {
addAllSubmoduleProjects(p, current, projects);
}
current.remove(project);
projects.add(project);
}
ImmutableSet<BranchNameKey> getBranchesInOrder() {
LinkedHashSet<BranchNameKey> branches = new LinkedHashSet<>();
branches.addAll(subscriptionGraph.getSortedSuperprojectAndSubmoduleBranches());
branches.addAll(subscriptionGraph.getUpdatedBranches());
return ImmutableSet.copyOf(branches);
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (C) 2020 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.submit;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
/**
* Marker on a {@link SubscriptionGraph.Factory} without gerrit configuration.
*
* <p>See {@link ConfiguredSubscriptionGraphFactory}.
*/
@Retention(RUNTIME)
@BindingAnnotation
public @interface VanillaSubscriptionGraph {}