Rebase-Always strategy: always generate footers.

Repurpose Cherry-Pick's strategy message generation for wider use.
This completes work on Rebase-Always strategy.

Bug: issue 4452
Change-Id: Id282999ae0c12a57edd35207220a640a27fc3659
This commit is contained in:
Andrii Shyshkalov 2016-11-22 12:30:19 +01:00
parent 45abaca745
commit 7fe4516c61
7 changed files with 97 additions and 13 deletions

View File

@ -103,7 +103,8 @@ submit type unless
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 @@ the same file has also been changed on the other side of the merge.
[[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]]

View File

@ -18,9 +18,12 @@ import static com.google.common.truth.Truth.assertThat;
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 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
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;
}
}

View File

@ -1112,7 +1112,7 @@ public class ChangeJson {
if (addFooters) {
out.commitWithFooters = mergeUtilFactory
.create(projectCache.get(project))
.createCherryPickCommitMessage(commit, ctl, in.getId());
.createDetailedCommitMessage(commit, ctl, in.getId());
}
}
}

View File

@ -68,6 +68,7 @@ public class RebaseChangeOp extends BatchUpdate.Op {
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 @@ public class RebaseChangeOp extends BatchUpdate.Op {
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 @@ public class RebaseChangeOp extends BatchUpdate.Op {
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 @@ public class RebaseChangeOp extends BatchUpdate.Op {
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 @@ public class RebaseChangeOp extends BatchUpdate.Op {
* @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 @@ public class RebaseChangeOp extends BatchUpdate.Op {
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 {

View File

@ -246,7 +246,24 @@ public class MergeUtil {
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 @@ public class MergeUtil {
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) {

View File

@ -100,7 +100,7 @@ public class CherryPick extends SubmitStrategy {
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());

View File

@ -139,7 +139,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
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 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
// 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);