Adds a "revert change"-button to a submitted patchset.
Change-Id: I409e656c88a7021f604c021ca3987d2e917c18d9
This commit is contained in:
committed by
Ulrik Sjölin
parent
0908bff975
commit
262629e6d3
@@ -31,6 +31,7 @@ public class ChangeDetail {
|
|||||||
protected boolean allowsAnonymous;
|
protected boolean allowsAnonymous;
|
||||||
protected boolean canAbandon;
|
protected boolean canAbandon;
|
||||||
protected boolean canRestore;
|
protected boolean canRestore;
|
||||||
|
protected boolean canRevert;
|
||||||
protected Change change;
|
protected Change change;
|
||||||
protected boolean starred;
|
protected boolean starred;
|
||||||
protected List<ChangeInfo> dependsOn;
|
protected List<ChangeInfo> dependsOn;
|
||||||
@@ -78,6 +79,14 @@ public class ChangeDetail {
|
|||||||
canRestore = a;
|
canRestore = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canRevert() {
|
||||||
|
return canRevert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCanRevert(boolean a) {
|
||||||
|
canRevert = a;
|
||||||
|
}
|
||||||
|
|
||||||
public Change getChange() {
|
public Change getChange() {
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ public interface ChangeManageService extends RemoteJsonService {
|
|||||||
void abandonChange(PatchSet.Id patchSetId, String message,
|
void abandonChange(PatchSet.Id patchSetId, String message,
|
||||||
AsyncCallback<ChangeDetail> callback);
|
AsyncCallback<ChangeDetail> callback);
|
||||||
|
|
||||||
|
@SignInRequired
|
||||||
|
void revertChange(PatchSet.Id patchSetId, String message,
|
||||||
|
AsyncCallback<ChangeDetail> callback);
|
||||||
|
|
||||||
@SignInRequired
|
@SignInRequired
|
||||||
void restoreChange(PatchSet.Id patchSetId, String message,
|
void restoreChange(PatchSet.Id patchSetId, String message,
|
||||||
AsyncCallback<ChangeDetail> callback);
|
AsyncCallback<ChangeDetail> callback);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public interface GerritCss extends CssResource {
|
|||||||
String greenCheckClass();
|
String greenCheckClass();
|
||||||
String abandonChangeDialog();
|
String abandonChangeDialog();
|
||||||
String abandonMessage();
|
String abandonMessage();
|
||||||
|
String revertChangeDialog();
|
||||||
|
String revertMessage();
|
||||||
String accountContactOnFile();
|
String accountContactOnFile();
|
||||||
String accountContactPrivacyDetails();
|
String accountContactPrivacyDetails();
|
||||||
String accountDashboard();
|
String accountDashboard();
|
||||||
|
|||||||
@@ -100,6 +100,12 @@ public interface ChangeConstants extends Constants {
|
|||||||
String patchSetInfoCommitter();
|
String patchSetInfoCommitter();
|
||||||
String patchSetInfoDownload();
|
String patchSetInfoDownload();
|
||||||
|
|
||||||
|
String buttonRevertChangeBegin();
|
||||||
|
String buttonRevertChangeSend();
|
||||||
|
String buttonRevertChangeCancel();
|
||||||
|
String headingRevertMessage();
|
||||||
|
String revertChangeTitle();
|
||||||
|
|
||||||
String buttonAbandonChangeBegin();
|
String buttonAbandonChangeBegin();
|
||||||
String buttonAbandonChangeSend();
|
String buttonAbandonChangeSend();
|
||||||
String buttonAbandonChangeCancel();
|
String buttonAbandonChangeCancel();
|
||||||
|
|||||||
@@ -83,6 +83,12 @@ buttonAbandonChangeCancel = Cancel
|
|||||||
headingAbandonMessage = Abandon Message:
|
headingAbandonMessage = Abandon Message:
|
||||||
abandonChangeTitle = Code Review - Abandon Change
|
abandonChangeTitle = Code Review - Abandon Change
|
||||||
|
|
||||||
|
buttonRevertChangeBegin = Revert Change
|
||||||
|
buttonRevertChangeSend = Revert Change
|
||||||
|
buttonRevertChangeCancel = Cancel
|
||||||
|
headingRevertMessage = Revert Commit Message:
|
||||||
|
revertChangeTitle = Code Review - Revert Merged Change
|
||||||
|
|
||||||
buttonRestoreChangeBegin = Restore Change
|
buttonRestoreChangeBegin = Restore Change
|
||||||
restoreChangeTitle = Code Review - Restore Change
|
restoreChangeTitle = Code Review - Restore Change
|
||||||
buttonRestoreChangeCancel = Cancel
|
buttonRestoreChangeCancel = Cancel
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ public interface ChangeMessages extends Messages {
|
|||||||
String changesMergedInProject(String string);
|
String changesMergedInProject(String string);
|
||||||
String changesAbandonedInProject(String string);
|
String changesAbandonedInProject(String string);
|
||||||
|
|
||||||
|
String revertChangeDefaultMessage(String commitMsg, String commitId);
|
||||||
|
|
||||||
String changeScreenTitleId(String changeId);
|
String changeScreenTitleId(String changeId);
|
||||||
String patchSetHeader(int id);
|
String patchSetHeader(int id);
|
||||||
String loadingPatchSet(int id);
|
String loadingPatchSet(int id);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ changesOpenInProject = Open Changes In {0}
|
|||||||
changesMergedInProject = Merged Changes In {0}
|
changesMergedInProject = Merged Changes In {0}
|
||||||
changesAbandonedInProject = Abandoned Changes In {0}
|
changesAbandonedInProject = Abandoned Changes In {0}
|
||||||
|
|
||||||
|
revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}
|
||||||
|
|
||||||
changeScreenTitleId = Change {0}
|
changeScreenTitleId = Change {0}
|
||||||
patchSetHeader = Patch Set {0}
|
patchSetHeader = Patch Set {0}
|
||||||
loadingPatchSet = Loading Patch Set {0} ...
|
loadingPatchSet = Loading Patch Set {0} ...
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ public abstract class CommentedChangeActionDialog extends AutoCenterDialogBox im
|
|||||||
final String dialogHeading, final String buttonSend,
|
final String dialogHeading, final String buttonSend,
|
||||||
final String buttonCancel, final String dialogStyle,
|
final String buttonCancel, final String dialogStyle,
|
||||||
final String messageStyle) {
|
final String messageStyle) {
|
||||||
|
this(psi, callback, dialogTitle, dialogHeading, buttonSend, buttonCancel, dialogStyle, messageStyle, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommentedChangeActionDialog(final PatchSet.Id psi,
|
||||||
|
final AsyncCallback<ChangeDetail> callback, final String dialogTitle,
|
||||||
|
final String dialogHeading, final String buttonSend,
|
||||||
|
final String buttonCancel, final String dialogStyle,
|
||||||
|
final String messageStyle, final String defaultMessage) {
|
||||||
super(/* auto hide */false, /* modal */true);
|
super(/* auto hide */false, /* modal */true);
|
||||||
setGlassEnabled(true);
|
setGlassEnabled(true);
|
||||||
|
|
||||||
@@ -67,6 +75,7 @@ public abstract class CommentedChangeActionDialog extends AutoCenterDialogBox im
|
|||||||
message = new NpTextArea();
|
message = new NpTextArea();
|
||||||
message.setCharacterWidth(60);
|
message.setCharacterWidth(60);
|
||||||
message.setVisibleLines(10);
|
message.setVisibleLines(10);
|
||||||
|
message.setText(defaultMessage);
|
||||||
DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
|
DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
|
||||||
mwrap.add(message);
|
mwrap.add(message);
|
||||||
|
|
||||||
|
|||||||
@@ -395,6 +395,26 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
|||||||
actionsPanel.add(b);
|
actionsPanel.add(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changeDetail.canRevert()) {
|
||||||
|
final Button b = new Button(Util.C.buttonRevertChangeBegin());
|
||||||
|
b.addClickHandler(new ClickHandler() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final ClickEvent event) {
|
||||||
|
b.setEnabled(false);
|
||||||
|
new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b),
|
||||||
|
Util.C.revertChangeTitle(), Util.C.headingRevertMessage(),
|
||||||
|
Util.C.buttonRevertChangeSend(), Util.C.buttonRevertChangeCancel(),
|
||||||
|
Gerrit.RESOURCES.css().revertChangeDialog(), Gerrit.RESOURCES.css().revertMessage(),
|
||||||
|
Util.M.revertChangeDefaultMessage(detail.getInfo().getSubject(), detail.getPatchSet().getRevision().get())) {
|
||||||
|
public void onSend() {
|
||||||
|
Util.MANAGE_SVC.revertChange(getPatchSetId() , getMessageText(), createCallback());
|
||||||
|
}
|
||||||
|
}.center();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
actionsPanel.add(b);
|
||||||
|
}
|
||||||
|
|
||||||
if (changeDetail.canAbandon()) {
|
if (changeDetail.canAbandon()) {
|
||||||
final Button b = new Button(Util.C.buttonAbandonChangeBegin());
|
final Button b = new Button(Util.C.buttonAbandonChangeBegin());
|
||||||
b.addClickHandler(new ClickHandler() {
|
b.addClickHandler(new ClickHandler() {
|
||||||
|
|||||||
@@ -1230,6 +1230,31 @@ a:hover.downloadLink {
|
|||||||
font-size: small;
|
font-size: small;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** RevertChangeDialog **/
|
||||||
|
|
||||||
|
.revertChangeDialog .gwt-DisclosurePanel .header td {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revertChangeDialog .smallHeading {
|
||||||
|
font-size: small;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.revertChangeDialog .revertMessage {
|
||||||
|
margin-left: 10px;
|
||||||
|
background: trimColor;
|
||||||
|
padding: 5px 5px 5px 5px;
|
||||||
|
}
|
||||||
|
.revertChangeDialog .revertMessage textarea {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
.revertChangeDialog .gwt-Hyperlink {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** PatchBrowserPopup **/
|
/** PatchBrowserPopup **/
|
||||||
.patchBrowserPopup {
|
.patchBrowserPopup {
|
||||||
|
|||||||
@@ -100,10 +100,14 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
|||||||
detail = new ChangeDetail();
|
detail = new ChangeDetail();
|
||||||
detail.setChange(change);
|
detail.setChange(change);
|
||||||
detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
|
detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
|
||||||
|
|
||||||
detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
|
detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
|
||||||
detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
|
detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
|
||||||
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
|
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
|
||||||
changeId));
|
changeId));
|
||||||
|
|
||||||
|
detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
|
||||||
|
|
||||||
loadPatchSets();
|
loadPatchSets();
|
||||||
loadMessages();
|
loadMessages();
|
||||||
if (change.currentPatchSetId() != null) {
|
if (change.currentPatchSetId() != null) {
|
||||||
|
|||||||
@@ -24,14 +24,17 @@ class ChangeManageServiceImpl implements ChangeManageService {
|
|||||||
private final SubmitAction.Factory submitAction;
|
private final SubmitAction.Factory submitAction;
|
||||||
private final AbandonChange.Factory abandonChangeFactory;
|
private final AbandonChange.Factory abandonChangeFactory;
|
||||||
private final RestoreChange.Factory restoreChangeFactory;
|
private final RestoreChange.Factory restoreChangeFactory;
|
||||||
|
private final RevertChange.Factory revertChangeFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
|
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
|
||||||
final AbandonChange.Factory abandonChangeFactory,
|
final AbandonChange.Factory abandonChangeFactory,
|
||||||
final RestoreChange.Factory restoreChangeFactory) {
|
final RestoreChange.Factory restoreChangeFactory,
|
||||||
|
final RevertChange.Factory revertChangeFactory) {
|
||||||
this.submitAction = patchSetAction;
|
this.submitAction = patchSetAction;
|
||||||
this.abandonChangeFactory = abandonChangeFactory;
|
this.abandonChangeFactory = abandonChangeFactory;
|
||||||
this.restoreChangeFactory = restoreChangeFactory;
|
this.restoreChangeFactory = restoreChangeFactory;
|
||||||
|
this.revertChangeFactory = revertChangeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(final PatchSet.Id patchSetId,
|
public void submit(final PatchSet.Id patchSetId,
|
||||||
@@ -44,6 +47,11 @@ class ChangeManageServiceImpl implements ChangeManageService {
|
|||||||
abandonChangeFactory.create(patchSetId, message).to(callback);
|
abandonChangeFactory.create(patchSetId, message).to(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void revertChange(final PatchSet.Id patchSetId, final String message,
|
||||||
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
|
revertChangeFactory.create(patchSetId, message).to(callback);
|
||||||
|
}
|
||||||
|
|
||||||
public void restoreChange(final PatchSet.Id patchSetId, final String message,
|
public void restoreChange(final PatchSet.Id patchSetId, final String message,
|
||||||
final AsyncCallback<ChangeDetail> callback) {
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
restoreChangeFactory.create(patchSetId, message).to(callback);
|
restoreChangeFactory.create(patchSetId, message).to(callback);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public class ChangeModule extends RpcServletModule {
|
|||||||
protected void configure() {
|
protected void configure() {
|
||||||
factory(AbandonChange.Factory.class);
|
factory(AbandonChange.Factory.class);
|
||||||
factory(RestoreChange.Factory.class);
|
factory(RestoreChange.Factory.class);
|
||||||
|
factory(RevertChange.Factory.class);
|
||||||
factory(ChangeDetailFactory.Factory.class);
|
factory(ChangeDetailFactory.Factory.class);
|
||||||
factory(IncludedInDetailFactory.Factory.class);
|
factory(IncludedInDetailFactory.Factory.class);
|
||||||
factory(PatchSetDetailFactory.Factory.class);
|
factory(PatchSetDetailFactory.Factory.class);
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// 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.httpd.rpc.changedetail;
|
||||||
|
|
||||||
|
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.Change;
|
||||||
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.ReviewDb;
|
||||||
|
import com.google.gerrit.server.ChangeUtil;
|
||||||
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.ReplicationQueue;
|
||||||
|
import com.google.gerrit.server.mail.EmailException;
|
||||||
|
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.ChangeControl;
|
||||||
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
|
import com.google.gwtorm.client.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
class RevertChange extends Handler<ChangeDetail> {
|
||||||
|
interface Factory {
|
||||||
|
RevertChange create(PatchSet.Id patchSetId, String message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ChangeControl.Factory changeControlFactory;
|
||||||
|
private final ReviewDb db;
|
||||||
|
private final IdentifiedUser currentUser;
|
||||||
|
private final RevertedSender.Factory revertedSenderFactory;
|
||||||
|
private final ChangeDetailFactory.Factory changeDetailFactory;
|
||||||
|
private final ReplicationQueue replication;
|
||||||
|
|
||||||
|
private final PatchSet.Id patchSetId;
|
||||||
|
@Nullable
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private final ChangeHookRunner hooks;
|
||||||
|
|
||||||
|
private final GitRepositoryManager gitManager;
|
||||||
|
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||||
|
|
||||||
|
private final PersonIdent myIdent;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RevertChange(final ChangeControl.Factory changeControlFactory,
|
||||||
|
final ReviewDb db, final IdentifiedUser currentUser,
|
||||||
|
final RevertedSender.Factory revertedSenderFactory,
|
||||||
|
final ChangeDetailFactory.Factory changeDetailFactory,
|
||||||
|
@Assisted final PatchSet.Id patchSetId,
|
||||||
|
@Assisted @Nullable final String message, final ChangeHookRunner hooks,
|
||||||
|
final GitRepositoryManager gitManager,
|
||||||
|
final PatchSetInfoFactory patchSetInfoFactory,
|
||||||
|
final ReplicationQueue replication,
|
||||||
|
@GerritPersonIdent final PersonIdent myIdent) {
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
|
this.db = db;
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
this.revertedSenderFactory = revertedSenderFactory;
|
||||||
|
this.changeDetailFactory = changeDetailFactory;
|
||||||
|
|
||||||
|
this.patchSetId = patchSetId;
|
||||||
|
this.message = message;
|
||||||
|
this.hooks = hooks;
|
||||||
|
this.gitManager = gitManager;
|
||||||
|
|
||||||
|
this.patchSetInfoFactory = patchSetInfoFactory;
|
||||||
|
this.replication = replication;
|
||||||
|
this.myIdent = myIdent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChangeDetail call() throws NoSuchChangeException, OrmException,
|
||||||
|
EmailException, NoSuchEntityException, PatchSetInfoNotAvailableException,
|
||||||
|
MissingObjectException, IncorrectObjectTypeException, IOException {
|
||||||
|
|
||||||
|
final Change.Id changeId = patchSetId.getParentKey();
|
||||||
|
final ChangeControl control = changeControlFactory.validateFor(changeId);
|
||||||
|
if (!control.canAddPatchSet()) {
|
||||||
|
throw new NoSuchChangeException(changeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeUtil.revert(patchSetId, currentUser, message, db,
|
||||||
|
revertedSenderFactory, hooks, gitManager, patchSetInfoFactory,
|
||||||
|
replication, myIdent);
|
||||||
|
|
||||||
|
return changeDetailFactory.create(changeId).call();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,31 +17,48 @@ package com.google.gerrit.server;
|
|||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
|
import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
|
||||||
|
|
||||||
import com.google.gerrit.common.ChangeHookRunner;
|
import com.google.gerrit.common.ChangeHookRunner;
|
||||||
import com.google.gerrit.common.data.ChangeDetail;
|
|
||||||
import com.google.gerrit.reviewdb.Change;
|
import com.google.gerrit.reviewdb.Change;
|
||||||
import com.google.gerrit.reviewdb.ChangeMessage;
|
import com.google.gerrit.reviewdb.ChangeMessage;
|
||||||
import com.google.gerrit.reviewdb.Change;
|
|
||||||
import com.google.gerrit.reviewdb.PatchSet;
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
|
import com.google.gerrit.reviewdb.PatchSetApproval;
|
||||||
|
import com.google.gerrit.reviewdb.PatchSetInfo;
|
||||||
|
import com.google.gerrit.reviewdb.RevId;
|
||||||
import com.google.gerrit.reviewdb.ReviewDb;
|
import com.google.gerrit.reviewdb.ReviewDb;
|
||||||
import com.google.gerrit.reviewdb.TrackingId;
|
import com.google.gerrit.reviewdb.TrackingId;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.config.TrackingFooter;
|
import com.google.gerrit.server.config.TrackingFooter;
|
||||||
import com.google.gerrit.server.config.TrackingFooters;
|
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.MergeOp;
|
||||||
import com.google.gerrit.server.git.MergeQueue;
|
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.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.mail.AbandonedSender;
|
import com.google.gerrit.server.mail.AbandonedSender;
|
||||||
import com.google.gerrit.server.mail.EmailException;
|
import com.google.gerrit.server.mail.EmailException;
|
||||||
|
import com.google.gerrit.server.mail.RevertedSender;
|
||||||
import com.google.gwtorm.client.AtomicUpdate;
|
import com.google.gwtorm.client.AtomicUpdate;
|
||||||
import com.google.gwtorm.client.OrmConcurrencyException;
|
import com.google.gwtorm.client.OrmConcurrencyException;
|
||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectInserter;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.FooterLine;
|
import org.eclipse.jgit.revwalk.FooterLine;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
import org.eclipse.jgit.util.Base64;
|
import org.eclipse.jgit.util.Base64;
|
||||||
import org.eclipse.jgit.util.NB;
|
import org.eclipse.jgit.util.NB;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -248,6 +265,108 @@ public class ChangeUtil {
|
|||||||
hooks.doChangeAbandonedHook(updatedChange, user.getAccount(), message);
|
hooks.doChangeAbandonedHook(updatedChange, user.getAccount(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void revert(final PatchSet.Id patchSetId,
|
||||||
|
final IdentifiedUser user, final String message, final ReviewDb db,
|
||||||
|
final RevertedSender.Factory revertedSenderFactory,
|
||||||
|
final ChangeHookRunner hooks, GitRepositoryManager gitManager,
|
||||||
|
final PatchSetInfoFactory patchSetInfoFactory,
|
||||||
|
final ReplicationQueue replication, PersonIdent myIdent)
|
||||||
|
throws NoSuchChangeException, EmailException, OrmException,
|
||||||
|
MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||||
|
PatchSetInfoNotAvailableException {
|
||||||
|
|
||||||
|
final Change.Id changeId = patchSetId.getParentKey();
|
||||||
|
final PatchSet patch = db.patchSets().get(patchSetId);
|
||||||
|
if (patch == null) {
|
||||||
|
throw new NoSuchChangeException(changeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Repository git;
|
||||||
|
try {
|
||||||
|
git = gitManager.openRepository(db.changes().get(changeId).getProject());
|
||||||
|
} catch (RepositoryNotFoundException e) {
|
||||||
|
throw new NoSuchChangeException(changeId, e);
|
||||||
|
};
|
||||||
|
|
||||||
|
final RevWalk revWalk = new RevWalk(git);
|
||||||
|
try {
|
||||||
|
RevCommit commitToRevert =
|
||||||
|
revWalk.parseCommit(ObjectId.fromString(patch.getRevision().get()));
|
||||||
|
|
||||||
|
PersonIdent authorIdent =
|
||||||
|
user.newCommitterIdent(myIdent.getWhen(), myIdent.getTimeZone());
|
||||||
|
|
||||||
|
RevCommit parentToCommitToRevert = commitToRevert.getParent(0);
|
||||||
|
revWalk.parseHeaders(parentToCommitToRevert);
|
||||||
|
|
||||||
|
CommitBuilder revertCommit = new CommitBuilder();
|
||||||
|
revertCommit.addParentId(commitToRevert);
|
||||||
|
revertCommit.setTreeId(parentToCommitToRevert.getTree());
|
||||||
|
revertCommit.setAuthor(authorIdent);
|
||||||
|
revertCommit.setCommitter(myIdent);
|
||||||
|
revertCommit.setMessage(message);
|
||||||
|
|
||||||
|
final ObjectInserter oi = git.newObjectInserter();;
|
||||||
|
ObjectId id;
|
||||||
|
try {
|
||||||
|
id = oi.insert(revertCommit);
|
||||||
|
oi.flush();
|
||||||
|
} finally {
|
||||||
|
oi.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Change.Key changeKey = new Change.Key("I" + id.name());
|
||||||
|
final Change change =
|
||||||
|
new Change(changeKey, new Change.Id(db.nextChangeId()),
|
||||||
|
user.getAccountId(), db.changes().get(changeId).getDest());
|
||||||
|
change.nextPatchSetId();
|
||||||
|
|
||||||
|
final PatchSet ps = new PatchSet(change.currPatchSetId());
|
||||||
|
ps.setCreatedOn(change.getCreatedOn());
|
||||||
|
ps.setUploader(user.getAccountId());
|
||||||
|
ps.setRevision(new RevId(id.getName()));
|
||||||
|
|
||||||
|
db.patchSets().insert(Collections.singleton(ps));
|
||||||
|
|
||||||
|
final PatchSetInfo info =
|
||||||
|
patchSetInfoFactory.get(revWalk.parseCommit(id), ps.getId());
|
||||||
|
change.setCurrentPatchSet(info);
|
||||||
|
ChangeUtil.updated(change);
|
||||||
|
db.changes().insert(Collections.singleton(change));
|
||||||
|
|
||||||
|
final RefUpdate ru = git.updateRef(ps.getRefName());
|
||||||
|
ru.setNewObjectId(id);
|
||||||
|
ru.disableRefLog();
|
||||||
|
if (ru.update(revWalk) != RefUpdate.Result.NEW) {
|
||||||
|
throw new IOException("Failed to create ref " + ps.getRefName()
|
||||||
|
+ " in " + git.getDirectory() + ": " + ru.getResult());
|
||||||
|
}
|
||||||
|
replication.scheduleUpdate(db.changes().get(changeId).getProject(),
|
||||||
|
ru.getName());
|
||||||
|
|
||||||
|
final ChangeMessage cmsg =
|
||||||
|
new ChangeMessage(new ChangeMessage.Key(changeId,
|
||||||
|
ChangeUtil.messageUUID(db)), user.getAccountId());
|
||||||
|
final StringBuilder msgBuf =
|
||||||
|
new StringBuilder("Patch Set " + patchSetId.get() + ": Reverted");
|
||||||
|
msgBuf.append("\n\n");
|
||||||
|
msgBuf.append("This patchset was reverted in change: " + changeKey.get());
|
||||||
|
|
||||||
|
cmsg.setMessage(msgBuf.toString());
|
||||||
|
db.changeMessages().insert(Collections.singleton(cmsg));
|
||||||
|
|
||||||
|
final RevertedSender cm = revertedSenderFactory.create(change);
|
||||||
|
cm.setFrom(user.getAccountId());
|
||||||
|
cm.setChangeMessage(cmsg);
|
||||||
|
cm.send();
|
||||||
|
|
||||||
|
hooks.doPatchsetCreatedHook(change, ps);
|
||||||
|
} finally {
|
||||||
|
revWalk.release();
|
||||||
|
git.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void restore(final PatchSet.Id patchSetId,
|
public static void restore(final PatchSet.Id patchSetId,
|
||||||
final IdentifiedUser user, final String message, final ReviewDb db,
|
final IdentifiedUser user, final String message, final ReviewDb db,
|
||||||
final AbandonedSender.Factory abandonedSenderFactory,
|
final AbandonedSender.Factory abandonedSenderFactory,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.google.gerrit.server.mail.MergeFailSender;
|
|||||||
import com.google.gerrit.server.mail.MergedSender;
|
import com.google.gerrit.server.mail.MergedSender;
|
||||||
import com.google.gerrit.server.mail.RegisterNewEmailSender;
|
import com.google.gerrit.server.mail.RegisterNewEmailSender;
|
||||||
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
||||||
|
import com.google.gerrit.server.mail.RevertedSender;
|
||||||
import com.google.gerrit.server.patch.PublishComments;
|
import com.google.gerrit.server.patch.PublishComments;
|
||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
@@ -66,6 +67,7 @@ public class GerritRequestModule extends FactoryModule {
|
|||||||
factory(PublishComments.Factory.class);
|
factory(PublishComments.Factory.class);
|
||||||
factory(ReplacePatchSetSender.Factory.class);
|
factory(ReplacePatchSetSender.Factory.class);
|
||||||
factory(AbandonedSender.Factory.class);
|
factory(AbandonedSender.Factory.class);
|
||||||
|
factory(RevertedSender.Factory.class);
|
||||||
factory(CommentSender.Factory.class);
|
factory(CommentSender.Factory.class);
|
||||||
factory(MergedSender.Factory.class);
|
factory(MergedSender.Factory.class);
|
||||||
factory(MergeFailSender.Factory.class);
|
factory(MergeFailSender.Factory.class);
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// 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.mail;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.Change;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
/** Send notice about a change being reverted. */
|
||||||
|
public class RevertedSender extends ReplyToChangeSender {
|
||||||
|
public static interface Factory {
|
||||||
|
RevertedSender create(Change change);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RevertedSender(EmailArguments ea, @Assisted Change c) {
|
||||||
|
super(ea, c, "revert");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() throws EmailException {
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
ccAllApprovals();
|
||||||
|
bccStarredBy();
|
||||||
|
bccWatchesNotifyAllComments();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void formatChange() throws EmailException {
|
||||||
|
appendText(velocifyFile("Reverted.vm"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
## Copyright (C) 2010 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.
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Template Type:
|
||||||
|
## -------------
|
||||||
|
## This is a velocity mail template, see: http://velocity.apache.org and the
|
||||||
|
## gerrit-docs:config-mail.txt for more info on modifying gerrit mail templates.
|
||||||
|
##
|
||||||
|
## Template File Names and extensions:
|
||||||
|
## ----------------------------------
|
||||||
|
## Gerrit will use templates ending in ".vm" but will ignore templates ending
|
||||||
|
## in ".vm.example". If a .vm template does not exist, the default internal
|
||||||
|
## gerrit template which is the same as the .vm.example will be used. If you
|
||||||
|
## want to override the default template, copy the .vm.exmaple file to a .vm
|
||||||
|
## file and edit it appropriately.
|
||||||
|
##
|
||||||
|
## This Template:
|
||||||
|
## --------------
|
||||||
|
## The Reverted.vm template will determine the contents of the email related
|
||||||
|
## to a change being reverted. It is a ChangeEmail: see ChangeSubject.vm and
|
||||||
|
## ChangeFooter.vm.
|
||||||
|
##
|
||||||
|
$fromName has reverted this change.
|
||||||
|
|
||||||
|
Change subject: $change.subject
|
||||||
|
......................................................................
|
||||||
|
|
||||||
|
|
||||||
|
#if ($coverLetter)
|
||||||
|
$coverLetter
|
||||||
|
|
||||||
|
#end
|
||||||
Reference in New Issue
Block a user