Merge "ProjectConfig sections refactoring"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 30a6c39..d2078ab 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -780,6 +780,14 @@
 and `-1 Do not have copyright` will block submit, while `+1 Copyright
 clear` is required to enable submit.
 
+[[restart_changes]]
+[NOTE]
+Restart the Gerrit web application and reload all browsers after
+making any database changes to approval categories.  Browsers are
+sent the list of known categories when they first visit the site,
+and don't notice changes until the page is closed and opened again,
+or is reloaded.
+
 
 Examples of typical roles in a project
 --------------------------------------
@@ -841,6 +849,115 @@
 * <<category_push_merge,`Push merge commit`>> to 'refs/heads/*'
 
 
+[[examples_cisystem]]
+CI system
+~~~~~~~~~
+
+A typical Continous Integration system should be able to download new changes
+to build and then leave a verdict somehow.
+
+As an example, the popular
+link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[gerrit-trigger plugin]
+for Jenkins/Hudson can set labels at:
+
+* The start of a build
+* A successful build
+* An unstable build (tests fails)
+* A failed build
+
+Usually the range chosen for this verdict is the verify label.  Depending on
+the size of your project and discipline of involved developers you might want
+to limit access right to the +1 `Verify` label to the CI system only.  That
+way it's guaranteed that submitted commits always get built and pass tests
+successfully.
+
+If the build doesn't complete successfully the CI system can set the
+`Verify` label to -1.  However that means that a failed build will block
+submit of the change even if someone else sets `Verify` +1.  Depending on the
+project and how much the CI system can be trusted for accurate results, a
+blocking label might not be feasible.  A recommended alternative is to set the
+label `Code-review` to -1 instead, as it isn't a blocking label but still
+shows a red label in the Gerrit UI.  Optionally; to enable the possibility to
+deliver different results (build error vs unstable for instance) it's also
+possible to set `Code-review` +1 as well.
+
+If pushing new changes is granted, it's possible to automate cherry-pick of
+submitted changes for upload to other branches under certain conditions.  This
+is probably not the first step of what a project wants to automate however,
+and so the push right can be found under the optional section.
+
+Suggested access rights to grant, that won't block changes:
+
+* <<category_read,`Read`>> on 'refs/heads/\*' and 'refs/tags/*'
+* <<category_label-Code-Review,`Label: Code review`>> with range '-1' to '0'
+* <<category_label-Verified,`Label: Verify`>> with range '0' to '+1'
+
+Optional access rights to grant:
+
+* <<category_label-Code-Review,`Label: Code review`>> with range '-1' to '+1'
+* <<category_push,`Push`>> to 'refs/for/refs/heads/\*' and 'refs/changes/*'
+
+
+[[examples_integrator]]
+Integrator
+~~~~~~~~~~
+
+Integrators are like developers but with some additional rights granted due
+to their administrative role in a project.  They can upload or push any commit
+with any committer email (not just their own) and they can also create new
+tags and branches.
+
+Suggested access rights to grant:
+
+* <<examples_developer,Developer rights>>
+* <<category_push,`Push`>> to 'refs/heads/*'
+* <<category_push_merge,`Push merge commit`>> to 'refs/heads/*'
+* <<category_forge_committer,`Forge Committer Identity`>> to 'refs/for/refs/heads/\*' and 'refs/changes/*'
+* <<category_create,`Create Reference`>> to 'refs/heads/*'
+* <<category_push_annotated,`Push Annotated Tag`>> to 'refs/tags/*'
+
+
+[[examples_project-owner]]
+Project owner
+~~~~~~~~~~~~~
+
+The project owner is almost like an integrator but with additional destructive
+power in the form of being able to delete branches.  Optionally these users
+also have the power to configure access rights in gits assigned to them.
+
+[WARNING]
+These users should be really knowledgable about git, for instance knowing why
+tags never should be removed from a server.  This role is granted potentially
+destructive access rights and cleaning up after such a mishap could be time
+consuming!
+
+Suggested access rights to grant:
+
+* <<examples_integrator,Integrator rights>>
+* <<category_push,`Push`>> with the force option to 'refs/heads/\*' and 'refs/tags/*'
+
+Optional access right to grant:
+
+* <<category_owner,`Owner`>> in the gits they mostly work with.
+
+[[examples_administrator]]
+Administrator
+~~~~~~~~~~~~~
+
+The administrator role is the most powerful role known in the Gerrit universe.
+This role may grant itself (or others) any access right, and it already has
+all capabilities granted as well.  By default the
+<<administrators,`Administrators` group>> is the group that has this role.
+
+Mandatory access rights:
+
+* <<capability_administrateServer,The `Administrate Server` capability>>
+
+Suggested access rights to grant:
+
+* <<examples_project-owner,Project owner rights>>
+
+
 [[conversion_table]]
 Conversion table from 2.1.x series to 2.2.x series
 --------------------------------------------------
