Merge "Rebase-Always strategy: always generate footers."
diff --git a/Documentation/project-configuration.txt b/Documentation/project-configuration.txt
index 1aaf7fd..54ddcff 100644
--- a/Documentation/project-configuration.txt
+++ b/Documentation/project-configuration.txt
@@ -103,7 +103,8 @@
 link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
 is enabled and depending changes share the same topic. So generally
 submitters must remember to submit changes in the right order when using this
-submit type.
+submit type. If all you want is extra information in the commit message,
+consider using the Rebase Always submit strategy.
 
 [[rebase_if_necessary]]
 * Rebase If Necessary
@@ -120,8 +121,12 @@
 [[rebase_always]]
 * Rebase Always
 +
-Basically, the same as Rebase If Necessary, but it creates a new patchset even if
-fast forward is possible. In this regard, it's similar to Cherry Pick, but with
+Basically, the same as Rebase If Necessary, but it creates a new patchset even
+if fast forward is possible AND like Cherry Pick it ensures footers such as
+Change-Id, Reviewed-On, and others are present in resulting commit that is
+merged.
+
+Thus, Rebase Always can be considered similar to Cherry Pick, but with
 the important distinction that Rebase Always does not ignore dependencies.
 
 [[content_merge]]
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index cf9651f..d1c9577 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -18,9 +18,12 @@
 
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.common.ChangeInfo;
 
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Test;
 
@@ -50,4 +53,44 @@
     assertRefUpdatedEvents(oldHead, head);
     assertChangeMergedEvents(change.getChangeId(), head.name());
   }
+
+  @Test
+  @TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
+  public void alwaysAddFooters() throws Exception {
+    PushOneCommit.Result change1 = createChange();
+    PushOneCommit.Result change2 = createChange();
+
+    assertThat(
+        getCurrentCommit(change1).getFooterLines(FooterConstants.REVIEWED_BY))
+            .isEmpty();
+    assertThat(
+        getCurrentCommit(change2).getFooterLines(FooterConstants.REVIEWED_BY))
+            .isEmpty();
+
+    // change1 is a fast-forward, but should be rebased in cherry pick style
+    // anyway, making change2 not a fast-forward, requiring a rebase.
+    approve(change1.getChangeId());
+    submit(change2.getChangeId());
+    // ... but both changes should get reviewed-by footers.
+    assertLatestRevisionHasFooters(change1);
+    assertLatestRevisionHasFooters(change2);
+  }
+
+  private void assertLatestRevisionHasFooters(PushOneCommit.Result change)
+      throws Exception {
+    RevCommit c = getCurrentCommit(change);
+    assertThat(c.getFooterLines(FooterConstants.CHANGE_ID)).isNotEmpty();
+    assertThat(c.getFooterLines(FooterConstants.REVIEWED_BY)).isNotEmpty();
+    assertThat(c.getFooterLines(FooterConstants.REVIEWED_ON)).isNotEmpty();
+  }
+
+  private RevCommit getCurrentCommit(PushOneCommit.Result change)
+      throws Exception {
+    testRepo.git().fetch().setRemote("origin").call();
+    ChangeInfo info = get(change.getChangeId());
+    RevCommit c = testRepo.getRevWalk()
+        .parseCommit(ObjectId.fromString(info.currentRevision));
+    testRepo.getRevWalk().parseBody(c);
+    return c;
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index e4326a8..3518ccd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -1112,7 +1112,7 @@
         if (addFooters) {
           out.commitWithFooters = mergeUtilFactory
               .create(projectCache.get(project))
-              .createCherryPickCommitMessage(commit, ctl, in.getId());
+              .createDetailedCommitMessage(commit, ctl, in.getId());
         }
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java
index d39f4fc..a7ab082 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -68,6 +68,7 @@
   private CommitValidators.Policy validate;
   private boolean forceContentMerge;
   private boolean copyApprovals = true;
+  private boolean detailedCommitMessage = false;
   private boolean postMessage = true;
 
   private RevCommit rebasedCommit;
@@ -118,6 +119,12 @@
     return this;
   }
 
