Merge "Add rebase button to the change screen"
This commit is contained in:
@@ -29,6 +29,7 @@ public class ChangeDetail {
|
|||||||
protected boolean allowsAnonymous;
|
protected boolean allowsAnonymous;
|
||||||
protected boolean canAbandon;
|
protected boolean canAbandon;
|
||||||
protected boolean canPublish;
|
protected boolean canPublish;
|
||||||
|
protected boolean canRebase;
|
||||||
protected boolean canRestore;
|
protected boolean canRestore;
|
||||||
protected boolean canRevert;
|
protected boolean canRevert;
|
||||||
protected boolean canDeleteDraft;
|
protected boolean canDeleteDraft;
|
||||||
@@ -80,6 +81,14 @@ public class ChangeDetail {
|
|||||||
canPublish = a;
|
canPublish = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canRebase() {
|
||||||
|
return canRebase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCanRebase(final boolean a) {
|
||||||
|
canRebase = a;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canRestore() {
|
public boolean canRestore() {
|
||||||
return canRestore;
|
return canRestore;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,4 +44,7 @@ public interface ChangeManageService extends RemoteJsonService {
|
|||||||
|
|
||||||
@SignInRequired
|
@SignInRequired
|
||||||
void deleteDraftChange(PatchSet.Id patchSetId, AsyncCallback<VoidResult> callback);
|
void deleteDraftChange(PatchSet.Id patchSetId, AsyncCallback<VoidResult> callback);
|
||||||
|
|
||||||
|
@SignInRequired
|
||||||
|
void rebaseChange(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ public interface ChangeConstants extends Constants {
|
|||||||
String patchSetInfoParents();
|
String patchSetInfoParents();
|
||||||
String initialCommit();
|
String initialCommit();
|
||||||
|
|
||||||
|
String buttonRebaseChange();
|
||||||
|
|
||||||
String buttonRevertChangeBegin();
|
String buttonRevertChangeBegin();
|
||||||
String buttonRevertChangeSend();
|
String buttonRevertChangeSend();
|
||||||
String headingRevertMessage();
|
String headingRevertMessage();
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ oldVersionHistory = Old Version History:
|
|||||||
baseDiffItem = Base
|
baseDiffItem = Base
|
||||||
autoMerge = Auto Merge
|
autoMerge = Auto Merge
|
||||||
|
|
||||||
|
buttonRebaseChange = Rebase Change
|
||||||
|
|
||||||
buttonRevertChangeBegin = Revert Change
|
buttonRevertChangeBegin = Revert Change
|
||||||
buttonRevertChangeSend = Revert Change
|
buttonRevertChangeSend = Revert Change
|
||||||
headingRevertMessage = Revert Commit Message:
|
headingRevertMessage = Revert Commit Message:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.google.gerrit.common.data.ChangeDetail;
|
|||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
|
import com.google.gwt.user.client.ui.FocusWidget;
|
||||||
|
|
||||||
public class ChangeDetailCache extends ListenableValue<ChangeDetail> {
|
public class ChangeDetailCache extends ListenableValue<ChangeDetail> {
|
||||||
public static class GerritCallback extends
|
public static class GerritCallback extends
|
||||||
@@ -29,6 +30,27 @@ public class ChangeDetailCache extends ListenableValue<ChangeDetail> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GerritCallback which will re-enable a FocusWidget
|
||||||
|
* {@link com.google.gwt.user.client.ui.FocusWidget} if we are returning
|
||||||
|
* with a failed result.
|
||||||
|
*
|
||||||
|
* It is up to the caller to handle the original disabling of the Widget.
|
||||||
|
*/
|
||||||
|
public static class GerritWidgetCallback extends GerritCallback {
|
||||||
|
private FocusWidget widget;
|
||||||
|
|
||||||
|
public GerritWidgetCallback(FocusWidget widget) {
|
||||||
|
this.widget = widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
widget.setEnabled(true);
|
||||||
|
super.onFailure(caught);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class IgnoreErrorCallback implements AsyncCallback<ChangeDetail> {
|
public static class IgnoreErrorCallback implements AsyncCallback<ChangeDetail> {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ChangeDetail detail) {
|
public void onSuccess(ChangeDetail detail) {
|
||||||
|
|||||||
@@ -321,6 +321,8 @@ public class ChangeScreen extends Screen
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependenciesPanel.setOpen(depsOpen);
|
dependenciesPanel.setOpen(depsOpen);
|
||||||
|
|
||||||
|
dependenciesPanel.getHeader().clear();
|
||||||
if (outdated > 0) {
|
if (outdated > 0) {
|
||||||
dependenciesPanel.getHeader().add(new InlineLabel(
|
dependenciesPanel.getHeader().add(new InlineLabel(
|
||||||
Util.M.outdatedHeader(outdated)));
|
Util.M.outdatedHeader(outdated)));
|
||||||
|
|||||||
@@ -229,7 +229,10 @@ public class ChangeTable extends NavigationTable<ChangeInfo> {
|
|||||||
if (! c.isLatest()) {
|
if (! c.isLatest()) {
|
||||||
s += " [OUTDATED]";
|
s += " [OUTDATED]";
|
||||||
table.getRowFormatter().addStyleName(row, Gerrit.RESOURCES.css().outdated());
|
table.getRowFormatter().addStyleName(row, Gerrit.RESOURCES.css().outdated());
|
||||||
|
} else {
|
||||||
|
table.getRowFormatter().removeStyleName(row, Gerrit.RESOURCES.css().outdated());
|
||||||
}
|
}
|
||||||
|
|
||||||
table.setWidget(row, C_SUBJECT, new TableChangeLink(s, c));
|
table.setWidget(row, C_SUBJECT, new TableChangeLink(s, c));
|
||||||
table.setWidget(row, C_OWNER, link(c.getOwner()));
|
table.setWidget(row, C_OWNER, link(c.getOwner()));
|
||||||
table.setWidget(row, C_PROJECT, new ProjectLink(c.getProject().getKey(), c
|
table.setWidget(row, C_PROJECT, new ProjectLink(c.getProject().getKey(), c
|
||||||
|
|||||||
@@ -551,6 +551,19 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
|
|||||||
});
|
});
|
||||||
actionsPanel.add(b);
|
actionsPanel.add(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changeDetail.canRebase()) {
|
||||||
|
final Button b = new Button(Util.C.buttonRebaseChange());
|
||||||
|
b.addClickHandler(new ClickHandler() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final ClickEvent event) {
|
||||||
|
b.setEnabled(false);
|
||||||
|
Util.MANAGE_SVC.rebaseChange(patchSet.getId(),
|
||||||
|
new ChangeDetailCache.GerritWidgetCallback(b));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
actionsPanel.add(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateDiffAllActions(final PatchSetDetail detail) {
|
private void populateDiffAllActions(final PatchSetDetail detail) {
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
|||||||
|
|
||||||
detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
|
detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
|
||||||
|
|
||||||
|
detail.setCanRebase(detail.getChange().getStatus().isOpen() && control.canRebase());
|
||||||
|
|
||||||
detail.setCanEdit(control.getRefControl().canWrite());
|
detail.setCanEdit(control.getRefControl().canWrite());
|
||||||
|
|
||||||
if (detail.getChange().getStatus().isOpen()) {
|
if (detail.getChange().getStatus().isOpen()) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.google.inject.Inject;
|
|||||||
class ChangeManageServiceImpl implements ChangeManageService {
|
class ChangeManageServiceImpl implements ChangeManageService {
|
||||||
private final SubmitAction.Factory submitAction;
|
private final SubmitAction.Factory submitAction;
|
||||||
private final AbandonChangeHandler.Factory abandonChangeHandlerFactory;
|
private final AbandonChangeHandler.Factory abandonChangeHandlerFactory;
|
||||||
|
private final RebaseChange.Factory rebaseChangeFactory;
|
||||||
private final RestoreChangeHandler.Factory restoreChangeHandlerFactory;
|
private final RestoreChangeHandler.Factory restoreChangeHandlerFactory;
|
||||||
private final RevertChange.Factory revertChangeFactory;
|
private final RevertChange.Factory revertChangeFactory;
|
||||||
private final PublishAction.Factory publishAction;
|
private final PublishAction.Factory publishAction;
|
||||||
@@ -32,12 +33,14 @@ class ChangeManageServiceImpl implements ChangeManageService {
|
|||||||
@Inject
|
@Inject
|
||||||
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
|
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
|
||||||
final AbandonChangeHandler.Factory abandonChangeHandlerFactory,
|
final AbandonChangeHandler.Factory abandonChangeHandlerFactory,
|
||||||
|
final RebaseChange.Factory rebaseChangeFactory,
|
||||||
final RestoreChangeHandler.Factory restoreChangeHandlerFactory,
|
final RestoreChangeHandler.Factory restoreChangeHandlerFactory,
|
||||||
final RevertChange.Factory revertChangeFactory,
|
final RevertChange.Factory revertChangeFactory,
|
||||||
final PublishAction.Factory publishAction,
|
final PublishAction.Factory publishAction,
|
||||||
final DeleteDraftChange.Factory deleteDraftChangeFactory) {
|
final DeleteDraftChange.Factory deleteDraftChangeFactory) {
|
||||||
this.submitAction = patchSetAction;
|
this.submitAction = patchSetAction;
|
||||||
this.abandonChangeHandlerFactory = abandonChangeHandlerFactory;
|
this.abandonChangeHandlerFactory = abandonChangeHandlerFactory;
|
||||||
|
this.rebaseChangeFactory = rebaseChangeFactory;
|
||||||
this.restoreChangeHandlerFactory = restoreChangeHandlerFactory;
|
this.restoreChangeHandlerFactory = restoreChangeHandlerFactory;
|
||||||
this.revertChangeFactory = revertChangeFactory;
|
this.revertChangeFactory = revertChangeFactory;
|
||||||
this.publishAction = publishAction;
|
this.publishAction = publishAction;
|
||||||
@@ -54,6 +57,11 @@ class ChangeManageServiceImpl implements ChangeManageService {
|
|||||||
abandonChangeHandlerFactory.create(patchSetId, message).to(callback);
|
abandonChangeHandlerFactory.create(patchSetId, message).to(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void rebaseChange(final PatchSet.Id patchSetId,
|
||||||
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
|
rebaseChangeFactory.create(patchSetId).to(callback);
|
||||||
|
}
|
||||||
|
|
||||||
public void revertChange(final PatchSet.Id patchSetId, final String message,
|
public void revertChange(final PatchSet.Id patchSetId, final String message,
|
||||||
final AsyncCallback<ChangeDetail> callback) {
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
revertChangeFactory.create(patchSetId, message).to(callback);
|
revertChangeFactory.create(patchSetId, message).to(callback);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class ChangeModule extends RpcServletModule {
|
|||||||
factory(AbandonChangeHandler.Factory.class);
|
factory(AbandonChangeHandler.Factory.class);
|
||||||
factory(RestoreChangeHandler.Factory.class);
|
factory(RestoreChangeHandler.Factory.class);
|
||||||
factory(RevertChange.Factory.class);
|
factory(RevertChange.Factory.class);
|
||||||
|
factory(RebaseChange.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,110 @@
|
|||||||
|
// Copyright (C) 2012 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.ApprovalTypes;
|
||||||
|
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.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.server.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.RebasedPatchSetSender;
|
||||||
|
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.InvalidChangeOperationException;
|
||||||
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
|
import com.google.gwtorm.server.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;
|
||||||
|
|
||||||
|
class RebaseChange extends Handler<ChangeDetail> {
|
||||||
|
interface Factory {
|
||||||
|
RebaseChange create(PatchSet.Id patchSetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ChangeControl.Factory changeControlFactory;
|
||||||
|
private final ReviewDb db;
|
||||||
|
private final IdentifiedUser currentUser;
|
||||||
|
private final RebasedPatchSetSender.Factory rebasedPatchSetSenderFactory;
|
||||||
|
|
||||||
|
private final ChangeDetailFactory.Factory changeDetailFactory;
|
||||||
|
private final ReplicationQueue replication;
|
||||||
|
|
||||||
|
private final PatchSet.Id patchSetId;
|
||||||
|
|
||||||
|
private final ChangeHookRunner hooks;
|
||||||
|
|
||||||
|
private final GitRepositoryManager gitManager;
|
||||||
|
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||||
|
|
||||||
|
private final PersonIdent myIdent;
|
||||||
|
|
||||||
|
private final ApprovalTypes approvalTypes;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RebaseChange(final ChangeControl.Factory changeControlFactory,
|
||||||
|
final ReviewDb db, final IdentifiedUser currentUser,
|
||||||
|
final RebasedPatchSetSender.Factory rebasedPatchSetSenderFactory,
|
||||||
|
final ChangeDetailFactory.Factory changeDetailFactory,
|
||||||
|
@Assisted final PatchSet.Id patchSetId, final ChangeHookRunner hooks,
|
||||||
|
final GitRepositoryManager gitManager,
|
||||||
|
final PatchSetInfoFactory patchSetInfoFactory,
|
||||||
|
final ReplicationQueue replication,
|
||||||
|
@GerritPersonIdent final PersonIdent myIdent,
|
||||||
|
final ApprovalTypes approvalTypes) {
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
|
this.db = db;
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
this.rebasedPatchSetSenderFactory = rebasedPatchSetSenderFactory;
|
||||||
|
this.changeDetailFactory = changeDetailFactory;
|
||||||
|
|
||||||
|
this.patchSetId = patchSetId;
|
||||||
|
this.hooks = hooks;
|
||||||
|
this.gitManager = gitManager;
|
||||||
|
|
||||||
|
this.patchSetInfoFactory = patchSetInfoFactory;
|
||||||
|
this.replication = replication;
|
||||||
|
this.myIdent = myIdent;
|
||||||
|
|
||||||
|
this.approvalTypes = approvalTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChangeDetail call() throws NoSuchChangeException, OrmException,
|
||||||
|
EmailException, NoSuchEntityException, PatchSetInfoNotAvailableException,
|
||||||
|
MissingObjectException, IncorrectObjectTypeException, IOException,
|
||||||
|
InvalidChangeOperationException {
|
||||||
|
|
||||||
|
ChangeUtil.rebaseChange(patchSetId, currentUser, db,
|
||||||
|
rebasedPatchSetSenderFactory, hooks, gitManager, patchSetInfoFactory,
|
||||||
|
replication, myIdent, changeControlFactory, approvalTypes);
|
||||||
|
|
||||||
|
return changeDetailFactory.create(patchSetId.getParentKey()).call();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -432,6 +432,10 @@ public final class Change {
|
|||||||
lastUpdatedOn = new Timestamp(System.currentTimeMillis());
|
lastUpdatedOn = new Timestamp(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNumberOfPatchSets() {
|
||||||
|
return nbrPatchSets;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSortKey() {
|
public String getSortKey() {
|
||||||
return sortKey;
|
return sortKey;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,17 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server;
|
package com.google.gerrit.server;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.data.ApprovalType;
|
||||||
|
import com.google.gerrit.common.data.ApprovalTypes;
|
||||||
|
import com.google.gerrit.reviewdb.client.ApprovalCategory;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ApprovalsUtil {
|
public class ApprovalsUtil {
|
||||||
@@ -33,4 +39,36 @@ public class ApprovalsUtil {
|
|||||||
}
|
}
|
||||||
db.patchSetApprovals().update(approvals);
|
db.patchSetApprovals().update(approvals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the PatchSetApprovals to the last PatchSet on the change while
|
||||||
|
* keeping the vetos.
|
||||||
|
*
|
||||||
|
* @param db The review database
|
||||||
|
* @param change Change to update
|
||||||
|
* @param approvalTypes The approval types
|
||||||
|
* @throws OrmException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void copyVetosToLatestPatchSet(final ReviewDb db, Change change,
|
||||||
|
ApprovalTypes approvalTypes) throws OrmException, IOException {
|
||||||
|
PatchSet.Id source;
|
||||||
|
if (change.getNumberOfPatchSets() > 1) {
|
||||||
|
source = new PatchSet.Id(change.getId(), change.getNumberOfPatchSets() - 1);
|
||||||
|
} else {
|
||||||
|
throw new IOException("Previous patch set could not be found");
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchSet.Id dest = change.currPatchSetId();
|
||||||
|
for (PatchSetApproval a : db.patchSetApprovals().byPatchSet(source)) {
|
||||||
|
// ApprovalCategory.SUBMIT is still in db but not relevant in git-store
|
||||||
|
if (!ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
|
||||||
|
final ApprovalType type = approvalTypes.byId(a.getCategoryId());
|
||||||
|
if (type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
|
||||||
|
db.patchSetApprovals().insert(
|
||||||
|
Collections.singleton(new PatchSetApproval(dest, a)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,16 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server;
|
package com.google.gerrit.server;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.ChangeHookRunner;
|
||||||
import com.google.gerrit.common.ChangeHooks;
|
import com.google.gerrit.common.ChangeHooks;
|
||||||
|
import com.google.gerrit.common.data.ApprovalTypes;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change.Status;
|
||||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSetAncestor;
|
||||||
|
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSetInfo;
|
import com.google.gerrit.reviewdb.client.PatchSetInfo;
|
||||||
import com.google.gerrit.reviewdb.client.RevId;
|
import com.google.gerrit.reviewdb.client.RevId;
|
||||||
import com.google.gerrit.reviewdb.client.TrackingId;
|
import com.google.gerrit.reviewdb.client.TrackingId;
|
||||||
@@ -28,12 +34,16 @@ 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.ReplicationQueue;
|
import com.google.gerrit.server.git.ReplicationQueue;
|
||||||
import com.google.gerrit.server.mail.EmailException;
|
import com.google.gerrit.server.mail.EmailException;
|
||||||
|
import com.google.gerrit.server.mail.RebasedPatchSetSender;
|
||||||
|
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
||||||
import com.google.gerrit.server.mail.ReplyToChangeSender;
|
import com.google.gerrit.server.mail.ReplyToChangeSender;
|
||||||
import com.google.gerrit.server.mail.RevertedSender;
|
import com.google.gerrit.server.mail.RevertedSender;
|
||||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||||
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
|
import com.google.gwtorm.server.AtomicUpdate;
|
||||||
import com.google.gwtorm.server.OrmConcurrencyException;
|
import com.google.gwtorm.server.OrmConcurrencyException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
@@ -44,8 +54,11 @@ import org.eclipse.jgit.lib.CommitBuilder;
|
|||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectInserter;
|
import org.eclipse.jgit.lib.ObjectInserter;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.merge.MergeStrategy;
|
||||||
|
import org.eclipse.jgit.merge.ThreeWayMerger;
|
||||||
import org.eclipse.jgit.revwalk.FooterLine;
|
import org.eclipse.jgit.revwalk.FooterLine;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
@@ -53,6 +66,8 @@ import org.eclipse.jgit.util.Base64;
|
|||||||
import org.eclipse.jgit.util.NB;
|
import org.eclipse.jgit.util.NB;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -159,6 +174,252 @@ public class ChangeUtil {
|
|||||||
opFactory.create(change.getDest()).verifyMergeability(change);
|
opFactory.create(change.getDest()).verifyMergeability(change);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void insertAncestors(ReviewDb db, PatchSet.Id id, RevCommit src)
|
||||||
|
throws OrmException {
|
||||||
|
final int cnt = src.getParentCount();
|
||||||
|
List<PatchSetAncestor> toInsert = new ArrayList<PatchSetAncestor>(cnt);
|
||||||
|
for (int p = 0; p < cnt; p++) {
|
||||||
|
PatchSetAncestor a =
|
||||||
|
new PatchSetAncestor(new PatchSetAncestor.Id(id, p + 1));
|
||||||
|
a.setAncestorRevision(new RevId(src.getParent(p).getId().getName()));
|
||||||
|
toInsert.add(a);
|
||||||
|
}
|
||||||
|
db.patchSetAncestors().insert(toInsert);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebases a commit
|
||||||
|
*
|
||||||
|
* @param git Repository to find commits in
|
||||||
|
* @param original The commit to rebase
|
||||||
|
* @param base Base to rebase against
|
||||||
|
* @return CommitBuilder the newly rebased commit
|
||||||
|
* @throws IOException Merged failed
|
||||||
|
*/
|
||||||
|
public static CommitBuilder rebaseCommit(Repository git, RevCommit original,
|
||||||
|
RevCommit base, PersonIdent committerIdent) throws IOException {
|
||||||
|
|
||||||
|
if (original.getParentCount() == 0) {
|
||||||
|
throw new IOException(
|
||||||
|
"Commits with no parents cannot be rebased (is this the initial commit?).");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.getParentCount() > 1) {
|
||||||
|
throw new IOException(
|
||||||
|
"Patch sets with multiple parents cannot be rebased (merge commits)."
|
||||||
|
+ " Parents: " + Arrays.toString(original.getParents()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final RevCommit parentCommit = original.getParent(0);
|
||||||
|
|
||||||
|
if (base.equals(parentCommit)) {
|
||||||
|
throw new IOException("Change is already up to date.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final ThreeWayMerger merger = MergeStrategy.RESOLVE.newMerger(git, true);
|
||||||
|
merger.setBase(parentCommit);
|
||||||
|
merger.merge(original, base);
|
||||||
|
|
||||||
|
if (merger.getResultTreeId() == null) {
|
||||||
|
throw new IOException(
|
||||||
|
"The rebase failed since conflicts occured during the merge.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommitBuilder rebasedCommitBuilder = new CommitBuilder();
|
||||||
|
|
||||||
|
rebasedCommitBuilder.setTreeId(merger.getResultTreeId());
|
||||||
|
rebasedCommitBuilder.setParentId(base);
|
||||||
|
rebasedCommitBuilder.setAuthor(original.getAuthorIdent());
|
||||||
|
rebasedCommitBuilder.setMessage(original.getFullMessage());
|
||||||
|
rebasedCommitBuilder.setCommitter(committerIdent);
|
||||||
|
|
||||||
|
return rebasedCommitBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void rebaseChange(final PatchSet.Id patchSetId,
|
||||||
|
final IdentifiedUser user, final ReviewDb db,
|
||||||
|
RebasedPatchSetSender.Factory rebasedPatchSetSenderFactory,
|
||||||
|
final ChangeHookRunner hooks, GitRepositoryManager gitManager,
|
||||||
|
final PatchSetInfoFactory patchSetInfoFactory,
|
||||||
|
final ReplicationQueue replication, PersonIdent myIdent,
|
||||||
|
final ChangeControl.Factory changeControlFactory,
|
||||||
|
final ApprovalTypes approvalTypes) throws NoSuchChangeException,
|
||||||
|
EmailException, OrmException, MissingObjectException,
|
||||||
|
IncorrectObjectTypeException, IOException,
|
||||||
|
PatchSetInfoNotAvailableException, InvalidChangeOperationException {
|
||||||
|
|
||||||
|
final Change.Id changeId = patchSetId.getParentKey();
|
||||||
|
final ChangeControl changeControl =
|
||||||
|
changeControlFactory.validateFor(changeId);
|
||||||
|
|
||||||
|
if (!changeControl.canRebase()) {
|
||||||
|
throw new InvalidChangeOperationException(
|
||||||
|
"Cannot rebase: New patch sets are not allowed to be added to change: "
|
||||||
|
+ changeId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Change change = changeControl.getChange();
|
||||||
|
final Repository git = gitManager.openRepository(change.getProject());
|
||||||
|
try {
|
||||||
|
final RevWalk revWalk = new RevWalk(git);
|
||||||
|
try {
|
||||||
|
final PatchSet originalPatchSet = db.patchSets().get(patchSetId);
|
||||||
|
RevCommit branchTipCommit = null;
|
||||||
|
|
||||||
|
List<PatchSetAncestor> patchSetAncestors =
|
||||||
|
db.patchSetAncestors().ancestorsOf(patchSetId).toList();
|
||||||
|
if (patchSetAncestors.size() > 1) {
|
||||||
|
throw new IOException(
|
||||||
|
"The patch set you are trying to rebase is dependent on several other patch sets: "
|
||||||
|
+ patchSetAncestors.toString());
|
||||||
|
}
|
||||||
|
if (patchSetAncestors.size() == 1) {
|
||||||
|
List<PatchSet> depPatchSetList = db.patchSets()
|
||||||
|
.byRevision(patchSetAncestors.get(0).getAncestorRevision())
|
||||||
|
.toList();
|
||||||
|
if (!depPatchSetList.isEmpty()) {
|
||||||
|
PatchSet depPatchSet = depPatchSetList.get(0);
|
||||||
|
|
||||||
|
Change.Id depChangeId = depPatchSet.getId().getParentKey();
|
||||||
|
Change depChange = db.changes().get(depChangeId);
|
||||||
|
|
||||||
|
if (depChange.getStatus() == Status.ABANDONED) {
|
||||||
|
throw new IOException("Cannot rebase against an abandoned change: "
|
||||||
|
+ depChange.getKey().toString());
|
||||||
|
}
|
||||||
|
if (depChange.getStatus().isOpen()) {
|
||||||
|
PatchSet latestDepPatchSet =
|
||||||
|
db.patchSets().get(depChange.currentPatchSetId());
|
||||||
|
if (!depPatchSet.getId().equals(depChange.currentPatchSetId())) {
|
||||||
|
branchTipCommit =
|
||||||
|
revWalk.parseCommit(ObjectId
|
||||||
|
.fromString(latestDepPatchSet.getRevision().get()));
|
||||||
|
} else {
|
||||||
|
throw new IOException(
|
||||||
|
"Change is already based on the latest patch set of the dependent change.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branchTipCommit == null) {
|
||||||
|
// We are dependent on a merged PatchSet or have no PatchSet
|
||||||
|
// dependencies at all.
|
||||||
|
Ref destRef = git.getRef(change.getDest().get());
|
||||||
|
if (destRef == null) {
|
||||||
|
throw new IOException(
|
||||||
|
"The destination branch does not exist: "
|
||||||
|
+ change.getDest().get());
|
||||||
|
}
|
||||||
|
branchTipCommit = revWalk.parseCommit(destRef.getObjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
final RevCommit originalCommit =
|
||||||
|
revWalk.parseCommit(ObjectId.fromString(originalPatchSet
|
||||||
|
.getRevision().get()));
|
||||||
|
|
||||||
|
CommitBuilder rebasedCommitBuilder =
|
||||||
|
rebaseCommit(git, originalCommit, branchTipCommit, myIdent);
|
||||||
|
|
||||||
|
final ObjectInserter oi = git.newObjectInserter();
|
||||||
|
final ObjectId rebasedCommitId;
|
||||||
|
try {
|
||||||
|
rebasedCommitId = oi.insert(rebasedCommitBuilder);
|
||||||
|
oi.flush();
|
||||||
|
} finally {
|
||||||
|
oi.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Change updatedChange =
|
||||||
|
db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() {
|
||||||
|
@Override
|
||||||
|
public Change update(Change change) {
|
||||||
|
if (change.getStatus().isOpen()) {
|
||||||
|
change.nextPatchSetId();
|
||||||
|
return change;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updatedChange == null) {
|
||||||
|
throw new InvalidChangeOperationException("Change is closed: "
|
||||||
|
+ change.toString());
|
||||||
|
} else {
|
||||||
|
change = updatedChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PatchSet rebasedPatchSet = new PatchSet(change.currPatchSetId());
|
||||||
|
rebasedPatchSet.setCreatedOn(change.getCreatedOn());
|
||||||
|
rebasedPatchSet.setUploader(user.getAccountId());
|
||||||
|
rebasedPatchSet.setRevision(new RevId(rebasedCommitId.getName()));
|
||||||
|
|
||||||
|
insertAncestors(db, rebasedPatchSet.getId(),
|
||||||
|
revWalk.parseCommit(rebasedCommitId));
|
||||||
|
|
||||||
|
db.patchSets().insert(Collections.singleton(rebasedPatchSet));
|
||||||
|
final PatchSetInfo info =
|
||||||
|
patchSetInfoFactory.get(db, rebasedPatchSet.getId());
|
||||||
|
|
||||||
|
change =
|
||||||
|
db.changes().atomicUpdate(change.getId(),
|
||||||
|
new AtomicUpdate<Change>() {
|
||||||
|
@Override
|
||||||
|
public Change update(Change change) {
|
||||||
|
change.setCurrentPatchSet(info);
|
||||||
|
ChangeUtil.updated(change);
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final RefUpdate ru = git.updateRef(rebasedPatchSet.getRefName());
|
||||||
|
ru.setNewObjectId(rebasedCommitId);
|
||||||
|
ru.disableRefLog();
|
||||||
|
if (ru.update(revWalk) != RefUpdate.Result.NEW) {
|
||||||
|
throw new IOException("Failed to create ref "
|
||||||
|
+ rebasedPatchSet.getRefName() + " in " + git.getDirectory()
|
||||||
|
+ ": " + ru.getResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
replication.scheduleUpdate(change.getProject(), ru.getName());
|
||||||
|
|
||||||
|
ApprovalsUtil.copyVetosToLatestPatchSet(db, change, approvalTypes);
|
||||||
|
|
||||||
|
final ChangeMessage cmsg =
|
||||||
|
new ChangeMessage(new ChangeMessage.Key(changeId,
|
||||||
|
ChangeUtil.messageUUID(db)), user.getAccountId(), patchSetId);
|
||||||
|
cmsg.setMessage("Patch Set " + patchSetId.get() + ": Rebased");
|
||||||
|
db.changeMessages().insert(Collections.singleton(cmsg));
|
||||||
|
|
||||||
|
final Set<Account.Id> oldReviewers = new HashSet<Account.Id>();
|
||||||
|
final Set<Account.Id> oldCC = new HashSet<Account.Id>();
|
||||||
|
|
||||||
|
for (PatchSetApproval a : db.patchSetApprovals().byChange(change.getId())) {
|
||||||
|
if (a.getValue() != 0) {
|
||||||
|
oldReviewers.add(a.getAccountId());
|
||||||
|
} else {
|
||||||
|
oldCC.add(a.getAccountId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ReplacePatchSetSender cm =
|
||||||
|
rebasedPatchSetSenderFactory.create(change);
|
||||||
|
cm.setFrom(user.getAccountId());
|
||||||
|
cm.setPatchSet(rebasedPatchSet);
|
||||||
|
cm.addReviewers(oldReviewers);
|
||||||
|
cm.addExtraCC(oldCC);
|
||||||
|
cm.send();
|
||||||
|
|
||||||
|
hooks.doPatchsetCreatedHook(change, rebasedPatchSet, db);
|
||||||
|
} finally {
|
||||||
|
revWalk.release();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
git.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Change.Id revert(final PatchSet.Id patchSetId,
|
public static Change.Id revert(final PatchSet.Id patchSetId,
|
||||||
final IdentifiedUser user, final String message, final ReviewDb db,
|
final IdentifiedUser user, final String message, final ReviewDb db,
|
||||||
final RevertedSender.Factory revertedSenderFactory,
|
final RevertedSender.Factory revertedSenderFactory,
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import com.google.gerrit.server.mail.CommentSender;
|
|||||||
import com.google.gerrit.server.mail.CreateChangeSender;
|
import com.google.gerrit.server.mail.CreateChangeSender;
|
||||||
import com.google.gerrit.server.mail.MergeFailSender;
|
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.RebasedPatchSetSender;
|
||||||
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
import com.google.gerrit.server.mail.ReplacePatchSetSender;
|
||||||
import com.google.gerrit.server.mail.RestoredSender;
|
import com.google.gerrit.server.mail.RestoredSender;
|
||||||
import com.google.gerrit.server.mail.RevertedSender;
|
import com.google.gerrit.server.mail.RevertedSender;
|
||||||
@@ -95,6 +96,7 @@ public class GerritRequestModule extends FactoryModule {
|
|||||||
factory(PublishComments.Factory.class);
|
factory(PublishComments.Factory.class);
|
||||||
factory(PublishDraft.Factory.class);
|
factory(PublishDraft.Factory.class);
|
||||||
factory(ReplacePatchSetSender.Factory.class);
|
factory(ReplacePatchSetSender.Factory.class);
|
||||||
|
factory(RebasedPatchSetSender.Factory.class);
|
||||||
factory(AbandonedSender.Factory.class);
|
factory(AbandonedSender.Factory.class);
|
||||||
factory(RemoveReviewer.Factory.class);
|
factory(RemoveReviewer.Factory.class);
|
||||||
factory(RestoreChange.Factory.class);
|
factory(RestoreChange.Factory.class);
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (C) 2012 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.client.Change;
|
||||||
|
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||||
|
import com.google.gerrit.server.ssh.SshInfo;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
/** Send notice to reviewers that a change has been rebased. */
|
||||||
|
public class RebasedPatchSetSender extends ReplacePatchSetSender {
|
||||||
|
public static interface Factory {
|
||||||
|
RebasedPatchSetSender create(Change change);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RebasedPatchSetSender(EmailArguments ea,
|
||||||
|
@AnonymousCowardName String anonymousCowardName, SshInfo si,
|
||||||
|
@Assisted Change c) {
|
||||||
|
super(ea, anonymousCowardName, si, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void formatChange() throws EmailException {
|
||||||
|
appendText(velocifyFile("RebasedPatchSet.vm"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -199,6 +199,11 @@ public class ChangeControl {
|
|||||||
return isOwner() && isVisible(db);
|
return isOwner() && isVisible(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Can this user rebase this change? */
|
||||||
|
public boolean canRebase() {
|
||||||
|
return canAddPatchSet();
|
||||||
|
}
|
||||||
|
|
||||||
/** Can this user restore this change? */
|
/** Can this user restore this change? */
|
||||||
public boolean canRestore() {
|
public boolean canRestore() {
|
||||||
return canAbandon(); // Anyone who can abandon the change can restore it back
|
return canAbandon(); // Anyone who can abandon the change can restore it back
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
## Copyright (C) 2012 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.example file to a .vm
|
||||||
|
## file and edit it appropriately.
|
||||||
|
##
|
||||||
|
## This Template:
|
||||||
|
## --------------
|
||||||
|
## The RebasedPatchSet.vm template will determine the contents of the email
|
||||||
|
## related to a user rebasing a patchset for a change through the Gerrit UI.
|
||||||
|
## It is a ChangeEmail: see ChangeSubject.vm and ChangeFooter.vm.
|
||||||
|
##
|
||||||
|
#if($email.reviewerNames)
|
||||||
|
Hello $email.joinStrings($email.reviewerNames, ', '),
|
||||||
|
|
||||||
|
I'd like you to reexamine a rebased change.#if($email.changeUrl) Please visit
|
||||||
|
|
||||||
|
$email.changeUrl
|
||||||
|
|
||||||
|
to look at the new rebased patch set (#$patchSet.patchSetId).
|
||||||
|
#end
|
||||||
|
#else
|
||||||
|
$fromName has created a new patch set by issuing a rebase in Gerrit (#$patchSet.patchSetId).
|
||||||
|
#end
|
||||||
|
|
||||||
|
Change subject: $change.subject
|
||||||
|
......................................................................
|
||||||
|
|
||||||
|
$email.changeDetail
|
||||||
|
#if($email.sshHost)
|
||||||
|
git pull ssh://$email.sshHost/$projectName $patchSet.refName
|
||||||
|
#end
|
||||||
Reference in New Issue
Block a user