@@ -894,11 +1011,6 @@
 Below you find a list of capabilities available:
 
 
-* View Connections
-
-* View Queue
-
-
 [[capability_administrateServer]]
 Administrate Server
 ~~~~~~~~~~~~~~~~~~~
@@ -942,7 +1054,7 @@
 Flush Caches
 ~~~~~~~~~~~~
 
-Allow the flushing of Gerrits caches.  This capability allows the granted
+Allow the flushing of Gerrit's caches.  This capability allows the granted
 group to link:cmd-flush-caches.html[flush some or all Gerrit caches via ssh].
 
 [NOTE]
@@ -1016,18 +1128,28 @@
 View Caches
 ~~~~~~~~~~~
 
-Allow querying for status of Gerrits internal caches.  This capability allows
+Allow querying for status of Gerrit's internal caches.  This capability allows
 the granted group to
 link:cmd-show-caches.html[look at some or all Gerrit caches via ssh].
 
 
-[[restart_changes]]
-[NOTE]
-Restart the Gerrit web application and reload all browsers after
-making any database changes to approval categories.  Browsers are
-sent the list of known categories when they first visit the site,
-and don't notice changes until the page is closed and opened again,
-or is reloaded.
+[[capability_viewConnections]]
+View Connections
+~~~~~~~~~~~~~~~~
+
+Allow querying for status of Gerrit's current client connections.  This
+capability allows the granted group to
+link:cmd-show-connections.html[look at Gerrit's current connections via ssh].
+
+
+[[capability_viewQueue]]
+View Queue
+~~~~~~~~~~
+
+Allow querying for status of Gerrit's internal task queue.  This capability
+allows the granted group to
+link:cmd-show-queue.html[look at the Gerrit task queue via ssh].
+
 
 GERRIT
 ------
diff --git a/Documentation/cmd-show-caches.txt b/Documentation/cmd-show-caches.txt
index 069bcf6..126b2a0 100644
--- a/Documentation/cmd-show-caches.txt
+++ b/Documentation/cmd-show-caches.txt
@@ -29,7 +29,7 @@
 ------
 Caller must be a member of the privileged 'Administrators' group,
 or have been granted
-link:access_control.html#capability_viewCaches[the 'View Caches' global capability].
+link:access-control.html#capability_viewCaches[the 'View Caches' global capability].
 
 SCRIPTING
 ---------
diff --git a/Documentation/cmd-show-connections.txt b/Documentation/cmd-show-connections.txt
index 16cd5b4..b5d41bd 100644
--- a/Documentation/cmd-show-connections.txt
+++ b/Documentation/cmd-show-connections.txt
@@ -19,7 +19,8 @@
 ACCESS
 ------
 Caller must be a member of the privileged 'Administrators' group,
-or have been granted the 'View Connections' global capability.
+or have been granted
+link:access-control.html#capability_viewConnections[the 'View Connections' global capability].
 
 SCRIPTING
 ---------
diff --git a/Documentation/cmd-show-queue.txt b/Documentation/cmd-show-queue.txt
index 98a954e..f99e342 100644
--- a/Documentation/cmd-show-queue.txt
+++ b/Documentation/cmd-show-queue.txt
@@ -29,7 +29,9 @@
 projects, or that do not have a specific project are hidden.
 
 Members of the group 'Administrators', or any group that has been
-granted the 'View Queue' capability can see all queue entries.
+granted
+link:access-control.html#capability_viewQueue[the 'View Queue' capability]
+can see all queue entries.
 
 SCRIPTING
 ---------
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index d02382f..39f0621 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1107,7 +1107,7 @@
 +
 Valid values are `gitweb`, `cgit`, `disabled` or `custom`.
 
-[[gitweb.type]]gitweb.revision::
+[[gitweb.revision]]gitweb.revision::
 +
 Optional pattern to use for constructing the gitweb URL when pointing
 at a specific commit when `custom` is used above.
@@ -1115,14 +1115,14 @@
 Valid replacements are `${project}` for the project name in Gerrit
 and `${commit}` for the SHA1 hash for the commit.
 
-[[gitweb.type]]gitweb.project::
+[[gitweb.project]]gitweb.project::
 +
 Optional pattern to use for constructing the gitweb URL when pointing
 at a specific project when `custom` is used above.
 +
 Valid replacements are `${project}` for the project name in Gerrit.
 
-[[gitweb.type]]gitweb.branch::
+[[gitweb.branch]]gitweb.branch::
 +
 Optional pattern to use for constructing the gitweb URL when pointing
 at a specific branch when `custom` is used above.
@@ -1130,6 +1130,16 @@
 Valid replacements are `${project}` for the project name in Gerrit
 and `${branch}` for the name of the branch.
 
+[[gitweb.filehistory]]gitweb.filehistory::
++
+Optional pattern to use for constructing the gitweb URL when pointing
+at the history of a file in a specific branch when `custom` is used
+above.
++
+Valid replacements are `${project}` for the project name in Gerrit,
+`${file}` for the file name and `${branch}` for the name of the
+branch.
+
 [[gitweb.linkname]]gitweb.linkname::
 +
 Optional setting for modifying the link name presented to the user
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 6a6ce16..3aafe8c 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -109,12 +109,9 @@
 [[branch]]
 branch:'BRANCH'::
 +
-Changes for 'BRANCH'.  The branch name is the short name shown
-in the web interface, without the traditional 'refs/heads/'
-prefix.  This operator is a shorthand for 'refs:'.  Searching for
-'branch:master' really means 'ref:refs/heads/master', and searching
-for 'branch:refs/heads/master' is the same as searching for
-'ref:refs/heads/refs/heads/master'.
+Changes for 'BRANCH'.  The branch name is either the short name shown
+in the web interface or the full name of the destination branch with
+the traditional 'refs/heads/' prefix.
 +
 If 'BRANCH' starts with `^` it matches branch names by regular
 expression patterns.  The
diff --git a/ReleaseNotes/ReleaseNotes-2.3.txt b/ReleaseNotes/ReleaseNotes-2.3.txt
index 20b14c6..965c8e3 100644
--- a/ReleaseNotes/ReleaseNotes-2.3.txt
+++ b/ReleaseNotes/ReleaseNotes-2.3.txt
@@ -274,7 +274,7 @@
 ~~~~~~~~~~~~~
 * Allow superprojects to subscribe to submodules updates
 +
-The feature introduced in this commit allows superprojects to
+The feature introduced in this release allows superprojects to
 subscribe to submodules updates.
 +
 When a commit is merged to a project, the commit content is scanned
@@ -451,4 +451,24 @@
 
 * Improve 'Push Merge Commit' access right documentation
 
-* Access control: capabilities
+* Access control: Capabilities documented
+** Administrate Server
+** Create Account
+** Create Group
+** Create Project
+** Flush Caches
+** Kill Task
+** Priority
+** Query Limit
+** Start Replication
+** View caches
+** View connections
+** View queue
+
+* Access control: Example roles documented
+** Contributor
+** Developer
+** CI System
+** Integrator
+** Project owner
+** Administrator
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 3a6bca8..74d1962 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
@@ -29,6 +29,7 @@
   protected boolean allowsAnonymous;
   protected boolean canAbandon;
   protected boolean canPublish;
+  protected boolean canRebase;
   protected boolean canRestore;
   protected boolean canRevert;
   protected boolean canDeleteDraft;
@@ -80,6 +81,14 @@
     canPublish = a;
   }
 