+  public RebaseChangeOp setDetailedCommitMessage(
+      boolean detailedCommitMessage) {
+    this.detailedCommitMessage = detailedCommitMessage;
+    return this;
+  }
+
   public RebaseChangeOp setPostMessage(boolean postMessage) {
     this.postMessage = postMessage;
     return this;
@@ -134,6 +141,15 @@
     RevWalk rw = ctx.getRevWalk();
     RevCommit original = rw.parseCommit(ObjectId.fromString(oldRev.get()));
     rw.parseBody(original);
+
+    String newCommitMessage;
+    if (detailedCommitMessage) {
+      newCommitMessage = newMergeUtil().createDetailedCommitMessage(original,
+          ctl, originalPatchSet.getId());
+    } else {
+      newCommitMessage = original.getFullMessage();
+    }
+
     RevCommit baseCommit;
     if (baseCommitish != null) {
        baseCommit = rw.parseCommit(ctx.getRepository().resolve(baseCommitish));
@@ -143,7 +159,7 @@
            ctx.getRepository(), ctx.getRevWalk()));
     }
 
-    rebasedCommit = rebaseCommit(ctx, original, baseCommit);
+    rebasedCommit = rebaseCommit(ctx, original, baseCommit, newCommitMessage);
 
     RevId baseRevId = new RevId((baseCommitish != null) ? baseCommitish
         : ObjectId.toString(baseCommit.getId()));
@@ -223,8 +239,8 @@
    * @throws IOException the merge failed for another reason.
    */
   private RevCommit rebaseCommit(RepoContext ctx, RevCommit original,
-      ObjectId base) throws ResourceConflictException, MergeConflictException,
-      IOException {
+      ObjectId base, String commitMessage)
+      throws ResourceConflictException, MergeConflictException, IOException {
     RevCommit parentCommit = original.getParent(0);
 
     if (base.equals(parentCommit)) {
@@ -245,7 +261,7 @@
     cb.setTreeId(merger.getResultTreeId());
     cb.setParentId(base);
     cb.setAuthor(original.getAuthorIdent());
-    cb.setMessage(original.getFullMessage());
+    cb.setMessage(commitMessage);
     if (committerIdent != null) {
       cb.setCommitter(committerIdent);
     } else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index 025db40..54361cc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -246,7 +246,24 @@
     return sb.toString();
   }
 
-  public String createCherryPickCommitMessage(RevCommit n, ChangeControl ctl,
+  /**
+   * Adds footers to existing commit message based on the state of the change.
+   *
+   * This adds the following footers if they are missing:
+   *
+   * <ul>
+   *   <li> Reviewed-on: <i>url</i></li>
+   *   <li> Reviewed-by | Tested-by | <i>Other-Label-Name</i>: <i>reviewer</i>
+   *   </li>
+   *   <li> Change-Id </li>
+   * </ul>
+   *
+   * @param n
+   * @param ctl
+   * @param psId
+   * @return new message
+   */
+  public String createDetailedCommitMessage(RevCommit n, ChangeControl ctl,
       PatchSet.Id psId) {
     Change c = ctl.getChange();
     final List<FooterLine> footers = n.getFooterLines();
@@ -354,8 +371,8 @@
     return msgbuf.toString();
   }
 
-  public String createCherryPickCommitMessage(final CodeReviewCommit n) {
-    return createCherryPickCommitMessage(n, n.getControl(), n.getPatchsetId());
+  public String createDetailedCommitMessage(final CodeReviewCommit n) {
+    return createDetailedCommitMessage(n, n.getControl(), n.getPatchsetId());
   }
 
   private static boolean isCodeReview(LabelId id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
index a4656ae..e9772e5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
@@ -100,7 +100,7 @@
       psId = ChangeUtil.nextPatchSetId(
           args.repo, toMerge.change().currentPatchSetId());
       String cherryPickCmtMsg =
-          args.mergeUtil.createCherryPickCommitMessage(toMerge);
+          args.mergeUtil.createDetailedCommitMessage(toMerge);
 
       PersonIdent committer = args.caller.newCommitterIdent(
           ctx.getWhen(), args.serverIdent.getTimeZone());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java
index 9beae1f..34d3a80 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java
@@ -139,7 +139,7 @@
             args.repo, toMerge.change().currentPatchSetId());
         // TODO(tandrii): add extension point to customize this commit message.
         String cherryPickCmtMsg =
-            args.mergeUtil.createCherryPickCommitMessage(toMerge);
+            args.mergeUtil.createDetailedCommitMessage(toMerge);
 
         PersonIdent committer = args.caller.newCommitterIdent(ctx.getWhen(),
             args.serverIdent.getTimeZone());
@@ -171,6 +171,9 @@
             // later anyway.
             .setCopyApprovals(false)
             .setValidatePolicy(CommitValidators.Policy.NONE)
+            // RebaseAlways should set always modify commit message like
+            // Cherry-Pick strategy.
+            .setDetailedCommitMessage(rebaseAlways)
             // Do not post message after inserting new patchset because there
             // will be one about change being merged already.
             .setPostMessage(false);