Merge "Disable submit button if merge will fail"
This commit is contained in:
@@ -563,6 +563,22 @@ link:cmd-flush-caches.html[gerrit flush-caches].
|
||||
Default is 5 minutes.
|
||||
|
||||
|
||||
[[changeMerge]]Section changeMerge
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Controls whether or not the mergeability test of changes is
|
||||
enabled. If enabled, when the change page is loaded, the test is
|
||||
triggered. The submit button will be enabled or disabled according to
|
||||
the result.
|
||||
|
||||
----
|
||||
[changeMerge]
|
||||
test = true
|
||||
----
|
||||
|
||||
+
|
||||
By default this is false (test is not enabled).
|
||||
|
||||
[[commentlink]]Section commentlink
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Comment links are find/replace strings applied to change descriptions,
|
||||
|
||||
@@ -41,6 +41,7 @@ public class GerritConfig implements Cloneable {
|
||||
protected Set<Account.FieldName> editableAccountFields;
|
||||
protected List<RegexFindReplace> commentLinks;
|
||||
protected boolean documentationAvailable;
|
||||
protected boolean testChangeMerge;
|
||||
|
||||
public String getRegisterUrl() {
|
||||
return registerUrl;
|
||||
@@ -160,4 +161,12 @@ public class GerritConfig implements Cloneable {
|
||||
public void setDocumentationAvailable(final boolean available) {
|
||||
documentationAvailable = available;
|
||||
}
|
||||
|
||||
public boolean testChangeMerge() {
|
||||
return testChangeMerge;
|
||||
}
|
||||
|
||||
public void setTestChangeMerge(final boolean test) {
|
||||
testChangeMerge = test;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +245,15 @@ public class ApprovalTable extends Composite {
|
||||
}
|
||||
|
||||
addReviewer.setVisible(Gerrit.isSignedIn());
|
||||
|
||||
if (Gerrit.getConfig().testChangeMerge()
|
||||
&& !detail.getChange().isMergeable()) {
|
||||
Element li = DOM.createElement("li");
|
||||
li.setClassName(Gerrit.RESOURCES.css().missingApproval());
|
||||
DOM.setInnerText(li, Util.C.messageNeedsRebaseOrHasDependency());
|
||||
DOM.appendChild(missingList, li);
|
||||
missing.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void doAddReviewer() {
|
||||
|
||||
@@ -91,6 +91,9 @@ public interface ChangeConstants extends Constants {
|
||||
String changeInfoBlockUpdated();
|
||||
String changeInfoBlockStatus();
|
||||
String changePermalink();
|
||||
String changeInfoBlockCanMerge();
|
||||
String changeInfoBlockCanMergeYes();
|
||||
String changeInfoBlockCanMergeNo();
|
||||
|
||||
String includedInTableBranch();
|
||||
String includedInTableTag();
|
||||
@@ -99,6 +102,7 @@ public interface ChangeConstants extends Constants {
|
||||
String messageExpandRecent();
|
||||
String messageExpandAll();
|
||||
String messageCollapseAll();
|
||||
String messageNeedsRebaseOrHasDependency();
|
||||
|
||||
String patchSetInfoAuthor();
|
||||
String patchSetInfoCommitter();
|
||||
@@ -142,4 +146,4 @@ public interface ChangeConstants extends Constants {
|
||||
|
||||
String buttonDiffAllSideBySide();
|
||||
String buttonDiffAllUnified();
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,9 @@ changeInfoBlockUploaded = Uploaded
|
||||
changeInfoBlockUpdated = Updated
|
||||
changeInfoBlockStatus = Status
|
||||
changePermalink = Permalink
|
||||
changeInfoBlockCanMerge = Can Merge
|
||||
changeInfoBlockCanMergeYes = Yes
|
||||
changeInfoBlockCanMergeNo = No
|
||||
|
||||
includedInTableBranch = Branch Name
|
||||
includedInTableTag = Tag Name
|
||||
@@ -76,6 +79,7 @@ messageNoAuthor = Gerrit Code Review
|
||||
messageExpandRecent = Expand Recent
|
||||
messageExpandAll = Expand All
|
||||
messageCollapseAll = Collapse All
|
||||
messageNeedsRebaseOrHasDependency = Need Rebase or Has Dependency
|
||||
|
||||
patchSetInfoAuthor = Author
|
||||
patchSetInfoCommitter = Committer
|
||||
@@ -122,4 +126,4 @@ submitFailed = Submit Failed
|
||||
buttonClose = Close
|
||||
|
||||
buttonDiffAllSideBySide = Diff All Side-by-Side
|
||||
buttonDiffAllUnified = Diff All Unified
|
||||
buttonDiffAllUnified = Diff All Unified
|
||||
@@ -39,13 +39,20 @@ public class ChangeInfoBlock extends Composite {
|
||||
private static final int R_UPLOADED = 5;
|
||||
private static final int R_UPDATED = 6;
|
||||
private static final int R_STATUS = 7;
|
||||
private static final int R_PERMALINK = 8;
|
||||
private static final int R_CNT = 9;
|
||||
private static final int R_MERGE_TEST = 8;
|
||||
private final int R_PERMALINK;
|
||||
private static final int R_CNT = 10;
|
||||
|
||||
private final Grid table;
|
||||
|
||||
public ChangeInfoBlock() {
|
||||
table = new Grid(R_CNT, 2);
|
||||
if (Gerrit.getConfig().testChangeMerge()) {
|
||||
table = new Grid(R_CNT, 2);
|
||||
R_PERMALINK = 9;
|
||||
} else {
|
||||
table = new Grid(R_CNT - 1, 2);
|
||||
R_PERMALINK = 8;
|
||||
}
|
||||
table.setStyleName(Gerrit.RESOURCES.css().infoBlock());
|
||||
table.addStyleName(Gerrit.RESOURCES.css().changeInfoBlock());
|
||||
|
||||
@@ -57,6 +64,9 @@ public class ChangeInfoBlock extends Composite {
|
||||
initRow(R_UPLOADED, Util.C.changeInfoBlockUploaded());
|
||||
initRow(R_UPDATED, Util.C.changeInfoBlockUpdated());
|
||||
initRow(R_STATUS, Util.C.changeInfoBlockStatus());
|
||||
if (Gerrit.getConfig().testChangeMerge()) {
|
||||
initRow(R_MERGE_TEST, Util.C.changeInfoBlockCanMerge());
|
||||
}
|
||||
|
||||
final CellFormatter fmt = table.getCellFormatter();
|
||||
fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
|
||||
@@ -91,6 +101,10 @@ public class ChangeInfoBlock extends Composite {
|
||||
table.setText(R_UPLOADED, 1, mediumFormat(chg.getCreatedOn()));
|
||||
table.setText(R_UPDATED, 1, mediumFormat(chg.getLastUpdatedOn()));
|
||||
table.setText(R_STATUS, 1, Util.toLongString(chg.getStatus()));
|
||||
if (Gerrit.getConfig().testChangeMerge()) {
|
||||
table.setText(R_MERGE_TEST, 1, chg.isMergeable() ? Util.C
|
||||
.changeInfoBlockCanMergeYes() : Util.C.changeInfoBlockCanMergeNo());
|
||||
}
|
||||
|
||||
if (chg.getStatus().isClosed()) {
|
||||
table.getCellFormatter().addStyleName(R_STATUS, 1, Gerrit.RESOURCES.css().closedstate());
|
||||
|
||||
@@ -405,6 +405,10 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
||||
final Button b =
|
||||
new Button(Util.M
|
||||
.submitPatchSet(detail.getPatchSet().getPatchSetId()));
|
||||
if (Gerrit.getConfig().testChangeMerge()) {
|
||||
b.setEnabled(changeDetail.getChange().isMergeable());
|
||||
}
|
||||
|
||||
b.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(final ClickEvent event) {
|
||||
@@ -640,4 +644,4 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,11 @@ import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.FormPanel;
|
||||
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.RadioButton;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
|
||||
import com.google.gwtexpui.globalkey.client.NpTextArea;
|
||||
import com.google.gwtjsonrpc.client.VoidResult;
|
||||
|
||||
@@ -311,6 +311,9 @@ public class PublishCommentScreen extends AccountScreen implements
|
||||
}
|
||||
|
||||
submit.setVisible(r.canSubmit());
|
||||
if (Gerrit.getConfig().testChangeMerge()) {
|
||||
submit.setEnabled(r.getChange().isMergeable());
|
||||
}
|
||||
}
|
||||
|
||||
private void onSend(final boolean submit) {
|
||||
|
||||
@@ -102,6 +102,8 @@ class GerritConfigProvider implements Provider<GerritConfig> {
|
||||
config.setApprovalTypes(approvalTypes);
|
||||
config.setDocumentationAvailable(servletContext
|
||||
.getResource("/Documentation/index.html") != null);
|
||||
config.setTestChangeMerge(cfg.getBoolean("changeMerge",
|
||||
"test", false));
|
||||
|
||||
final Set<Account.FieldName> fields = new HashSet<Account.FieldName>();
|
||||
for (final Account.FieldName n : Account.FieldName.values()) {
|
||||
|
||||
@@ -31,8 +31,11 @@ import com.google.gerrit.reviewdb.PatchSetApproval;
|
||||
import com.google.gerrit.reviewdb.RevId;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.server.AnonymousUser;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountInfoCacheFactory;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.MergeOp;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
@@ -42,6 +45,8 @@ import com.google.gwtorm.client.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -70,6 +75,9 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
||||
private ChangeDetail detail;
|
||||
private ChangeControl control;
|
||||
|
||||
private final MergeOp.Factory opFactory;
|
||||
private boolean testMerge;
|
||||
|
||||
@Inject
|
||||
ChangeDetailFactory(final ApprovalTypes approvalTypes,
|
||||
final FunctionState.Factory functionState,
|
||||
@@ -77,6 +85,8 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
||||
final ChangeControl.Factory changeControlFactory,
|
||||
final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
|
||||
final AnonymousUser anonymousUser,
|
||||
final MergeOp.Factory opFactory,
|
||||
@GerritServerConfig final Config cfg,
|
||||
@Assisted final Change.Id id) {
|
||||
this.approvalTypes = approvalTypes;
|
||||
this.functionState = functionState;
|
||||
@@ -86,6 +96,9 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
||||
this.anonymousUser = anonymousUser;
|
||||
this.aic = accountInfoCacheFactory.create();
|
||||
|
||||
this.opFactory = opFactory;
|
||||
this.testMerge = cfg.getBoolean("changeMerge", "test", false);
|
||||
|
||||
this.changeId = id;
|
||||
}
|
||||
|
||||
@@ -148,7 +161,11 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
||||
}
|
||||
}
|
||||
|
||||
private void load() throws OrmException {
|
||||
private void load() throws OrmException, NoSuchChangeException {
|
||||
if (detail.getChange().getStatus().equals(Change.Status.NEW) && testMerge) {
|
||||
ChangeUtil.testMerge(opFactory, detail.getChange());
|
||||
}
|
||||
|
||||
final PatchSet.Id psId = detail.getChange().currentPatchSetId();
|
||||
final List<PatchSetApproval> allApprovals =
|
||||
db.patchSetApprovals().byChange(changeId).toList();
|
||||
|
||||
@@ -18,7 +18,9 @@ import com.google.gerrit.common.ChangeHookRunner;
|
||||
import com.google.gerrit.common.data.ChangeDetail;
|
||||
import com.google.gerrit.common.errors.NoSuchEntityException;
|
||||
import com.google.gerrit.httpd.rpc.Handler;
|
||||
import com.google.gerrit.reviewdb.*;
|
||||
import com.google.gerrit.reviewdb.Change;
|
||||
import com.google.gerrit.reviewdb.PatchSet;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.mail.EmailException;
|
||||
|
||||
@@ -355,6 +355,17 @@ public final class Change {
|
||||
@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() {
|
||||
}
|
||||
|
||||
@@ -367,6 +378,7 @@ public final class Change {
|
||||
owner = ownedBy;
|
||||
dest = forBranch;
|
||||
setStatus(Status.NEW);
|
||||
setLastSha1MergeTested(null);
|
||||
}
|
||||
|
||||
/** Legacy 32 bit integer identity for a change. */
|
||||
@@ -466,4 +478,20 @@ public final class Change {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -25,27 +25,25 @@ import com.google.gerrit.reviewdb.PatchSetInfo;
|
||||
import com.google.gerrit.reviewdb.RevId;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.reviewdb.TrackingId;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.config.TrackingFooter;
|
||||
import com.google.gerrit.server.config.TrackingFooters;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MergeOp;
|
||||
import com.google.gerrit.server.git.MergeQueue;
|
||||
import com.google.gerrit.server.git.ReplicationQueue;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.mail.AbandonedSender;
|
||||
import com.google.gerrit.server.mail.EmailException;
|
||||
import com.google.gerrit.server.mail.ReplyToChangeSender;
|
||||
import com.google.gerrit.server.mail.RestoredSender;
|
||||
import com.google.gerrit.server.mail.RevertedSender;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gwtorm.client.AtomicUpdate;
|
||||
import com.google.gwtorm.client.OrmConcurrencyException;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
|
||||
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
@@ -165,6 +163,10 @@ public class ChangeUtil {
|
||||
db.trackingIds().delete(toDelete);
|
||||
}
|
||||
|
||||
public static void testMerge(MergeOp.Factory opFactory, Change change) {
|
||||
opFactory.create(change.getDest()).verifyMergeability(change);
|
||||
}
|
||||
|
||||
public static void submit(final PatchSet.Id patchSetId,
|
||||
final IdentifiedUser user, final ReviewDb db,
|
||||
final MergeOp.Factory opFactory, final MergeQueue merger)
|
||||
|
||||
@@ -16,32 +16,50 @@ package com.google.gerrit.server.git;
|
||||
|
||||
enum CommitMergeStatus {
|
||||
/** */
|
||||
CLEAN_MERGE,
|
||||
CLEAN_MERGE("Change has been successfully merged into the git repository."),
|
||||
|
||||
/** */
|
||||
CLEAN_PICK,
|
||||
CLEAN_PICK("Change has been successfully cherry-picked"),
|
||||
|
||||
/** */
|
||||
ALREADY_MERGED,
|
||||
ALREADY_MERGED(""),
|
||||
|
||||
/** */
|
||||
PATH_CONFLICT,
|
||||
PATH_CONFLICT("Your change could not be merged due to a path conflict.\n"
|
||||
+ "\n"
|
||||
+ "Please merge (or rebase) the change locally and upload the resolution for review."),
|
||||
|
||||
/** */
|
||||
MISSING_DEPENDENCY,
|
||||
MISSING_DEPENDENCY(""),
|
||||
|
||||
/** */
|
||||
NO_PATCH_SET,
|
||||
NO_PATCH_SET(""),
|
||||
|
||||
/** */
|
||||
REVISION_GONE,
|
||||
REVISION_GONE(""),
|
||||
|
||||
/** */
|
||||
CRISS_CROSS_MERGE,
|
||||
CRISS_CROSS_MERGE("Your change requires a recursive merge to resolve.\n"
|
||||
+ "\n"
|
||||
+ "Please merge (or rebase) the change locally and upload the resolution for review."),
|
||||
|
||||
/** */
|
||||
CANNOT_CHERRY_PICK_ROOT,
|
||||
CANNOT_CHERRY_PICK_ROOT("Cannot cherry-pick an initial commit onto an existing branch.\n"
|
||||
+ "\n"
|
||||
+ "Please merge the change locally and upload the merge commit for review."),
|
||||
|
||||
/** */
|
||||
NOT_FAST_FORWARD;
|
||||
}
|
||||
NOT_FAST_FORWARD("Project policy requires all submissions to be a fast-forward.\n"
|
||||
+ "\n"
|
||||
+ "Please rebase the change locally and upload again for review.");
|
||||
|
||||
private String message;
|
||||
|
||||
CommitMergeStatus(String message){
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage(){
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import com.google.gerrit.common.ChangeHookRunner;
|
||||
import com.google.gerrit.common.data.ApprovalType;
|
||||
import com.google.gerrit.common.data.ApprovalTypes;
|
||||
import com.google.gerrit.common.data.Capable;
|
||||
import com.google.gerrit.reviewdb.Account;
|
||||
import com.google.gerrit.reviewdb.ApprovalCategory;
|
||||
import com.google.gerrit.reviewdb.Branch;
|
||||
@@ -174,8 +175,7 @@ public class MergeOp {
|
||||
@GerritPersonIdent final PersonIdent myIdent,
|
||||
final MergeQueue mergeQueue, @Assisted final Branch.NameKey branch,
|
||||
final ChangeHookRunner hooks, final AccountCache accountCache,
|
||||
final TagCache tagCache,
|
||||
final CreateCodeReviewNotes.Factory crnf) {
|
||||
final TagCache tagCache, final CreateCodeReviewNotes.Factory crnf) {
|
||||
repoManager = grm;
|
||||
schemaFactory = sf;
|
||||
functionState = fs;
|
||||
@@ -200,20 +200,76 @@ public class MergeOp {
|
||||
commits = new HashMap<Change.Id, CodeReviewCommit>();
|
||||
}
|
||||
|
||||
public void merge() throws MergeException {
|
||||
public void verifyMergeability(Change change) {
|
||||
try {
|
||||
setDestProject();
|
||||
openRepository();
|
||||
final Ref destBranchRef = db.getRef(destBranch.get());
|
||||
submitted = new ArrayList<Change>();
|
||||
submitted.add(change);
|
||||
|
||||
// Test mergeability of the change if the last merged sha1
|
||||
// in the branch is different from the last sha1
|
||||
// the change was tested against.
|
||||
if ((destBranchRef == null && change.getLastSha1MergeTested() == null)
|
||||
|| change.getLastSha1MergeTested() == null
|
||||
|| (destBranchRef != null && !destBranchRef.getObjectId().getName()
|
||||
.equals(change.getLastSha1MergeTested().get()))) {
|
||||
openSchema();
|
||||
preMerge();
|
||||
|
||||
// update sha1 tested merge.
|
||||
if (destBranchRef != null) {
|
||||
change.setLastSha1MergeTested(new RevId(destBranchRef
|
||||
.getObjectId().getName()));
|
||||
} else {
|
||||
change.setLastSha1MergeTested(new RevId(""));
|
||||
}
|
||||
change.setMergeable(isMergeable(change));
|
||||
schema.changes().update(Collections.singleton(change));
|
||||
}
|
||||
} catch (MergeException e) {
|
||||
log.error("Test merge attempt for change: " + change.getId()
|
||||
+ " failed", e);
|
||||
} catch (OrmException e) {
|
||||
log.error("Test merge attempt for change: " + change.getId()
|
||||
+ " failed: Not able to query the database", e);
|
||||
} catch (IOException e) {
|
||||
log.error("Test merge attempt for change: " + change.getId()
|
||||
+ " failed", e);
|
||||
} finally {
|
||||
if (schema != null) {
|
||||
schema.close();
|
||||
}
|
||||
schema = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setDestProject() throws MergeException {
|
||||
final ProjectState pe = projectCache.get(destBranch.getParentKey());
|
||||
if (pe == null) {
|
||||
throw new MergeException("No such project: " + destBranch.getParentKey());
|
||||
}
|
||||
destProject = pe.getProject();
|
||||
}
|
||||
|
||||
try {
|
||||
private void openSchema() throws OrmException {
|
||||
if (schema == null) {
|
||||
schema = schemaFactory.open();
|
||||
} catch (OrmException e) {
|
||||
throw new MergeException("Cannot open database", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void merge() throws MergeException {
|
||||
setDestProject();
|
||||
try {
|
||||
mergeImpl();
|
||||
openSchema();
|
||||
openRepository();
|
||||
submitted = schema.changes().submitted(destBranch).toList();
|
||||
preMerge();
|
||||
updateBranch();
|
||||
updateChangeStatus();
|
||||
} catch (OrmException e) {
|
||||
throw new MergeException("Cannot query the database", e);
|
||||
} finally {
|
||||
if (rw != null) {
|
||||
rw.release();
|
||||
@@ -226,10 +282,8 @@ public class MergeOp {
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeImpl() throws MergeException {
|
||||
openRepository();
|
||||
private void preMerge() throws MergeException {
|
||||
openBranch();
|
||||
listPendingSubmits();
|
||||
validateChangeList();
|
||||
mergeTip = branchTip;
|
||||
switch (destProject.getSubmitType()) {
|
||||
@@ -246,8 +300,6 @@ public class MergeOp {
|
||||
markCleanMerges();
|
||||
break;
|
||||
}
|
||||
updateBranch();
|
||||
updateChangeStatus();
|
||||
}
|
||||
|
||||
private void openRepository() throws MergeException {
|
||||
@@ -298,14 +350,6 @@ public class MergeOp {
|
||||
}
|
||||
}
|
||||
|
||||
private void listPendingSubmits() throws MergeException {
|
||||
try {
|
||||
submitted = schema.changes().submitted(destBranch).toList();
|
||||
} catch (OrmException e) {
|
||||
throw new MergeException("Cannot query the database", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateChangeList() throws MergeException {
|
||||
final Set<ObjectId> tips = new HashSet<ObjectId>();
|
||||
for (final Ref r : db.getAllRefs().values()) {
|
||||
@@ -942,6 +986,20 @@ public class MergeOp {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMergeable(Change c) throws OrmException {
|
||||
final CodeReviewCommit commit = commits.get(c.getId());
|
||||
final CommitMergeStatus s = commit != null ? commit.statusCode : null;
|
||||
boolean isMergeable = false;
|
||||
if (s != null
|
||||
&& (s.equals(CommitMergeStatus.CLEAN_MERGE)
|
||||
|| s.equals(CommitMergeStatus.CLEAN_PICK) || s
|
||||
.equals(CommitMergeStatus.ALREADY_MERGED))) {
|
||||
isMergeable = true;
|
||||
}
|
||||
|
||||
return isMergeable;
|
||||
}
|
||||
|
||||
private void updateChangeStatus() throws MergeException {
|
||||
List<CodeReviewCommit> merged = new ArrayList<CodeReviewCommit>();
|
||||
|
||||
@@ -955,19 +1013,16 @@ public class MergeOp {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String txt = s.getMessage();
|
||||
|
||||
switch (s) {
|
||||
case CLEAN_MERGE: {
|
||||
final String txt =
|
||||
"Change has been successfully merged into the git repository.";
|
||||
setMerged(c, message(c, txt));
|
||||
merged.add(commit);
|
||||
break;
|
||||
}
|
||||
|
||||
case CLEAN_PICK: {
|
||||
final String txt =
|
||||
"Change has been successfully cherry-picked as " + commit.name()
|
||||
+ ".";
|
||||
setMerged(c, message(c, txt));
|
||||
merged.add(commit);
|
||||
break;
|
||||
@@ -978,44 +1033,19 @@ public class MergeOp {
|
||||
merged.add(commit);
|
||||
break;
|
||||
|
||||
case PATH_CONFLICT: {
|
||||
final String txt =
|
||||
"Your change could not be merged due to a path conflict.\n"
|
||||
+ "\n"
|
||||
+ "Please merge (or rebase) the change locally and upload the resolution for review.";
|
||||
setNew(c, message(c, txt));
|
||||
break;
|
||||
}
|
||||
|
||||
case CRISS_CROSS_MERGE: {
|
||||
final String txt =
|
||||
"Your change requires a recursive merge to resolve.\n"
|
||||
+ "\n"
|
||||
+ "Please merge (or rebase) the change locally and upload the resolution for review.";
|
||||
setNew(c, message(c, txt));
|
||||
break;
|
||||
}
|
||||
|
||||
case CANNOT_CHERRY_PICK_ROOT: {
|
||||
final String txt =
|
||||
"Cannot cherry-pick an initial commit onto an existing branch.\n"
|
||||
+ "\n"
|
||||
+ "Please merge the change locally and upload the merge commit for review.";
|
||||
setNew(c, message(c, txt));
|
||||
break;
|
||||
}
|
||||
|
||||
case PATH_CONFLICT:
|
||||
case CRISS_CROSS_MERGE:
|
||||
case CANNOT_CHERRY_PICK_ROOT:
|
||||
case NOT_FAST_FORWARD: {
|
||||
final String txt =
|
||||
"Project policy requires all submissions to be a fast-forward.\n"
|
||||
+ "\n"
|
||||
+ "Please rebase the change locally and upload again for review.";
|
||||
setNew(c, message(c, txt));
|
||||
break;
|
||||
}
|
||||
|
||||
case MISSING_DEPENDENCY: {
|
||||
dependencyError(commit);
|
||||
final Capable capable = isSubmitStillPossible(commit);
|
||||
if (capable != Capable.OK) {
|
||||
sendMergeFail(c, message(c, capable.getMessage()), false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1036,7 +1066,8 @@ public class MergeOp {
|
||||
GitRepositoryManager.REFS_NOTES_REVIEW);
|
||||
}
|
||||
|
||||
private void dependencyError(final CodeReviewCommit commit) {
|
||||
private Capable isSubmitStillPossible(final CodeReviewCommit commit) {
|
||||
final Capable capable;
|
||||
final Change c = commit.change;
|
||||
if (commit.missing == null) {
|
||||
commit.missing = new ArrayList<CodeReviewCommit>();
|
||||
@@ -1071,7 +1102,7 @@ public class MergeOp {
|
||||
// this change submitted. Reschedule an attempt in a bit.
|
||||
//
|
||||
mergeQueue.recheckAfter(destBranch, waitUntil - now, MILLISECONDS);
|
||||
|
||||
capable = Capable.OK;
|
||||
} else if (submitStillPossible) {
|
||||
// It would be possible to submit the change if the missing
|
||||
// dependencies are also submitted. Perhaps the user just
|
||||
@@ -1095,9 +1126,7 @@ public class MergeOp {
|
||||
}
|
||||
txt = m.toString();
|
||||
}
|
||||
|
||||
sendMergeFail(c, message(c, txt), false);
|
||||
|
||||
capable = new Capable(txt);
|
||||
} else {
|
||||
// It is impossible to submit this change as-is. The author
|
||||
// needs to rebase it in order to work around the missing
|
||||
@@ -1127,9 +1156,10 @@ public class MergeOp {
|
||||
}
|
||||
m.append("\n");
|
||||
m.append("Please rebase the change and upload a replacement commit.");
|
||||
|
||||
setNew(c, message(c, m.toString()));
|
||||
capable = new Capable(m.toString());
|
||||
}
|
||||
|
||||
return capable;
|
||||
}
|
||||
|
||||
private void loadChangeInfo(final CodeReviewCommit commit) {
|
||||
@@ -1215,6 +1245,10 @@ public class MergeOp {
|
||||
@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.
|
||||
|
||||
@@ -1169,6 +1169,7 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
|
||||
public Change update(Change change) {
|
||||
if (change.getStatus().isOpen()) {
|
||||
change.nextPatchSetId();
|
||||
change.setLastSha1MergeTested(null);
|
||||
return change;
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
/** A version of the database schema. */
|
||||
public abstract class SchemaVersion {
|
||||
/** The current schema version. */
|
||||
private static final Class<? extends SchemaVersion> C = Schema_58.class;
|
||||
private static final Class<? extends SchemaVersion> C = Schema_59.class;
|
||||
|
||||
public static class Module extends AbstractModule {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2011 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.gerrit.reviewdb.Change;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gwtorm.client.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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user