Line data Source code
1 : // Copyright (C) 2012 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.submit;
16 :
17 : import static com.google.common.collect.ImmutableMap.toImmutableMap;
18 : import static com.google.gerrit.server.project.ProjectCache.illegalState;
19 : import static java.util.Objects.requireNonNull;
20 :
21 : import com.google.common.collect.ImmutableList;
22 : import com.google.common.collect.ImmutableMap;
23 : import com.google.common.collect.Sets;
24 : import com.google.gerrit.entities.BranchNameKey;
25 : import com.google.gerrit.entities.Change;
26 : import com.google.gerrit.entities.SubmissionId;
27 : import com.google.gerrit.extensions.api.changes.SubmitInput;
28 : import com.google.gerrit.extensions.client.SubmitType;
29 : import com.google.gerrit.extensions.config.FactoryModule;
30 : import com.google.gerrit.server.ChangeMessagesUtil;
31 : import com.google.gerrit.server.GerritPersonIdent;
32 : import com.google.gerrit.server.IdentifiedUser;
33 : import com.google.gerrit.server.PatchSetUtil;
34 : import com.google.gerrit.server.account.AccountCache;
35 : import com.google.gerrit.server.approval.ApprovalsUtil;
36 : import com.google.gerrit.server.change.LabelNormalizer;
37 : import com.google.gerrit.server.change.RebaseChangeOp;
38 : import com.google.gerrit.server.change.SetPrivateOp;
39 : import com.google.gerrit.server.change.TestSubmitInput;
40 : import com.google.gerrit.server.extensions.events.ChangeMerged;
41 : import com.google.gerrit.server.git.CodeReviewCommit;
42 : import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
43 : import com.google.gerrit.server.git.GitRepositoryManager;
44 : import com.google.gerrit.server.git.MergeTip;
45 : import com.google.gerrit.server.git.MergeUtil;
46 : import com.google.gerrit.server.git.MergeUtilFactory;
47 : import com.google.gerrit.server.git.TagCache;
48 : import com.google.gerrit.server.git.validators.OnSubmitValidators;
49 : import com.google.gerrit.server.patch.PatchSetInfoFactory;
50 : import com.google.gerrit.server.patch.SubmitWithStickyApprovalDiff;
51 : import com.google.gerrit.server.project.ProjectCache;
52 : import com.google.gerrit.server.project.ProjectConfig;
53 : import com.google.gerrit.server.project.ProjectState;
54 : import com.google.gerrit.server.query.change.InternalChangeQuery;
55 : import com.google.gerrit.server.submit.MergeOp.CommitStatus;
56 : import com.google.gerrit.server.update.BatchUpdate;
57 : import com.google.inject.Inject;
58 : import com.google.inject.Module;
59 : import com.google.inject.Provider;
60 : import com.google.inject.assistedinject.Assisted;
61 : import java.util.ArrayList;
62 : import java.util.Collection;
63 : import java.util.Collections;
64 : import java.util.HashSet;
65 : import java.util.List;
66 : import java.util.Optional;
67 : import java.util.Set;
68 : import org.eclipse.jgit.lib.PersonIdent;
69 : import org.eclipse.jgit.revwalk.RevCommit;
70 : import org.eclipse.jgit.revwalk.RevFlag;
71 :
72 : /**
73 : * Base class that submit strategies must extend.
74 : *
75 : * <p>A submit strategy for a certain {@link SubmitType} defines how the submitted commits should be
76 : * merged.
77 : */
78 : public abstract class SubmitStrategy {
79 : public static Module module() {
80 152 : return new FactoryModule() {
81 : @Override
82 : protected void configure() {
83 152 : factory(SubmitStrategy.Arguments.Factory.class);
84 152 : factory(EmailMerge.Factory.class);
85 152 : }
86 : };
87 : }
88 :
89 : static class Arguments {
90 : interface Factory {
91 : Arguments create(
92 : SubmitType submitType,
93 : BranchNameKey destBranch,
94 : CommitStatus commitStatus,
95 : CodeReviewRevWalk rw,
96 : IdentifiedUser caller,
97 : MergeTip mergeTip,
98 : RevFlag canMergeFlag,
99 : Set<RevCommit> alreadyAccepted,
100 : Set<CodeReviewCommit> incoming,
101 : SubmissionId submissionId,
102 : SubmitInput submitInput,
103 : SubmoduleCommits submoduleCommits,
104 : SubscriptionGraph subscriptionGraph,
105 : boolean dryrun);
106 : }
107 :
108 : final AccountCache accountCache;
109 : final ApprovalsUtil approvalsUtil;
110 : final ChangeMerged changeMerged;
111 : final ChangeMessagesUtil cmUtil;
112 : final EmailMerge.Factory mergedSenderFactory;
113 : final GitRepositoryManager repoManager;
114 : final LabelNormalizer labelNormalizer;
115 : final PatchSetInfoFactory patchSetInfoFactory;
116 : final PatchSetUtil psUtil;
117 : final ProjectCache projectCache;
118 : final PersonIdent serverIdent;
119 : final RebaseChangeOp.Factory rebaseFactory;
120 : final OnSubmitValidators.Factory onSubmitValidatorsFactory;
121 : final TagCache tagCache;
122 : final Provider<InternalChangeQuery> queryProvider;
123 : final ProjectConfig.Factory projectConfigFactory;
124 : final SetPrivateOp.Factory setPrivateOpFactory;
125 : final SubmitWithStickyApprovalDiff submitWithStickyApprovalDiff;
126 :
127 : final BranchNameKey destBranch;
128 : final CodeReviewRevWalk rw;
129 : final CommitStatus commitStatus;
130 : final IdentifiedUser caller;
131 : final MergeTip mergeTip;
132 : final RevFlag canMergeFlag;
133 : final Set<RevCommit> alreadyAccepted;
134 : final SubmissionId submissionId;
135 : final SubmitType submitType;
136 : final SubmitInput submitInput;
137 : final SubscriptionGraph subscriptionGraph;
138 : final SubmoduleCommits submoduleCommits;
139 :
140 : final ProjectState project;
141 : final MergeSorter mergeSorter;
142 : final RebaseSorter rebaseSorter;
143 : final MergeUtil mergeUtil;
144 : final boolean dryrun;
145 :
146 : @Inject
147 : Arguments(
148 : AccountCache accountCache,
149 : ApprovalsUtil approvalsUtil,
150 : ChangeMerged changeMerged,
151 : ChangeMessagesUtil cmUtil,
152 : EmailMerge.Factory mergedSenderFactory,
153 : GitRepositoryManager repoManager,
154 : LabelNormalizer labelNormalizer,
155 : MergeUtilFactory mergeUtilFactory,
156 : PatchSetInfoFactory patchSetInfoFactory,
157 : PatchSetUtil psUtil,
158 : @GerritPersonIdent PersonIdent serverIdent,
159 : ProjectCache projectCache,
160 : RebaseChangeOp.Factory rebaseFactory,
161 : OnSubmitValidators.Factory onSubmitValidatorsFactory,
162 : TagCache tagCache,
163 : Provider<InternalChangeQuery> queryProvider,
164 : ProjectConfig.Factory projectConfigFactory,
165 : SetPrivateOp.Factory setPrivateOpFactory,
166 : SubmitWithStickyApprovalDiff submitWithStickyApprovalDiff,
167 : @Assisted BranchNameKey destBranch,
168 : @Assisted CommitStatus commitStatus,
169 : @Assisted CodeReviewRevWalk rw,
170 : @Assisted IdentifiedUser caller,
171 : @Assisted MergeTip mergeTip,
172 : @Assisted RevFlag canMergeFlag,
173 : @Assisted Set<RevCommit> alreadyAccepted,
174 : @Assisted Set<CodeReviewCommit> incoming,
175 : @Assisted SubmissionId submissionId,
176 : @Assisted SubmitType submitType,
177 : @Assisted SubmitInput submitInput,
178 : @Assisted SubscriptionGraph subscriptionGraph,
179 : @Assisted SubmoduleCommits submoduleCommits,
180 53 : @Assisted boolean dryrun) {
181 53 : this.accountCache = accountCache;
182 53 : this.approvalsUtil = approvalsUtil;
183 53 : this.changeMerged = changeMerged;
184 53 : this.mergedSenderFactory = mergedSenderFactory;
185 53 : this.repoManager = repoManager;
186 53 : this.cmUtil = cmUtil;
187 53 : this.labelNormalizer = labelNormalizer;
188 53 : this.projectConfigFactory = projectConfigFactory;
189 53 : this.patchSetInfoFactory = patchSetInfoFactory;
190 53 : this.psUtil = psUtil;
191 53 : this.projectCache = projectCache;
192 53 : this.rebaseFactory = rebaseFactory;
193 53 : this.tagCache = tagCache;
194 53 : this.queryProvider = queryProvider;
195 53 : this.setPrivateOpFactory = setPrivateOpFactory;
196 53 : this.submitWithStickyApprovalDiff = submitWithStickyApprovalDiff;
197 :
198 53 : this.serverIdent = serverIdent;
199 53 : this.destBranch = destBranch;
200 53 : this.commitStatus = commitStatus;
201 53 : this.rw = rw;
202 53 : this.caller = caller;
203 53 : this.mergeTip = mergeTip;
204 53 : this.canMergeFlag = canMergeFlag;
205 53 : this.alreadyAccepted = alreadyAccepted;
206 53 : this.submissionId = submissionId;
207 53 : this.submitType = submitType;
208 53 : this.submitInput = submitInput;
209 53 : this.submoduleCommits = submoduleCommits;
210 53 : this.subscriptionGraph = subscriptionGraph;
211 53 : this.dryrun = dryrun;
212 :
213 53 : this.project =
214 53 : projectCache.get(destBranch.project()).orElseThrow(illegalState(destBranch.project()));
215 53 : this.mergeSorter =
216 : new MergeSorter(caller, rw, alreadyAccepted, canMergeFlag, queryProvider, incoming);
217 53 : this.rebaseSorter =
218 : new RebaseSorter(
219 : caller,
220 : rw,
221 53 : mergeTip.getInitialTip(),
222 : alreadyAccepted,
223 : canMergeFlag,
224 : queryProvider,
225 : incoming);
226 53 : this.mergeUtil = mergeUtilFactory.create(project);
227 53 : this.onSubmitValidatorsFactory = onSubmitValidatorsFactory;
228 53 : }
229 : }
230 :
231 : final Arguments args;
232 :
233 : private final Set<SubmitStrategyOp> submitStrategyOps;
234 :
235 53 : SubmitStrategy(Arguments args) {
236 53 : this.args = requireNonNull(args);
237 53 : this.submitStrategyOps = new HashSet<>();
238 53 : }
239 :
240 : /**
241 : * Returns the updated changed after this submit strategy has been executed.
242 : *
243 : * @return the updated changes after this submit strategy has been executed
244 : */
245 : public ImmutableMap<Change.Id, Change> getUpdatedChanges() {
246 53 : return submitStrategyOps.stream()
247 53 : .map(SubmitStrategyOp::getUpdatedChange)
248 53 : .filter(Optional::isPresent)
249 53 : .map(Optional::get)
250 53 : .collect(toImmutableMap(c -> c.getId(), c -> c));
251 : }
252 :
253 : /**
254 : * Add operations to a batch update that execute this submit strategy.
255 : *
256 : * <p>Guarantees exactly one op is added to the update for each change in the input set.
257 : *
258 : * @param bu batch update to add operations to.
259 : * @param toMerge the set of submitted commits that should be merged using this submit strategy.
260 : * Implementations are responsible for ordering of commits, and will not modify the input in
261 : * place.
262 : */
263 : public final void addOps(BatchUpdate bu, Set<CodeReviewCommit> toMerge) {
264 53 : List<SubmitStrategyOp> ops = buildOps(toMerge);
265 53 : Set<CodeReviewCommit> added = Sets.newHashSetWithExpectedSize(ops.size());
266 :
267 53 : for (SubmitStrategyOp op : ops) {
268 53 : added.add(op.getCommit());
269 53 : }
270 :
271 : // First add ops for any implicitly merged changes.
272 53 : List<CodeReviewCommit> difference = new ArrayList<>(Sets.difference(toMerge, added));
273 53 : Collections.reverse(difference);
274 53 : for (CodeReviewCommit c : difference) {
275 15 : Change.Id id = c.change().getId();
276 15 : bu.addOp(id, args.setPrivateOpFactory.create(false, null));
277 15 : ImplicitIntegrateOp implicitIntegrateOp = new ImplicitIntegrateOp(args, c);
278 15 : bu.addOp(id, implicitIntegrateOp);
279 15 : maybeAddTestHelperOp(bu, id);
280 15 : this.submitStrategyOps.add(implicitIntegrateOp);
281 15 : }
282 :
283 : // Then ops for explicitly merged changes
284 53 : for (SubmitStrategyOp op : ops) {
285 53 : bu.addOp(op.getId(), args.setPrivateOpFactory.create(false, null));
286 53 : bu.addOp(op.getId(), op);
287 53 : maybeAddTestHelperOp(bu, op.getId());
288 53 : this.submitStrategyOps.add(op);
289 53 : }
290 53 : }
291 :
292 : private void maybeAddTestHelperOp(BatchUpdate bu, Change.Id changeId) {
293 53 : if (args.submitInput instanceof TestSubmitInput) {
294 8 : bu.addOp(changeId, new TestHelperOp(changeId, args));
295 : }
296 53 : }
297 :
298 : protected abstract ImmutableList<SubmitStrategyOp> buildOps(Collection<CodeReviewCommit> toMerge);
299 : }
|