Convert replace patch set in ReceiveCommits to BatchUpdate
Change-Id: I94005689341edce5c6551e8c7c4a79988728cdc6 Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
@@ -85,6 +85,7 @@ import com.google.gerrit.server.git.GitModule;
|
|||||||
import com.google.gerrit.server.git.MergeUtil;
|
import com.google.gerrit.server.git.MergeUtil;
|
||||||
import com.google.gerrit.server.git.NotesBranchUtil;
|
import com.google.gerrit.server.git.NotesBranchUtil;
|
||||||
import com.google.gerrit.server.git.ReceivePackInitializer;
|
import com.google.gerrit.server.git.ReceivePackInitializer;
|
||||||
|
import com.google.gerrit.server.git.ReplaceOp;
|
||||||
import com.google.gerrit.server.git.TagCache;
|
import com.google.gerrit.server.git.TagCache;
|
||||||
import com.google.gerrit.server.git.TransferConfig;
|
import com.google.gerrit.server.git.TransferConfig;
|
||||||
import com.google.gerrit.server.git.strategy.SubmitStrategy;
|
import com.google.gerrit.server.git.strategy.SubmitStrategy;
|
||||||
@@ -316,6 +317,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
factory(ProjectConfigValidator.Factory.class);
|
factory(ProjectConfigValidator.Factory.class);
|
||||||
factory(NotesBranchUtil.Factory.class);
|
factory(NotesBranchUtil.Factory.class);
|
||||||
factory(SubmoduleSectionParser.Factory.class);
|
factory(SubmoduleSectionParser.Factory.class);
|
||||||
|
factory(ReplaceOp.Factory.class);
|
||||||
|
|
||||||
bind(AccountManager.class);
|
bind(AccountManager.class);
|
||||||
factory(ChangeUserName.Factory.class);
|
factory(ChangeUserName.Factory.class);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
|
|||||||
import static com.google.gerrit.server.change.HashtagsUtil.cleanupHashtag;
|
import static com.google.gerrit.server.change.HashtagsUtil.cleanupHashtag;
|
||||||
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
|
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
|
||||||
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
|
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
|
||||||
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromReviewers;
|
|
||||||
import static org.eclipse.jgit.lib.Constants.R_HEADS;
|
import static org.eclipse.jgit.lib.Constants.R_HEADS;
|
||||||
import static org.eclipse.jgit.lib.RefDatabase.ALL;
|
import static org.eclipse.jgit.lib.RefDatabase.ALL;
|
||||||
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
|
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
|
||||||
@@ -81,7 +80,6 @@ import com.google.gerrit.reviewdb.client.Project;
|
|||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
|
import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.ApprovalCopier;
|
|
||||||
import com.google.gerrit.server.ApprovalsUtil;
|
import com.google.gerrit.server.ApprovalsUtil;
|
||||||
import com.google.gerrit.server.ChangeMessagesUtil;
|
import com.google.gerrit.server.ChangeMessagesUtil;
|
||||||
import com.google.gerrit.server.ChangeUtil;
|
import com.google.gerrit.server.ChangeUtil;
|
||||||
@@ -91,8 +89,6 @@ import com.google.gerrit.server.Sequences;
|
|||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountResolver;
|
import com.google.gerrit.server.account.AccountResolver;
|
||||||
import com.google.gerrit.server.change.ChangeInserter;
|
import com.google.gerrit.server.change.ChangeInserter;
|
||||||
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.ChangesCollection;
|
||||||
import com.google.gerrit.server.change.RevisionResource;
|
import com.google.gerrit.server.change.RevisionResource;
|
||||||
import com.google.gerrit.server.change.SetHashtagsOp;
|
import com.google.gerrit.server.change.SetHashtagsOp;
|
||||||
@@ -110,10 +106,8 @@ import com.google.gerrit.server.git.MultiProgressMonitor.Task;
|
|||||||
import com.google.gerrit.server.git.validators.CommitValidationException;
|
import com.google.gerrit.server.git.validators.CommitValidationException;
|
||||||
import com.google.gerrit.server.git.validators.CommitValidationMessage;
|
import com.google.gerrit.server.git.validators.CommitValidationMessage;
|
||||||
import com.google.gerrit.server.git.validators.CommitValidators;
|
import com.google.gerrit.server.git.validators.CommitValidators;
|
||||||
import com.google.gerrit.server.index.ChangeIndexer;
|
|
||||||
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
|
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
|
||||||
import com.google.gerrit.server.mail.MergedSender;
|
import com.google.gerrit.server.mail.MergedSender;
|
||||||
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
|
||||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||||
import com.google.gerrit.server.notedb.ChangeUpdate;
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
import com.google.gerrit.server.notedb.NotesMigration;
|
import com.google.gerrit.server.notedb.NotesMigration;
|
||||||
@@ -130,7 +124,6 @@ import com.google.gerrit.server.util.LabelVote;
|
|||||||
import com.google.gerrit.server.util.MagicBranch;
|
import com.google.gerrit.server.util.MagicBranch;
|
||||||
import com.google.gerrit.server.util.RequestScopePropagator;
|
import com.google.gerrit.server.util.RequestScopePropagator;
|
||||||
import com.google.gerrit.util.cli.CmdLineParser;
|
import com.google.gerrit.util.cli.CmdLineParser;
|
||||||
import com.google.gwtorm.server.AtomicUpdate;
|
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.ResultSet;
|
import com.google.gwtorm.server.ResultSet;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
@@ -172,7 +165,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -292,19 +284,14 @@ public class ReceiveCommits {
|
|||||||
private final ReviewDb db;
|
private final ReviewDb db;
|
||||||
private final Sequences seq;
|
private final Sequences seq;
|
||||||
private final Provider<InternalChangeQuery> queryProvider;
|
private final Provider<InternalChangeQuery> queryProvider;
|
||||||
private final ChangeData.Factory changeDataFactory;
|
|
||||||
private final ChangeNotes.Factory notesFactory;
|
private final ChangeNotes.Factory notesFactory;
|
||||||
private final ChangeUpdate.Factory updateFactory;
|
|
||||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||||
private final AccountResolver accountResolver;
|
private final AccountResolver accountResolver;
|
||||||
private final CmdLineParser.Factory optionParserFactory;
|
private final CmdLineParser.Factory optionParserFactory;
|
||||||
private final MergedSender.Factory mergedSenderFactory;
|
private final MergedSender.Factory mergedSenderFactory;
|
||||||
private final ReplacePatchSetSender.Factory replacePatchSetFactory;
|
|
||||||
private final GitReferenceUpdated gitRefUpdated;
|
private final GitReferenceUpdated gitRefUpdated;
|
||||||
private final PatchSetInfoFactory patchSetInfoFactory;
|
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||||
private final ChangeHooks hooks;
|
private final ChangeHooks hooks;
|
||||||
private final ApprovalsUtil approvalsUtil;
|
|
||||||
private final ApprovalCopier approvalCopier;
|
|
||||||
private final ChangeMessagesUtil cmUtil;
|
private final ChangeMessagesUtil cmUtil;
|
||||||
private final PatchSetUtil psUtil;
|
private final PatchSetUtil psUtil;
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
@@ -318,14 +305,13 @@ public class ReceiveCommits {
|
|||||||
private final ExecutorService sendEmailExecutor;
|
private final ExecutorService sendEmailExecutor;
|
||||||
private final ListeningExecutorService changeUpdateExector;
|
private final ListeningExecutorService changeUpdateExector;
|
||||||
private final RequestScopePropagator requestScopePropagator;
|
private final RequestScopePropagator requestScopePropagator;
|
||||||
private final ChangeIndexer indexer;
|
|
||||||
private final SshInfo sshInfo;
|
private final SshInfo sshInfo;
|
||||||
private final AllProjectsName allProjectsName;
|
private final AllProjectsName allProjectsName;
|
||||||
private final ReceiveConfig receiveConfig;
|
private final ReceiveConfig receiveConfig;
|
||||||
private final DynamicSet<ReceivePackInitializer> initializers;
|
private final DynamicSet<ReceivePackInitializer> initializers;
|
||||||
private final ChangeKindCache changeKindCache;
|
|
||||||
private final BatchUpdate.Factory batchUpdateFactory;
|
private final BatchUpdate.Factory batchUpdateFactory;
|
||||||
private final SetHashtagsOp.Factory hashtagsFactory;
|
private final SetHashtagsOp.Factory hashtagsFactory;
|
||||||
|
private final ReplaceOp.Factory replaceOpFactory;
|
||||||
|
|
||||||
private final ProjectControl projectControl;
|
private final ProjectControl projectControl;
|
||||||
private final Project project;
|
private final Project project;
|
||||||
@@ -367,18 +353,13 @@ public class ReceiveCommits {
|
|||||||
final Sequences seq,
|
final Sequences seq,
|
||||||
final Provider<InternalChangeQuery> queryProvider,
|
final Provider<InternalChangeQuery> queryProvider,
|
||||||
final SchemaFactory<ReviewDb> schemaFactory,
|
final SchemaFactory<ReviewDb> schemaFactory,
|
||||||
final ChangeData.Factory changeDataFactory,
|
|
||||||
final ChangeNotes.Factory notesFactory,
|
final ChangeNotes.Factory notesFactory,
|
||||||
final ChangeUpdate.Factory updateFactory,
|
|
||||||
final AccountResolver accountResolver,
|
final AccountResolver accountResolver,
|
||||||
final CmdLineParser.Factory optionParserFactory,
|
final CmdLineParser.Factory optionParserFactory,
|
||||||
final MergedSender.Factory mergedSenderFactory,
|
final MergedSender.Factory mergedSenderFactory,
|
||||||
final ReplacePatchSetSender.Factory replacePatchSetFactory,
|
|
||||||
final GitReferenceUpdated gitRefUpdated,
|
final GitReferenceUpdated gitRefUpdated,
|
||||||
final PatchSetInfoFactory patchSetInfoFactory,
|
final PatchSetInfoFactory patchSetInfoFactory,
|
||||||
final ChangeHooks hooks,
|
final ChangeHooks hooks,
|
||||||
final ApprovalsUtil approvalsUtil,
|
|
||||||
final ApprovalCopier approvalCopier,
|
|
||||||
final ChangeMessagesUtil cmUtil,
|
final ChangeMessagesUtil cmUtil,
|
||||||
final PatchSetUtil psUtil,
|
final PatchSetUtil psUtil,
|
||||||
final ProjectCache projectCache,
|
final ProjectCache projectCache,
|
||||||
@@ -393,7 +374,6 @@ public class ReceiveCommits {
|
|||||||
@SendEmailExecutor final ExecutorService sendEmailExecutor,
|
@SendEmailExecutor final ExecutorService sendEmailExecutor,
|
||||||
@ChangeUpdateExecutor ListeningExecutorService changeUpdateExector,
|
@ChangeUpdateExecutor ListeningExecutorService changeUpdateExector,
|
||||||
final RequestScopePropagator requestScopePropagator,
|
final RequestScopePropagator requestScopePropagator,
|
||||||
final ChangeIndexer indexer,
|
|
||||||
final SshInfo sshInfo,
|
final SshInfo sshInfo,
|
||||||
final AllProjectsName allProjectsName,
|
final AllProjectsName allProjectsName,
|
||||||
ReceiveConfig receiveConfig,
|
ReceiveConfig receiveConfig,
|
||||||
@@ -405,29 +385,24 @@ public class ReceiveCommits {
|
|||||||
final Provider<SubmoduleOp> subOpProvider,
|
final Provider<SubmoduleOp> subOpProvider,
|
||||||
final Provider<Submit> submitProvider,
|
final Provider<Submit> submitProvider,
|
||||||
final Provider<MergeOp> mergeOpProvider,
|
final Provider<MergeOp> mergeOpProvider,
|
||||||
final ChangeKindCache changeKindCache,
|
|
||||||
final DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
final DynamicMap<ProjectConfigEntry> pluginConfigEntries,
|
||||||
final NotesMigration notesMigration,
|
final NotesMigration notesMigration,
|
||||||
final ChangeEditUtil editUtil,
|
final ChangeEditUtil editUtil,
|
||||||
final BatchUpdate.Factory batchUpdateFactory,
|
final BatchUpdate.Factory batchUpdateFactory,
|
||||||
final SetHashtagsOp.Factory hashtagsFactory) throws IOException {
|
final SetHashtagsOp.Factory hashtagsFactory,
|
||||||
|
final ReplaceOp.Factory replaceOpFactory) throws IOException {
|
||||||
this.user = projectControl.getUser().asIdentifiedUser();
|
this.user = projectControl.getUser().asIdentifiedUser();
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.seq = seq;
|
this.seq = seq;
|
||||||
this.queryProvider = queryProvider;
|
this.queryProvider = queryProvider;
|
||||||
this.changeDataFactory = changeDataFactory;
|
|
||||||
this.notesFactory = notesFactory;
|
this.notesFactory = notesFactory;
|
||||||
this.updateFactory = updateFactory;
|
|
||||||
this.schemaFactory = schemaFactory;
|
this.schemaFactory = schemaFactory;
|
||||||
this.accountResolver = accountResolver;
|
this.accountResolver = accountResolver;
|
||||||
this.optionParserFactory = optionParserFactory;
|
this.optionParserFactory = optionParserFactory;
|
||||||
this.mergedSenderFactory = mergedSenderFactory;
|
this.mergedSenderFactory = mergedSenderFactory;
|
||||||
this.replacePatchSetFactory = replacePatchSetFactory;
|
|
||||||
this.gitRefUpdated = gitRefUpdated;
|
this.gitRefUpdated = gitRefUpdated;
|
||||||
this.patchSetInfoFactory = patchSetInfoFactory;
|
this.patchSetInfoFactory = patchSetInfoFactory;
|
||||||
this.hooks = hooks;
|
this.hooks = hooks;
|
||||||
this.approvalsUtil = approvalsUtil;
|
|
||||||
this.approvalCopier = approvalCopier;
|
|
||||||
this.cmUtil = cmUtil;
|
this.cmUtil = cmUtil;
|
||||||
this.psUtil = psUtil;
|
this.psUtil = psUtil;
|
||||||
this.projectCache = projectCache;
|
this.projectCache = projectCache;
|
||||||
@@ -441,14 +416,13 @@ public class ReceiveCommits {
|
|||||||
this.sendEmailExecutor = sendEmailExecutor;
|
this.sendEmailExecutor = sendEmailExecutor;
|
||||||
this.changeUpdateExector = changeUpdateExector;
|
this.changeUpdateExector = changeUpdateExector;
|
||||||
this.requestScopePropagator = requestScopePropagator;
|
this.requestScopePropagator = requestScopePropagator;
|
||||||
this.indexer = indexer;
|
|
||||||
this.sshInfo = sshInfo;
|
this.sshInfo = sshInfo;
|
||||||
this.allProjectsName = allProjectsName;
|
this.allProjectsName = allProjectsName;
|
||||||
this.receiveConfig = receiveConfig;
|
this.receiveConfig = receiveConfig;
|
||||||
this.initializers = initializers;
|
this.initializers = initializers;
|
||||||
this.changeKindCache = changeKindCache;
|
|
||||||
this.batchUpdateFactory = batchUpdateFactory;
|
this.batchUpdateFactory = batchUpdateFactory;
|
||||||
this.hashtagsFactory = hashtagsFactory;
|
this.hashtagsFactory = hashtagsFactory;
|
||||||
|
this.replaceOpFactory = replaceOpFactory;
|
||||||
|
|
||||||
this.projectControl = projectControl;
|
this.projectControl = projectControl;
|
||||||
this.labelTypes = projectControl.getLabelTypes();
|
this.labelTypes = projectControl.getLabelTypes();
|
||||||
@@ -1157,7 +1131,7 @@ public class ReceiveCommits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MagicBranchInput {
|
static class MagicBranchInput {
|
||||||
private static final Splitter COMMAS = Splitter.on(',').omitEmptyStrings();
|
private static final Splitter COMMAS = Splitter.on(',').omitEmptyStrings();
|
||||||
|
|
||||||
final ReceiveCommand cmd;
|
final ReceiveCommand cmd;
|
||||||
@@ -1956,7 +1930,6 @@ public class ReceiveCommits {
|
|||||||
final ObjectId newCommitId;
|
final ObjectId newCommitId;
|
||||||
final ReceiveCommand inputCommand;
|
final ReceiveCommand inputCommand;
|
||||||
final boolean checkMergedInto;
|
final boolean checkMergedInto;
|
||||||
final Timestamp createdOn;
|
|
||||||
Change change;
|
Change change;
|
||||||
ChangeControl changeCtl;
|
ChangeControl changeCtl;
|
||||||
BiMap<RevCommit, PatchSet.Id> revisions;
|
BiMap<RevCommit, PatchSet.Id> revisions;
|
||||||
@@ -1964,8 +1937,6 @@ public class ReceiveCommits {
|
|||||||
ReceiveCommand prev;
|
ReceiveCommand prev;
|
||||||
ReceiveCommand cmd;
|
ReceiveCommand cmd;
|
||||||
PatchSetInfo info;
|
PatchSetInfo info;
|
||||||
ChangeMessage msg;
|
|
||||||
String mergedIntoRef;
|
|
||||||
boolean skip;
|
boolean skip;
|
||||||
private PatchSet.Id priorPatchSet;
|
private PatchSet.Id priorPatchSet;
|
||||||
List<String> groups = ImmutableList.of();
|
List<String> groups = ImmutableList.of();
|
||||||
@@ -1976,7 +1947,6 @@ public class ReceiveCommits {
|
|||||||
this.newCommitId = newCommit.copy();
|
this.newCommitId = newCommit.copy();
|
||||||
this.inputCommand = cmd;
|
this.inputCommand = cmd;
|
||||||
this.checkMergedInto = checkMergedInto;
|
this.checkMergedInto = checkMergedInto;
|
||||||
createdOn = TimeUtil.nowTs();
|
|
||||||
|
|
||||||
revisions = HashBiMap.create();
|
revisions = HashBiMap.create();
|
||||||
for (Ref ref : refs(toChange)) {
|
for (Ref ref : refs(toChange)) {
|
||||||
@@ -2177,54 +2147,6 @@ public class ReceiveCommits {
|
|||||||
return Futures.makeChecked(future, INSERT_EXCEPTION);
|
return Futures.makeChecked(future, INSERT_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChangeMessage newChangeMessage(ReviewDb db, ChangeKind changeKind,
|
|
||||||
Map<String, Short> approvals)
|
|
||||||
throws OrmException {
|
|
||||||
msg =
|
|
||||||
new ChangeMessage(new ChangeMessage.Key(change.getId(), ChangeUtil
|
|
||||||
.messageUUID(db)), user.getAccountId(), createdOn, psId);
|
|
||||||
|
|
||||||
msg.setMessage(renderMessageWithApprovals(psId.get(),
|
|
||||||
changeKindMessage(changeKind), approvals, scanLabels(db, approvals)));
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String changeKindMessage(ChangeKind changeKind) {
|
|
||||||
switch (changeKind) {
|
|
||||||
case MERGE_FIRST_PARENT_UPDATE:
|
|
||||||
case TRIVIAL_REBASE:
|
|
||||||
case NO_CHANGE:
|
|
||||||
return ": Patch Set " + priorPatchSet.get() + " was rebased";
|
|
||||||
case NO_CODE_CHANGE:
|
|
||||||
return ": Commit message was updated";
|
|
||||||
case REWORK:
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, PatchSetApproval> scanLabels(ReviewDb db,
|
|
||||||
Map<String, Short> approvals)
|
|
||||||
throws OrmException {
|
|
||||||
Map<String, PatchSetApproval> current = new HashMap<>();
|
|
||||||
// We optimize here and only retrieve current when approvals provided
|
|
||||||
if (!approvals.isEmpty()) {
|
|
||||||
for (PatchSetApproval a : approvalsUtil.byPatchSetUser(
|
|
||||||
db, changeCtl, priorPatchSet, user.getAccountId())) {
|
|
||||||
if (a.isSubmit()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelType lt = labelTypes.byLabel(a.getLabelId());
|
|
||||||
if (lt != null) {
|
|
||||||
current.put(lt.getName(), a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
PatchSet.Id upsertEdit() {
|
PatchSet.Id upsertEdit() {
|
||||||
if (cmd.getResult() == NOT_ATTEMPTED) {
|
if (cmd.getResult() == NOT_ATTEMPTED) {
|
||||||
cmd.execute(rp);
|
cmd.execute(rp);
|
||||||
@@ -2234,187 +2156,41 @@ public class ReceiveCommits {
|
|||||||
|
|
||||||
PatchSet.Id insertPatchSet(RequestState state)
|
PatchSet.Id insertPatchSet(RequestState state)
|
||||||
throws OrmException, IOException, RestApiException, UpdateException {
|
throws OrmException, IOException, RestApiException, UpdateException {
|
||||||
ReviewDb db = state.db;
|
RevCommit newCommit = state.rw.parseCommit(newCommitId);
|
||||||
Repository repo = state.repo;
|
|
||||||
final RevCommit newCommit = state.rw.parseCommit(newCommitId);
|
|
||||||
state.rw.parseBody(newCommit);
|
state.rw.parseBody(newCommit);
|
||||||
final Account.Id me = user.getAccountId();
|
|
||||||
final List<FooterLine> footerLines = newCommit.getFooterLines();
|
|
||||||
final MailRecipients recipients = new MailRecipients();
|
|
||||||
final PatchSet newPatchSet;
|
|
||||||
|
|
||||||
Map<String, Short> approvals = new HashMap<>();
|
RevCommit priorCommit = revisions.inverse().get(priorPatchSet);
|
||||||
ChangeUpdate update = updateFactory.create(changeCtl, createdOn);
|
|
||||||
update.setSubjectForCommit("Create patch set " + psId.get());
|
|
||||||
update.setPatchSetId(psId);
|
|
||||||
|
|
||||||
if (magicBranch != null) {
|
ReplaceOp replaceOp = replaceOpFactory.create(requestScopePropagator,
|
||||||
recipients.add(magicBranch.getMailRecipients());
|
projectControl, checkMergedInto, priorPatchSet, priorCommit, psId,
|
||||||
approvals = magicBranch.labels;
|
newCommit, info, groups, magicBranch, rp.getPushCertificate());
|
||||||
Set<String> hashtags = magicBranch.hashtags;
|
try (BatchUpdate bu = batchUpdateFactory.create(state.db, project.getNameKey(),
|
||||||
ChangeNotes notes = changeCtl.getNotes().load();
|
user, TimeUtil.nowTs())) {
|
||||||
if (!hashtags.isEmpty()) {
|
bu.setRepository(state.repo, state.rw, state.ins);
|
||||||
hashtags.addAll(notes.getHashtags());
|
bu.addOp(change.getId(), replaceOp);
|
||||||
update.setHashtags(hashtags);
|
bu.execute();
|
||||||
}
|
|
||||||
if (magicBranch.topic != null
|
|
||||||
&& !magicBranch.topic.equals(notes.getChange().getTopic())) {
|
|
||||||
update.setTopic(magicBranch.topic);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.changes().beginTransaction(change.getId());
|
if (replaceOp.getRejectMessage() != null) {
|
||||||
ChangeKind changeKind = ChangeKind.REWORK;
|
reject(inputCommand, replaceOp.getRejectMessage());
|
||||||
try {
|
return null;
|
||||||
change = db.changes().get(change.getId());
|
|
||||||
if (change == null || change.getStatus().isClosed()) {
|
|
||||||
reject(inputCommand, "change is closed");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> newGroups = groups;
|
|
||||||
if (newGroups.isEmpty()) {
|
|
||||||
PatchSet prevPs = psUtil.current(db, update.getChangeNotes());
|
|
||||||
newGroups = prevPs != null
|
|
||||||
? prevPs.getGroups()
|
|
||||||
: ImmutableList.<String> of();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean draft = magicBranch != null && magicBranch.draft;
|
|
||||||
newPatchSet = psUtil.insert(
|
|
||||||
db, state.rw, update, psId, newCommit, draft, newGroups,
|
|
||||||
rp.getPushCertificate() != null
|
|
||||||
? rp.getPushCertificate().toTextWithSignature()
|
|
||||||
: null);
|
|
||||||
|
|
||||||
if (checkMergedInto) {
|
|
||||||
final Ref mergedInto = findMergedInto(change.getDest().get(), newCommit);
|
|
||||||
mergedIntoRef = mergedInto != null ? mergedInto.getName() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
recipients.add(getRecipientsFromFooters(
|
|
||||||
accountResolver, draft, footerLines));
|
|
||||||
recipients.remove(me);
|
|
||||||
ChangeData cd = changeDataFactory.create(db, changeCtl);
|
|
||||||
MailRecipients oldRecipients =
|
|
||||||
getRecipientsFromReviewers(cd.reviewers());
|
|
||||||
approvalCopier.copy(db, changeCtl, newPatchSet);
|
|
||||||
approvalsUtil.addReviewers(db, update, labelTypes, change, newPatchSet,
|
|
||||||
info, recipients.getReviewers(), oldRecipients.getAll());
|
|
||||||
approvalsUtil.addApprovals(db, update, labelTypes, newPatchSet,
|
|
||||||
changeCtl, approvals);
|
|
||||||
recipients.add(oldRecipients);
|
|
||||||
|
|
||||||
RevCommit priorCommit = revisions.inverse().get(priorPatchSet);
|
|
||||||
changeKind = changeKindCache.getChangeKind(
|
|
||||||
projectControl.getProjectState(), repo, priorCommit, newCommit);
|
|
||||||
|
|
||||||
cmUtil.addChangeMessage(db, update, newChangeMessage(db, changeKind,
|
|
||||||
approvals));
|
|
||||||
|
|
||||||
if (mergedIntoRef == null) {
|
|
||||||
// Change should be new, so it can go through review again.
|
|
||||||
//
|
|
||||||
change =
|
|
||||||
db.changes().atomicUpdate(change.getId(), new AtomicUpdate<Change>() {
|
|
||||||
@Override
|
|
||||||
public Change update(Change change) {
|
|
||||||
if (change.getStatus().isClosed()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!change.currentPatchSetId().equals(priorPatchSet)) {
|
|
||||||
return change;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (magicBranch != null && magicBranch.topic != null) {
|
|
||||||
change.setTopic(magicBranch.topic);
|
|
||||||
}
|
|
||||||
if (change.getStatus() == Change.Status.DRAFT && newPatchSet.isDraft()) {
|
|
||||||
// Leave in draft status.
|
|
||||||
} else {
|
|
||||||
change.setStatus(Change.Status.NEW);
|
|
||||||
}
|
|
||||||
change.setCurrentPatchSet(info);
|
|
||||||
|
|
||||||
final List<String> idList = newCommit.getFooterLines(CHANGE_ID);
|
|
||||||
if (idList.isEmpty()) {
|
|
||||||
change.setKey(new Change.Key("I" + newCommit.name()));
|
|
||||||
} else {
|
|
||||||
change.setKey(new Change.Key(idList.get(idList.size() - 1).trim()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangeUtil.updated(change);
|
|
||||||
return change;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (change == null) {
|
|
||||||
db.patchSets().delete(Collections.singleton(newPatchSet));
|
|
||||||
db.changeMessages().delete(Collections.singleton(msg));
|
|
||||||
reject(inputCommand, "change is closed");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
} finally {
|
|
||||||
db.rollback();
|
|
||||||
}
|
}
|
||||||
update.commit();
|
groups = replaceOp.getGroups();
|
||||||
|
|
||||||
if (mergedIntoRef != null) {
|
//TODO(ekempin): mark changes as merged inside of ReplaceOp
|
||||||
|
if (replaceOp.getMergedIntoRef() != null) {
|
||||||
// Change was already submitted to a branch, close it.
|
// Change was already submitted to a branch, close it.
|
||||||
//
|
//
|
||||||
markChangeMergedByPush(db, info, mergedIntoRef);
|
markChangeMergedByPush(db, info, replaceOp.getMergedIntoRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.getResult() == NOT_ATTEMPTED) {
|
if (cmd.getResult() == NOT_ATTEMPTED) {
|
||||||
cmd.execute(rp);
|
cmd.execute(rp);
|
||||||
}
|
}
|
||||||
indexer.index(db, change);
|
|
||||||
if (changeKind != ChangeKind.TRIVIAL_REBASE) {
|
|
||||||
sendEmailExecutor.submit(requestScopePropagator.wrap(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
ReplacePatchSetSender cm = replacePatchSetFactory
|
|
||||||
.create(project.getNameKey(), change.getId());
|
|
||||||
cm.setFrom(me);
|
|
||||||
cm.setPatchSet(newPatchSet, info);
|
|
||||||
cm.setChangeMessage(msg);
|
|
||||||
if (magicBranch != null) {
|
|
||||||
cm.setNotify(magicBranch.notify);
|
|
||||||
}
|
|
||||||
cm.addReviewers(recipients.getReviewers());
|
|
||||||
cm.addExtraCC(recipients.getCcOnly());
|
|
||||||
cm.send();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Cannot send email for new patch set " + newPatchSet.getId(), e);
|
|
||||||
}
|
|
||||||
if (mergedIntoRef != null) {
|
|
||||||
sendMergedEmail(newPatchSet, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "send-email newpatchset";
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
PatchSet newPatchSet = replaceOp.getPatchSet();
|
||||||
gitRefUpdated.fire(project.getNameKey(), newPatchSet.getRefName(),
|
gitRefUpdated.fire(project.getNameKey(), newPatchSet.getRefName(),
|
||||||
ObjectId.zeroId(), newCommit);
|
ObjectId.zeroId(), newCommit);
|
||||||
hooks.doPatchsetCreatedHook(change, newPatchSet, db);
|
|
||||||
if (mergedIntoRef != null) {
|
|
||||||
hooks.doChangeMergedHook(
|
|
||||||
change, user.getAccount(), newPatchSet, db, newCommit.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!approvals.isEmpty()) {
|
|
||||||
hooks.doCommentAddedHook(change, user.getAccount(), newPatchSet,
|
|
||||||
null, approvals, db);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (magicBranch != null && magicBranch.submit) {
|
if (magicBranch != null && magicBranch.submit) {
|
||||||
submit(changeCtl, newPatchSet);
|
submit(changeCtl, newPatchSet);
|
||||||
@@ -2549,33 +2325,6 @@ public class ReceiveCommits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Ref findMergedInto(final String first, final RevCommit commit) {
|
|
||||||
try {
|
|
||||||
final Map<String, Ref> all = repo.getRefDatabase().getRefs(ALL);
|
|
||||||
Ref firstRef = all.get(first);
|
|
||||||
if (firstRef != null && isMergedInto(commit, firstRef)) {
|
|
||||||
return firstRef;
|
|
||||||
}
|
|
||||||
for (Ref ref : all.values()) {
|
|
||||||
if (isHead(ref)) {
|
|
||||||
if (isMergedInto(commit, ref)) {
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warn("Can't check for already submitted change", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMergedInto(final RevCommit commit, final Ref ref)
|
|
||||||
throws IOException {
|
|
||||||
final RevWalk rw = rp.getRevWalk();
|
|
||||||
return rw.isMergedInto(commit, rw.parseCommit(ref.getObjectId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateNewCommits(RefControl ctl, ReceiveCommand cmd) {
|
private void validateNewCommits(RefControl ctl, ReceiveCommand cmd) {
|
||||||
if (ctl.canForgeAuthor()
|
if (ctl.canForgeAuthor()
|
||||||
&& ctl.canForgeCommitter()
|
&& ctl.canForgeCommitter()
|
||||||
@@ -2845,10 +2594,6 @@ public class ReceiveCommits {
|
|||||||
commandProgress.update(1);
|
commandProgress.update(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isHead(final Ref ref) {
|
|
||||||
return ref.getName().startsWith(Constants.R_HEADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isHead(final ReceiveCommand cmd) {
|
private static boolean isHead(final ReceiveCommand cmd) {
|
||||||
return cmd.getRefName().startsWith(Constants.R_HEADS);
|
return cmd.getRefName().startsWith(Constants.R_HEADS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,468 @@
|
|||||||
|
// Copyright (C) 2016 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.git;
|
||||||
|
|
||||||
|
import static com.google.gerrit.common.FooterConstants.CHANGE_ID;
|
||||||
|
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
|
||||||
|
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromReviewers;
|
||||||
|
import static org.eclipse.jgit.lib.RefDatabase.ALL;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.gerrit.common.ChangeHooks;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.common.data.LabelType;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSetInfo;
|
||||||
|
import com.google.gerrit.server.ApprovalCopier;
|
||||||
|
import com.google.gerrit.server.ApprovalsUtil;
|
||||||
|
import com.google.gerrit.server.ChangeMessagesUtil;
|
||||||
|
import com.google.gerrit.server.ChangeUtil;
|
||||||
|
import com.google.gerrit.server.PatchSetUtil;
|
||||||
|
import com.google.gerrit.server.account.AccountResolver;
|
||||||
|
import com.google.gerrit.server.change.ChangeKind;
|
||||||
|
import com.google.gerrit.server.change.ChangeKindCache;
|
||||||
|
import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
|
||||||
|
import com.google.gerrit.server.git.BatchUpdate.Context;
|
||||||
|
import com.google.gerrit.server.git.BatchUpdate.RepoContext;
|
||||||
|
import com.google.gerrit.server.git.ReceiveCommits.MagicBranchInput;
|
||||||
|
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
|
||||||
|
import com.google.gerrit.server.mail.MergedSender;
|
||||||
|
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
||||||
|
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||||
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
|
import com.google.gerrit.server.util.LabelVote;
|
||||||
|
import com.google.gerrit.server.util.RequestScopePropagator;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
import com.google.inject.assistedinject.AssistedInject;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.transport.PushCertificate;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
public class ReplaceOp extends BatchUpdate.Op {
|
||||||
|
public interface Factory {
|
||||||
|
ReplaceOp create(
|
||||||
|
RequestScopePropagator requestScopePropagator,
|
||||||
|
ProjectControl projectControl,
|
||||||
|
boolean checkMergedInto,
|
||||||
|
@Assisted("priorPatchSetId") PatchSet.Id priorPatchSetId,
|
||||||
|
@Assisted("priorCommit") RevCommit priorCommit,
|
||||||
|
@Assisted("patchSetId") PatchSet.Id patchSetId,
|
||||||
|
@Assisted("commit") RevCommit commit,
|
||||||
|
PatchSetInfo info,
|
||||||
|
List<String> groups,
|
||||||
|
@Nullable MagicBranchInput magicBranch,
|
||||||
|
@Nullable PushCertificate pushCertificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger log =
|
||||||
|
LoggerFactory.getLogger(ReplaceOp.class);
|
||||||
|
|
||||||
|
private static final String CHANGE_IS_CLOSED = "change is closed";
|
||||||
|
|
||||||
|
private final PatchSetUtil psUtil;
|
||||||
|
private final ChangeData.Factory changeDataFactory;
|
||||||
|
private final ChangeKindCache changeKindCache;
|
||||||
|
private final ChangeMessagesUtil cmUtil;
|
||||||
|
private final ChangeHooks hooks;
|
||||||
|
private final ApprovalsUtil approvalsUtil;
|
||||||
|
private final ApprovalCopier approvalCopier;
|
||||||
|
private final AccountResolver accountResolver;
|
||||||
|
private final ExecutorService sendEmailExecutor;
|
||||||
|
private final ReplacePatchSetSender.Factory replacePatchSetFactory;
|
||||||
|
private final MergedSender.Factory mergedSenderFactory;
|
||||||
|
|
||||||
|
private final RequestScopePropagator requestScopePropagator;
|
||||||
|
private final ProjectControl projectControl;
|
||||||
|
private final boolean checkMergedInto;
|
||||||
|
private final PatchSet.Id priorPatchSetId;
|
||||||
|
private final RevCommit priorCommit;
|
||||||
|
private final PatchSet.Id patchSetId;
|
||||||
|
private final RevCommit commit;
|
||||||
|
private final PatchSetInfo info;
|
||||||
|
private final MagicBranchInput magicBranch;
|
||||||
|
private final PushCertificate pushCertificate;
|
||||||
|
private List<String> groups = ImmutableList.of();
|
||||||
|
|
||||||
|
private final Map<String, Short> approvals = new HashMap<>();
|
||||||
|
private final MailRecipients recipients = new MailRecipients();
|
||||||
|
private Change change;
|
||||||
|
private PatchSet newPatchSet;
|
||||||
|
private ChangeKind changeKind;
|
||||||
|
private ChangeMessage msg;
|
||||||
|
private String rejectMessage;
|
||||||
|
private String mergedIntoRef;
|
||||||
|
|
||||||
|
@AssistedInject
|
||||||
|
ReplaceOp(PatchSetUtil psUtil,
|
||||||
|
ChangeData.Factory changeDataFactory,
|
||||||
|
ChangeKindCache changeKindCache,
|
||||||
|
ChangeMessagesUtil cmUtil,
|
||||||
|
ChangeHooks hooks,
|
||||||
|
ApprovalsUtil approvalsUtil,
|
||||||
|
ApprovalCopier approvalCopier,
|
||||||
|
AccountResolver accountResolver,
|
||||||
|
@SendEmailExecutor ExecutorService sendEmailExecutor,
|
||||||
|
ReplacePatchSetSender.Factory replacePatchSetFactory,
|
||||||
|
MergedSender.Factory mergedSenderFactory,
|
||||||
|
@Assisted RequestScopePropagator requestScopePropagator,
|
||||||
|
@Assisted ProjectControl projectControl,
|
||||||
|
@Assisted boolean checkMergedInto,
|
||||||
|
@Assisted("priorPatchSetId") PatchSet.Id priorPatchSetId,
|
||||||
|
@Assisted("priorCommit") RevCommit priorCommit,
|
||||||
|
@Assisted("patchSetId") PatchSet.Id patchSetId,
|
||||||
|
@Assisted("commit") RevCommit commit,
|
||||||
|
@Assisted PatchSetInfo info,
|
||||||
|
@Assisted List<String> groups,
|
||||||
|
@Assisted @Nullable MagicBranchInput magicBranch,
|
||||||
|
@Assisted @Nullable PushCertificate pushCertificate) {
|
||||||
|
this.psUtil = psUtil;
|
||||||
|
this.changeDataFactory = changeDataFactory;
|
||||||
|
this.changeKindCache = changeKindCache;
|
||||||
|
this.cmUtil = cmUtil;
|
||||||
|
this.hooks = hooks;
|
||||||
|
this.approvalsUtil = approvalsUtil;
|
||||||
|
this.approvalCopier = approvalCopier;
|
||||||
|
this.accountResolver = accountResolver;
|
||||||
|
this.sendEmailExecutor = sendEmailExecutor;
|
||||||
|
this.replacePatchSetFactory = replacePatchSetFactory;
|
||||||
|
this.mergedSenderFactory = mergedSenderFactory;
|
||||||
|
|
||||||
|
this.requestScopePropagator = requestScopePropagator;
|
||||||
|
this.projectControl = projectControl;
|
||||||
|
this.checkMergedInto = checkMergedInto;
|
||||||
|
this.priorPatchSetId = priorPatchSetId;
|
||||||
|
this.priorCommit = priorCommit;
|
||||||
|
this.patchSetId = patchSetId;
|
||||||
|
this.commit = commit;
|
||||||
|
this.info = info;
|
||||||
|
this.groups = groups;
|
||||||
|
this.magicBranch = magicBranch;
|
||||||
|
this.pushCertificate = pushCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateRepo(RepoContext ctx) throws Exception {
|
||||||
|
changeKind = changeKindCache.getChangeKind(projectControl.getProjectState(),
|
||||||
|
ctx.getRepository(), priorCommit, commit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateChange(BatchUpdate.ChangeContext ctx)
|
||||||
|
throws OrmException, IOException {
|
||||||
|
change = ctx.getChange();
|
||||||
|
if (change == null || change.getStatus().isClosed()) {
|
||||||
|
rejectMessage = CHANGE_IS_CLOSED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (groups.isEmpty()) {
|
||||||
|
PatchSet prevPs = psUtil.current(ctx.getDb(), ctx.getNotes());
|
||||||
|
groups = prevPs != null
|
||||||
|
? prevPs.getGroups()
|
||||||
|
: ImmutableList.<String> of();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeUpdate update = ctx.getUpdate(patchSetId);
|
||||||
|
update.setSubjectForCommit("Create patch set " + patchSetId.get());
|
||||||
|
|
||||||
|
if (magicBranch != null) {
|
||||||
|
recipients.add(magicBranch.getMailRecipients());
|
||||||
|
approvals.putAll(magicBranch.labels);
|
||||||
|
Set<String> hashtags = magicBranch.hashtags;
|
||||||
|
if (hashtags != null && !hashtags.isEmpty()) {
|
||||||
|
hashtags.addAll(ctx.getNotes().getHashtags());
|
||||||
|
update.setHashtags(hashtags);
|
||||||
|
}
|
||||||
|
if (magicBranch.topic != null
|
||||||
|
&& !magicBranch.topic.equals(ctx.getChange().getTopic())) {
|
||||||
|
update.setTopic(magicBranch.topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean draft = magicBranch != null && magicBranch.draft;
|
||||||
|
newPatchSet = psUtil.insert(
|
||||||
|
ctx.getDb(), ctx.getRevWalk(), update, patchSetId, commit, draft, groups,
|
||||||
|
pushCertificate != null
|
||||||
|
? pushCertificate.toTextWithSignature()
|
||||||
|
: null);
|
||||||
|
|
||||||
|
if (checkMergedInto) {
|
||||||
|
Ref mergedInto = findMergedInto(ctx, change.getDest().get(), commit);
|
||||||
|
mergedIntoRef = mergedInto != null ? mergedInto.getName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
recipients.add(getRecipientsFromFooters(
|
||||||
|
accountResolver, draft, commit.getFooterLines()));
|
||||||
|
recipients.remove(ctx.getUser().getAccountId());
|
||||||
|
ChangeData cd = changeDataFactory.create(ctx.getDb(), ctx.getControl());
|
||||||
|
MailRecipients oldRecipients =
|
||||||
|
getRecipientsFromReviewers(cd.reviewers());
|
||||||
|
approvalCopier.copy(ctx.getDb(), ctx.getControl(), newPatchSet);
|
||||||
|
approvalsUtil.addReviewers(ctx.getDb(), update,
|
||||||
|
projectControl.getLabelTypes(), change, newPatchSet, info,
|
||||||
|
recipients.getReviewers(), oldRecipients.getAll());
|
||||||
|
approvalsUtil.addApprovals(ctx.getDb(), update,
|
||||||
|
projectControl.getLabelTypes(), newPatchSet, ctx.getControl(),
|
||||||
|
approvals);
|
||||||
|
recipients.add(oldRecipients);
|
||||||
|
|
||||||
|
msg = new ChangeMessage(
|
||||||
|
new ChangeMessage.Key(change.getId(),
|
||||||
|
ChangeUtil.messageUUID(ctx.getDb())),
|
||||||
|
ctx.getUser().getAccountId(), ctx.getWhen(), patchSetId);
|
||||||
|
msg.setMessage(renderMessageWithApprovals(patchSetId.get(),
|
||||||
|
changeKindMessage(changeKind), approvals, scanLabels(ctx, approvals)));
|
||||||
|
cmUtil.addChangeMessage(ctx.getDb(), update, msg);
|
||||||
|
|
||||||
|
if (mergedIntoRef == null) {
|
||||||
|
resetChange(ctx, msg);
|
||||||
|
}
|
||||||
|
ctx.saveChange();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String changeKindMessage(ChangeKind changeKind) {
|
||||||
|
switch (changeKind) {
|
||||||
|
case MERGE_FIRST_PARENT_UPDATE:
|
||||||
|
case TRIVIAL_REBASE:
|
||||||
|
case NO_CHANGE:
|
||||||
|
return ": Patch Set " + priorPatchSetId.get() + " was rebased";
|
||||||
|
case NO_CODE_CHANGE:
|
||||||
|
return ": Commit message was updated";
|
||||||
|
case REWORK:
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String renderMessageWithApprovals(int patchSetId,
|
||||||
|
String suffix, Map<String, Short> n, Map<String, PatchSetApproval> c) {
|
||||||
|
StringBuilder msgs = new StringBuilder("Uploaded patch set " + patchSetId);
|
||||||
|
if (!n.isEmpty()) {
|
||||||
|
boolean first = true;
|
||||||
|
for (Map.Entry<String, Short> e : n.entrySet()) {
|
||||||
|
if (c.containsKey(e.getKey())
|
||||||
|
&& c.get(e.getKey()).getValue() == e.getValue()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (first) {
|
||||||
|
msgs.append(":");
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
msgs.append(" ")
|
||||||
|
.append(LabelVote.create(e.getKey(), e.getValue()).format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Strings.isNullOrEmpty(suffix)) {
|
||||||
|
msgs.append(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgs.append('.').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, PatchSetApproval> scanLabels(ChangeContext ctx,
|
||||||
|
Map<String, Short> approvals) throws OrmException {
|
||||||
|
Map<String, PatchSetApproval> current = new HashMap<>();
|
||||||
|
// We optimize here and only retrieve current when approvals provided
|
||||||
|
if (!approvals.isEmpty()) {
|
||||||
|
for (PatchSetApproval a : approvalsUtil.byPatchSetUser(ctx.getDb(),
|
||||||
|
ctx.getControl(), priorPatchSetId,
|
||||||
|
ctx.getUser().getAccountId())) {
|
||||||
|
if (a.isSubmit()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelType lt = projectControl.getLabelTypes().byLabel(a.getLabelId());
|
||||||
|
if (lt != null) {
|
||||||
|
current.put(lt.getName(), a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetChange(ChangeContext ctx, ChangeMessage msg)
|
||||||
|
throws OrmException {
|
||||||
|
Change change = ctx.getChange();
|
||||||
|
if (change.getStatus().isClosed()) {
|
||||||
|
ctx.getDb().patchSets().delete(Collections.singleton(newPatchSet));
|
||||||
|
ctx.getDb().changeMessages().delete(Collections.singleton(msg));
|
||||||
|
rejectMessage = CHANGE_IS_CLOSED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!change.currentPatchSetId().equals(priorPatchSetId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (magicBranch != null && magicBranch.topic != null) {
|
||||||
|
change.setTopic(magicBranch.topic);
|
||||||
|
}
|
||||||
|
if (change.getStatus() == Change.Status.DRAFT && newPatchSet.isDraft()) {
|
||||||
|
// Leave in draft status.
|
||||||
|
} else {
|
||||||
|
change.setStatus(Change.Status.NEW);
|
||||||
|
}
|
||||||
|
change.setCurrentPatchSet(info);
|
||||||
|
|
||||||
|
List<String> idList = commit.getFooterLines(CHANGE_ID);
|
||||||
|
if (idList.isEmpty()) {
|
||||||
|
change.setKey(new Change.Key("I" + commit.name()));
|
||||||
|
} else {
|
||||||
|
change.setKey(new Change.Key(idList.get(idList.size() - 1).trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postUpdate(final Context ctx) throws Exception {
|
||||||
|
if (changeKind != ChangeKind.TRIVIAL_REBASE) {
|
||||||
|
Runnable sender = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
ReplacePatchSetSender cm = replacePatchSetFactory.create(
|
||||||
|
projectControl.getProject().getNameKey(), change.getId());
|
||||||
|
cm.setFrom(ctx.getUser().getAccountId());
|
||||||
|
cm.setPatchSet(newPatchSet, info);
|
||||||
|
cm.setChangeMessage(msg);
|
||||||
|
if (magicBranch != null && magicBranch.notify != null) {
|
||||||
|
cm.setNotify(magicBranch.notify);
|
||||||
|
}
|
||||||
|
cm.addReviewers(recipients.getReviewers());
|
||||||
|
cm.addExtraCC(recipients.getCcOnly());
|
||||||
|
cm.send();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Cannot send email for new patch set " + newPatchSet.getId(), e);
|
||||||
|
}
|
||||||
|
if (mergedIntoRef != null) {
|
||||||
|
sendMergedEmail(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "send-email newpatchset";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (requestScopePropagator != null) {
|
||||||
|
sendEmailExecutor.submit(requestScopePropagator.wrap(sender));
|
||||||
|
} else {
|
||||||
|
sender.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Account account = ctx.getUser().asIdentifiedUser().getAccount();
|
||||||
|
hooks.doPatchsetCreatedHook(change, newPatchSet, ctx.getDb());
|
||||||
|
if (mergedIntoRef != null) {
|
||||||
|
hooks.doChangeMergedHook(change, account, newPatchSet, ctx.getDb(),
|
||||||
|
commit.getName());
|
||||||
|
}
|
||||||
|
if (!approvals.isEmpty()) {
|
||||||
|
hooks.doCommentAddedHook(change, account, newPatchSet, null, approvals,
|
||||||
|
ctx.getDb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMergedEmail(final Context ctx) {
|
||||||
|
sendEmailExecutor.submit(requestScopePropagator.wrap(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
MergedSender cm = mergedSenderFactory
|
||||||
|
.create(projectControl.getProject().getNameKey(), change.getId());
|
||||||
|
cm.setFrom(ctx.getUser().getAccountId());
|
||||||
|
cm.setPatchSet(newPatchSet, info);
|
||||||
|
cm.send();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Cannot send email for submitted patch set "
|
||||||
|
+ patchSetId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "send-email merged";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PatchSet getPatchSet() {
|
||||||
|
return newPatchSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getGroups() {
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMergedIntoRef() {
|
||||||
|
return mergedIntoRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRejectMessage() {
|
||||||
|
return rejectMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ref findMergedInto(ChangeContext ctx, String first, RevCommit commit) {
|
||||||
|
try {
|
||||||
|
Map<String, Ref> all = ctx.getRepository().getRefDatabase().getRefs(ALL);
|
||||||
|
Ref firstRef = all.get(first);
|
||||||
|
if (firstRef != null && isMergedInto(ctx.getRevWalk(), commit, firstRef)) {
|
||||||
|
return firstRef;
|
||||||
|
}
|
||||||
|
for (Ref ref : all.values()) {
|
||||||
|
if (isBranch(ref)) {
|
||||||
|
if (isMergedInto(ctx.getRevWalk(), commit, ref)) {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Can't check for already submitted change", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMergedInto(RevWalk rw, RevCommit commit, Ref ref)
|
||||||
|
throws IOException {
|
||||||
|
return rw.isMergedInto(commit, rw.parseCommit(ref.getObjectId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBranch(Ref ref) {
|
||||||
|
return ref.getName().startsWith(Constants.R_HEADS);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user