Merge changes from topic 'author-commiter-timestamp'

* changes:
  Implement MatchAuthorToCommitterDate for applicable strategies
  Add option to match author timestamp and committed timestamp
This commit is contained in:
Edwin Kempin
2017-06-23 14:04:24 +00:00
committed by Gerrit Code Review
19 changed files with 131 additions and 3 deletions

View File

@@ -212,6 +212,12 @@ are 'true', 'false', or 'INHERIT'. Default is 'INHERIT'.
values are 'fast forward only', 'merge if necessary', 'rebase if necessary',
'merge always' and 'cherry pick'. The default is 'merge if necessary'.
- 'matchAuthorToCommitterDate': Defines whether to the author date will be changed to match the
submitter date upon submit, so that git log shows when the change was submitted instead of when the
author last committed. Valid values are 'true', 'false', or 'INHERIT'. The default is 'INHERIT'.
This option only takes effect in submit strategies which already modify the commit, i.e.
Cherry Pick, Rebase Always, and (perhaps) Rebase If Necessary.
Merge strategy

View File

@@ -2472,6 +2472,9 @@ MaxObjectSizeLimitInfo] entity.
The default submit type of the project, can be `MERGE_IF_NECESSARY`,
`FAST_FORWARD_ONLY`, `REBASE_IF_NECESSARY`, `MERGE_ALWAYS` or
`CHERRY_PICK`.
|`match_author_to_committer_date` |optional|
link:#inherited-boolean-info[InheritedBooleanInfo] that indicates whether
a change's author date will be changed to match its submitter date upon submit.
|`state` |optional|
The state of the project, can be `ACTIVE`, `READ_ONLY` or `HIDDEN`. +
Not set if the project state is `ACTIVE`.

View File

