summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustaf Lundh <gustaf.lundh@sonyericsson.com>2011-05-18 19:58:59 -0700
committerUlrik Sjölin <ulrik.sjolin@sonyericsson.com>2011-05-19 14:23:03 -0700
commit262629e6d3ed6ec296fc4c2fc0b4ce4e93c8cfb8 (patch)
tree5ba2cc8f631de679580d6a88a5f56fc840bd84aa
parent0908bff975f27dfca0f6d4afa0cc970835ee8ec6 (diff)
Adds a "revert change"-button to a submitted patchset.
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java9
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java4
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java2
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java6
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties6
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java2
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties2
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentedChangeActionDialog.java9
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java20
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css25
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java4
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java10
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java1
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java114
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java123
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java2
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java45
-rw-r--r--gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.vm44
18 files changed, 425 insertions, 3 deletions
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
index cd992fdfe8..572fd7aad7 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -31,6 +31,7 @@ public class ChangeDetail {
protected boolean allowsAnonymous;
protected boolean canAbandon;
protected boolean canRestore;
+ protected boolean canRevert;
protected Change change;
protected boolean starred;
protected List<ChangeInfo> dependsOn;
@@ -78,6 +79,14 @@ public class ChangeDetail {
canRestore = a;
}
+ public boolean canRevert() {
+ return canRevert;
+ }
+
+ public void setCanRevert(boolean a) {
+ canRevert = a;
+ }
+
public Change getChange() {
return change;
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
index b61c35c1e4..cb38c3b802 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
@@ -31,6 +31,10 @@ public interface ChangeManageService extends RemoteJsonService {
AsyncCallback<ChangeDetail> callback);
@SignInRequired
+ void revertChange(PatchSet.Id patchSetId, String message,
+ AsyncCallback<ChangeDetail> callback);
+
+ @SignInRequired
void restoreChange(PatchSet.Id patchSetId, String message,
AsyncCallback<ChangeDetail> callback);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index e3dbb01526..e988540d9f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -20,6 +20,8 @@ public interface GerritCss extends CssResource {
String greenCheckClass();
String abandonChangeDialog();
String abandonMessage();
+ String revertChangeDialog();
+ String revertMessage();
String accountContactOnFile();
String accountContactPrivacyDetails();
String accountDashboard();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index abdaa15242..f87b5c28fe 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -100,6 +100,12 @@ public interface ChangeConstants extends Constants {
String patchSetInfoCommitter();
String patchSetInfoDownload();
+ String buttonRevertChangeBegin();
+ String buttonRevertChangeSend();
+ String buttonRevertChangeCancel();
+ String headingRevertMessage();
+ String revertChangeTitle();
+
String buttonAbandonChangeBegin();
String buttonAbandonChangeSend();
String buttonAbandonChangeCancel();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index 7cd0208f6b..5dc259c253 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -83,6 +83,12 @@ buttonAbandonChangeCancel = Cancel
headingAbandonMessage = Abandon Message:
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
restoreChangeTitle = Code Review - Restore Change
buttonRestoreChangeCancel = Cancel
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
index 312cc106c0..cbf4a6bd23 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
@@ -24,6 +24,8 @@ public interface ChangeMessages extends Messages {
String changesMergedInProject(String string);
String changesAbandonedInProject(String string);
+ String revertChangeDefaultMessage(String commitMsg, String commitId);
+
String changeScreenTitleId(String changeId);
String patchSetHeader(int id);
String loadingPatchSet(int id);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
index 625a9ecb8b..5982ad5d06 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
@@ -5,6 +5,8 @@ changesOpenInProject = Open Changes In {0}
changesMergedInProject = Merged Changes In {0}
changesAbandonedInProject = Abandoned Changes In {0}
+revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}
+
changeScreenTitleId = Change {0}
patchSetHeader = Patch Set {0}
loadingPatchSet = Loading Patch Set {0} ...
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentedChangeActionDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentedChangeActionDialog.java
index 5540b715bb..d725ac0800 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentedChangeActionDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentedChangeActionDialog.java
@@ -47,6 +47,14 @@ public abstract class CommentedChangeActionDialog extends AutoCenterDialogBox im
final String dialogHeading, final String buttonSend,
final String buttonCancel, final String dialogStyle,
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);
setGlassEnabled(true);
@@ -67,6 +75,7 @@ public abstract class CommentedChangeActionDialog extends AutoCenterDialogBox im
message = new NpTextArea();
message.setCharacterWidth(60);
message.setVisibleLines(10);
+ message.setText(defaultMessage);
DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
mwrap.add(message);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index e41d572a7f..a65c05d5d4 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -395,6 +395,26 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
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()) {
final Button b = new Button(Util.C.buttonAbandonChangeBegin());
b.addClickHandler(new ClickHandler() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 319b90c064..84354f7bf0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -1230,6 +1230,31 @@ a:hover.downloadLink {
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 {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
index 14cb1e15ee..00d83e2d07 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -100,10 +100,14 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
detail = new ChangeDetail();
detail.setChange(change);
detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
+
detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
changeId));
+
+ detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
+
loadPatchSets();
loadMessages();
if (change.currentPatchSetId() != null) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
index 0eccc2c3fd..9e04756edf 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
@@ -24,14 +24,17 @@ class ChangeManageServiceImpl implements ChangeManageService {
private final SubmitAction.Factory submitAction;
private final AbandonChange.Factory abandonChangeFactory;
private final RestoreChange.Factory restoreChangeFactory;
+ private final RevertChange.Factory revertChangeFactory;
@Inject
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
final AbandonChange.Factory abandonChangeFactory,
- final RestoreChange.Factory restoreChangeFactory) {
+ final RestoreChange.Factory restoreChangeFactory,
+ final RevertChange.Factory revertChangeFactory) {
this.submitAction = patchSetAction;
this.abandonChangeFactory = abandonChangeFactory;
this.restoreChangeFactory = restoreChangeFactory;
+ this.revertChangeFactory = revertChangeFactory;
}
public void submit(final PatchSet.Id patchSetId,
@@ -44,6 +47,11 @@ class ChangeManageServiceImpl implements ChangeManageService {
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,
final AsyncCallback<ChangeDetail> callback) {
restoreChangeFactory.create(patchSetId, message).to(callback);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
index 85e2aef018..95c438c15d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
@@ -30,6 +30,7 @@ public class ChangeModule extends RpcServletModule {
protected void configure() {
factory(AbandonChange.Factory.class);
factory(RestoreChange.Factory.class);
+ factory(RevertChange.Factory.class);
factory(ChangeDetailFactory.Factory.class);
factory(IncludedInDetailFactory.Factory.class);
factory(PatchSetDetailFactory.Factory.class);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
new file mode 100644
index 0000000000..3dfa0e3765
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
@@ -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();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 11ab9e05cb..6886d2c85d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -17,31 +17,48 @@ package com.google.gerrit.server;
import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
import com.google.gerrit.common.ChangeHookRunner;
-import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.ChangeMessage;
-import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
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.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.NoSuchChangeException;
import com.google.gerrit.server.mail.AbandonedSender;
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.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;
+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.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.Base64;
import org.eclipse.jgit.util.NB;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -248,6 +265,108 @@ public class ChangeUtil {
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,
final IdentifiedUser user, final String message, final ReviewDb db,
final AbandonedSender.Factory abandonedSenderFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index 15e266546c..c3ee776329 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -32,6 +32,7 @@ import com.google.gerrit.server.mail.MergeFailSender;
import com.google.gerrit.server.mail.MergedSender;
import com.google.gerrit.server.mail.RegisterNewEmailSender;
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.project.ChangeControl;
import com.google.gerrit.server.project.ProjectControl;
@@ -66,6 +67,7 @@ public class GerritRequestModule extends FactoryModule {
factory(PublishComments.Factory.class);
factory(ReplacePatchSetSender.Factory.class);
factory(AbandonedSender.Factory.class);
+ factory(RevertedSender.Factory.class);
factory(CommentSender.Factory.class);
factory(MergedSender.Factory.class);
factory(MergeFailSender.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java
new file mode 100644
index 0000000000..8ad993b957
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java
@@ -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"));
+ }
+}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.vm
new file mode 100644
index 0000000000..a2350d849d
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/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