Merge "Change default replacement if the account could not be matched by name/email."
diff --git a/java/com/google/gerrit/server/approval/ApprovalInference.java b/java/com/google/gerrit/server/approval/ApprovalInference.java
index 4cb080a..695997a 100644
--- a/java/com/google/gerrit/server/approval/ApprovalInference.java
+++ b/java/com/google/gerrit/server/approval/ApprovalInference.java
@@ -54,6 +54,7 @@
import java.util.Map;
import java.util.Optional;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
/**
@@ -134,8 +135,9 @@
PatchSet.Id psId,
ChangeKind kind,
LabelType type,
- @Nullable Map<String, FileDiffOutput> modifiedFiles,
- @Nullable Map<String, FileDiffOutput> modifiedFilesLastPatchset) {
+ @Nullable Map<String, FileDiffOutput> baseVsCurrentDiff,
+ @Nullable Map<String, FileDiffOutput> baseVsPriorDiff,
+ @Nullable Map<String, FileDiffOutput> priorVsCurrentDiff) {
int n = psa.key().patchSetId().get();
checkArgument(n != psId.get());
@@ -185,7 +187,8 @@
project.getName());
return true;
} else if (type.isCopyAllScoresIfListOfFilesDidNotChange()
- && listOfFilesUnchangedPredicate.match(modifiedFiles, modifiedFilesLastPatchset)) {
+ && listOfFilesUnchangedPredicate.match(
+ baseVsCurrentDiff, baseVsPriorDiff, priorVsCurrentDiff)) {
logger.atFine().log(
"approval %d on label %s of patch set %d of change %d can be copied"
+ " to patch set %d because the label has set "
@@ -402,8 +405,9 @@
priorPatchSet.getValue().id().changeId(),
changeKind);
- Map<String, FileDiffOutput> modifiedFiles = null;
- Map<String, FileDiffOutput> modifiedFilesLastPatchSet = null;
+ Map<String, FileDiffOutput> baseVsCurrent = null;
+ Map<String, FileDiffOutput> baseVsPrior = null;
+ Map<String, FileDiffOutput> priorVsCurrent = null;
LabelTypes labelTypes = project.getLabelTypes();
for (PatchSetApproval psa : priorApprovals) {
if (resultByUser.contains(psa.label(), psa.accountId())) {
@@ -411,11 +415,13 @@
}
Optional<LabelType> type = labelTypes.byLabel(psa.labelId());
// Only compute modified files if there is a relevant label, since this is expensive.
- if (modifiedFiles == null
+ if (baseVsCurrent == null
&& type.isPresent()
&& type.get().isCopyAllScoresIfListOfFilesDidNotChange()) {
- modifiedFiles = listModifiedFiles(project, patchSet);
- modifiedFilesLastPatchSet = listModifiedFiles(project, priorPatchSet.getValue());
+ baseVsCurrent = listModifiedFiles(project, patchSet);
+ baseVsPrior = listModifiedFiles(project, priorPatchSet.getValue());
+ priorVsCurrent =
+ listModifiedFiles(project, priorPatchSet.getValue().commitId(), patchSet.commitId());
}
if (!type.isPresent()) {
logger.atFine().log(
@@ -435,8 +441,9 @@
patchSet.id(),
changeKind,
type.get(),
- modifiedFiles,
- modifiedFilesLastPatchSet)
+ baseVsCurrent,
+ baseVsPrior,
+ priorVsCurrent)
&& !canCopyBasedOnCopyCondition(notes, psa, patchSet, type.get(), changeKind)) {
continue;
}
@@ -465,4 +472,21 @@
ex);
}
}
+
+ /**
+ * Gets the modified files between two commits corresponding to different patchsets of the same
+ * change.
+ */
+ private Map<String, FileDiffOutput> listModifiedFiles(
+ ProjectState project, ObjectId sourceCommit, ObjectId targetCommit) {
+ try {
+ return diffOperations.listModifiedFiles(project.getNameKey(), sourceCommit, targetCommit);
+ } catch (DiffNotAvailableException ex) {
+ throw new StorageException(
+ "failed to compute difference in files, so won't copy"
+ + " votes on labels even if list of files is the same and "
+ + "copyAllIfListOfFilesDidNotChange",
+ ex);
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java b/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
index 55c27be..de7dd0a 100644
--- a/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
+++ b/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
@@ -58,13 +58,18 @@
Integer parentNum =
isInitialCommit(ctx.changeNotes().getProjectName(), targetPatchSet.commitId()) ? 0 : 1;
try {
- Map<String, FileDiffOutput> modifiedTargetPatchSet =
+ Map<String, FileDiffOutput> baseVsCurrent =
diffOperations.listModifiedFilesAgainstParent(
ctx.changeNotes().getProjectName(), targetPatchSet.commitId(), parentNum);
- Map<String, FileDiffOutput> modifiedSourcePatchSet =
+ Map<String, FileDiffOutput> baseVsPrior =
diffOperations.listModifiedFilesAgainstParent(
ctx.changeNotes().getProjectName(), sourcePatchSet.commitId(), parentNum);
- return match(modifiedTargetPatchSet, modifiedSourcePatchSet);
+ Map<String, FileDiffOutput> priorVsCurrent =
+ diffOperations.listModifiedFiles(
+ ctx.changeNotes().getProjectName(),
+ sourcePatchSet.commitId(),
+ targetPatchSet.commitId());
+ return match(baseVsCurrent, baseVsPrior, priorVsCurrent);
} catch (DiffNotAvailableException ex) {
throw new StorageException(
"failed to compute difference in files, so won't copy"
@@ -79,16 +84,23 @@
* {@link ChangeType} matches for each modified file.
*/
public boolean match(
- Map<String, FileDiffOutput> modifiedFiles1, Map<String, FileDiffOutput> modifiedFiles2) {
+ Map<String, FileDiffOutput> baseVsCurrent,
+ Map<String, FileDiffOutput> baseVsPrior,
+ Map<String, FileDiffOutput> priorVsCurrent) {
Set<String> allFiles = new HashSet<>();
- allFiles.addAll(modifiedFiles1.keySet());
- allFiles.addAll(modifiedFiles2.keySet());
+ allFiles.addAll(baseVsCurrent.keySet());
+ allFiles.addAll(baseVsPrior.keySet());
for (String file : allFiles) {
if (Patch.isMagic(file)) {
continue;
}
- FileDiffOutput fileDiffOutput1 = modifiedFiles1.get(file);
- FileDiffOutput fileDiffOutput2 = modifiedFiles2.get(file);
+ FileDiffOutput fileDiffOutput1 = baseVsCurrent.get(file);
+ FileDiffOutput fileDiffOutput2 = baseVsPrior.get(file);
+ if (!priorVsCurrent.containsKey(file)) {
+ // If the file is not modified between prior and current patchsets, then scan safely skip
+ // it. The file might has been modified due to rebase.
+ continue;
+ }
if (fileDiffOutput1 == null || fileDiffOutput2 == null) {
return false;
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index cd9e876..3888679 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -43,6 +43,7 @@
import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.LabelId;
import com.google.gerrit.entities.LabelType;
@@ -557,6 +558,46 @@
}
@Test
+ public void
+ stickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsModifiedDueToRebase_withoutCopyCondition()
+ throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig()
+ .updateLabelType(
+ LabelId.CODE_REVIEW, b -> b.setCopyAllScoresIfListOfFilesDidNotChange(true));
+ u.save();
+ }
+ // Create two changes both with the same parent
+ PushOneCommit.Result r = createChange();
+ testRepo.reset("HEAD~1");
+ PushOneCommit.Result r2 = createChange();
+
+ // Modify f.txt in change 1. Approve and submit the first change
+ gApi.changes().id(r.getChangeId()).edit().modifyFile("f.txt", RawInputUtil.create("content"));
+ gApi.changes().id(r.getChangeId()).edit().publish();
+ RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
+ revision.review(ReviewInput.approve().label(LabelId.VERIFIED, 1));
+ revision.submit();
+
+ // Add an approval whose score should be copied on change 2.
+ gApi.changes().id(r2.getChangeId()).current().review(ReviewInput.recommend());
+
+ // Rebase the second change. The rebase adds f1.txt.
+ gApi.changes().id(r2.getChangeId()).rebase();
+
+ // The code-review approval is copied for the second change between PS1 and PS2 since the only
+ // modified file is due to rebase.
+ List<PatchSetApproval> patchSetApprovals =
+ r2.getChange().notes().getApprovalsWithCopied().values().stream()
+ .sorted(comparing(a -> a.patchSetId().get()))
+ .collect(toImmutableList());
+ PatchSetApproval nonCopied = patchSetApprovals.get(0);
+ PatchSetApproval copied = patchSetApprovals.get(1);
+ assertCopied(nonCopied, /* psId= */ 1, LabelId.CODE_REVIEW, (short) 1, false);
+ assertCopied(copied, /* psId= */ 2, LabelId.CODE_REVIEW, (short) 1, true);
+ }
+
+ @Test
public void stickyWithCopyAllScoresIfListOfFilesDidNotChangeWhenFileIsModified_withCopyCondition()
throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
@@ -1072,17 +1113,9 @@
.sorted(comparing(a -> a.patchSetId().get()))
.collect(toImmutableList());
PatchSetApproval nonCopied = patchSetApprovals.get(0);
-
- assertThat(nonCopied.patchSetId().get()).isEqualTo(1);
- assertThat(nonCopied.label()).isEqualTo(LabelId.CODE_REVIEW);
- assertThat(nonCopied.value()).isEqualTo((short) 1);
- assertThat(nonCopied.copied()).isFalse();
-
PatchSetApproval copied = patchSetApprovals.get(1);
- assertThat(copied.patchSetId().get()).isEqualTo(2);
- assertThat(copied.label()).isEqualTo(LabelId.CODE_REVIEW);
- assertThat(copied.value()).isEqualTo((short) 1);
- assertThat(copied.copied()).isTrue();
+ assertCopied(nonCopied, 1, LabelId.CODE_REVIEW, (short) 1, /* copied= */ false);
+ assertCopied(copied, 2, LabelId.CODE_REVIEW, (short) 1, /* copied= */ true);
}
@Test
@@ -1311,4 +1344,12 @@
}
assertWithMessage(name).that(vote).isEqualTo(expectedVote);
}
+
+ private void assertCopied(
+ PatchSetApproval approval, int psId, String label, short value, boolean copied) {
+ assertThat(approval.patchSetId().get()).isEqualTo(psId);
+ assertThat(approval.label()).isEqualTo(label);
+ assertThat(approval.value()).isEqualTo(value);
+ assertThat(approval.copied()).isEqualTo(copied);
+ }
}
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index 3b93bea..38f55bd 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -242,7 +242,7 @@
this.addEventListener(EventType.DIALOG_CHANGE, e => {
this._handleDialogChange(e as CustomEvent<DialogChangeEventDetail>);
});
- this.addEventListener('location-change', e =>
+ this.addEventListener(EventType.LOCATION_CHANGE, e =>
this._handleLocationChange(e)
);
this.addEventListener(EventType.RECREATE_CHANGE_VIEW, () =>
@@ -251,7 +251,7 @@
this.addEventListener(EventType.RECREATE_DIFF_VIEW, () =>
this.handleRecreateView(GerritView.DIFF)
);
- document.addEventListener('gr-rpc-log', e => this._handleRpcLog(e));
+ document.addEventListener(EventType.GR_RPC_LOG, e => this._handleRpcLog(e));
}
override ready() {
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts
index ea81aae..5fc53e6 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.ts
@@ -18,6 +18,8 @@
import {customElement} from 'lit/decorators';
import {HovercardMixin} from '../../../mixins/hovercard-mixin/hovercard-mixin';
import {css, html, LitElement} from 'lit';
+import {hovercardStyles} from '../../../styles/gr-hovercard-styles';
+import {sharedStyles} from '../../../styles/shared-styles';
// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
const base = HovercardMixin(LitElement);
@@ -26,7 +28,8 @@
export class GrHovercard extends base {
static override get styles() {
return [
- super.styles || [],
+ sharedStyles,
+ hovercardStyles,
css`
#container {
padding: var(--spacing-l);