@@ -42,6 +42,7 @@ import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.InheritableBoolean;
@@ -989,6 +990,31 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertThat(input.generateLockFailures).containsExactly(false);
}
@Test
public void authorAndCommitDateAreEqual() throws Exception {
assume().that(getSubmitType()).isNotEqualTo(SubmitType.FAST_FORWARD_ONLY);
ConfigInput ci = new ConfigInput();
ci.matchAuthorToCommitterDate = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(ci);
RevCommit initialHead = getRemoteHead();
testRepo.reset(initialHead);
PushOneCommit.Result change = createChange("Change 1", "b", "b");
testRepo.reset(initialHead);
PushOneCommit.Result change2 = createChange("Change 2", "c", "c");
if (getSubmitType() == SubmitType.MERGE_IF_NECESSARY
|| getSubmitType() == SubmitType.REBASE_IF_NECESSARY) {
// Merge another change so that change2 is not a fast-forward
submit(change.getChangeId());
}
submit(change2.getChangeId());
assertAuthorAndCommitDateEquals(getRemoteHead());
}
private void setChangeStatusToNew(PushOneCommit.Result... changes) throws Exception {
for (PushOneCommit.Result change : changes) {
try (BatchUpdate bu =
@@ -1142,6 +1168,12 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertThat(actual.getTimeZone()).isEqualTo(expected.getTimeZone());
}
protected void assertAuthorAndCommitDateEquals(RevCommit commit) {
assertThat(commit.getAuthorIdent().getWhen()).isEqualTo(commit.getCommitterIdent().getWhen());
assertThat(commit.getAuthorIdent().getTimeZone())
.isEqualTo(commit.getCommitterIdent().getTimeZone());
}
protected void assertSubmitter(String changeId, int psId) throws Exception {
assertSubmitter(changeId, psId, admin);
}

View File

@@ -32,6 +32,7 @@ public class ConfigInfo {
public InheritedBooleanInfo requireSignedPush;
public InheritedBooleanInfo rejectImplicitMerges;
public InheritedBooleanInfo enableReviewerByEmail;
public InheritedBooleanInfo matchAuthorToCommitterDate;
public MaxObjectSizeLimitInfo maxObjectSizeLimit;
public SubmitType submitType;
public ProjectState state;

View File

@@ -30,6 +30,7 @@ public class ConfigInput {
public InheritableBoolean requireSignedPush;
public InheritableBoolean rejectImplicitMerges;
public InheritableBoolean enableReviewerByEmail;
public InheritableBoolean matchAuthorToCommitterDate;
public String maxObjectSizeLimit;
public SubmitType submitType;
public ProjectState state;

View File

@@ -77,6 +77,8 @@ public interface AdminConstants extends Constants {
String enableReviewerByEmail();
String matchAuthorToCommitterDate();
String headingMaxObjectSizeLimit();
String headingGroupOptions();

View File

@@ -38,6 +38,7 @@ headingParentProjectName = Rights Inherit From
parentSuggestions = Parent Suggestion
columnProjectName = Project Name
enableReviewerByEmail = Enable adding unregistered users as reviewers and CCs on changes
matchAuthorToCommitterDate = Match authored date with committer date upon submit
headingGroupUUID = Group UUID
headingOwner = Owners

View File

@@ -87,6 +87,7 @@ public class ProjectInfoScreen extends ProjectScreen {
private ListBox requireSignedPush;
private ListBox rejectImplicitMerges;
private ListBox enableReviewerByEmail;
private ListBox matchAuthorToCommitterDate;
private NpTextBox maxObjectSizeLimit;
private Label effectiveMaxObjectSizeLimit;
private Map<String, Map<String, HasEnabled>> pluginConfigWidgets;
@@ -193,6 +194,7 @@ public class ProjectInfoScreen extends ProjectScreen {
rejectImplicitMerges.setEnabled(isOwner);
maxObjectSizeLimit.setEnabled(isOwner);
enableReviewerByEmail.setEnabled(isOwner);
matchAuthorToCommitterDate.setEnabled(isOwner);
if (pluginConfigWidgets != null) {
for (Map<String, HasEnabled> widgetMap : pluginConfigWidgets.values()) {
@@ -270,6 +272,10 @@ public class ProjectInfoScreen extends ProjectScreen {
saveEnabler.listenTo(enableReviewerByEmail);
grid.addHtml(AdminConstants.I.enableReviewerByEmail(), enableReviewerByEmail);
matchAuthorToCommitterDate = newInheritedBooleanBox();
saveEnabler.listenTo(matchAuthorToCommitterDate);
grid.addHtml(AdminConstants.I.matchAuthorToCommitterDate(), matchAuthorToCommitterDate);
maxObjectSizeLimit = new NpTextBox();
saveEnabler.listenTo(maxObjectSizeLimit);
effectiveMaxObjectSizeLimit = new Label();
@@ -402,6 +408,7 @@ public class ProjectInfoScreen extends ProjectScreen {
}
setBool(rejectImplicitMerges, result.rejectImplicitMerges());
setBool(enableReviewerByEmail, result.enableReviewerByEmail());
setBool(matchAuthorToCommitterDate, result.matchAuthorToCommitterDate());
setSubmitType(result.submitType());
setState(result.state());
maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue());
@@ -673,6 +680,7 @@ public class ProjectInfoScreen extends ProjectScreen {
rsp,
getBool(rejectImplicitMerges),
getBool(enableReviewerByEmail),
getBool(matchAuthorToCommitterDate),
maxObjectSizeLimit.getText().trim(),
SubmitType.valueOf(submitType.getValue(submitType.getSelectedIndex())),
ProjectState.valueOf(state.getValue(state.getSelectedIndex())),

View File

@@ -60,6 +60,9 @@ public class ConfigInfo extends JavaScriptObject {
public final native InheritedBooleanInfo enableReviewerByEmail()
/*-{ return this.enable_reviewer_by_email; }-*/ ;
public final native InheritedBooleanInfo matchAuthorToCommitterDate()
/*-{ return this.match_author_to_committer_date; }-*/ ;
public final SubmitType submitType() {
return SubmitType.valueOf(submitTypeRaw());
}

View File

@@ -148,6 +148,7 @@ public class ProjectApi {
InheritableBoolean requireSignedPush,
InheritableBoolean rejectImplicitMerges,
InheritableBoolean enableReviewerByEmail,
InheritableBoolean matchAuthorToCommitterDate,
String maxObjectSizeLimit,
SubmitType submitType,
ProjectState state,
@@ -172,6 +173,7 @@ public class ProjectApi {
in.setState(state);
in.setPluginConfigValues(pluginConfigValues);
in.setEnableReviewerByEmail(enableReviewerByEmail);
in.setMatchAuthorToCommitterDate(matchAuthorToCommitterDate);
project(name).view("config").put(in, cb);
}
@@ -300,6 +302,13 @@ public class ProjectApi {
setEnableReviewerByEmailRaw(v.name());
}
final void setMatchAuthorToCommitterDate(InheritableBoolean v) {
setMatchAuthorToCommitterDateRaw(v.name());
}
private native void setMatchAuthorToCommitterDateRaw(String v)
/*-{ if(v)this.match_author_to_committer_date=v; }-*/ ;
private native void setEnableReviewerByEmailRaw(String v)
/*-{ if(v)this.enable_reviewer_by_email=v; }-*/ ;

View File

@@ -101,6 +101,8 @@ public final class Project {
protected InheritableBoolean enableReviewerByEmail;
protected InheritableBoolean matchAuthorToCommitterDate;
protected Project() {}
public Project(Project.NameKey nameKey) {
@@ -115,6 +117,7 @@ public final class Project {
enableSignedPush = InheritableBoolean.INHERIT;
requireSignedPush = InheritableBoolean.INHERIT;
enableReviewerByEmail = InheritableBoolean.INHERIT;
matchAuthorToCommitterDate = InheritableBoolean.INHERIT;
}
public Project.NameKey getNameKey() {
@@ -165,6 +168,14 @@ public final class Project {
enableReviewerByEmail = enable;
}
public InheritableBoolean getMatchAuthorToCommitterDate() {
return matchAuthorToCommitterDate;
}
public void setMatchAuthorToCommitterDate(InheritableBoolean match) {
matchAuthorToCommitterDate = match;
}
public void setUseContributorAgreements(InheritableBoolean u) {
useContributorAgreements = u;
}

View File

@@ -67,6 +67,7 @@ public class RebaseChangeOp implements BatchUpdateOp {
private boolean copyApprovals = true;
private boolean detailedCommitMessage;
private boolean postMessage = true;
private boolean matchAuthorToCommitterDate = false;
private RevCommit rebasedCommit;
private PatchSet.Id rebasedPatchSetId;
@@ -131,6 +132,11 @@ public class RebaseChangeOp implements BatchUpdateOp {
return this;
}
public RebaseChangeOp setMatchAuthorToCommitterDate(boolean matchAuthorToCommitterDate) {
this.matchAuthorToCommitterDate = matchAuthorToCommitterDate;
return this;
}
@Override
public void updateRepo(RepoContext ctx)
throws MergeConflictException, InvalidChangeOperationException, RestApiException, IOException,
@@ -263,6 +269,11 @@ public class RebaseChangeOp implements BatchUpdateOp {
} else {
cb.setCommitter(ctx.getIdentifiedUser().newCommitterIdent(ctx.getWhen(), ctx.getTimeZone()));
}
if (matchAuthorToCommitterDate) {
cb.setAuthor(
new PersonIdent(
cb.getAuthor(), cb.getCommitter().getWhen(), cb.getCommitter().getTimeZone()));
}
ObjectId objectId = ctx.getInserter().insert(cb);
ctx.getInserter().flush();
return ctx.getRevWalk().parseCommit(objectId);

View File

@@ -248,6 +248,7 @@ public class MergeUtil {
mergeCommit.setAuthor(originalCommit.getAuthorIdent());
mergeCommit.setCommitter(cherryPickCommitterIdent);
mergeCommit.setMessage(commitMsg);
matchAuthorToCommitterDate(project, mergeCommit);
return rw.parseCommit(inserter.insert(mergeCommit));
}
throw new MergeConflictException("merge conflict");
@@ -857,4 +858,14 @@ public class MergeUtil {
throw new ResourceNotFoundException(e.getMessage());
}
}
private static void matchAuthorToCommitterDate(ProjectState project, CommitBuilder commit) {
if (project.isMatchAuthorToCommitterDate()) {
commit.setAuthor(
new PersonIdent(
commit.getAuthor(),
commit.getCommitter().getWhen(),
commit.getCommitter().getTimeZone()));
}
}
}

View File

@@ -88,6 +88,8 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final String PROJECT = "project";
private static final String KEY_DESCRIPTION = "description";
private static final String KEY_MATCH_AUTHOR_DATE_WITH_COMMITTER_DATE =
"matchAuthorToCommitterDate";
public static final String ACCESS = "access";
private static final String KEY_INHERIT_FROM = "inheritFrom";
@@ -532,6 +534,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, DEFAULT_SUBMIT_ACTION));
p.setUseContentMerge(getEnum(rc, SUBMIT, null, KEY_MERGE_CONTENT, InheritableBoolean.INHERIT));
p.setMatchAuthorToCommitterDate(
getEnum(
rc,
SUBMIT,
null,
KEY_MATCH_AUTHOR_DATE_WITH_COMMITTER_DATE,
InheritableBoolean.INHERIT));
p.setState(getEnum(rc, PROJECT, null, KEY_STATE, DEFAULT_STATE_VALUE));
p.setDefaultDashboard(rc.getString(DASHBOARD, null, KEY_DEFAULT));
@@ -1036,7 +1045,6 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
rc.unset(PROJECT, null, KEY_DESCRIPTION);
}
set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName());
set(
rc,
RECEIVE,
@@ -1102,6 +1110,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), DEFAULT_SUBMIT_ACTION);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.getUseContentMerge(), InheritableBoolean.INHERIT);
set(
rc,
SUBMIT,
null,
KEY_MATCH_AUTHOR_DATE_WITH_COMMITTER_DATE,
p.getMatchAuthorToCommitterDate(),
InheritableBoolean.INHERIT);
set(rc, PROJECT, null, KEY_STATE, p.getState(), DEFAULT_STATE_VALUE);

View File

@@ -28,7 +28,8 @@ class MergeOneOp extends SubmitStrategyOp {
@Override
public void updateRepoImpl(RepoContext ctx) throws IntegrationException, IOException {
PersonIdent caller =
ctx.getIdentifiedUser().newCommitterIdent(ctx.getWhen(), ctx.getTimeZone());
ctx.getIdentifiedUser()
.newCommitterIdent(args.serverIdent.getWhen(), args.serverIdent.getTimeZone());
if (args.mergeTip.getCurrentTip() == null) {
throw new IllegalStateException(
"cannot merge commit "

View File

@@ -179,7 +179,8 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
.setDetailedCommitMessage(rebaseAlways)
// Do not post message after inserting new patchset because there
// will be one about change being merged already.
.setPostMessage(false);
.setPostMessage(false)
.setMatchAuthorToCommitterDate(args.project.isMatchAuthorToCommitterDate());
try {
rebaseOp.updateRepo(ctx);
} catch (MergeConflictException | NoSuchChangeException e) {

View File

@@ -59,6 +59,7 @@ public class ConfigInfoImpl extends ConfigInfo {
InheritedBooleanInfo requireSignedPush = new InheritedBooleanInfo();
InheritedBooleanInfo rejectImplicitMerges = new InheritedBooleanInfo();
InheritedBooleanInfo enableReviewerByEmail = new InheritedBooleanInfo();
InheritedBooleanInfo matchAuthorToCommitterDate = new InheritedBooleanInfo();
useContributorAgreements.value = projectState.isUseContributorAgreements();
useSignedOffBy.value = projectState.isUseSignedOffBy();
@@ -75,6 +76,7 @@ public class ConfigInfoImpl extends ConfigInfo {
requireSignedPush.configuredValue = p.getRequireSignedPush();
rejectImplicitMerges.configuredValue = p.getRejectImplicitMerges();
enableReviewerByEmail.configuredValue = p.getEnableReviewerByEmail();
matchAuthorToCommitterDate.configuredValue = p.getMatchAuthorToCommitterDate();
ProjectState parentState = Iterables.getFirst(projectState.parents(), null);
if (parentState != null) {
@@ -88,6 +90,7 @@ public class ConfigInfoImpl extends ConfigInfo {
requireSignedPush.inheritedValue = projectState.isRequireSignedPush();
rejectImplicitMerges.inheritedValue = projectState.isRejectImplicitMerges();
enableReviewerByEmail.inheritedValue = projectState.isEnableReviewerByEmail();
matchAuthorToCommitterDate.inheritedValue = projectState.isMatchAuthorToCommitterDate();
}
this.useContributorAgreements = useContributorAgreements;
@@ -97,6 +100,7 @@ public class ConfigInfoImpl extends ConfigInfo {
this.rejectImplicitMerges = rejectImplicitMerges;
this.createNewChangeForAllNotInTarget = createNewChangeForAllNotInTarget;
this.enableReviewerByEmail = enableReviewerByEmail;
this.matchAuthorToCommitterDate = matchAuthorToCommitterDate;
if (serverEnableSignedPush) {
this.enableSignedPush = enableSignedPush;
this.requireSignedPush = requireSignedPush;

View File

@@ -398,6 +398,10 @@ public class ProjectState {
return getInheritableBoolean(Project::getEnableReviewerByEmail);
}
public boolean isMatchAuthorToCommitterDate() {
return getInheritableBoolean(Project::getMatchAuthorToCommitterDate);
}
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = new LinkedHashMap<>();
for (ProjectState s : treeInOrder()) {

View File

@@ -163,6 +163,10 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
p.setEnableReviewerByEmail(input.enableReviewerByEmail);
}
if (input.matchAuthorToCommitterDate != null) {
p.setMatchAuthorToCommitterDate(input.matchAuthorToCommitterDate);
}
if (input.pluginConfigValues != null) {
setPluginConfigValues(ctrl.getProjectState(), projectConfig, input.pluginConfigValues);
}