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:
@@ -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
|
||||
|
||||
|
||||
|
@@ -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`.
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -77,6 +77,8 @@ public interface AdminConstants extends Constants {
|
||||
|
||||
String enableReviewerByEmail();
|
||||
|
||||
String matchAuthorToCommitterDate();
|
||||
|
||||
String headingMaxObjectSizeLimit();
|
||||
|
||||
String headingGroupOptions();
|
||||
|
@@ -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
|
||||
|
@@ -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())),
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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; }-*/ ;
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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 "
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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()) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user