+  public boolean canRebase() {
+    return canRebase;
+  }
+
+  public void setCanRebase(final boolean a) {
+    canRebase = a;
+  }
+
   public boolean canRestore() {
     return canRestore;
   }
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 a43ac94..18453ca 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
@@ -44,4 +44,7 @@
 
   @SignInRequired
   void deleteDraftChange(PatchSet.Id patchSetId, AsyncCallback<VoidResult> callback);
+
+  @SignInRequired
+  void rebaseChange(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java
index d090f27..27f76f6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeCache.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.client.changes;
 
+import com.google.gerrit.client.ui.ListenableValue;
+import com.google.gerrit.common.data.ChangeInfo;
 import com.google.gerrit.reviewdb.client.Change;
 
 import java.util.HashMap;
@@ -35,6 +37,7 @@
 
   private Change.Id changeId;
   private ChangeDetailCache detail;
+  private ListenableValue<ChangeInfo> info;
   private StarCache starred;
 
   protected ChangeCache(Change.Id chg) {
@@ -52,6 +55,13 @@
     return detail;
   }
 
+  public ListenableValue<ChangeInfo> getChangeInfoCache() {
+    if (info == null) {
+      info = new ListenableValue<ChangeInfo>();
+    }
+    return info;
+  }
+
   public StarCache getStarCache() {
     if (starred == null) {
       starred = new StarCache(changeId);
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 0af35b6..3372096 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
@@ -111,6 +111,8 @@
   String patchSetInfoParents();
   String initialCommit();
 
+  String buttonRebaseChange();
+
   String buttonRevertChangeBegin();
   String buttonRevertChangeSend();
   String headingRevertMessage();
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 6735b63..ad70674 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
@@ -96,6 +96,8 @@
 baseDiffItem = Base
 autoMerge = Auto Merge
 
+buttonRebaseChange = Rebase Change
+
 buttonRevertChangeBegin = Revert Change
 buttonRevertChangeSend = Revert Change
 headingRevertMessage = Revert Commit Message:
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java
index 7245db5..8f48ebd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDetailCache.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.reviewdb.client.Change;
 
 import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.FocusWidget;
 
 public class ChangeDetailCache extends ListenableValue<ChangeDetail> {
   public static class GerritCallback extends
@@ -29,6 +30,27 @@
     }
   }
 
+  /*
+   * 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> {
     @Override
     public void onSuccess(ChangeDetail detail) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
index 867abdd..8d5e105 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
@@ -321,6 +321,8 @@
     }
 
     dependenciesPanel.setOpen(depsOpen);
+
+    dependenciesPanel.getHeader().clear();
     if (outdated > 0) {
       dependenciesPanel.getHeader().add(new InlineLabel(
         Util.M.outdatedHeader(outdated)));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
index 943e967..5a568f1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.common.data.ApprovalSummarySet;
 import com.google.gerrit.common.data.ApprovalType;
 import com.google.gerrit.common.data.ChangeInfo;
-import com.google.gerrit.common.data.ToggleStarRequest;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
 import com.google.gerrit.reviewdb.client.ApprovalCategory;
@@ -45,7 +44,6 @@
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
 import com.google.gwt.user.client.ui.FlowPanel;
@@ -54,8 +52,6 @@
 import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtjsonrpc.client.VoidResult;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -136,7 +132,7 @@
           return;
         }
         if (cell.getCellIndex() == C_STAR) {
-          onStarClick(cell.getRowIndex());
+          // Don't do anything (handled by star itself).
         } else if (cell.getCellIndex() == C_OWNER) {
           // Don't do anything.
         } else if (getRowItem(cell.getRowIndex()) != null) {
@@ -149,23 +145,7 @@
   protected void onStarClick(final int row) {
     final ChangeInfo c = getRowItem(row);
     if (c != null && Gerrit.isSignedIn()) {
-      final boolean prior = c.isStarred();
-      c.setStarred(!prior);
-      setStar(row, c);
-
-      final ToggleStarRequest req = new ToggleStarRequest();
-      req.toggle(c.getId(), c.isStarred());
-      Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
-        public void onSuccess(final VoidResult result) {
-        }
-
-        @Override
-        public void onFailure(final Throwable caught) {
-          super.onFailure(caught);
-          c.setStarred(prior);
-          setStar(row, c);
-        }
-      });
+       ChangeCache.get(c.getId()).getStarCache().toggleStar();
     }
   }
 
@@ -212,10 +192,13 @@
   }
 
   private void populateChangeRow(final int row, final ChangeInfo c) {
+    ChangeCache cache = ChangeCache.get(c.getId());
+    cache.getChangeInfoCache().set(c);
+
     final String idstr = c.getKey().abbreviate();
     table.setWidget(row, C_ARROW, null);
     if (Gerrit.isSignedIn()) {
-      setStar(row, c);
+      table.setWidget(row, C_STAR, cache.getStarCache().createStar());
     }
     table.setWidget(row, C_ID, new TableChangeLink(idstr, c));
 
@@ -229,7 +212,10 @@
     if (! c.isLatest()) {
       s += " [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_OWNER, link(c.getOwner()));
     table.setWidget(row, C_PROJECT, new ProjectLink(c.getProject().getKey(), c
@@ -244,22 +230,6 @@
     return AccountDashboardLink.link(accountCache, id);
   }
 
-  private void setStar(final int row, final ChangeInfo c) {
-    final ImageResource star;
-    if (c.isStarred()) {
-      star = Gerrit.RESOURCES.starFilled();
-    } else {
-      star = Gerrit.RESOURCES.starOpen();
-    }
-
-    final Widget i = table.getWidget(row, C_STAR);
-    if (i instanceof Image) {
-      ((Image) i).setResource(star);
-    } else {
-      table.setWidget(row, C_STAR, new Image(star));
-    }
-  }
-
   public void addSection(final Section s) {
     assert s.parent == null;
 
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 199dc9d..50a8a6a 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
@@ -551,6 +551,19 @@
       });
       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) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java
index dff12f5..7758843 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarCache.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
 import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ChangeInfo;
 import com.google.gerrit.common.data.ToggleStarRequest;
 import com.google.gerrit.reviewdb.client.Change;
 
@@ -59,6 +60,10 @@
     if (detail != null) {
       return detail.isStarred();
     }
+    ChangeInfo info = cache.getChangeInfoCache().get();
+    if (info != null) {
+      return info.isStarred();
+    }
     return false;
   }
 
@@ -81,6 +86,10 @@
     if (detail != null) {
       detail.setStarred(s);
     }
+    ChangeInfo info = cache.getChangeInfoCache().get();
+    if (info != null) {
+      info.setStarred(s);
+    }
   }
 
   public void toggleStar() {
@@ -106,6 +115,7 @@
       };
 
     cache.getChangeDetailCache().addValueChangeHandler(starUpdater);
+    cache.getChangeInfoCache().addValueChangeHandler(starUpdater);
 
     this.addValueChangeHandler(starUpdater);
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ChangeQueryServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ChangeQueryServlet.java
index 468c6d8..9c4c598 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ChangeQueryServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ChangeQueryServlet.java
@@ -69,6 +69,7 @@
         return;
     }
 
+    p.setIncludeComments(get(req, "comments", false));
     p.setIncludeCurrentPatchSet(get(req, "current-patch-set", false));
     p.setIncludePatchSets(get(req, "patch-sets", false));
     p.setIncludeApprovals(get(req, "all-approvals", false));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
index 9f437bf..7de4bc3 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
@@ -54,6 +54,7 @@
     type.setBranch(cfg.getString("gitweb", null, "branch"));
     type.setProject(cfg.getString("gitweb", null, "project"));
     type.setRevision(cfg.getString("gitweb", null, "revision"));
+    type.setFileHistory(cfg.getString("gitweb", null, "filehistory"));
     String pathSeparator = cfg.getString("gitweb", null, "pathSeparator");
     if (pathSeparator != null) {
       if (pathSeparator.length() == 1) {
@@ -77,6 +78,9 @@
     } else if (type.getRevision() == null) {
       log.warn("No Pattern specified for gitweb.revision, disabling.");
       type = null;
+    } else if (type.getFileHistory() == null) {
+      log.warn("No Pattern specified for gitweb.filehistory, disabling.");
+      type = null;
     }
 
     if ((cfgUrl != null && cfgUrl.isEmpty())
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 23d5865..47a9395 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
@@ -129,6 +129,8 @@
 
     detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
 
+    detail.setCanRebase(detail.getChange().getStatus().isOpen() && control.canRebase());
+
     detail.setCanEdit(control.getRefControl().canWrite());
 
     if (detail.getChange().getStatus().isOpen()) {
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 e53f0bf..4178437 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,6 +24,7 @@
 class ChangeManageServiceImpl implements ChangeManageService {
   private final SubmitAction.Factory submitAction;
   private final AbandonChangeHandler.Factory abandonChangeHandlerFactory;
+  private final RebaseChange.Factory rebaseChangeFactory;
   private final RestoreChangeHandler.Factory restoreChangeHandlerFactory;
   private final RevertChange.Factory revertChangeFactory;
   private final PublishAction.Factory publishAction;
@@ -32,12 +33,14 @@
   @Inject
   ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
       final AbandonChangeHandler.Factory abandonChangeHandlerFactory,
+      final RebaseChange.Factory rebaseChangeFactory,
       final RestoreChangeHandler.Factory restoreChangeHandlerFactory,
       final RevertChange.Factory revertChangeFactory,
       final PublishAction.Factory publishAction,
       final DeleteDraftChange.Factory deleteDraftChangeFactory) {
     this.submitAction = patchSetAction;
     this.abandonChangeHandlerFactory = abandonChangeHandlerFactory;
+    this.rebaseChangeFactory = rebaseChangeFactory;
     this.restoreChangeHandlerFactory = restoreChangeHandlerFactory;
     this.revertChangeFactory = revertChangeFactory;
     this.publishAction = publishAction;
@@ -54,6 +57,11 @@
     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,
       final AsyncCallback<ChangeDetail> callback) {
     revertChangeFactory.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 4224293..b672a439 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
@@ -31,6 +31,7 @@
         factory(AbandonChangeHandler.Factory.class);
         factory(RestoreChangeHandler.Factory.class);
         factory(RevertChange.Factory.class);
+        factory(RebaseChange.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/RebaseChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RebaseChange.java
new file mode 100644
index 0000000..3c29074
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RebaseChange.java
@@ -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();
+  }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
index 42e822f..61e6a97 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
@@ -432,6 +432,10 @@
     lastUpdatedOn = new Timestamp(System.currentTimeMillis());
   }
 
+  public int getNumberOfPatchSets() {
+    return nbrPatchSets;
+  }
+
   public String getSortKey() {
     return sortKey;
   }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ChangeAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ChangeAccess.java
index b62aada..4660291 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ChangeAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ChangeAccess.java
@@ -101,6 +101,16 @@
   ResultSet<Change> allClosedNext(char status, String sortKey, int limit)
       throws OrmException;
 
+  @Query("WHERE open = false AND status = ? AND dest = ? AND sortKey > ?"
+      + " ORDER BY sortKey LIMIT ?")
+  ResultSet<Change> byBranchClosedPrev(char status, Branch.NameKey p,
+      String sortKey, int limit) throws OrmException;
+
+  @Query("WHERE open = false AND status = ? AND dest = ? AND sortKey < ?"
+      + " ORDER BY sortKey DESC LIMIT ?")
+  ResultSet<Change> byBranchClosedNext(char status, Branch.NameKey p,
+      String sortKey, int limit) throws OrmException;
+
   @Query
   ResultSet<Change> all() throws OrmException;
 }
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
index 83f7d15..5d0e15c 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
@@ -109,6 +109,10 @@
 CREATE INDEX changes_allClosed
 ON changes (open, status, sort_key);
 
+--    covers:             byBranchClosedPrev, byBranchClosedNext
+CREATE INDEX changes_byBranchClosed
+ON changes (status, dest_project_name, dest_branch_name, sort_key);
+
 CREATE INDEX changes_key
 ON changes (change_key);
 
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
index 3afa7ef..97ad126 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
@@ -187,6 +187,11 @@
 CREATE INDEX changes_byProject
 ON changes (dest_project_name);
 
+--    covers:             byBranchClosedPrev, byBranchClosedNext
+CREATE INDEX changes_byBranchClosed
+ON changes (status, dest_project_name, dest_branch_name, sort_key)
+WHERE open = 'N';
+
 CREATE INDEX changes_key
 ON changes (change_key);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
index 19e3e00..3417111 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -14,11 +14,17 @@
 
 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.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gwtorm.server.OrmException;
 
+import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 
 public class ApprovalsUtil {
@@ -33,4 +39,36 @@
     }
     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)));
+        }
+      }
+    }
+  }
 }
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 bbcb14b..9dc572d 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
@@ -14,10 +14,16 @@
 
 package com.google.gerrit.server;
 
+import com.google.gerrit.common.ChangeHookRunner;
 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.Status;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 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.RevId;
 import com.google.gerrit.reviewdb.client.TrackingId;
@@ -28,12 +34,16 @@
 import com.google.gerrit.server.git.MergeOp;
 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.mail.ReplacePatchSetSender;
 import com.google.gerrit.server.mail.ReplyToChangeSender;
 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.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.server.AtomicUpdate;
 import com.google.gwtorm.server.OrmConcurrencyException;
 import com.google.gwtorm.server.OrmException;
 
@@ -44,8 +54,11 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 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.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -53,6 +66,8 @@
 import org.eclipse.jgit.util.NB;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -159,6 +174,252 @@
     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,
       final IdentifiedUser user, final String message, final ReviewDb db,
       final RevertedSender.Factory revertedSenderFactory,
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 bf741a4..00562b0 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
@@ -44,6 +44,7 @@
 import com.google.gerrit.server.mail.CreateChangeSender;
 import com.google.gerrit.server.mail.MergeFailSender;
 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.RestoredSender;
 import com.google.gerrit.server.mail.RevertedSender;
@@ -95,6 +96,7 @@
     factory(PublishComments.Factory.class);
     factory(PublishDraft.Factory.class);
     factory(ReplacePatchSetSender.Factory.class);
+    factory(RebasedPatchSetSender.Factory.class);
     factory(AbandonedSender.Factory.class);
     factory(RemoveReviewer.Factory.class);
     factory(RestoreChange.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 7be2b23..d14820a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -1146,7 +1146,7 @@
   private void doReplaces() {
     for (final ReplaceRequest request : replaceByChange.values()) {
       try {
-        doReplace(request);
+        doReplace(request, false);
         replaceProgress.update(1);
       } catch (IOException err) {
         log.error("Error computing replacement patch for change "
@@ -1166,7 +1166,7 @@
     }
   }
 
-  private PatchSet.Id doReplace(final ReplaceRequest request)
+  private PatchSet.Id doReplace(final ReplaceRequest request, boolean ignoreNoChanges)
       throws IOException, OrmException {
     final RevCommit c = request.newCommit;
     rp.getRevWalk().parseBody(c);
@@ -1262,7 +1262,7 @@
           final boolean parentsEq = parentsEqual(c, prior);
           final boolean authorEq = authorEqual(c, prior);
 
-          if (messageEq && parentsEq && authorEq) {
+          if (messageEq && parentsEq && authorEq && !ignoreNoChanges) {
             reject(request.cmd, "no changes made");
             return null;
           } else {
@@ -1866,7 +1866,6 @@
           rw.parseBody(c);
           closeChange(cmd, PatchSet.Id.fromRef(ref.getName()), c);
           closeProgress.update(1);
-          continue;
         }
 
         rw.parseBody(c);
@@ -1880,7 +1879,7 @@
       }
 
       for (final ReplaceRequest req : toClose) {
-        final PatchSet.Id psi = doReplace(req);
+        final PatchSet.Id psi = doReplace(req, true);
         if (psi != null) {
           closeChange(req.cmd, psi, req.newCommit);
           closeProgress.update(1);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RebasedPatchSetSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RebasedPatchSetSender.java
new file mode 100644
index 0000000..8fc8238
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RebasedPatchSetSender.java
@@ -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"));
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 3e65a11..1a50030 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -199,6 +199,11 @@
     return isOwner() && isVisible(db);
   }
 
+  /** Can this user rebase this change? */
+  public boolean canRebase() {
+    return canAddPatchSet();
+  }
+
   /** Can this user restore this change? */
   public boolean canRestore() {
     return canAbandon(); // Anyone who can abandon the change can restore it back
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BranchPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BranchPredicate.java
index 15712e02c..e5b4143 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BranchPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/BranchPredicate.java
@@ -23,12 +23,11 @@
 
 class BranchPredicate extends OperatorPredicate<ChangeData> {
   private final Provider<ReviewDb> dbProvider;
-  private final String shortName;
 
   BranchPredicate(Provider<ReviewDb> dbProvider, String branch) {
-    super(ChangeQueryBuilder.FIELD_BRANCH, branch);
+    super(ChangeQueryBuilder.FIELD_BRANCH, branch.startsWith(Branch.R_HEADS)
+        ? branch : Branch.R_HEADS + branch);
     this.dbProvider = dbProvider;
-    this.shortName = branch;
   }
 
   @Override
@@ -38,7 +37,7 @@
       return false;
     }
     return change.getDest().get().startsWith(Branch.R_HEADS)
-        && shortName.equals(change.getDest().getShortName());
+        && getValue().equals(change.getDest().get());
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
index f8ee134..bf2e4cc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.change;
 
+import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ChangeAccess;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -118,6 +119,102 @@
     return a.getValue().compareTo(b.getValue()) >= 0 ? a : b;
   }
 
+  @Rewrite("status:open P=(project:*) B=(branch:*) S=(sortkey_after:*) L=(limit:*)")
+  public Predicate<ChangeData> r05_byBranchOpenPrev(
+      @Named("P") final ProjectPredicate p,
+      @Named("B") final BranchPredicate b,
+      @Named("S") final SortKeyPredicate.After s,
+      @Named("L") final IntPredicate<ChangeData> l) {
+    return new PaginatedSource(500, s.getValue(), l.intValue()) {
+      @Override
+      ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+          throws OrmException {
+        return a.byBranchOpenAll(new Branch.NameKey(p.getValueKey(), b
+            .getValue()));
+      }
+
+      @Override
+      public boolean match(ChangeData cd) throws OrmException {
+        return cd.change(dbProvider).getStatus().isOpen() //
+            && p.match(cd) //
+            && b.match(cd) //
+            && s.match(cd);
+      }
+    };
+  }
+
+  @Rewrite("status:open P=(project:*) B=(branch:*) S=(sortkey_before:*) L=(limit:*)")
+  public Predicate<ChangeData> r05_byBranchOpenNext(
+      @Named("P") final ProjectPredicate p,
+      @Named("B") final BranchPredicate b,
+      @Named("S") final SortKeyPredicate.Before s,
+      @Named("L") final IntPredicate<ChangeData> l) {
+    return new PaginatedSource(500, s.getValue(), l.intValue()) {
+      @Override
+      ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+          throws OrmException {
+        return a.byBranchOpenAll(new Branch.NameKey(p.getValueKey(), b
+            .getValue()));
+      }
+
+      @Override
+      public boolean match(ChangeData cd) throws OrmException {
+        return cd.change(dbProvider).getStatus().isOpen() //
+            && p.match(cd) //
+            && b.match(cd) //
+            && s.match(cd);
+      }
+    };
+  }
+
+  @Rewrite("status:merged P=(project:*) B=(branch:*) S=(sortkey_after:*) L=(limit:*)")
+  public Predicate<ChangeData> r05_byBranchMergedPrev(
+      @Named("P") final ProjectPredicate p,
+      @Named("B") final BranchPredicate b,
+      @Named("S") final SortKeyPredicate.After s,
+      @Named("L") final IntPredicate<ChangeData> l) {
+    return new PaginatedSource(40000, s.getValue(), l.intValue()) {
+      @Override
+      ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+          throws OrmException {
+        return a.byBranchClosedPrev(Change.Status.MERGED.getCode(), //
+            new Branch.NameKey(p.getValueKey(), b.getValue()), key, limit);
+      }
+
+      @Override
+      public boolean match(ChangeData cd) throws OrmException {
+        return cd.change(dbProvider).getStatus() == Change.Status.MERGED
+            && p.match(cd) //
+            && b.match(cd) //
+            && s.match(cd);
+      }
+    };
+  }
+
+  @Rewrite("status:merged P=(project:*) B=(branch:*) S=(sortkey_before:*) L=(limit:*)")
+  public Predicate<ChangeData> r05_byBranchMergedNext(
+      @Named("P") final ProjectPredicate p,
+      @Named("B") final BranchPredicate b,
+      @Named("S") final SortKeyPredicate.Before s,
+      @Named("L") final IntPredicate<ChangeData> l) {
+    return new PaginatedSource(40000, s.getValue(), l.intValue()) {
+      @Override
+      ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+          throws OrmException {
+        return a.byBranchClosedNext(Change.Status.MERGED.getCode(), //
+            new Branch.NameKey(p.getValueKey(), b.getValue()), key, limit);
+      }
+
+      @Override
+      public boolean match(ChangeData cd) throws OrmException {
+        return cd.change(dbProvider).getStatus() == Change.Status.MERGED
+            && p.match(cd) //
+            && b.match(cd) //
+            && s.match(cd);
+      }
+    };
+  }
+
   @Rewrite("status:open P=(project:*) S=(sortkey_after:*) L=(limit:*)")
   public Predicate<ChangeData> r10_byProjectOpenPrev(
       @Named("P") final ProjectPredicate p,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index d5b5a26..9caeed3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_62> C = Schema_62.class;
+  public static final Class<Schema_63> C = Schema_63.class;
 
   public static class Module extends AbstractModule {
     @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_63.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_63.java
new file mode 100644
index 0000000..761c36a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_63.java
@@ -0,0 +1,48 @@
+// 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.schema;
+
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.schema.sql.DialectPostgreSQL;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class Schema_63 extends SchemaVersion {
+  @Inject
+  Schema_63(Provider<Schema_62> prior) {
+    super(prior);
+  }
+
+  @Override
+  protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
+    Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+    try {
+      if (((JdbcSchema) db).getDialect() instanceof DialectPostgreSQL) {
+        stmt.execute("CREATE INDEX changes_byBranchClosed"
+            + " ON changes (status, dest_project_name, dest_branch_name, sort_key)"
+            + " WHERE open = 'N'");
+      } else {
+        stmt.execute("CREATE INDEX changes_byBranchClosed"
+            + " ON changes (status, dest_project_name, dest_branch_name, sort_key)");
+      }
+    } finally {
+      stmt.close();
+    }
+  }
+}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RebasedPatchSet.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RebasedPatchSet.vm
new file mode 100644
index 0000000..e761627
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RebasedPatchSet.vm
@@ -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