Merge "Use a fake in-memory implementation for AccountPatchReviewStore in tests"
diff --git a/Documentation/config-submit-requirements.txt b/Documentation/config-submit-requirements.txt
index ba20cea..5ff9425 100644
--- a/Documentation/config-submit-requirements.txt
+++ b/Documentation/config-submit-requirements.txt
@@ -246,6 +246,15 @@
name contains the file pattern, or the edits of the file diff contain the edit
pattern.
+[[operator_label]]
+label:labelName=+1,user=non_contributor::
++
+Submit requirements support an additional `user=non_contributor` argument for
+labels that returns true if the change has a label vote matching the specified
+value and the vote is applied from a gerrit account that's not the uploader,
+author or committer of the latest patchset. See the documentation for the labels
+operator in the link:user-search.html[user search] page.
+
[[unsupported_operators]]
=== Unsupported Operators
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 40470a6..d4a1864 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -357,6 +357,22 @@
Submit any previously uploaded notes change on the homepage project.
+[[update-supported-releases]]
+=== Update list of supported releases
+
+If you created a new stable release update the list of supported releases
+in the link:https://www.gerritcodereview.com/support.html[support page].
+
+Gerrit releases are also listed on the
+link:https://endoflife.date/gerrit[endoflife website].
+Push a PR to
+link:https://github.com/endoflife-date/endoflife.date.git[endoflife.date repository]
+to update supported releases in `products/gerrit.md`. New release tags
+should be updated automatically by the site's automation job which uses
+Dependabot to
+link:https://github.com/endoflife-date/endoflife.date/wiki/Automation[auto-create PRs]
+for new release tags.
+
[[finalize-issues]]
==== Update the Issues
diff --git a/contrib/git-gc-preserve b/contrib/git-gc-preserve
index a886721..33c8f5b 100755
--- a/contrib/git-gc-preserve
+++ b/contrib/git-gc-preserve
@@ -49,10 +49,10 @@
[2] https://git.eclipse.org/r/c/jgit/jgit/+/122288
CONFIGURATION
- "gc.prunepreserved": if set to "true" preserved packs from the last gc run
+ "pack.prunepreserved": if set to "true" preserved packs from the last gc run
are pruned before current packs are preserved.
- "gc.preserveoldpacks": if set to "true" current packs will be hard linked
+ "pack.preserveoldpacks": if set to "true" current packs will be hard linked
to objects/pack/preserved before git gc is executed. JGit will
fallback to the preserved packs in this directory in case it comes
across missing objects which might be caused by a concurrent run of
@@ -84,9 +84,9 @@
exec 9>&-
}
-# prune preserved packs if gc.prunepreserved == true
+# prune preserved packs if pack.prunepreserved == true
prune_preserved() { # repo
- configured=$(git --git-dir="$1" config --get gc.prunepreserved)
+ configured=$(git --git-dir="$1" config --get pack.prunepreserved)
if [ "$configured" != "true" ]; then
return 0
fi
@@ -99,9 +99,9 @@
fi
}
-# preserve packs if gc.preserveoldpacks == true
+# preserve packs if pack.preserveoldpacks == true
preserve_packs() { # repo
- configured=$(git --git-dir="$1" config --get gc.preserveoldpacks)
+ configured=$(git --git-dir="$1" config --get pack.preserveoldpacks)
if [ "$configured" != "true" ]; then
return 0
fi
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index a4f973e..e93c152 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -33,6 +33,7 @@
import static com.google.gerrit.server.project.testing.TestLabels.label;
import static com.google.gerrit.server.project.testing.TestLabels.value;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
@@ -1707,11 +1708,14 @@
}
public void save() throws Exception {
- metaDataUpdate.setAuthor(identifiedUserFactory.create(admin.id()));
- projectConfig.commit(metaDataUpdate);
- metaDataUpdate.close();
- metaDataUpdate = null;
- projectCache.evictAndReindex(projectConfig.getProject());
+ testRefAction(
+ () -> {
+ metaDataUpdate.setAuthor(identifiedUserFactory.create(admin.id()));
+ projectConfig.commit(metaDataUpdate);
+ metaDataUpdate.close();
+ metaDataUpdate = null;
+ projectCache.evictAndReindex(projectConfig.getProject());
+ });
}
@Override
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index 3c7ec2b..8b2160c 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -53,6 +53,7 @@
"//lib/bouncycastle:bcpg",
"//lib/bouncycastle:bcpkix",
"//lib/bouncycastle:bcprov",
+ "//lib/bouncycastle:bcutil",
"//prolog:gerrit-prolog-common",
]
@@ -75,6 +76,7 @@
"//java/com/google/gerrit/gpg/testing:gpg-test-util",
"//java/com/google/gerrit/git/testing",
"//java/com/google/gerrit/index/testing",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib/errorprone:annotations",
]
diff --git a/java/com/google/gerrit/acceptance/ProjectResetter.java b/java/com/google/gerrit/acceptance/ProjectResetter.java
index 46f7496..c8ab1a9 100644
--- a/java/com/google/gerrit/acceptance/ProjectResetter.java
+++ b/java/com/google/gerrit/acceptance/ProjectResetter.java
@@ -16,6 +16,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.entities.RefNames.REFS_USERS;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
@@ -202,10 +203,12 @@
keptRefsByProject = MultimapBuilder.hashKeys().arrayListValues().build();
restoredRefsByProject = MultimapBuilder.hashKeys().arrayListValues().build();
deletedRefsByProject = MultimapBuilder.hashKeys().arrayListValues().build();
-
- restoreRefs();
- deleteNewlyCreatedRefs();
- evictCachesAndReindex();
+ testRefAction(
+ () -> {
+ restoreRefs();
+ deleteNewlyCreatedRefs();
+ evictCachesAndReindex();
+ });
}
/** Read the states of all matching refs. */
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
index 277d219..e510ba3 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.testsuite.account;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.gerrit.acceptance.SshEnabled;
@@ -88,7 +89,8 @@
private KeyPair createKeyPair(Account.Id accountId, String username, @Nullable String email)
throws Exception {
KeyPair keyPair = SshSessionFactory.genSshKey();
- authorizedKeys.addKey(accountId, publicKey(keyPair, email));
+ testRefAction(() -> authorizedKeys.addKey(accountId, publicKey(keyPair, email)));
+
sshKeyCache.evict(username);
return keyPair;
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java
index 62ad7c4..1dcd662 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/ChangeOperationsImpl.java
@@ -16,6 +16,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -44,6 +45,7 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -132,30 +134,32 @@
}
private Change.Id createChange(TestChangeCreation changeCreation) throws Exception {
- Change.Id changeId = Change.id(seq.nextChangeId());
- Project.NameKey project = getTargetProject(changeCreation);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ Change.Id changeId = Change.id(seq.nextChangeId());
+ Project.NameKey project = getTargetProject(changeCreation);
- try (Repository repository = repositoryManager.openRepository(project);
- ObjectInserter objectInserter = repository.newObjectInserter();
- RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
- Instant now = TimeUtil.now();
- IdentifiedUser changeOwner = getChangeOwner(changeCreation);
- PersonIdent author = getAuthorIdent(now, changeCreation);
- PersonIdent committer = getCommitterIdent(now, changeCreation);
- ObjectId commitId =
- createCommit(repository, revWalk, objectInserter, changeCreation, author, committer);
+ try (Repository repository = repositoryManager.openRepository(project);
+ ObjectInserter objectInserter = repository.newObjectInserter();
+ RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
+ Instant now = TimeUtil.now();
+ IdentifiedUser changeOwner = getChangeOwner(changeCreation);
+ PersonIdent author = getAuthorIdent(now, changeCreation);
+ PersonIdent committer = getCommitterIdent(now, changeCreation);
+ ObjectId commitId =
+ createCommit(repository, revWalk, objectInserter, changeCreation, author, committer);
- String refName = RefNames.fullName(changeCreation.branch());
- ChangeInserter inserter = getChangeInserter(changeId, refName, commitId);
- changeCreation.topic().ifPresent(t -> inserter.setTopic(t));
- inserter.setApprovals(changeCreation.approvals());
+ String refName = RefNames.fullName(changeCreation.branch());
+ ChangeInserter inserter = getChangeInserter(changeId, refName, commitId);
+ changeCreation.topic().ifPresent(t -> inserter.setTopic(t));
+ inserter.setApprovals(changeCreation.approvals());
- try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, changeOwner, now)) {
- batchUpdate.setRepository(repository, revWalk, objectInserter);
- batchUpdate.insertChange(inserter);
- batchUpdate.execute();
+ try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, changeOwner, now)) {
+ batchUpdate.setRepository(repository, revWalk, objectInserter);
+ batchUpdate.insertChange(inserter);
+ batchUpdate.execute();
+ }
+ return changeId;
}
- return changeId;
}
}
@@ -452,39 +456,41 @@
private PatchSet.Id createPatchset(TestPatchsetCreation patchsetCreation)
throws IOException, RestApiException, UpdateException {
- ChangeNotes changeNotes = getChangeNotes();
- Project.NameKey project = changeNotes.getProjectName();
- try (Repository repository = repositoryManager.openRepository(project);
- ObjectInserter objectInserter = repository.newObjectInserter();
- RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
- Instant now = TimeUtil.now();
- PersonIdent authorIdent = getAuthorIdent(now, patchsetCreation);
- PersonIdent committerIdent = getCommitterIdent(now, patchsetCreation);
- ObjectId newPatchsetCommit =
- createPatchsetCommit(
- repository,
- revWalk,
- objectInserter,
- changeNotes,
- patchsetCreation,
- authorIdent,
- committerIdent,
- now);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ ChangeNotes changeNotes = getChangeNotes();
+ Project.NameKey project = changeNotes.getProjectName();
+ try (Repository repository = repositoryManager.openRepository(project);
+ ObjectInserter objectInserter = repository.newObjectInserter();
+ RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
+ Instant now = TimeUtil.now();
+ PersonIdent authorIdent = getAuthorIdent(now, patchsetCreation);
+ PersonIdent committerIdent = getCommitterIdent(now, patchsetCreation);
+ ObjectId newPatchsetCommit =
+ createPatchsetCommit(
+ repository,
+ revWalk,
+ objectInserter,
+ changeNotes,
+ patchsetCreation,
+ authorIdent,
+ committerIdent,
+ now);
- PatchSet.Id patchsetId =
- ChangeUtil.nextPatchSetId(repository, changeNotes.getCurrentPatchSet().id());
- PatchSetInserter patchSetInserter =
- getPatchSetInserter(changeNotes, newPatchsetCommit, patchsetId);
+ PatchSet.Id patchsetId =
+ ChangeUtil.nextPatchSetId(repository, changeNotes.getCurrentPatchSet().id());
+ PatchSetInserter patchSetInserter =
+ getPatchSetInserter(changeNotes, newPatchsetCommit, patchsetId);
- Account.Id uploaderId =
- patchsetCreation.uploader().orElse(changeNotes.getChange().getOwner());
- IdentifiedUser uploader = userFactory.create(uploaderId);
- try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, uploader, now)) {
- batchUpdate.setRepository(repository, revWalk, objectInserter);
- batchUpdate.addOp(changeId, patchSetInserter);
- batchUpdate.execute();
+ Account.Id uploaderId =
+ patchsetCreation.uploader().orElse(changeNotes.getChange().getOwner());
+ IdentifiedUser uploader = userFactory.create(uploaderId);
+ try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, uploader, now)) {
+ batchUpdate.setRepository(repository, revWalk, objectInserter);
+ batchUpdate.addOp(changeId, patchSetInserter);
+ batchUpdate.execute();
+ }
+ return patchsetId;
}
- return patchsetId;
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java
index 9b393ef..3bd355b 100644
--- a/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/change/PerPatchsetOperationsImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.acceptance.testsuite.change;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
+
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Comment.Status;
import com.google.gerrit.entities.HumanComment;
@@ -34,6 +36,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -101,21 +104,23 @@
private String createComment(TestCommentCreation commentCreation)
throws IOException, RestApiException, UpdateException {
+
Project.NameKey project = changeNotes.getProjectName();
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (Repository repository = repositoryManager.openRepository(project);
+ ObjectInserter objectInserter = repository.newObjectInserter();
+ RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
+ Instant now = TimeUtil.now();
- try (Repository repository = repositoryManager.openRepository(project);
- ObjectInserter objectInserter = repository.newObjectInserter();
- RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
- Instant now = TimeUtil.now();
-
- IdentifiedUser author = getAuthor(commentCreation);
- CommentAdditionOp commentAdditionOp = new CommentAdditionOp(commentCreation);
- try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, author, now)) {
- batchUpdate.setRepository(repository, revWalk, objectInserter);
- batchUpdate.addOp(changeNotes.getChangeId(), commentAdditionOp);
- batchUpdate.execute();
+ IdentifiedUser author = getAuthor(commentCreation);
+ CommentAdditionOp commentAdditionOp = new CommentAdditionOp(commentCreation);
+ try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, author, now)) {
+ batchUpdate.setRepository(repository, revWalk, objectInserter);
+ batchUpdate.addOp(changeNotes.getChangeId(), commentAdditionOp);
+ batchUpdate.execute();
+ }
+ return commentAdditionOp.createdCommentUuid;
}
- return commentAdditionOp.createdCommentUuid;
}
}
@@ -197,21 +202,22 @@
private String createRobotComment(TestRobotCommentCreation robotCommentCreation)
throws IOException, RestApiException, UpdateException {
Project.NameKey project = changeNotes.getProjectName();
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (Repository repository = repositoryManager.openRepository(project);
+ ObjectInserter objectInserter = repository.newObjectInserter();
+ RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
+ Instant now = TimeUtil.now();
- try (Repository repository = repositoryManager.openRepository(project);
- ObjectInserter objectInserter = repository.newObjectInserter();
- RevWalk revWalk = new RevWalk(objectInserter.newReader())) {
- Instant now = TimeUtil.now();
-
- IdentifiedUser author = getAuthor(robotCommentCreation);
- RobotCommentAdditionOp robotCommentAdditionOp =
- new RobotCommentAdditionOp(robotCommentCreation);
- try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, author, now)) {
- batchUpdate.setRepository(repository, revWalk, objectInserter);
- batchUpdate.addOp(changeNotes.getChangeId(), robotCommentAdditionOp);
- batchUpdate.execute();
+ IdentifiedUser author = getAuthor(robotCommentCreation);
+ RobotCommentAdditionOp robotCommentAdditionOp =
+ new RobotCommentAdditionOp(robotCommentCreation);
+ try (BatchUpdate batchUpdate = batchUpdateFactory.create(project, author, now)) {
+ batchUpdate.setRepository(repository, revWalk, objectInserter);
+ batchUpdate.addOp(changeNotes.getChangeId(), robotCommentAdditionOp);
+ batchUpdate.execute();
+ }
+ return robotCommentAdditionOp.createdRobotCommentUuid;
}
- return robotCommentAdditionOp.createdRobotCommentUuid;
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/BUILD b/java/com/google/gerrit/acceptance/testsuite/group/BUILD
index d4f1175..2052105 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/BUILD
+++ b/java/com/google/gerrit/acceptance/testsuite/group/BUILD
@@ -14,6 +14,7 @@
"//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:guava",
"//lib:jgit",
"//lib:jgit-junit",
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
index 8bb7b23..99899cf 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
@@ -14,6 +14,8 @@
package com.google.gerrit.acceptance.testsuite.group;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
+
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -106,7 +108,7 @@
*/
public AccountGroup.UUID create() {
TestGroupCreation groupCreation = autoBuild();
- return groupCreation.groupCreator().applyAndThrowSilently(groupCreation);
+ return testRefAction(() -> groupCreation.groupCreator().applyAndThrowSilently(groupCreation));
}
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/BUILD b/java/com/google/gerrit/acceptance/testsuite/project/BUILD
index d34b79a..4ac2705 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/BUILD
+++ b/java/com/google/gerrit/acceptance/testsuite/project/BUILD
@@ -4,6 +4,7 @@
java_library(
name = "project",
+ testonly = True,
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
deps = [
@@ -13,6 +14,7 @@
"//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:guava",
"//lib:jgit",
"//lib:jgit-junit",
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
index 69139ce..bd3d656 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -17,6 +17,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.entities.RefNames.REFS_CONFIG;
import static com.google.gerrit.server.project.ProjectConfig.PROJECT_CONFIG;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
@@ -41,6 +42,7 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectCreator;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -136,19 +138,21 @@
private void updateProject(TestProjectUpdate projectUpdate)
throws IOException, ConfigInvalidException {
- try (MetaDataUpdate metaDataUpdate = metaDataUpdateFactory.create(nameKey)) {
- ProjectConfig projectConfig = projectConfigFactory.read(metaDataUpdate);
- if (projectUpdate.removeAllAccessSections()) {
- projectConfig.getAccessSections().forEach(as -> projectConfig.remove(as));
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (MetaDataUpdate metaDataUpdate = metaDataUpdateFactory.create(nameKey)) {
+ ProjectConfig projectConfig = projectConfigFactory.read(metaDataUpdate);
+ if (projectUpdate.removeAllAccessSections()) {
+ projectConfig.getAccessSections().forEach(as -> projectConfig.remove(as));
+ }
+ removePermissions(projectConfig, projectUpdate.removedPermissions());
+ addCapabilities(projectConfig, projectUpdate.addedCapabilities());
+ addPermissions(projectConfig, projectUpdate.addedPermissions());
+ addLabelPermissions(projectConfig, projectUpdate.addedLabelPermissions());
+ setExclusiveGroupPermissions(projectConfig, projectUpdate.exclusiveGroupPermissions());
+ projectConfig.commit(metaDataUpdate);
}
- removePermissions(projectConfig, projectUpdate.removedPermissions());
- addCapabilities(projectConfig, projectUpdate.addedCapabilities());
- addPermissions(projectConfig, projectUpdate.addedPermissions());
- addLabelPermissions(projectConfig, projectUpdate.addedLabelPermissions());
- setExclusiveGroupPermissions(projectConfig, projectUpdate.exclusiveGroupPermissions());
- projectConfig.commit(metaDataUpdate);
+ projectCache.evictAndReindex(nameKey);
}
- projectCache.evictAndReindex(nameKey);
}
private void removePermissions(
diff --git a/java/com/google/gerrit/entities/RefNames.java b/java/com/google/gerrit/entities/RefNames.java
index 9745fc5..e79c530 100644
--- a/java/com/google/gerrit/entities/RefNames.java
+++ b/java/com/google/gerrit/entities/RefNames.java
@@ -185,6 +185,21 @@
return ref.startsWith(REFS_CHANGES);
}
+ /** True if the provided ref is in {@code refs/sequences/*}. */
+ public static boolean isSequenceRef(String ref) {
+ return ref.startsWith(REFS_SEQUENCES);
+ }
+
+ /** True if the provided ref is in {@code refs/tags/*}. */
+ public static boolean isTagRef(String ref) {
+ return ref.startsWith(REFS_TAGS);
+ }
+
+ /** True if the provided ref is {@link #REFS_EXTERNAL_IDS}. */
+ public static boolean isExternalIdRef(String ref) {
+ return REFS_EXTERNAL_IDS.equals(ref);
+ }
+
public static String refsGroups(AccountGroup.UUID groupUuid) {
return REFS_GROUPS + shardUuid(groupUuid.get());
}
@@ -330,6 +345,21 @@
return REFS_CONFIG.equals(ref);
}
+ /** Whether the ref is the version branch, i.e. {@code refs/meta/version}. */
+ public static boolean isVersionRef(String ref) {
+ return REFS_VERSION.equals(ref);
+ }
+
+ /** Whether the ref is an auto-merge ref. */
+ public static boolean isAutoMergeRef(String ref) {
+ return ref.startsWith(REFS_CACHE_AUTOMERGE);
+ }
+
+ /** Whether the ref is an reject commit ref, i.e. {@code refs/meta/reject-commits} */
+ public static boolean isRejectCommitsRef(String ref) {
+ return REFS_REJECT_COMMITS.equals(ref);
+ }
+
/**
* Whether the ref is managed by Gerrit. Covers all Gerrit-internal refs like refs/cache-automerge
* and refs/meta as well as refs/changes. Does not cover user-created refs like branches or custom
diff --git a/java/com/google/gerrit/gpg/PublicKeyStore.java b/java/com/google/gerrit/gpg/PublicKeyStore.java
index d167ac8..cbd2bba 100644
--- a/java/com/google/gerrit/gpg/PublicKeyStore.java
+++ b/java/com/google/gerrit/gpg/PublicKeyStore.java
@@ -15,6 +15,7 @@
package com.google.gerrit.gpg;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.GPG_KEYS_MODIFICATION;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -24,6 +25,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.git.ObjectIds;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -377,35 +379,36 @@
newTip = ins.insert(cb);
ins.flush();
}
-
- RefUpdate ru = repo.updateRef(PublicKeyStore.REFS_GPG_KEYS);
- ru.setExpectedOldObjectId(tip);
- ru.setNewObjectId(newTip);
- ru.setRefLogIdent(cb.getCommitter());
- ru.setRefLogMessage("Store public keys", true);
- RefUpdate.Result result = ru.update();
- reset();
- switch (result) {
- case FAST_FORWARD:
- case NEW:
- case NO_CHANGE:
- toAdd.clear();
- toRemove.clear();
- break;
- case LOCK_FAILURE:
- throw new LockFailureException("Failed to store public keys", ru);
- case FORCED:
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- break;
+ try (RefUpdateContext ctx = RefUpdateContext.open(GPG_KEYS_MODIFICATION)) {
+ RefUpdate ru = repo.updateRef(PublicKeyStore.REFS_GPG_KEYS);
+ ru.setExpectedOldObjectId(tip);
+ ru.setNewObjectId(newTip);
+ ru.setRefLogIdent(cb.getCommitter());
+ ru.setRefLogMessage("Store public keys", true);
+ RefUpdate.Result result = ru.update();
+ reset();
+ switch (result) {
+ case FAST_FORWARD:
+ case NEW:
+ case NO_CHANGE:
+ toAdd.clear();
+ toRemove.clear();
+ break;
+ case LOCK_FAILURE:
+ throw new LockFailureException("Failed to store public keys", ru);
+ case FORCED:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ break;
+ }
+ return result;
}
- return result;
}
private void saveToNotes(ObjectInserter ins, PGPPublicKeyRing keyRing)
diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
index 72bfe40..92788b7 100644
--- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
@@ -121,7 +121,7 @@
data.put("userIsAuthenticated", true);
if (page == RequestedPage.DASHBOARD) {
data.put("defaultDashboardHex", ListOption.toHex(IndexPreloadingUtil.DASHBOARD_OPTIONS));
- data.put("dashboardQuery", IndexPreloadingUtil.computeDashboardQueryList(serverApi));
+ data.put("dashboardQuery", IndexPreloadingUtil.computeDashboardQueryList());
}
} catch (AuthException e) {
logger.atFine().log("Can't inline account-related data because user is unauthenticated");
diff --git a/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java b/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java
index bb3b6d5..afaeaf6 100644
--- a/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexPreloadingUtil.java
@@ -22,9 +22,7 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.UsedAt.Project;
-import com.google.gerrit.extensions.api.config.Server;
import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.Url;
import java.net.URI;
import java.net.URISyntaxException;
@@ -188,7 +186,7 @@
return Optional.empty();
}
- public static List<String> computeDashboardQueryList(Server serverApi) throws RestApiException {
+ public static List<String> computeDashboardQueryList() {
List<String> queryList = new ArrayList<>();
queryList.add(SELF_DASHBOARD_HAS_UNPUBLISHED_DRAFTS_QUERY);
queryList.add(SELF_YOUR_TURN);
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index 9403105..2d18054 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
@@ -41,6 +42,7 @@
import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -388,27 +390,29 @@
u.setNewObjectId(writeLabels(repo, labels));
u.setRefLogIdent(serverIdent.get());
u.setRefLogMessage("Update star labels", true);
- RefUpdate.Result result = u.update(rw);
- switch (result) {
- case NEW:
- case FORCED:
- case NO_CHANGE:
- case FAST_FORWARD:
- gitRefUpdated.fire(allUsers, u, null);
- return;
- case LOCK_FAILURE:
- throw new LockFailureException(
- String.format("Update star labels on ref %s failed", refName), u);
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new StorageException(
- String.format("Update star labels on ref %s failed: %s", refName, result.name()));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ RefUpdate.Result result = u.update(rw);
+ switch (result) {
+ case NEW:
+ case FORCED:
+ case NO_CHANGE:
+ case FAST_FORWARD:
+ gitRefUpdated.fire(allUsers, u, null);
+ return;
+ case LOCK_FAILURE:
+ throw new LockFailureException(
+ String.format("Update star labels on ref %s failed", refName), u);
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new StorageException(
+ String.format("Update star labels on ref %s failed: %s", refName, result.name()));
+ }
}
}
}
@@ -427,26 +431,28 @@
u.setExpectedOldObjectId(oldObjectId);
u.setRefLogIdent(serverIdent.get());
u.setRefLogMessage("Unstar change", true);
- RefUpdate.Result result = u.delete();
- switch (result) {
- case FORCED:
- gitRefUpdated.fire(allUsers, u, null);
- return;
- case LOCK_FAILURE:
- throw new LockFailureException(String.format("Delete star ref %s failed", refName), u);
- case NEW:
- case NO_CHANGE:
- case FAST_FORWARD:
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new StorageException(
- String.format("Delete star ref %s failed: %s", refName, result.name()));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ RefUpdate.Result result = u.delete();
+ switch (result) {
+ case FORCED:
+ gitRefUpdated.fire(allUsers, u, null);
+ return;
+ case LOCK_FAILURE:
+ throw new LockFailureException(String.format("Delete star ref %s failed", refName), u);
+ case NEW:
+ case NO_CHANGE:
+ case FAST_FORWARD:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new StorageException(
+ String.format("Delete star ref %s failed: %s", refName, result.name()));
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index d6ea294..1f2bc8c 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -16,6 +16,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.ACCOUNTS_UPDATE;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
@@ -45,6 +46,7 @@
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryableAction.Action;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -443,28 +445,32 @@
private ImmutableList<Optional<AccountState>> execute(List<ExecutableUpdate> executableUpdates)
throws IOException, ConfigInvalidException {
- List<Optional<AccountState>> accountState = new ArrayList<>();
- List<UpdatedAccount> updatedAccounts = new ArrayList<>();
- executeWithRetry(
- () -> {
- // Reset state for retry.
- externalIdNotes = null;
- accountState.clear();
- updatedAccounts.clear();
+ try (RefUpdateContext ctx = RefUpdateContext.open(ACCOUNTS_UPDATE)) {
+ List<Optional<AccountState>> accountState = new ArrayList<>();
+ List<UpdatedAccount> updatedAccounts = new ArrayList<>();
+ executeWithRetry(
+ () -> {
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- for (ExecutableUpdate executableUpdate : executableUpdates) {
- updatedAccounts.add(executableUpdate.execute(allUsersRepo));
+ // Reset state for retry.
+ externalIdNotes = null;
+ accountState.clear();
+ updatedAccounts.clear();
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ for (ExecutableUpdate executableUpdate : executableUpdates) {
+ updatedAccounts.add(executableUpdate.execute(allUsersRepo));
+ }
+ commit(
+ allUsersRepo,
+ updatedAccounts.stream().filter(Objects::nonNull).collect(toList()));
+ for (UpdatedAccount ua : updatedAccounts) {
+ accountState.add(ua == null ? Optional.empty() : ua.getAccountState());
+ }
}
- commit(
- allUsersRepo, updatedAccounts.stream().filter(Objects::nonNull).collect(toList()));
- for (UpdatedAccount ua : updatedAccounts) {
- accountState.add(ua == null ? Optional.empty() : ua.getAccountState());
- }
- }
- return null;
- });
- return ImmutableList.copyOf(accountState);
+ return null;
+ });
+
+ return ImmutableList.copyOf(accountState);
+ }
}
private void executeWithRetry(Action<Void> action) throws IOException, ConfigInvalidException {
diff --git a/java/com/google/gerrit/server/account/externalids/testing/BUILD b/java/com/google/gerrit/server/account/externalids/testing/BUILD
index 0e469e3..e2de6da 100644
--- a/java/com/google/gerrit/server/account/externalids/testing/BUILD
+++ b/java/com/google/gerrit/server/account/externalids/testing/BUILD
@@ -8,6 +8,7 @@
deps = [
"//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:jgit",
],
)
diff --git a/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
index a42afc3..7878ee2 100644
--- a/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
+++ b/java/com/google/gerrit/server/account/externalids/testing/ExternalIdTestUtil.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.account.externalids.testing;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
@@ -143,7 +144,7 @@
RefUpdate u = repo.updateRef(RefNames.REFS_EXTERNAL_IDS);
u.setExpectedOldObjectId(rev);
u.setNewObjectId(commitId);
- RefUpdate.Result res = u.update();
+ RefUpdate.Result res = testRefAction(() -> u.update());
switch (res) {
case NEW:
case FAST_FORWARD:
diff --git a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 3a892bc..ad42ae6 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -16,6 +16,8 @@
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
import static com.google.gerrit.server.restapi.project.DashboardsCollection.DEFAULT_DASHBOARD_NAME;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.HEAD_MODIFICATION;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
@@ -89,6 +91,7 @@
import com.google.gerrit.server.restapi.project.SetAccess;
import com.google.gerrit.server.restapi.project.SetHead;
import com.google.gerrit.server.restapi.project.SetParent;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -595,7 +598,9 @@
@Override
public void deleteBranches(DeleteBranchesInput in) throws RestApiException {
try {
- deleteBranches.apply(checkExists(), in);
+ try (RefUpdateContext ctx = RefUpdateContext.open(BRANCH_MODIFICATION)) {
+ deleteBranches.apply(checkExists(), in);
+ }
} catch (Exception e) {
throw asRestApiException("Cannot delete branches", e);
}
@@ -686,7 +691,9 @@
HeadInput input = new HeadInput();
input.ref = head;
try {
- setHead.apply(checkExists(), input);
+ try (RefUpdateContext ctx = RefUpdateContext.open(HEAD_MODIFICATION)) {
+ setHead.apply(checkExists(), input);
+ }
} catch (Exception e) {
throw asRestApiException("Cannot set HEAD", e);
}
diff --git a/java/com/google/gerrit/server/api/projects/TagApiImpl.java b/java/com/google/gerrit/server/api/projects/TagApiImpl.java
index 005486a..f9bd048 100644
--- a/java/com/google/gerrit/server/api/projects/TagApiImpl.java
+++ b/java/com/google/gerrit/server/api/projects/TagApiImpl.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.api.projects;
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.TAG_MODIFICATION;
import com.google.gerrit.extensions.api.projects.TagApi;
import com.google.gerrit.extensions.api.projects.TagInfo;
@@ -29,6 +30,7 @@
import com.google.gerrit.server.restapi.project.DeleteTag;
import com.google.gerrit.server.restapi.project.ListTags;
import com.google.gerrit.server.restapi.project.TagsCollection;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -83,7 +85,9 @@
@Override
public void delete() throws RestApiException {
try {
- deleteTag.apply(resource(), new Input());
+ try (RefUpdateContext ctx = RefUpdateContext.open(TAG_MODIFICATION)) {
+ deleteTag.apply(resource(), new Input());
+ }
} catch (Exception e) {
throw asRestApiException("Cannot delete tag", e);
}
diff --git a/java/com/google/gerrit/server/change/BatchAbandon.java b/java/com/google/gerrit/server/change/BatchAbandon.java
index 2efa027..9070006 100644
--- a/java/com/google/gerrit/server/change/BatchAbandon.java
+++ b/java/com/google/gerrit/server/change/BatchAbandon.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -25,6 +27,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -68,24 +71,27 @@
return;
}
AccountState accountState = user.isIdentifiedUser() ? user.asIdentifiedUser().state() : null;
- try (BatchUpdate u = updateFactory.create(project, user, TimeUtil.now())) {
- u.setNotify(notify);
- for (ChangeData change : changes) {
- if (!project.equals(change.project())) {
- throw new ResourceConflictException(
- String.format(
- "Project name \"%s\" doesn't match \"%s\"",
- change.project().get(), project.get()));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u = updateFactory.create(project, user, TimeUtil.now())) {
+ u.setNotify(notify);
+ for (ChangeData change : changes) {
+ if (!project.equals(change.project())) {
+ throw new ResourceConflictException(
+ String.format(
+ "Project name \"%s\" doesn't match \"%s\"",
+ change.project().get(), project.get()));
+ }
+ u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
+ u.addOp(
+ change.getId(),
+ storeSubmitRequirementsOpFactory.create(
+ change.submitRequirements().values(), change));
}
- u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
- u.addOp(
- change.getId(),
- storeSubmitRequirementsOpFactory.create(change.submitRequirements().values(), change));
- }
- u.execute();
+ u.execute();
- if (cfg.getCleanupAccountPatchReview()) {
- cleanupAccountPatchReview(changes);
+ if (cfg.getCleanupAccountPatchReview()) {
+ cleanupAccountPatchReview(changes);
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/change/ConsistencyChecker.java b/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 38efc44..063903b 100644
--- a/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -18,6 +18,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.entities.RefNames.REFS_CHANGES;
import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
@@ -57,6 +58,7 @@
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -174,29 +176,31 @@
public Result check(ChangeNotes notes, @Nullable FixInput f) {
requireNonNull(notes);
try {
- return retryHelper
- .changeUpdate(
- "checkChangeConsistency",
- buf -> {
- try {
- reset();
- this.updateFactory = buf;
- this.notes = notes;
- fix = f;
- checkImpl();
- return result();
- } finally {
- if (rw != null) {
- rw.getObjectReader().close();
- rw.close();
- oi.close();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ return retryHelper
+ .changeUpdate(
+ "checkChangeConsistency",
+ buf -> {
+ try {
+ reset();
+ this.updateFactory = buf;
+ this.notes = notes;
+ fix = f;
+ checkImpl();
+ return result();
+ } finally {
+ if (rw != null) {
+ rw.getObjectReader().close();
+ rw.close();
+ oi.close();
+ }
+ if (repo != null) {
+ repo.close();
+ }
}
- if (repo != null) {
- repo.close();
- }
- }
- })
- .call();
+ })
+ .call();
+ }
} catch (RestApiException e) {
return logAndReturnOneProblem(e, notes, "Error checking change: " + e.getMessage());
} catch (UpdateException e) {
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index ad0dd8b..a2ce866 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.edit;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.Charsets;
import com.google.gerrit.common.Nullable;
@@ -49,6 +50,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -408,8 +410,10 @@
ObjectId newEditCommit =
createCommit(repository, basePatchsetCommit, newTreeId, newCommitMessage, nowTimestamp);
- return editBehavior.updateEditInStorage(
- repository, notes, basePatchset, newEditCommit, nowTimestamp);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ return editBehavior.updateEditInStorage(
+ repository, notes, basePatchset, newEditCommit, nowTimestamp);
+ }
}
private void assertCanEdit(ChangeNotes notes)
@@ -801,23 +805,25 @@
ObjectId targetObjectId,
Instant timestamp)
throws IOException {
- RefUpdate ru = repository.updateRef(refName);
- ru.setExpectedOldObjectId(currentObjectId);
- ru.setNewObjectId(targetObjectId);
- ru.setRefLogIdent(getRefLogIdent(timestamp));
- ru.setRefLogMessage("inline edit (amend)", false);
- ru.setForceUpdate(true);
- try (RevWalk revWalk = new RevWalk(repository)) {
- RefUpdate.Result res = ru.update(revWalk);
- String message = "cannot update " + ru.getName() + " in " + projectName + ": " + res;
- if (res == RefUpdate.Result.LOCK_FAILURE) {
- throw new LockFailureException(message, ru);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ RefUpdate ru = repository.updateRef(refName);
+ ru.setExpectedOldObjectId(currentObjectId);
+ ru.setNewObjectId(targetObjectId);
+ ru.setRefLogIdent(getRefLogIdent(timestamp));
+ ru.setRefLogMessage("inline edit (amend)", false);
+ ru.setForceUpdate(true);
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ RefUpdate.Result res = ru.update(revWalk);
+ String message = "cannot update " + ru.getName() + " in " + projectName + ": " + res;
+ if (res == RefUpdate.Result.LOCK_FAILURE) {
+ throw new LockFailureException(message, ru);
+ }
+ if (res != RefUpdate.Result.NEW && res != RefUpdate.Result.FORCED) {
+ throw new IOException(message);
+ }
}
- if (res != RefUpdate.Result.NEW && res != RefUpdate.Result.FORCED) {
- throw new IOException(message);
- }
+ gitReferenceUpdated.fire(projectName, ru, getUpdater());
}
- gitReferenceUpdated.fire(projectName, ru, getUpdater());
}
void baseEditOnDifferentPatchset(
@@ -850,21 +856,24 @@
ObjectId targetObjectId,
Instant timestamp)
throws IOException {
- BatchRefUpdate batchRefUpdate = repository.getRefDatabase().newBatchUpdate();
- batchRefUpdate.addCommand(new ReceiveCommand(ObjectId.zeroId(), targetObjectId, newRefName));
- batchRefUpdate.addCommand(
- new ReceiveCommand(currentObjectId, ObjectId.zeroId(), currentRefName));
- batchRefUpdate.setRefLogMessage("rebase edit", false);
- batchRefUpdate.setRefLogIdent(getRefLogIdent(timestamp));
- try (RevWalk revWalk = new RevWalk(repository)) {
- batchRefUpdate.execute(revWalk, NullProgressMonitor.INSTANCE);
- }
- for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
- if (cmd.getResult() != ReceiveCommand.Result.OK) {
- throw new IOException("failed: " + cmd);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ BatchRefUpdate batchRefUpdate = repository.getRefDatabase().newBatchUpdate();
+ batchRefUpdate.addCommand(
+ new ReceiveCommand(ObjectId.zeroId(), targetObjectId, newRefName));
+ batchRefUpdate.addCommand(
+ new ReceiveCommand(currentObjectId, ObjectId.zeroId(), currentRefName));
+ batchRefUpdate.setRefLogMessage("rebase edit", false);
+ batchRefUpdate.setRefLogIdent(getRefLogIdent(timestamp));
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ batchRefUpdate.execute(revWalk, NullProgressMonitor.INSTANCE);
}
+ for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
+ if (cmd.getResult() != ReceiveCommand.Result.OK) {
+ throw new IOException("failed: " + cmd);
+ }
+ }
+ gitReferenceUpdated.fire(projectName, batchRefUpdate, getUpdater());
}
- gitReferenceUpdated.fire(projectName, batchRefUpdate, getUpdater());
}
static RevCommit lookupCommit(Repository repository, ObjectId commitId) throws IOException {
diff --git a/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index 3474590..3594afb 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.edit;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
@@ -40,6 +41,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -189,20 +191,22 @@
} else {
message.append("Published edit on patch set ").append(basePatchSet.number()).append(".");
}
-
- try (BatchUpdate bu = updateFactory.create(change.getProject(), user, TimeUtil.now())) {
- bu.setRepository(repo, rw, oi);
- bu.setNotify(notify);
- bu.addOp(change.getId(), inserter.setMessage(message.toString()));
- bu.addOp(
- change.getId(),
- new BatchUpdateOp() {
- @Override
- public void updateRepo(RepoContext ctx) throws Exception {
- ctx.addRefUpdate(edit.getEditCommit().copy(), ObjectId.zeroId(), edit.getRefName());
- }
- });
- bu.execute();
+ try (RefUpdateContext changeCtx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu = updateFactory.create(change.getProject(), user, TimeUtil.now())) {
+ bu.setRepository(repo, rw, oi);
+ bu.setNotify(notify);
+ bu.addOp(change.getId(), inserter.setMessage(message.toString()));
+ bu.addOp(
+ change.getId(),
+ new BatchUpdateOp() {
+ @Override
+ public void updateRepo(RepoContext ctx) throws Exception {
+ ctx.addRefUpdate(
+ edit.getEditCommit().copy(), ObjectId.zeroId(), edit.getRefName());
+ }
+ });
+ bu.execute();
+ }
}
}
}
@@ -243,33 +247,35 @@
}
private void deleteRef(Repository repo, ChangeEdit edit) throws IOException {
- String refName = edit.getRefName();
- RefUpdate ru = repo.updateRef(refName, true);
- ru.setExpectedOldObjectId(edit.getEditCommit());
- ru.setForceUpdate(true);
- RefUpdate.Result result = ru.delete();
- switch (result) {
- case FORCED:
- case NEW:
- case NO_CHANGE:
- break;
- case LOCK_FAILURE:
- throw new LockFailureException(String.format("Failed to delete ref %s", refName), ru);
- case FAST_FORWARD:
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new IOException(String.format("Failed to delete ref %s: %s", refName, result));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ String refName = edit.getRefName();
+ RefUpdate ru = repo.updateRef(refName, true);
+ ru.setExpectedOldObjectId(edit.getEditCommit());
+ ru.setForceUpdate(true);
+ RefUpdate.Result result = ru.delete();
+ switch (result) {
+ case FORCED:
+ case NEW:
+ case NO_CHANGE:
+ break;
+ case LOCK_FAILURE:
+ throw new LockFailureException(String.format("Failed to delete ref %s", refName), ru);
+ case FAST_FORWARD:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new IOException(String.format("Failed to delete ref %s: %s", refName, result));
+ }
+ gitReferenceUpdated.fire(
+ edit.getChange().getProject(),
+ ru,
+ /* updater= */ userProvider.get().asIdentifiedUser().state());
}
- gitReferenceUpdated.fire(
- edit.getChange().getProject(),
- ru,
- /* updater= */ userProvider.get().asIdentifiedUser().state());
}
private static RevCommit writeSquashedCommit(
diff --git a/java/com/google/gerrit/server/git/BanCommit.java b/java/com/google/gerrit/server/git/BanCommit.java
index e27197c..e00012a 100644
--- a/java/com/google/gerrit/server/git/BanCommit.java
+++ b/java/com/google/gerrit/server/git/BanCommit.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.git;
import static com.google.gerrit.entities.RefNames.REFS_REJECT_COMMITS;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BAN_COMMIT;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.entities.Project;
@@ -26,6 +27,7 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -128,21 +130,23 @@
banCommitNotes.set(commitToBan, noteId);
}
NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(project, repo, inserter);
- NoteMap newlyCreated =
- notesBranchUtil.commitNewNotes(
- banCommitNotes,
- REFS_REJECT_COMMITS,
- createPersonIdent(),
- buildCommitMessage(commitsToBan, reason));
+ try (RefUpdateContext ctx = RefUpdateContext.open(BAN_COMMIT)) {
+ NoteMap newlyCreated =
+ notesBranchUtil.commitNewNotes(
+ banCommitNotes,
+ REFS_REJECT_COMMITS,
+ createPersonIdent(),
+ buildCommitMessage(commitsToBan, reason));
- for (Note n : banCommitNotes) {
- if (newlyCreated.contains(n)) {
- result.commitBanned(n);
- } else {
- result.commitAlreadyBanned(n);
+ for (Note n : banCommitNotes) {
+ if (newlyCreated.contains(n)) {
+ result.commitBanned(n);
+ } else {
+ result.commitAlreadyBanned(n);
+ }
}
+ return result;
}
- return result;
}
}
diff --git a/java/com/google/gerrit/server/git/CommitUtil.java b/java/com/google/gerrit/server/git/CommitUtil.java
index f0b2a78..ffb6c66 100644
--- a/java/com/google/gerrit/server/git/CommitUtil.java
+++ b/java/com/google/gerrit/server/git/CommitUtil.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.git;
import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
@@ -54,6 +55,7 @@
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.PostUpdateContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -162,18 +164,19 @@
ChangeNotes notes, CurrentUser user, RevertInput input, Instant timestamp)
throws RestApiException, UpdateException, ConfigInvalidException, IOException {
String message = Strings.emptyToNull(input.message);
-
- try (Repository git = repoManager.openRepository(notes.getProjectName());
- ObjectInserter oi = git.newObjectInserter();
- ObjectReader reader = oi.newReader();
- RevWalk revWalk = new RevWalk(reader)) {
- ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
- ObjectId revCommit =
- createRevertCommit(message, notes, user, timestamp, oi, revWalk, generatedChangeId);
- return createRevertChangeFromCommit(
- revCommit, input, notes, user, generatedChangeId, timestamp, oi, revWalk, git);
- } catch (RepositoryNotFoundException e) {
- throw new ResourceNotFoundException(notes.getChangeId().toString(), e);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (Repository git = repoManager.openRepository(notes.getProjectName());
+ ObjectInserter oi = git.newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ RevWalk revWalk = new RevWalk(reader)) {
+ ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
+ ObjectId revCommit =
+ createRevertCommit(message, notes, user, timestamp, oi, revWalk, generatedChangeId);
+ return createRevertChangeFromCommit(
+ revCommit, input, notes, user, generatedChangeId, timestamp, oi, revWalk, git);
+ } catch (RepositoryNotFoundException e) {
+ throw new ResourceNotFoundException(notes.getChangeId().toString(), e);
+ }
}
}
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
index 61bd8a8..4f0bde8 100644
--- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
+++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.git.meta;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.VERSIONED_META_DATA_CHANGE;
import com.google.common.base.MoreObjects;
import com.google.common.flogger.FluentLogger;
@@ -27,6 +28,7 @@
import com.google.gerrit.server.logging.Metadata;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import java.io.BufferedReader;
import java.io.File;
@@ -438,53 +440,55 @@
private RevCommit updateRef(AnyObjectId oldId, AnyObjectId newId, String refName)
throws IOException {
- BatchRefUpdate bru = update.getBatch();
- if (bru != null) {
- bru.addCommand(new ReceiveCommand(oldId.toObjectId(), newId.toObjectId(), refName));
- if (objInserter == null) {
- inserter.flush();
- }
- revision = rw.parseCommit(newId);
- return revision;
- }
-
- RefUpdate ru = db.updateRef(refName);
- ru.setExpectedOldObjectId(oldId);
- ru.setNewObjectId(newId);
- ru.setRefLogIdent(update.getCommitBuilder().getAuthor());
- String message = update.getCommitBuilder().getMessage();
- if (message == null) {
- message = "meta data update";
- }
- try (BufferedReader reader = new BufferedReader(new StringReader(message))) {
- // read the subject line and use it as reflog message
- ru.setRefLogMessage("commit: " + reader.readLine(), true);
- }
- logger.atFine().log("Saving commit '%s' on project '%s'", message.trim(), projectName);
- inserter.flush();
- RefUpdate.Result result = ru.update();
- switch (result) {
- case NEW:
- case FAST_FORWARD:
- revision = rw.parseCommit(ru.getNewObjectId());
- update.fireGitRefUpdatedEvent(ru);
- logger.atFine().log(
- "Saved commit '%s' as revision '%s' on project '%s'",
- message.trim(), revision.name(), projectName);
+ try (RefUpdateContext ctx = RefUpdateContext.open(VERSIONED_META_DATA_CHANGE)) {
+ BatchRefUpdate bru = update.getBatch();
+ if (bru != null) {
+ bru.addCommand(new ReceiveCommand(oldId.toObjectId(), newId.toObjectId(), refName));
+ if (objInserter == null) {
+ inserter.flush();
+ }
+ revision = rw.parseCommit(newId);
return revision;
- case LOCK_FAILURE:
- throw new LockFailureException(errorMsg(ru, db.getDirectory()), ru);
- case FORCED:
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case NO_CHANGE:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new GitUpdateFailureException(errorMsg(ru, db.getDirectory()), ru);
+ }
+
+ RefUpdate ru = db.updateRef(refName);
+ ru.setExpectedOldObjectId(oldId);
+ ru.setNewObjectId(newId);
+ ru.setRefLogIdent(update.getCommitBuilder().getAuthor());
+ String message = update.getCommitBuilder().getMessage();
+ if (message == null) {
+ message = "meta data update";
+ }
+ try (BufferedReader reader = new BufferedReader(new StringReader(message))) {
+ // read the subject line and use it as reflog message
+ ru.setRefLogMessage("commit: " + reader.readLine(), true);
+ }
+ logger.atFine().log("Saving commit '%s' on project '%s'", message.trim(), projectName);
+ inserter.flush();
+ RefUpdate.Result result = ru.update();
+ switch (result) {
+ case NEW:
+ case FAST_FORWARD:
+ revision = rw.parseCommit(ru.getNewObjectId());
+ update.fireGitRefUpdatedEvent(ru);
+ logger.atFine().log(
+ "Saved commit '%s' as revision '%s' on project '%s'",
+ message.trim(), revision.name(), projectName);
+ return revision;
+ case LOCK_FAILURE:
+ throw new LockFailureException(errorMsg(ru, db.getDirectory()), ru);
+ case FORCED:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case NO_CHANGE:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new GitUpdateFailureException(errorMsg(ru, db.getDirectory()), ru);
+ }
}
}
};
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 093ca78..d25e022 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -33,6 +33,8 @@
import static com.google.gerrit.server.git.validators.CommitValidators.NEW_PATCHSET_PATTERN;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.DIRECT_PUSH;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
@@ -192,6 +194,7 @@
import com.google.gerrit.server.update.SubmissionListener;
import com.google.gerrit.server.update.SuperprojectUpdateOnSubmission;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.server.util.RequestScopePropagator;
@@ -759,13 +762,15 @@
String pushKind = magicBranch != null && magicBranch.submit ? "direct_submit" : "magic";
metrics.pushCount.increment(pushKind, project.getName(), getUpdateType(magicCommands));
}
- if (!regularCommands.isEmpty()) {
- metrics.pushCount.increment("direct", project.getName(), getUpdateType(regularCommands));
- }
+ try (RefUpdateContext ctx = RefUpdateContext.open(DIRECT_PUSH)) {
+ if (!regularCommands.isEmpty()) {
+ metrics.pushCount.increment("direct", project.getName(), getUpdateType(regularCommands));
+ }
- if (!regularCommands.isEmpty()) {
- handleRegularCommands(regularCommands, progress);
- return;
+ if (!regularCommands.isEmpty()) {
+ handleRegularCommands(regularCommands, progress);
+ return;
+ }
}
boolean first = true;
@@ -1015,120 +1020,124 @@
private void insertChangesAndPatchSets(
ImmutableList<CreateRequest> newChanges, Task replaceProgress) {
- try (TraceTimer traceTimer =
- newTimer(
- "insertChangesAndPatchSets", Metadata.builder().resourceCount(newChanges.size()))) {
- ReceiveCommand magicBranchCmd = magicBranch != null ? magicBranch.cmd : null;
- if (magicBranchCmd != null && magicBranchCmd.getResult() != NOT_ATTEMPTED) {
- logger.atWarning().log(
- "Skipping change updates on %s because ref update failed: %s %s",
- project.getName(),
- magicBranchCmd.getResult(),
- Strings.nullToEmpty(magicBranchCmd.getMessage()));
- return;
- }
-
- try (BatchUpdate bu =
- batchUpdateFactory.create(
- project.getNameKey(), user.materializedCopy(), TimeUtil.now());
- ObjectInserter ins = repo.newObjectInserter();
- ObjectReader reader = ins.newReader();
- RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo, rw, ins);
- bu.setRefLogMessage("push");
- if (magicBranch != null) {
- bu.setNotify(magicBranch.getNotifyForNewChange());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (TraceTimer traceTimer =
+ newTimer(
+ "insertChangesAndPatchSets", Metadata.builder().resourceCount(newChanges.size()))) {
+ ReceiveCommand magicBranchCmd = magicBranch != null ? magicBranch.cmd : null;
+ if (magicBranchCmd != null && magicBranchCmd.getResult() != NOT_ATTEMPTED) {
+ logger.atWarning().log(
+ "Skipping change updates on %s because ref update failed: %s %s",
+ project.getName(),
+ magicBranchCmd.getResult(),
+ Strings.nullToEmpty(magicBranchCmd.getMessage()));
+ return;
}
- logger.atFine().log("Adding %d replace requests", newChanges.size());
- for (ReplaceRequest replace : replaceByChange.values()) {
- replace.addOps(bu, replaceProgress);
+ try (BatchUpdate bu =
+ batchUpdateFactory.create(
+ project.getNameKey(), user.materializedCopy(), TimeUtil.now());
+ ObjectInserter ins = repo.newObjectInserter();
+ ObjectReader reader = ins.newReader();
+ RevWalk rw = new RevWalk(reader)) {
+ bu.setRepository(repo, rw, ins);
+ bu.setRefLogMessage("push");
if (magicBranch != null) {
- bu.setNotifyHandling(replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
- if (magicBranch.shouldPublishComments()) {
- bu.addOp(
- replace.notes.getChangeId(),
- publishCommentsOp.create(replace.psId, project.getNameKey()));
- Optional<ChangeNotes> changeNotes = getChangeNotes(replace.notes.getChangeId());
- if (!changeNotes.isPresent()) {
- // If not present, no need to update attention set here since this is a new change.
- continue;
+ bu.setNotify(magicBranch.getNotifyForNewChange());
+ }
+
+ logger.atFine().log("Adding %d replace requests", newChanges.size());
+ for (ReplaceRequest replace : replaceByChange.values()) {
+ replace.addOps(bu, replaceProgress);
+ if (magicBranch != null) {
+ bu.setNotifyHandling(
+ replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
+ if (magicBranch.shouldPublishComments()) {
+ bu.addOp(
+ replace.notes.getChangeId(),
+ publishCommentsOp.create(replace.psId, project.getNameKey()));
+ Optional<ChangeNotes> changeNotes = getChangeNotes(replace.notes.getChangeId());
+ if (!changeNotes.isPresent()) {
+ // If not present, no need to update attention set here since this is a new
+ // change.
+ continue;
+ }
+ List<HumanComment> drafts =
+ commentsUtil.draftByChangeAuthor(changeNotes.get(), user.getAccountId());
+ if (drafts.isEmpty()) {
+ // If no comments, attention set shouldn't update since the user didn't reply.
+ continue;
+ }
+ replyAttentionSetUpdates.processAutomaticAttentionSetRulesOnReply(
+ bu, changeNotes.get(), isReadyForReview(changeNotes.get()), user, drafts);
}
- List<HumanComment> drafts =
- commentsUtil.draftByChangeAuthor(changeNotes.get(), user.getAccountId());
- if (drafts.isEmpty()) {
- // If no comments, attention set shouldn't update since the user didn't reply.
- continue;
- }
- replyAttentionSetUpdates.processAutomaticAttentionSetRulesOnReply(
- bu, changeNotes.get(), isReadyForReview(changeNotes.get()), user, drafts);
}
}
- }
- logger.atFine().log("Adding %d create requests", newChanges.size());
- for (CreateRequest create : newChanges) {
- create.addOps(bu);
- }
-
- logger.atFine().log("Adding %d group update requests", newChanges.size());
- updateGroups.forEach(r -> r.addOps(bu));
-
- logger.atFine().log("Executing batch");
- try {
- bu.execute();
- } catch (UpdateException e) {
- throw asRestApiException(e);
- }
-
- replaceByChange.values().stream()
- .forEach(
- req ->
- result.addChange(ReceiveCommitsResult.ChangeStatus.REPLACED, req.ontoChange));
- newChanges.stream()
- .forEach(
- req -> result.addChange(ReceiveCommitsResult.ChangeStatus.CREATED, req.changeId));
-
- if (magicBranchCmd != null) {
- magicBranchCmd.setResult(OK);
- }
- for (ReplaceRequest replace : replaceByChange.values()) {
- String rejectMessage = replace.getRejectMessage();
- if (rejectMessage == null) {
- if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
- // Not necessarily the magic branch, so need to set OK on the original value.
- replace.inputCommand.setResult(OK);
- }
- } else {
- logger.atFine().log("Rejecting due to message from ReplaceOp");
- reject(replace.inputCommand, rejectMessage);
+ logger.atFine().log("Adding %d create requests", newChanges.size());
+ for (CreateRequest create : newChanges) {
+ create.addOps(bu);
}
- }
- } catch (ResourceConflictException e) {
- addError(e.getMessage());
- reject(magicBranchCmd, "conflict");
- } catch (BadRequestException | UnprocessableEntityException | AuthException e) {
- logger.atFine().withCause(e).log("Rejecting due to client error");
- reject(magicBranchCmd, e.getMessage());
- } catch (RestApiException | IOException e) {
- throw new StorageException("Can't insert change/patch set for " + project.getName(), e);
- }
+ logger.atFine().log("Adding %d group update requests", newChanges.size());
+ updateGroups.forEach(r -> r.addOps(bu));
- if (magicBranch != null && magicBranch.submit) {
- try {
- submit(newChanges, replaceByChange.values());
+ logger.atFine().log("Executing batch");
+ try {
+ bu.execute();
+ } catch (UpdateException e) {
+ throw asRestApiException(e);
+ }
+
+ replaceByChange.values().stream()
+ .forEach(
+ req ->
+ result.addChange(ReceiveCommitsResult.ChangeStatus.REPLACED, req.ontoChange));
+ newChanges.stream()
+ .forEach(
+ req -> result.addChange(ReceiveCommitsResult.ChangeStatus.CREATED, req.changeId));
+
+ if (magicBranchCmd != null) {
+ magicBranchCmd.setResult(OK);
+ }
+ for (ReplaceRequest replace : replaceByChange.values()) {
+ String rejectMessage = replace.getRejectMessage();
+ if (rejectMessage == null) {
+ if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
+ // Not necessarily the magic branch, so need to set OK on the original value.
+ replace.inputCommand.setResult(OK);
+ }
+ } else {
+ logger.atFine().log("Rejecting due to message from ReplaceOp");
+ reject(replace.inputCommand, rejectMessage);
+ }
+ }
+
} catch (ResourceConflictException e) {
addError(e.getMessage());
reject(magicBranchCmd, "conflict");
- } catch (RestApiException
- | StorageException
- | UpdateException
- | IOException
- | ConfigInvalidException
- | PermissionBackendException e) {
- logger.atSevere().withCause(e).log("Error submitting changes to %s", project.getName());
- reject(magicBranchCmd, "error during submit");
+ } catch (BadRequestException | UnprocessableEntityException | AuthException e) {
+ logger.atFine().withCause(e).log("Rejecting due to client error");
+ reject(magicBranchCmd, e.getMessage());
+ } catch (RestApiException | IOException e) {
+ throw new StorageException("Can't insert change/patch set for " + project.getName(), e);
+ }
+
+ if (magicBranch != null && magicBranch.submit) {
+ try {
+ submit(newChanges, replaceByChange.values());
+ } catch (ResourceConflictException e) {
+ addError(e.getMessage());
+ reject(magicBranchCmd, "conflict");
+ } catch (RestApiException
+ | StorageException
+ | UpdateException
+ | IOException
+ | ConfigInvalidException
+ | PermissionBackendException e) {
+ logger.atSevere().withCause(e).log("Error submitting changes to %s", project.getName());
+ reject(magicBranchCmd, "error during submit");
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/group/db/GroupsUpdate.java b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
index 87d8db1..14f8825 100644
--- a/java/com/google/gerrit/server/group/db/GroupsUpdate.java
+++ b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.group.db;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.GROUPS_UPDATE;
+
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
@@ -45,6 +47,7 @@
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -305,16 +308,18 @@
private InternalGroup createGroupInNoteDbWithRetry(
InternalGroupCreation groupCreation, GroupDelta groupDelta)
throws IOException, ConfigInvalidException, DuplicateKeyException {
- try {
- return retryHelper
- .groupUpdate("createGroup", () -> createGroupInNoteDb(groupCreation, groupDelta))
- .call();
- } catch (Exception e) {
- Throwables.throwIfUnchecked(e);
- Throwables.throwIfInstanceOf(e, IOException.class);
- Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
- Throwables.throwIfInstanceOf(e, DuplicateKeyException.class);
- throw new IOException(e);
+ try (RefUpdateContext ctx = RefUpdateContext.open(GROUPS_UPDATE)) {
+ try {
+ return retryHelper
+ .groupUpdate("createGroup", () -> createGroupInNoteDb(groupCreation, groupDelta))
+ .call();
+ } catch (Exception e) {
+ Throwables.throwIfUnchecked(e);
+ Throwables.throwIfInstanceOf(e, IOException.class);
+ Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
+ Throwables.throwIfInstanceOf(e, DuplicateKeyException.class);
+ throw new IOException(e);
+ }
}
}
@@ -361,30 +366,32 @@
@VisibleForTesting
public UpdateResult updateGroupInNoteDb(AccountGroup.UUID groupUuid, GroupDelta groupDelta)
throws IOException, ConfigInvalidException, DuplicateKeyException, NoSuchGroupException {
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersName, allUsersRepo, groupUuid);
- groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
- if (!groupConfig.getLoadedGroup().isPresent()) {
- throw new NoSuchGroupException(groupUuid);
+ try (RefUpdateContext ctx = RefUpdateContext.open(GROUPS_UPDATE)) {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersName, allUsersRepo, groupUuid);
+ groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
+ if (!groupConfig.getLoadedGroup().isPresent()) {
+ throw new NoSuchGroupException(groupUuid);
+ }
+
+ InternalGroup originalGroup = groupConfig.getLoadedGroup().get();
+ GroupNameNotes groupNameNotes = null;
+ if (groupDelta.getName().isPresent()) {
+ AccountGroup.NameKey oldName = originalGroup.getNameKey();
+ AccountGroup.NameKey newName = groupDelta.getName().get();
+ groupNameNotes =
+ GroupNameNotes.forRename(allUsersName, allUsersRepo, groupUuid, oldName, newName);
+ }
+
+ commit(allUsersRepo, groupConfig, groupNameNotes);
+
+ InternalGroup updatedGroup =
+ groupConfig
+ .getLoadedGroup()
+ .orElseThrow(
+ () -> new IllegalStateException("Updated group wasn't automatically loaded"));
+ return getUpdateResult(originalGroup, updatedGroup);
}
-
- InternalGroup originalGroup = groupConfig.getLoadedGroup().get();
- GroupNameNotes groupNameNotes = null;
- if (groupDelta.getName().isPresent()) {
- AccountGroup.NameKey oldName = originalGroup.getNameKey();
- AccountGroup.NameKey newName = groupDelta.getName().get();
- groupNameNotes =
- GroupNameNotes.forRename(allUsersName, allUsersRepo, groupUuid, oldName, newName);
- }
-
- commit(allUsersRepo, groupConfig, groupNameNotes);
-
- InternalGroup updatedGroup =
- groupConfig
- .getLoadedGroup()
- .orElseThrow(
- () -> new IllegalStateException("Updated group wasn't automatically loaded"));
- return getUpdateResult(originalGroup, updatedGroup);
}
}
diff --git a/java/com/google/gerrit/server/group/db/testing/BUILD b/java/com/google/gerrit/server/group/db/testing/BUILD
index 8f33f98..a4f49e9 100644
--- a/java/com/google/gerrit/server/group/db/testing/BUILD
+++ b/java/com/google/gerrit/server/group/db/testing/BUILD
@@ -8,6 +8,7 @@
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:guava",
"//lib:jgit",
"//lib:jgit-junit",
diff --git a/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java b/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java
index fa06281..e36ccf0 100644
--- a/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java
+++ b/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java
@@ -14,8 +14,11 @@
package com.google.gerrit.server.group.db.testing;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
+
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
@@ -45,25 +48,27 @@
String fileName,
String contents)
throws Exception {
- try (RevWalk rw = new RevWalk(allUsersRepo);
- TestRepository<Repository> testRepository = new TestRepository<>(allUsersRepo, rw)) {
- TestRepository<Repository>.CommitBuilder builder =
- testRepository
- .branch(refName)
- .commit()
- .add(fileName, contents)
- .message("update group file")
- .author(serverIdent)
- .committer(serverIdent);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (RevWalk rw = new RevWalk(allUsersRepo);
+ TestRepository<Repository> testRepository = new TestRepository<>(allUsersRepo, rw)) {
+ TestRepository<Repository>.CommitBuilder builder =
+ testRepository
+ .branch(refName)
+ .commit()
+ .add(fileName, contents)
+ .message("update group file")
+ .author(serverIdent)
+ .committer(serverIdent);
- Ref ref = allUsersRepo.exactRef(refName);
- if (ref != null) {
- RevCommit c = rw.parseCommit(ref.getObjectId());
- if (c != null) {
- builder.parent(c);
+ Ref ref = allUsersRepo.exactRef(refName);
+ if (ref != null) {
+ RevCommit c = rw.parseCommit(ref.getObjectId());
+ if (c != null) {
+ builder.parent(c);
+ }
}
+ builder.create();
}
- builder.create();
}
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 8e443f82..7984737 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -505,9 +505,11 @@
/** The user assigned to the change. */
// The getter always returns NO_ASSIGNEE, since assignee field is deprecated.
+ @Deprecated
public static final IndexedField<ChangeData, Integer> ASSIGNEE_FIELD =
IndexedField.<ChangeData>integerBuilder("Assignee").build(changeGetter(c -> NO_ASSIGNEE));
+ @Deprecated
public static final IndexedField<ChangeData, Integer>.SearchSpec ASSIGNEE_SPEC =
ASSIGNEE_FIELD.integer(ChangeQueryBuilder.FIELD_ASSIGNEE);
diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index 6ddf7a3..82b8f18 100644
--- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -240,6 +240,7 @@
.build();
/** Remove assignee field. */
+ @SuppressWarnings("deprecation")
static final Schema<ChangeData> V82 =
new Schema.Builder<ChangeData>()
.add(V81)
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 2b8a501..93da997 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.mail.receive;
import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
@@ -69,6 +70,7 @@
import com.google.gerrit.server.update.PostUpdateContext;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -319,9 +321,11 @@
}
Op o = new Op(PatchSet.id(cd.getId(), metadata.patchSet), parsedComments, message.id());
- BatchUpdate batchUpdate = buf.create(project, ctx.getUser(), TimeUtil.now());
- batchUpdate.addOp(cd.getId(), o);
- batchUpdate.execute();
+ try (RefUpdateContext updCtx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ BatchUpdate batchUpdate = buf.create(project, ctx.getUser(), TimeUtil.now());
+ batchUpdate.addOp(cd.getId(), o);
+ batchUpdate.execute();
+ }
}
}
diff --git a/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java b/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
index ce54708..0eaafb8 100644
--- a/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
+++ b/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
@@ -90,7 +90,7 @@
"RevertedHtml.soy",
};
- private static final SoySauce DEFAULT = getDefault().build().compileTemplates();
+ private static final SoySauce DEFAULT = getDefault(null).build().compileTemplates();
private final SitePaths site;
private final PluginSetContext<MailSoyTemplateProvider> templateProviders;
@@ -106,7 +106,7 @@
return DEFAULT;
}
- SoyFileSet.Builder builder = getDefault();
+ SoyFileSet.Builder builder = getDefault(site);
templateProviders.runEach(
e -> e.getFileNames().forEach(p -> addTemplate(builder, site, e.getPath(), p)));
return builder.build().compileTemplates();
@@ -124,10 +124,10 @@
}
}
- private static SoyFileSet.Builder getDefault() {
+ private static SoyFileSet.Builder getDefault(@Nullable SitePaths site) {
SoyFileSet.Builder builder = SoyFileSet.builder();
for (String name : TEMPLATES) {
- addTemplate(builder, null, "com/google/gerrit/server/mail/", name);
+ addTemplate(builder, site, "com/google/gerrit/server/mail/", name);
}
return builder;
}
diff --git a/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java b/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java
index 5417494..0289e17 100644
--- a/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java
+++ b/java/com/google/gerrit/server/notedb/AllUsersAsyncUpdate.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
@@ -25,6 +26,7 @@
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.Map;
@@ -90,26 +92,28 @@
Future<?> possiblyIgnoredError =
executor.submit(
() -> {
- try (OpenRepo allUsersRepo = OpenRepo.open(repoManager, allUsersName)) {
- allUsersRepo.addUpdatesNoLimits(draftUpdates);
- allUsersRepo.flush();
- BatchRefUpdate bru = allUsersRepo.repo.getRefDatabase().newBatchUpdate();
- bru.setPushCertificate(pushCert);
- if (refLogMessage != null) {
- bru.setRefLogMessage(refLogMessage, false);
- } else {
- bru.setRefLogMessage(
- firstNonNull(NoteDbUtil.guessRestApiHandler(), "Update NoteDb refs async"),
- false);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (OpenRepo allUsersRepo = OpenRepo.open(repoManager, allUsersName)) {
+ allUsersRepo.addUpdatesNoLimits(draftUpdates);
+ allUsersRepo.flush();
+ BatchRefUpdate bru = allUsersRepo.repo.getRefDatabase().newBatchUpdate();
+ bru.setPushCertificate(pushCert);
+ if (refLogMessage != null) {
+ bru.setRefLogMessage(refLogMessage, false);
+ } else {
+ bru.setRefLogMessage(
+ firstNonNull(NoteDbUtil.guessRestApiHandler(), "Update NoteDb refs async"),
+ false);
+ }
+ bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent);
+ bru.setAtomic(true);
+ allUsersRepo.cmds.addTo(bru);
+ bru.setAllowNonFastForwards(true);
+ RefUpdateUtil.executeChecked(bru, allUsersRepo.rw);
+ } catch (IOException e) {
+ logger.atSevere().withCause(e).log(
+ "Failed to delete draft comments asynchronously after publishing them");
}
- bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent);
- bru.setAtomic(true);
- allUsersRepo.cmds.addTo(bru);
- bru.setAllowNonFastForwards(true);
- RefUpdateUtil.executeChecked(bru, allUsersRepo.rw);
- } catch (IOException e) {
- logger.atSevere().withCause(e).log(
- "Failed to delete draft comments asynchronously after publishing them");
}
});
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index ef62f2e..f8c7426 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -42,6 +42,7 @@
import static com.google.gerrit.server.notedb.ChangeNoteFooters.FOOTER_WORK_IN_PROGRESS;
import static com.google.gerrit.server.notedb.NoteDbUtil.sanitizeFooter;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.util.Comparator.naturalOrder;
import static java.util.Objects.requireNonNull;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -79,6 +80,7 @@
import com.google.gerrit.server.account.ServiceUserClassifier;
import com.google.gerrit.server.approval.PatchSetApprovalUuidGenerator;
import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AttentionSetUtil;
import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.server.validators.ValidationException;
@@ -248,9 +250,11 @@
}
public ObjectId commit() throws IOException {
- try (NoteDbUpdateManager updateManager = updateManagerFactory.create(getProjectName())) {
- updateManager.add(this);
- updateManager.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (NoteDbUpdateManager updateManager = updateManagerFactory.create(getProjectName())) {
+ updateManager.add(this);
+ updateManager.execute();
+ }
}
return getResult();
}
diff --git a/java/com/google/gerrit/server/notedb/CommitRewriter.java b/java/com/google/gerrit/server/notedb/CommitRewriter.java
index 4d71d84..f4262e7 100644
--- a/java/com/google/gerrit/server/notedb/CommitRewriter.java
+++ b/java/com/google/gerrit/server/notedb/CommitRewriter.java
@@ -20,6 +20,7 @@
import static com.google.gerrit.server.notedb.ChangeNoteFooters.FOOTER_REAL_USER;
import static com.google.gerrit.server.notedb.ChangeNoteFooters.FOOTER_SUBMITTED_WITH;
import static com.google.gerrit.server.notedb.ChangeNoteFooters.FOOTER_TAG;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static com.google.gerrit.server.util.AccountTemplateUtil.ACCOUNT_TEMPLATE_PATTERN;
import static com.google.gerrit.server.util.AccountTemplateUtil.ACCOUNT_TEMPLATE_REGEX;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -48,6 +49,7 @@
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.notedb.ChangeNoteUtil.AttentionStatusInNoteDb;
import com.google.gerrit.server.notedb.ChangeNoteUtil.CommitMessageRange;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AccountTemplateUtil;
import com.google.gson.Gson;
import com.google.inject.Inject;
@@ -341,13 +343,15 @@
if (refsUpdate == null) {
return;
}
- if (!refsUpdate.batchRefUpdate().getCommands().isEmpty()) {
- if (!options.dryRun) {
- refsUpdate.inserter().flush();
- RefUpdateUtil.executeChecked(refsUpdate.batchRefUpdate(), refsUpdate.revWalk());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ if (!refsUpdate.batchRefUpdate().getCommands().isEmpty()) {
+ if (!options.dryRun) {
+ refsUpdate.inserter().flush();
+ RefUpdateUtil.executeChecked(refsUpdate.batchRefUpdate(), refsUpdate.revWalk());
+ }
}
+ refsUpdate.close();
}
- refsUpdate.close();
}
/**
diff --git a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
index c8d93f8..3f3ede1 100644
--- a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
+++ b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
@@ -16,6 +16,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.entities.RefNames.REFS_DRAFT_COMMENTS;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID;
import com.google.auto.value.AutoValue;
@@ -36,6 +37,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -402,17 +404,19 @@
private void deleteBatchZombieRefs(Repository allUsersRepo, List<Ref> refsBatch)
throws IOException {
- List<ReceiveCommand> deleteCommands =
- refsBatch.stream()
- .map(
- zombieRef ->
- new ReceiveCommand(
- zombieRef.getObjectId(), ObjectId.zeroId(), zombieRef.getName()))
- .collect(toImmutableList());
- BatchRefUpdate bru = allUsersRepo.getRefDatabase().newBatchUpdate();
- bru.setAtomic(true);
- bru.addCommand(deleteCommands);
- RefUpdateUtil.executeChecked(bru, allUsersRepo);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ List<ReceiveCommand> deleteCommands =
+ refsBatch.stream()
+ .map(
+ zombieRef ->
+ new ReceiveCommand(
+ zombieRef.getObjectId(), ObjectId.zeroId(), zombieRef.getName()))
+ .collect(toImmutableList());
+ BatchRefUpdate bru = allUsersRepo.getRefDatabase().newBatchUpdate();
+ bru.setAtomic(true);
+ bru.addCommand(deleteCommands);
+ RefUpdateUtil.executeChecked(bru, allUsersRepo);
+ }
}
private List<Ref> filterZombieRefs(Repository allUsersRepo, List<Ref> allDraftRefs)
diff --git a/java/com/google/gerrit/server/notedb/RepoSequence.java b/java/com/google/gerrit/server/notedb/RepoSequence.java
index d743921..9aaac19 100644
--- a/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.entities.RefNames.REFS;
import static com.google.gerrit.entities.RefNames.REFS_SEQUENCES;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.REPO_SEQ;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -39,6 +40,7 @@
import com.google.gerrit.git.RefUpdateUtil;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -265,29 +267,31 @@
* @param count the number of sequence numbers which should be retrieved
*/
private void acquire(int count) {
- try (Repository repo = repoManager.openRepository(projectName);
- RevWalk rw = new RevWalk(repo)) {
- logger.atFine().log("acquire %d ids on %s in %s", count, refName, projectName);
- Optional<IntBlob> blob = IntBlob.parse(repo, refName, rw);
- afterReadRef.run();
- ObjectId oldId;
- int next;
- if (!blob.isPresent()) {
- oldId = ObjectId.zeroId();
- next = seed.get();
- } else {
- oldId = blob.get().id();
- next = blob.get().value();
+ try (RefUpdateContext ctx = RefUpdateContext.open(REPO_SEQ)) {
+ try (Repository repo = repoManager.openRepository(projectName);
+ RevWalk rw = new RevWalk(repo)) {
+ logger.atFine().log("acquire %d ids on %s in %s", count, refName, projectName);
+ Optional<IntBlob> blob = IntBlob.parse(repo, refName, rw);
+ afterReadRef.run();
+ ObjectId oldId;
+ int next;
+ if (!blob.isPresent()) {
+ oldId = ObjectId.zeroId();
+ next = seed.get();
+ } else {
+ oldId = blob.get().id();
+ next = blob.get().value();
+ }
+ next = Math.max(floor, next);
+ RefUpdate refUpdate =
+ IntBlob.tryStore(repo, rw, projectName, refName, oldId, next + count, gitRefUpdated);
+ RefUpdateUtil.checkResult(refUpdate);
+ counter = next;
+ limit = counter + count;
+ acquireCount++;
+ } catch (IOException e) {
+ throw new StorageException(e);
}
- next = Math.max(floor, next);
- RefUpdate refUpdate =
- IntBlob.tryStore(repo, rw, projectName, refName, oldId, next + count, gitRefUpdated);
- RefUpdateUtil.checkResult(refUpdate);
- counter = next;
- limit = counter + count;
- acquireCount++;
- } catch (IOException e) {
- throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java b/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
index 60dff84..e91f7b7 100644
--- a/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
+++ b/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
@@ -72,13 +72,15 @@
if (!ready) {
synchronized (dataDir) {
if (!ready) {
- try {
- Files.createDirectories(dataDir);
- } catch (IOException e) {
- throw new ProvisionException(
- String.format(
- "Cannot create %s for plugin %s", dataDir.toAbsolutePath(), plugin.getName()),
- e);
+ if (!Files.isDirectory(dataDir)) {
+ try {
+ Files.createDirectories(dataDir);
+ } catch (IOException e) {
+ throw new ProvisionException(
+ String.format(
+ "Cannot create %s for plugin %s", dataDir.toAbsolutePath(), plugin.getName()),
+ e);
+ }
}
ready = true;
}
diff --git a/java/com/google/gerrit/server/project/ProjectCreator.java b/java/com/google/gerrit/server/project/ProjectCreator.java
index f1c161d..485d926 100644
--- a/java/com/google/gerrit/server/project/ProjectCreator.java
+++ b/java/com/google/gerrit/server/project/ProjectCreator.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.project;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.INIT_REPO;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
@@ -43,6 +44,7 @@
import com.google.gerrit.server.git.RepositoryExistsException;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -105,36 +107,38 @@
public ProjectState createProject(CreateProjectArgs args)
throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
- final Project.NameKey nameKey = args.getProject();
- try {
- final String head = args.permissionsOnly ? RefNames.REFS_CONFIG : args.branch.get(0);
- Status status = repoManager.getRepositoryStatus(nameKey);
- if (!status.equals(Status.NON_EXISTENT)) {
- throw new RepositoryExistsException(nameKey, "Repository status: " + status);
- }
- try (Repository repo = repoManager.createRepository(nameKey)) {
- RefUpdate u = repo.updateRef(Constants.HEAD);
- u.disableRefLog();
- u.link(head);
-
- createProjectConfig(args);
-
- if (!args.permissionsOnly && args.createEmptyCommit) {
- createEmptyCommits(repo, nameKey, args.branch);
+ try (RefUpdateContext ctx = RefUpdateContext.open(INIT_REPO)) {
+ final Project.NameKey nameKey = args.getProject();
+ try {
+ final String head = args.permissionsOnly ? RefNames.REFS_CONFIG : args.branch.get(0);
+ Status status = repoManager.getRepositoryStatus(nameKey);
+ if (!status.equals(Status.NON_EXISTENT)) {
+ throw new RepositoryExistsException(nameKey, "Repository status: " + status);
}
+ try (Repository repo = repoManager.createRepository(nameKey)) {
+ RefUpdate u = repo.updateRef(Constants.HEAD);
+ u.disableRefLog();
+ u.link(head);
- fire(nameKey, head);
+ createProjectConfig(args);
- return projectCache.get(nameKey).orElseThrow(illegalState(nameKey));
+ if (!args.permissionsOnly && args.createEmptyCommit) {
+ createEmptyCommits(repo, nameKey, args.branch);
+ }
+
+ fire(nameKey, head);
+
+ return projectCache.get(nameKey).orElseThrow(illegalState(nameKey));
+ }
+ } catch (RepositoryExistsException e) {
+ throw new ResourceConflictException(
+ "Cannot create "
+ + nameKey.get()
+ + " because the name is already occupied by another project.",
+ e);
+ } catch (RepositoryNotFoundException badName) {
+ throw new BadRequestException("invalid project name: " + nameKey, badName);
}
- } catch (RepositoryExistsException e) {
- throw new ResourceConflictException(
- "Cannot create "
- + nameKey.get()
- + " because the name is already occupied by another project.",
- e);
- } catch (RepositoryNotFoundException badName) {
- throw new BadRequestException("invalid project name: " + nameKey, badName);
}
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 738eab3..f6fc8db 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -225,9 +225,11 @@
public static final String ARG_ID_GROUP = "group";
public static final String ARG_ID_OWNER = "owner";
public static final String ARG_ID_NON_UPLOADER = "non_uploader";
+ public static final String ARG_ID_NON_CONTRIBUTOR = "non_contributor";
public static final String ARG_COUNT = "count";
public static final Account.Id OWNER_ACCOUNT_ID = Account.id(0);
public static final Account.Id NON_UPLOADER_ACCOUNT_ID = Account.id(-1);
+ public static final Account.Id NON_CONTRIBUTOR_ACCOUNT_ID = Account.id(-2);
public static final String OPERATOR_MERGED_BEFORE = "mergedbefore";
public static final String OPERATOR_MERGED_AFTER = "mergedafter";
@@ -485,13 +487,13 @@
}
}
- private final Arguments args;
+ protected final Arguments args;
protected Map<String, String> hasOperandAliases = Collections.emptyMap();
private Map<Account.Id, DestinationList> destinationListByAccount = new HashMap<>();
private static final Splitter RULE_SPLITTER = Splitter.on("=");
private static final Splitter PLUGIN_SPLITTER = Splitter.on("_");
- private static final Splitter LABEL_SPLITTER = Splitter.on(",");
+ protected static final Splitter LABEL_SPLITTER = Splitter.on(",");
@Inject
protected ChangeQueryBuilder(Arguments args) {
@@ -1038,6 +1040,8 @@
accounts = Collections.singleton(OWNER_ACCOUNT_ID);
} else if (value.equals(ARG_ID_NON_UPLOADER)) {
accounts = Collections.singleton(NON_UPLOADER_ACCOUNT_ID);
+ } else if (value.equals(ARG_ID_NON_CONTRIBUTOR)) {
+ accounts = Collections.singleton(NON_CONTRIBUTOR_ACCOUNT_ID);
} else {
accounts = parseAccount(value);
}
@@ -1072,6 +1076,8 @@
accounts = Collections.singleton(OWNER_ACCOUNT_ID);
} else if (value.equals(ARG_ID_NON_UPLOADER)) {
accounts = Collections.singleton(NON_UPLOADER_ACCOUNT_ID);
+ } else if (value.equals(ARG_ID_NON_CONTRIBUTOR)) {
+ accounts = Collections.singleton(NON_CONTRIBUTOR_ACCOUNT_ID);
} else {
accounts = parseAccount(value);
}
@@ -1106,9 +1112,16 @@
}
}
+ validateLabelArgs(accounts);
return new LabelPredicate(args, name, accounts, group, count, countOp);
}
+ protected void validateLabelArgs(Set<Account.Id> accounts) throws QueryParseException {
+ if (accounts != null && accounts.contains(NON_CONTRIBUTOR_ACCOUNT_ID)) {
+ throw new QueryParseException("non_contributor arg is not allowed in change queries");
+ }
+ }
+
/** Assert that keys {@code k1} and {@code k2} do not exist in {@code labelArgs} together. */
private void assertDisjunctive(PredicateArgs labelArgs, String k1, String k2)
throws QueryParseException {
diff --git a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index 5662e4d..83dd5ba 100644
--- a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.change;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.AccountGroup;
@@ -23,6 +24,8 @@
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -30,9 +33,15 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData.StorageConstraint;
+import java.io.IOException;
+import java.util.List;
import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
public class EqualsLabelPredicate extends ChangeIndexPostFilterPredicate {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ protected final AccountResolver accountResolver;
protected final ProjectCache projectCache;
protected final PermissionBackend permissionBackend;
protected final IdentifiedUser.GenericFactory userFactory;
@@ -61,6 +70,7 @@
@Nullable Integer count) {
super(ChangeField.LABEL_SPEC, ChangeField.formatLabel(label, expVal, account, count));
this.permissionBackend = args.permissionBackend;
+ this.accountResolver = args.accountResolver;
this.projectCache = args.projectCache;
this.userFactory = args.userFactory;
this.count = count;
@@ -155,6 +165,14 @@
&& cd.currentPatchSet().uploader().equals(approver)) {
return false;
}
+
+ if (account.equals(ChangeQueryBuilder.NON_CONTRIBUTOR_ACCOUNT_ID)) {
+ if ((cd.currentPatchSet().uploader().equals(approver)
+ || matchAccount(cd.getCommitter().getEmailAddress(), approver)
+ || matchAccount(cd.getAuthor().getEmailAddress(), approver))) {
+ return false;
+ }
+ }
}
IdentifiedUser reviewer = userFactory.create(approver);
@@ -176,9 +194,24 @@
}
}
+ /**
+ * Returns true if the {@code email} parameter belongs to the account identified by the {@code
+ * accountId} parameter.
+ */
+ private boolean matchAccount(String email, Account.Id accountId) {
+ try {
+ List<AccountState> accountsList = accountResolver.resolve(email).asList();
+ return accountsList.stream().anyMatch(c -> c.account().id().equals(accountId));
+ } catch (ConfigInvalidException | IOException e) {
+ logger.atWarning().withCause(e).log("Failed to resolve account %s", email);
+ }
+ return false;
+ }
+
private boolean isMagicUser() {
return account.equals(ChangeQueryBuilder.OWNER_ACCOUNT_ID)
- || account.equals(ChangeQueryBuilder.NON_UPLOADER_ACCOUNT_ID);
+ || account.equals(ChangeQueryBuilder.NON_UPLOADER_ACCOUNT_ID)
+ || account.equals(ChangeQueryBuilder.NON_CONTRIBUTOR_ACCOUNT_ID);
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/LabelPredicate.java b/java/com/google/gerrit/server/query/change/LabelPredicate.java
index 2a5a47d..d89940d 100644
--- a/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -23,6 +23,7 @@
import com.google.gerrit.index.query.RangeUtil;
import com.google.gerrit.index.query.RangeUtil.Range;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.LabelVote;
@@ -36,6 +37,7 @@
protected static final int MAX_COUNT = 5; // inclusive
protected static class Args {
+ protected final AccountResolver accountResolver;
protected final ProjectCache projectCache;
protected final PermissionBackend permissionBackend;
protected final IdentifiedUser.GenericFactory userFactory;
@@ -46,6 +48,7 @@
protected final PredicateArgs.Operator countOp;
protected Args(
+ AccountResolver accountResolver,
ProjectCache projectCache,
PermissionBackend permissionBackend,
IdentifiedUser.GenericFactory userFactory,
@@ -54,6 +57,7 @@
AccountGroup.UUID group,
@Nullable Integer count,
@Nullable PredicateArgs.Operator countOp) {
+ this.accountResolver = accountResolver;
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.userFactory = userFactory;
@@ -89,6 +93,7 @@
super(
predicates(
new Args(
+ a.accountResolver,
a.projectCache,
a.permissionBackend,
a.userFactory,
diff --git a/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
index 5632c14..cb92ddd 100644
--- a/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/SubmitRequirementChangeQueryBuilder.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.query.change;
import com.google.common.base.Splitter;
+import com.google.gerrit.entities.Account;
import com.google.gerrit.index.SchemaFieldDefs.SchemaField;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
@@ -30,6 +31,7 @@
import com.google.inject.Inject;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -179,6 +181,9 @@
return fileEditsPredicateFactory.create(FileEditsArgs.create(filePattern, contentPattern));
}
+ @Override
+ protected void validateLabelArgs(Set<Account.Id> accountIds) throws QueryParseException {}
+
private static void validateRegularExpression(String pattern, String errorMessage)
throws QueryParseException {
try {
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index 62da2f2..dd0ec78d 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -34,6 +34,7 @@
"//lib/auto:auto-factory",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
+ "//lib/commons:codec",
"//lib/commons:compress",
"//lib/commons:lang3",
"//lib/errorprone:annotations",
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftCommentsUtil.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftCommentsUtil.java
index 2ae3166..9e02592 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftCommentsUtil.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftCommentsUtil.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.account;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
@@ -43,6 +44,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -106,10 +108,13 @@
update.addOp(cd.getId(), op);
ops.add(op);
}
- // Currently there's no way to let some updates succeed even if others fail. Even if there were,
- // all updates from this operation only happen in All-Users and thus are fully atomic, so
- // allowing partial failure would have little value.
- BatchUpdate.execute(updates.values(), ImmutableList.of(), false);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ // Currently there's no way to let some updates succeed even if others fail. Even if there
+ // were,
+ // all updates from this operation only happen in All-Users and thus are fully atomic, so
+ // allowing partial failure would have little value.
+ BatchUpdate.execute(updates.values(), ImmutableList.of(), false);
+ }
return ops.stream().map(Op::getResult).filter(Objects::nonNull).collect(toImmutableList());
}
diff --git a/java/com/google/gerrit/server/restapi/change/Abandon.java b/java/com/google/gerrit/server/restapi/change/Abandon.java
index 8dd0e78..36080a4 100644
--- a/java/com/google/gerrit/server/restapi/change/Abandon.java
+++ b/java/com/google/gerrit/server/restapi/change/Abandon.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.AbandonInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -36,6 +38,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -126,16 +129,18 @@
AccountState accountState = user.isIdentifiedUser() ? user.asIdentifiedUser().state() : null;
AbandonOp op = abandonOpFactory.create(accountState, msgTxt);
ChangeData changeData = changeDataFactory.create(notes.getProjectName(), notes.getChangeId());
- try (BatchUpdate u = updateFactory.create(notes.getProjectName(), user, TimeUtil.now())) {
- u.setNotify(notify);
- u.addOp(notes.getChangeId(), op);
- u.addOp(
- notes.getChangeId(),
- storeSubmitRequirementsOpFactory.create(
- changeData.submitRequirements().values(), changeData));
- u.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u = updateFactory.create(notes.getProjectName(), user, TimeUtil.now())) {
+ u.setNotify(notify);
+ u.addOp(notes.getChangeId(), op);
+ u.addOp(
+ notes.getChangeId(),
+ storeSubmitRequirementsOpFactory.create(
+ changeData.submitRequirements().values(), changeData));
+ u.execute();
+ }
+ return op.getChange();
}
- return op.getChange();
}
@Override
diff --git a/java/com/google/gerrit/server/restapi/change/AddToAttentionSet.java b/java/com/google/gerrit/server/restapi/change/AddToAttentionSet.java
index 03d383f..155e66f 100644
--- a/java/com/google/gerrit/server/restapi/change/AddToAttentionSet.java
+++ b/java/com/google/gerrit/server/restapi/change/AddToAttentionSet.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.changes.AttentionSetInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -32,6 +34,7 @@
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.update.BatchUpdate;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AttentionSetUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -87,17 +90,18 @@
.test(ChangePermission.READ)) {
throw new AuthException("read not permitted for " + attentionUserId);
}
-
- try (BatchUpdate bu =
- updateFactory.create(
- changeResource.getChange().getProject(), changeResource.getUser(), TimeUtil.now())) {
- AddToAttentionSetOp op = opFactory.create(attentionUserId, input.reason, true);
- bu.addOp(changeResource.getId(), op);
- NotifyHandling notify = input.notify == null ? NotifyHandling.OWNER : input.notify;
- NotifyResolver.Result notifyResult = notifyResolver.resolve(notify, input.notifyDetails);
- bu.setNotify(notifyResult);
- bu.execute();
- return Response.ok(accountLoaderFactory.create(true).fillOne(attentionUserId));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(
+ changeResource.getChange().getProject(), changeResource.getUser(), TimeUtil.now())) {
+ AddToAttentionSetOp op = opFactory.create(attentionUserId, input.reason, true);
+ bu.addOp(changeResource.getId(), op);
+ NotifyHandling notify = input.notify == null ? NotifyHandling.OWNER : input.notify;
+ NotifyResolver.Result notifyResult = notifyResolver.resolve(notify, input.notifyDetails);
+ bu.setNotify(notifyResult);
+ bu.execute();
+ return Response.ok(accountLoaderFactory.create(true).fillOne(attentionUserId));
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ApplyPatch.java b/java/com/google/gerrit/server/restapi/change/ApplyPatch.java
index 044fa0d..b6a106a 100644
--- a/java/com/google/gerrit/server/restapi/change/ApplyPatch.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyPatch.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
@@ -47,6 +49,7 @@
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -179,13 +182,15 @@
ChangeNotes destNotes,
CodeReviewCommit commit)
throws IOException, UpdateException, RestApiException {
- Change destChange = destNotes.getChange();
- PatchSet.Id psId = ChangeUtil.nextPatchSetId(git, destChange.currentPatchSetId());
- PatchSetInserter inserter = patchSetInserterFactory.create(destNotes, psId, commit);
- inserter.setMessage(buildMessageForPatchSet(psId));
- bu.addOp(destChange.getId(), inserter);
- bu.execute();
- return inserter.getChange();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ Change destChange = destNotes.getChange();
+ PatchSet.Id psId = ChangeUtil.nextPatchSetId(git, destChange.currentPatchSetId());
+ PatchSetInserter inserter = patchSetInserterFactory.create(destNotes, psId, commit);
+ inserter.setMessage(buildMessageForPatchSet(psId));
+ bu.addOp(destChange.getId(), inserter);
+ bu.execute();
+ return inserter.getChange();
+ }
}
private static String buildMessageForPatchSet(PatchSet.Id psId) {
diff --git a/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java b/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java
index d4f549a..4021f77 100644
--- a/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyPatchUtil.java
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import org.apache.commons.codec.binary.Base64;
import org.eclipse.jgit.api.errors.PatchApplyException;
import org.eclipse.jgit.api.errors.PatchFormatException;
import org.eclipse.jgit.lib.ObjectId;
@@ -51,8 +52,12 @@
throws IOException, RestApiException {
checkNotNull(mergeTip);
RevTree tip = mergeTip.getTree();
- InputStream patchStream =
- new ByteArrayInputStream(input.patch.getBytes(StandardCharsets.UTF_8));
+ InputStream patchStream;
+ if (Base64.isBase64(input.patch)) {
+ patchStream = new ByteArrayInputStream(org.eclipse.jgit.util.Base64.decode(input.patch));
+ } else {
+ patchStream = new ByteArrayInputStream(input.patch.getBytes(StandardCharsets.UTF_8));
+ }
try {
PatchApplier applier = new PatchApplier(repo, tip, oi);
PatchApplier.Result applyResult = applier.applyPatch(patchStream);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index b688e2d..6fd75de 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -283,6 +283,7 @@
/** Put handler that is activated when PUT request is called on collection element. */
@Singleton
public static class Put implements RestModifyView<ChangeEditResource, FileContentInput> {
+
private static final Pattern BINARY_DATA_PATTERN =
Pattern.compile("data:([\\w/.-]*);([\\w]+),(.*)");
private static final String BASE64 = "base64";
@@ -348,7 +349,6 @@
+ ") was invalid: supported values are 0, 644, or 755.");
}
}
-
try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
editModifier.modifyFile(
repository, rsrc.getNotes(), path, newContent, fileContentInput.fileMode);
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index c192500..1bfb6bd 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.server.project.ProjectCache.noSuchProject;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
@@ -62,6 +63,7 @@
import com.google.gerrit.server.submit.MergeIdenticalTreeException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -331,52 +333,53 @@
} catch (MergeIdenticalTreeException | MergeConflictException e) {
throw new IntegrationConflictException("Cherry pick failed: " + e.getMessage(), e);
}
-
- try (BatchUpdate bu = batchUpdateFactory.create(project, identifiedUser, timestamp)) {
- bu.setRepository(git, revWalk, oi);
- bu.setNotify(resolveNotify(input));
- Change.Id changeId;
- String newTopic = null;
- if (input.topic != null) {
- newTopic = Strings.emptyToNull(input.topic.trim());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, identifiedUser, timestamp)) {
+ bu.setRepository(git, revWalk, oi);
+ bu.setNotify(resolveNotify(input));
+ Change.Id changeId;
+ String newTopic = null;
+ if (input.topic != null) {
+ newTopic = Strings.emptyToNull(input.topic.trim());
+ }
+ if (newTopic == null
+ && sourceChange != null
+ && !Strings.isNullOrEmpty(sourceChange.getTopic())) {
+ newTopic = sourceChange.getTopic() + "-" + dest.shortName();
+ }
+ if (destChange != null) {
+ // The change key exists on the destination branch. The cherry pick
+ // will be added as a new patch set.
+ changeId =
+ insertPatchSet(
+ bu,
+ git,
+ destChange.notes(),
+ cherryPickCommit,
+ sourceChange,
+ newTopic,
+ input,
+ workInProgress);
+ } else {
+ // Change key not found on destination branch. We can create a new
+ // change.
+ changeId =
+ createNewChange(
+ bu,
+ cherryPickCommit,
+ dest.branch(),
+ newTopic,
+ project,
+ sourceChange,
+ sourceCommit,
+ input,
+ revertedChange,
+ idForNewChange,
+ workInProgress);
+ }
+ bu.execute();
+ return Result.create(changeId, cherryPickCommit.getFilesWithGitConflicts());
}
- if (newTopic == null
- && sourceChange != null
- && !Strings.isNullOrEmpty(sourceChange.getTopic())) {
- newTopic = sourceChange.getTopic() + "-" + dest.shortName();
- }
- if (destChange != null) {
- // The change key exists on the destination branch. The cherry pick
- // will be added as a new patch set.
- changeId =
- insertPatchSet(
- bu,
- git,
- destChange.notes(),
- cherryPickCommit,
- sourceChange,
- newTopic,
- input,
- workInProgress);
- } else {
- // Change key not found on destination branch. We can create a new
- // change.
- changeId =
- createNewChange(
- bu,
- cherryPickCommit,
- dest.branch(),
- newTopic,
- project,
- sourceChange,
- sourceCommit,
- input,
- revertedChange,
- idForNewChange,
- workInProgress);
- }
- bu.execute();
- return Result.create(changeId, cherryPickCommit.getFilesWithGitConflicts());
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 2cb427a..36dec86 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static org.eclipse.jgit.lib.Constants.SIGNED_OFF_BY_TAG;
import com.google.common.base.Joiner;
@@ -80,6 +81,7 @@
import com.google.gerrit.server.restapi.project.ProjectsCollection;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -329,102 +331,122 @@
BatchUpdate.Factory updateFactory)
throws RestApiException, PermissionBackendException, IOException, ConfigInvalidException,
UpdateException {
- logger.atFine().log(
- "Creating new change for target branch %s in project %s"
- + " (new branch = %s, base change = %s, base commit = %s)",
- input.branch, projectState.getName(), input.newBranch, input.baseChange, input.baseCommit);
-
- try (Repository git = gitManager.openRepository(projectState.getNameKey());
- ObjectInserter oi = git.newObjectInserter();
- ObjectReader reader = oi.newReader();
- CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(reader)) {
- PatchSet basePatchSet = null;
- List<String> groups = Collections.emptyList();
-
- if (input.baseChange != null) {
- ChangeNotes baseChange = getBaseChange(input.baseChange);
- basePatchSet = psUtil.current(baseChange);
- groups = basePatchSet.groups();
- logger.atFine().log("base patch set = %s (groups = %s)", basePatchSet.id(), groups);
- }
-
- ObjectId parentCommit =
- getParentCommit(
- git, rw, input.branch, input.newBranch, basePatchSet, input.baseCommit, input.merge);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
logger.atFine().log(
- "parent commit = %s", parentCommit != null ? parentCommit.name() : "NULL");
+ "Creating new change for target branch %s in project %s"
+ + " (new branch = %s, base change = %s, base commit = %s)",
+ input.branch,
+ projectState.getName(),
+ input.newBranch,
+ input.baseChange,
+ input.baseCommit);
- RevCommit mergeTip = parentCommit == null ? null : rw.parseCommit(parentCommit);
+ try (Repository git = gitManager.openRepository(projectState.getNameKey());
+ ObjectInserter oi = git.newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(reader)) {
+ PatchSet basePatchSet = null;
+ List<String> groups = Collections.emptyList();
- Instant now = TimeUtil.now();
-
- PersonIdent committer = me.newCommitterIdent(now, serverZoneId);
- PersonIdent author =
- input.author == null
- ? committer
- : new PersonIdent(input.author.name, input.author.email, now, serverZoneId);
-
- String commitMessage = getCommitMessage(input.subject, me);
-
- CodeReviewCommit c;
- if (input.merge != null) {
- // create a merge commit
- c =
- newMergeCommit(
- git, oi, rw, projectState, mergeTip, input.merge, author, committer, commitMessage);
- if (!c.getFilesWithGitConflicts().isEmpty()) {
- logger.atFine().log(
- "merge commit has conflicts in the following files: %s",
- c.getFilesWithGitConflicts());
+ if (input.baseChange != null) {
+ ChangeNotes baseChange = getBaseChange(input.baseChange);
+ basePatchSet = psUtil.current(baseChange);
+ groups = basePatchSet.groups();
+ logger.atFine().log("base patch set = %s (groups = %s)", basePatchSet.id(), groups);
}
- } else if (input.patch != null) {
- // create a commit with the given patch.
- if (mergeTip == null) {
- throw new BadRequestException("Cannot apply patch on top of an empty tree.");
+
+ ObjectId parentCommit =
+ getParentCommit(
+ git,
+ rw,
+ input.branch,
+ input.newBranch,
+ basePatchSet,
+ input.baseCommit,
+ input.merge);
+ logger.atFine().log(
+ "parent commit = %s", parentCommit != null ? parentCommit.name() : "NULL");
+
+ RevCommit mergeTip = parentCommit == null ? null : rw.parseCommit(parentCommit);
+
+ Instant now = TimeUtil.now();
+
+ PersonIdent committer = me.newCommitterIdent(now, serverZoneId);
+ PersonIdent author =
+ input.author == null
+ ? committer
+ : new PersonIdent(input.author.name, input.author.email, now, serverZoneId);
+
+ String commitMessage = getCommitMessage(input.subject, me);
+
+ CodeReviewCommit c;
+ if (input.merge != null) {
+ // create a merge commit
+ c =
+ newMergeCommit(
+ git,
+ oi,
+ rw,
+ projectState,
+ mergeTip,
+ input.merge,
+ author,
+ committer,
+ commitMessage);
+ if (!c.getFilesWithGitConflicts().isEmpty()) {
+ logger.atFine().log(
+ "merge commit has conflicts in the following files: %s",
+ c.getFilesWithGitConflicts());
+ }
+ } else if (input.patch != null) {
+ // create a commit with the given patch.
+ if (mergeTip == null) {
+ throw new BadRequestException("Cannot apply patch on top of an empty tree.");
+ }
+ ObjectId treeId = ApplyPatchUtil.applyPatch(git, oi, input.patch, mergeTip);
+ c =
+ rw.parseCommit(
+ CommitUtil.createCommitWithTree(
+ oi, author, committer, mergeTip, commitMessage, treeId));
+ } else {
+ // create an empty commit.
+ c = createEmptyCommit(oi, rw, author, committer, mergeTip, commitMessage);
}
- ObjectId treeId = ApplyPatchUtil.applyPatch(git, oi, input.patch, mergeTip);
- c =
- rw.parseCommit(
- CommitUtil.createCommitWithTree(
- oi, author, committer, mergeTip, commitMessage, treeId));
- } else {
- // create an empty commit.
- c = createEmptyCommit(oi, rw, author, committer, mergeTip, commitMessage);
- }
- // Flush inserter so that commit becomes visible to validators
- oi.flush();
+ // Flush inserter so that commit becomes visible to validators
+ oi.flush();
- Change.Id changeId = Change.id(seq.nextChangeId());
- ChangeInserter ins = changeInserterFactory.create(changeId, c, input.branch);
- ins.setMessage(messageForNewChange(ins.getPatchSetId(), c));
- ins.setTopic(input.topic);
- ins.setPrivate(input.isPrivate);
- ins.setWorkInProgress(input.workInProgress || !c.getFilesWithGitConflicts().isEmpty());
- ins.setGroups(groups);
+ Change.Id changeId = Change.id(seq.nextChangeId());
+ ChangeInserter ins = changeInserterFactory.create(changeId, c, input.branch);
+ ins.setMessage(messageForNewChange(ins.getPatchSetId(), c));
+ ins.setTopic(input.topic);
+ ins.setPrivate(input.isPrivate);
+ ins.setWorkInProgress(input.workInProgress || !c.getFilesWithGitConflicts().isEmpty());
+ ins.setGroups(groups);
- if (input.validationOptions != null) {
- ImmutableListMultimap.Builder<String, String> validationOptions =
- ImmutableListMultimap.builder();
- input
- .validationOptions
- .entrySet()
- .forEach(e -> validationOptions.put(e.getKey(), e.getValue()));
- ins.setValidationOptions(validationOptions.build());
- }
+ if (input.validationOptions != null) {
+ ImmutableListMultimap.Builder<String, String> validationOptions =
+ ImmutableListMultimap.builder();
+ input
+ .validationOptions
+ .entrySet()
+ .forEach(e -> validationOptions.put(e.getKey(), e.getValue()));
+ ins.setValidationOptions(validationOptions.build());
+ }
- try (BatchUpdate bu = updateFactory.create(projectState.getNameKey(), me, now)) {
- bu.setRepository(git, rw, oi);
- bu.setNotify(
- notifyResolver.resolve(
- firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
- bu.insertChange(ins);
- bu.execute();
+ try (BatchUpdate bu = updateFactory.create(projectState.getNameKey(), me, now)) {
+ bu.setRepository(git, rw, oi);
+ bu.setNotify(
+ notifyResolver.resolve(
+ firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
+ bu.insertChange(ins);
+ bu.execute();
+ }
+ ChangeInfo changeInfo = jsonFactory.noOptions().format(ins.getChange());
+ changeInfo.containsGitConflicts = !c.getFilesWithGitConflicts().isEmpty() ? true : null;
+ return changeInfo;
+ } catch (InvalidMergeStrategyException | MergeWithConflictsNotSupportedException e) {
+ throw new BadRequestException(e.getMessage());
}
- ChangeInfo changeInfo = jsonFactory.noOptions().format(ins.getChange());
- changeInfo.containsGitConflicts = !c.getFilesWithGitConflicts().isEmpty() ? true : null;
- return changeInfo;
- } catch (InvalidMergeStrategyException | MergeWithConflictsNotSupportedException e) {
- throw new BadRequestException(e.getMessage());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index 9e9cf6a..cd0025f 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.Strings;
import com.google.gerrit.entities.HumanComment;
@@ -36,6 +37,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -81,13 +83,15 @@
throw new BadRequestException(
String.format("Invalid inReplyTo, comment %s not found", in.inReplyTo));
}
-
- try (BatchUpdate bu = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- Op op = new Op(rsrc.getPatchSet().id(), in);
- bu.addOp(rsrc.getChange().getId(), op);
- bu.execute();
- return Response.created(
- commentJson.get().setFillAccounts(false).newHumanCommentFormatter().format(op.comment));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ Op op = new Op(rsrc.getPatchSet().id(), in);
+ bu.addOp(rsrc.getChange().getId(), op);
+ bu.execute();
+ return Response.created(
+ commentJson.get().setFillAccounts(false).newHumanCommentFormatter().format(op.comment));
+ }
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index 4b66cdc..51094b7 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
@@ -63,6 +64,7 @@
import com.google.gerrit.server.submit.MergeIdenticalTreeException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -200,18 +202,20 @@
PatchSet.Id nextPsId = ChangeUtil.nextPatchSetId(ps.id());
PatchSetInserter psInserter =
patchSetInserterFactory.create(rsrc.getNotes(), nextPsId, newCommit);
- try (BatchUpdate bu = updateFactory.create(project, me, now)) {
- bu.setRepository(git, rw, oi);
- bu.setNotify(NotifyResolver.Result.none());
- psInserter
- .setMessage(messageForChange(nextPsId, newCommit))
- .setWorkInProgress(!newCommit.getFilesWithGitConflicts().isEmpty())
- .setCheckAddPatchSetPermission(false);
- if (groups != null) {
- psInserter.setGroups(groups);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu = updateFactory.create(project, me, now)) {
+ bu.setRepository(git, rw, oi);
+ bu.setNotify(NotifyResolver.Result.none());
+ psInserter
+ .setMessage(messageForChange(nextPsId, newCommit))
+ .setWorkInProgress(!newCommit.getFilesWithGitConflicts().isEmpty())
+ .setCheckAddPatchSetPermission(false);
+ if (groups != null) {
+ psInserter.setGroups(groups);
+ }
+ bu.addOp(rsrc.getId(), psInserter);
+ bu.execute();
}
- bu.addOp(rsrc.getId(), psInserter);
- bu.execute();
}
ChangeJson json = jsonFactory.create(ListChangesOption.CURRENT_REVISION);
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChange.java b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
index 8298abb..9153703 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChange.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.common.Input;
@@ -30,6 +31,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -53,11 +55,13 @@
throw new MethodNotAllowedException("delete not permitted");
}
rsrc.permissions().check(ChangePermission.DELETE);
-
- try (BatchUpdate bu = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- Change.Id id = rsrc.getChange().getId();
- bu.addOp(id, opFactory.create(id));
- bu.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ Change.Id id = rsrc.getChange().getId();
+ bu.addOp(id, opFactory.create(id));
+ bu.execute();
+ }
}
return Response.none();
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
index 588d56e..ca6bfad 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
@@ -41,6 +42,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AccountTemplateUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -88,9 +90,11 @@
createNewChangeMessage(user.asIdentifiedUser().getAccountId(), input.reason);
DeleteChangeMessageOp deleteChangeMessageOp =
new DeleteChangeMessageOp(resource.getChangeMessageId(), newChangeMessage);
- try (BatchUpdate batchUpdate =
- updateFactory.create(resource.getChangeResource().getProject(), user, TimeUtil.now())) {
- batchUpdate.addOp(resource.getChangeId(), deleteChangeMessageOp).execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate batchUpdate =
+ updateFactory.create(resource.getChangeResource().getProject(), user, TimeUtil.now())) {
+ batchUpdate.addOp(resource.getChangeId(), deleteChangeMessageOp).execute();
+ }
}
ChangeMessageInfo updatedMessageInfo =
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
index 2056664..1397582 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.common.base.Strings;
import com.google.gerrit.entities.HumanComment;
import com.google.gerrit.entities.PatchSet;
@@ -35,6 +37,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -83,9 +86,13 @@
String newMessage = getCommentNewMessage(user.asIdentifiedUser().getName(), input.reason);
DeleteCommentOp deleteCommentOp = new DeleteCommentOp(rsrc, newMessage);
- try (BatchUpdate batchUpdate =
- updateFactory.create(rsrc.getRevisionResource().getProject(), user, TimeUtil.now())) {
- batchUpdate.addOp(rsrc.getRevisionResource().getChange().getId(), deleteCommentOp).execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate batchUpdate =
+ updateFactory.create(rsrc.getRevisionResource().getProject(), user, TimeUtil.now())) {
+ batchUpdate
+ .addOp(rsrc.getRevisionResource().getChange().getId(), deleteCommentOp)
+ .execute();
+ }
}
ChangeNotes updatedNotes =
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index 7d28a39..f55e9c7 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.HumanComment;
import com.google.gerrit.entities.PatchSet;
@@ -30,6 +32,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -53,11 +56,13 @@
@Override
public Response<CommentInfo> apply(DraftCommentResource rsrc, Input input)
throws RestApiException, UpdateException {
- try (BatchUpdate bu =
- updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
- Op op = new Op(rsrc.getComment().key);
- bu.addOp(rsrc.getChange().getId(), op);
- bu.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
+ Op op = new Op(rsrc.getComment().key);
+ bu.addOp(rsrc.getChange().getId(), op);
+ bu.execute();
+ }
}
return Response.none();
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
index 08725b5..5c63bd7 100644
--- a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.common.InputWithMessage;
@@ -30,6 +31,7 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -62,8 +64,11 @@
}
SetPrivateOp op = setPrivateOpFactory.create(false, input);
- try (BatchUpdate u = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- u.addOp(rsrc.getId(), op).execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ u.addOp(rsrc.getId(), op).execute();
+ }
}
return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
index 7a409e8..cbc3b5e 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -27,6 +29,7 @@
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -53,21 +56,22 @@
if (input == null) {
input = new DeleteReviewerInput();
}
-
- try (BatchUpdate bu =
- updateFactory.create(
- rsrc.getChangeResource().getProject(),
- rsrc.getChangeResource().getUser(),
- TimeUtil.now())) {
- bu.setNotify(getNotify(rsrc.getChange(), input));
- BatchUpdateOp op;
- if (rsrc.isByEmail()) {
- op = deleteReviewerByEmailOpFactory.create(rsrc.getReviewerByEmail());
- } else {
- op = deleteReviewerOpFactory.create(rsrc.getReviewerUser().getAccount(), input);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(
+ rsrc.getChangeResource().getProject(),
+ rsrc.getChangeResource().getUser(),
+ TimeUtil.now())) {
+ bu.setNotify(getNotify(rsrc.getChange(), input));
+ BatchUpdateOp op;
+ if (rsrc.isByEmail()) {
+ op = deleteReviewerByEmailOpFactory.create(rsrc.getReviewerByEmail());
+ } else {
+ op = deleteReviewerOpFactory.create(rsrc.getReviewerUser().getAccount(), input);
+ }
+ bu.addOp(rsrc.getChange().getId(), op);
+ bu.execute();
}
- bu.addOp(rsrc.getChange().getId(), op);
- bu.execute();
}
return Response.none();
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 9fa3160..b3d7fa2 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
@@ -32,6 +33,7 @@
import com.google.gerrit.server.change.VoteResource;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -80,34 +82,37 @@
if (r.getRevisionResource() != null && !r.getRevisionResource().isCurrent()) {
throw new MethodNotAllowedException("Cannot delete vote on non-current patch set");
}
-
- try (BatchUpdate bu =
- updateFactory.create(
- change.getProject(), r.getChangeResource().getUser(), TimeUtil.now())) {
- bu.setNotify(
- notifyResolver.resolve(
- firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
- bu.addOp(
- change.getId(),
- deleteVoteOpFactory.create(
- r.getChange().getProject(),
- r.getReviewerUser().state(),
- rsrc.getLabel(),
- input,
- true));
- if (!input.ignoreAutomaticAttentionSetRules
- && !r.getReviewerUser().getAccountId().equals(currentUserProvider.get().getAccountId())) {
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(
+ change.getProject(), r.getChangeResource().getUser(), TimeUtil.now())) {
+ bu.setNotify(
+ notifyResolver.resolve(
+ firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
bu.addOp(
change.getId(),
- attentionSetOpFactory.create(
- r.getReviewerUser().getAccountId(),
- /* reason= */ "Their vote was deleted",
- /* notify= */ false));
+ deleteVoteOpFactory.create(
+ r.getChange().getProject(),
+ r.getReviewerUser().state(),
+ rsrc.getLabel(),
+ input,
+ true));
+ if (!input.ignoreAutomaticAttentionSetRules
+ && !r.getReviewerUser()
+ .getAccountId()
+ .equals(currentUserProvider.get().getAccountId())) {
+ bu.addOp(
+ change.getId(),
+ attentionSetOpFactory.create(
+ r.getReviewerUser().getAccountId(),
+ /* reason= */ "Their vote was deleted",
+ /* notify= */ false));
+ }
+ if (input.ignoreAutomaticAttentionSetRules) {
+ bu.addOp(change.getId(), new AttentionSetUnchangedOp());
+ }
+ bu.execute();
}
- if (input.ignoreAutomaticAttentionSetRules) {
- bu.addOp(change.getId(), new AttentionSetUnchangedOp());
- }
- bu.execute();
}
return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index c1b36d7..94f9b8d 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -19,6 +19,7 @@
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
@@ -59,6 +60,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -156,9 +158,11 @@
projectCache.get(project).orElseThrow(illegalState(project)).checkStatePermitsWrite();
Op op = new Op(input);
- try (BatchUpdate u = updateFactory.create(project, caller, TimeUtil.now())) {
- u.addOp(change.getId(), op);
- u.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u = updateFactory.create(project, caller, TimeUtil.now())) {
+ u.addOp(change.getId(), op);
+ u.execute();
+ }
}
return Response.ok(json.noOptions().format(op.getChange()));
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostHashtags.java b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
index bcaa145..a503eda 100644
--- a/java/com/google/gerrit/server/restapi/change/PostHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.common.collect.ImmutableSortedSet;
import com.google.gerrit.extensions.api.changes.HashtagsInput;
import com.google.gerrit.extensions.restapi.Response;
@@ -26,6 +28,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -46,13 +49,14 @@
public Response<ImmutableSortedSet<String>> apply(ChangeResource req, HashtagsInput input)
throws RestApiException, UpdateException, PermissionBackendException {
req.permissions().check(ChangePermission.EDIT_HASHTAGS);
-
- try (BatchUpdate bu =
- updateFactory.create(req.getChange().getProject(), req.getUser(), TimeUtil.now())) {
- SetHashtagsOp op = hashtagsFactory.create(input);
- bu.addOp(req.getId(), op);
- bu.execute();
- return Response.ok(op.getUpdatedHashtags());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(req.getChange().getProject(), req.getUser(), TimeUtil.now())) {
+ SetHashtagsOp op = hashtagsFactory.create(input);
+ bu.addOp(req.getId(), op);
+ bu.execute();
+ return Response.ok(op.getUpdatedHashtags());
+ }
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostPrivate.java b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
index 45d7250..56b81b8 100644
--- a/java/com/google/gerrit/server/restapi/change/PostPrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
@@ -16,6 +16,7 @@
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.common.InputWithMessage;
@@ -33,6 +34,7 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -74,8 +76,11 @@
}
SetPrivateOp op = setPrivateOpFactory.create(true, input);
- try (BatchUpdate u = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- u.addOp(rsrc.getId(), op).execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ u.addOp(rsrc.getId(), op).execute();
+ }
}
return Response.created();
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 22eb32c..9940637 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -19,6 +19,7 @@
import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL;
import static com.google.gerrit.server.permissions.AbstractLabelPermission.ForUser.ON_BEHALF_OF;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
@@ -104,6 +105,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -302,91 +304,93 @@
// Notify based on ReviewInput, ignoring the notify settings from any ReviewerInputs.
NotifyResolver.Result notify = notifyResolver.resolve(input.notify, input.notifyDetails);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(revision.getChange().getProject(), revision.getUser(), ts)) {
+ bu.setNotify(notify);
- try (BatchUpdate bu =
- updateFactory.create(revision.getChange().getProject(), revision.getUser(), ts)) {
- bu.setNotify(notify);
-
- Account account = revision.getUser().asIdentifiedUser().getAccount();
- boolean ccOrReviewer = false;
- if (input.labels != null && !input.labels.isEmpty()) {
- ccOrReviewer = input.labels.values().stream().anyMatch(v -> v != 0);
- if (ccOrReviewer) {
- logger.atFine().log("calling user is cc/reviewer on the change due to voting on a label");
- }
- }
-
- if (!ccOrReviewer) {
- // Check if user was already CCed or reviewing prior to this review.
- ReviewerSet currentReviewers =
- approvalsUtil.getReviewers(revision.getChangeResource().getNotes());
- ccOrReviewer = currentReviewers.all().contains(account.id());
- if (ccOrReviewer) {
- logger.atFine().log("calling user is already cc/reviewer on the change");
- }
- }
-
- // Apply reviewer changes first. Revision emails should be sent to the
- // updated set of reviewers. Also keep track of whether the user added
- // themselves as a reviewer or to the CC list.
- logger.atFine().log("adding reviewer additions");
- for (ReviewerModification reviewerResult : reviewerResults) {
- reviewerResult.op.suppressEmail(); // Send a single batch email below.
- reviewerResult.op.suppressEvent(); // Send events below, if possible as batch.
- bu.addOp(revision.getChange().getId(), reviewerResult.op);
- if (!ccOrReviewer && reviewerResult.reviewers.contains(account)) {
- logger.atFine().log("calling user is explicitly added as reviewer or CC");
- ccOrReviewer = true;
- }
- }
-
- if (!ccOrReviewer) {
- // User posting this review isn't currently in the reviewer or CC list,
- // isn't being explicitly added, and isn't voting on any label.
- // Automatically CC them on this change so they receive replies.
- logger.atFine().log("CCing calling user");
- ReviewerModification selfAddition =
- reviewerModifier.ccCurrentUser(revision.getUser(), revision);
- selfAddition.op.suppressEmail();
- selfAddition.op.suppressEvent();
- bu.addOp(revision.getChange().getId(), selfAddition.op);
- }
-
- // Add WorkInProgressOp if requested.
- if ((input.ready || input.workInProgress)
- && didWorkInProgressChange(revision.getChange().isWorkInProgress(), input)) {
- if (input.ready && input.workInProgress) {
- output.error = ERROR_WIP_READY_MUTUALLY_EXCLUSIVE;
- return Response.withStatusCode(SC_BAD_REQUEST, output);
+ Account account = revision.getUser().asIdentifiedUser().getAccount();
+ boolean ccOrReviewer = false;
+ if (input.labels != null && !input.labels.isEmpty()) {
+ ccOrReviewer = input.labels.values().stream().anyMatch(v -> v != 0);
+ if (ccOrReviewer) {
+ logger.atFine().log(
+ "calling user is cc/reviewer on the change due to voting on a label");
+ }
}
- revision
- .getChangeResource()
- .permissions()
- .check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
-
- if (input.ready) {
- output.ready = true;
+ if (!ccOrReviewer) {
+ // Check if user was already CCed or reviewing prior to this review.
+ ReviewerSet currentReviewers =
+ approvalsUtil.getReviewers(revision.getChangeResource().getNotes());
+ ccOrReviewer = currentReviewers.all().contains(account.id());
+ if (ccOrReviewer) {
+ logger.atFine().log("calling user is already cc/reviewer on the change");
+ }
}
- logger.atFine().log("setting work-in-progress to %s", input.workInProgress);
- WorkInProgressOp wipOp =
- workInProgressOpFactory.create(input.workInProgress, new WorkInProgressOp.Input());
- wipOp.suppressEmail();
- bu.addOp(revision.getChange().getId(), wipOp);
+ // Apply reviewer changes first. Revision emails should be sent to the
+ // updated set of reviewers. Also keep track of whether the user added
+ // themselves as a reviewer or to the CC list.
+ logger.atFine().log("adding reviewer additions");
+ for (ReviewerModification reviewerResult : reviewerResults) {
+ reviewerResult.op.suppressEmail(); // Send a single batch email below.
+ reviewerResult.op.suppressEvent(); // Send events below, if possible as batch.
+ bu.addOp(revision.getChange().getId(), reviewerResult.op);
+ if (!ccOrReviewer && reviewerResult.reviewers.contains(account)) {
+ logger.atFine().log("calling user is explicitly added as reviewer or CC");
+ ccOrReviewer = true;
+ }
+ }
+
+ if (!ccOrReviewer) {
+ // User posting this review isn't currently in the reviewer or CC list,
+ // isn't being explicitly added, and isn't voting on any label.
+ // Automatically CC them on this change so they receive replies.
+ logger.atFine().log("CCing calling user");
+ ReviewerModification selfAddition =
+ reviewerModifier.ccCurrentUser(revision.getUser(), revision);
+ selfAddition.op.suppressEmail();
+ selfAddition.op.suppressEvent();
+ bu.addOp(revision.getChange().getId(), selfAddition.op);
+ }
+
+ // Add WorkInProgressOp if requested.
+ if ((input.ready || input.workInProgress)
+ && didWorkInProgressChange(revision.getChange().isWorkInProgress(), input)) {
+ if (input.ready && input.workInProgress) {
+ output.error = ERROR_WIP_READY_MUTUALLY_EXCLUSIVE;
+ return Response.withStatusCode(SC_BAD_REQUEST, output);
+ }
+
+ revision
+ .getChangeResource()
+ .permissions()
+ .check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
+
+ if (input.ready) {
+ output.ready = true;
+ }
+
+ logger.atFine().log("setting work-in-progress to %s", input.workInProgress);
+ WorkInProgressOp wipOp =
+ workInProgressOpFactory.create(input.workInProgress, new WorkInProgressOp.Input());
+ wipOp.suppressEmail();
+ bu.addOp(revision.getChange().getId(), wipOp);
+ }
+
+ // Add the review ops.
+ logger.atFine().log("posting review");
+ PostReviewOp postReviewOp =
+ postReviewOpFactory.create(
+ projectState, revision.getPatchSet().id(), input, revision.getAccountId());
+ bu.addOp(revision.getChange().getId(), postReviewOp);
+
+ // Adjust the attention set based on the input
+ replyAttentionSetUpdates.updateAttentionSet(
+ bu, revision.getNotes(), input, revision.getUser());
+ bu.execute();
}
-
- // Add the review ops.
- logger.atFine().log("posting review");
- PostReviewOp postReviewOp =
- postReviewOpFactory.create(
- projectState, revision.getPatchSet().id(), input, revision.getAccountId());
- bu.addOp(revision.getChange().getId(), postReviewOp);
-
- // Adjust the attention set based on the input
- replyAttentionSetUpdates.updateAttentionSet(
- bu, revision.getNotes(), input, revision.getUser());
- bu.execute();
}
// Re-read change to take into account results of the update.
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
index 9bc80a4..e46f9e4 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewerInput;
@@ -31,6 +33,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -70,11 +73,14 @@
if (modification.op == null) {
return Response.ok(modification.result);
}
- try (BatchUpdate bu = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- bu.setNotify(resolveNotify(rsrc, input));
- Change.Id id = rsrc.getChange().getId();
- bu.addOp(id, modification.op);
- bu.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ bu.setNotify(resolveNotify(rsrc, input));
+ Change.Id id = rsrc.getChange().getId();
+ bu.addOp(id, modification.op);
+ bu.execute();
+ }
}
// Re-read change to take into account results of the update.
diff --git a/java/com/google/gerrit/server/restapi/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
index 5b5bc15..0d633db 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.common.base.Strings;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.common.DescriptionInput;
@@ -31,6 +33,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -56,10 +59,12 @@
rsrc.permissions().check(ChangePermission.EDIT_DESCRIPTION);
Op op = new Op(input != null ? input : new DescriptionInput(), rsrc.getPatchSet().id());
- try (BatchUpdate u =
- updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
- u.addOp(rsrc.getChange().getId(), op);
- u.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u =
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
+ u.addOp(rsrc.getChange().getId(), op);
+ u.execute();
+ }
}
return Strings.isNullOrEmpty(op.newDescription)
? Response.none()
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 6411087..681e1b1 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.HumanComment;
@@ -36,6 +37,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -86,13 +88,15 @@
throw new BadRequestException(
String.format("Invalid inReplyTo, comment %s not found", in.inReplyTo));
}
- try (BatchUpdate bu =
- updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
- Op op = new Op(rsrc.getComment().key, in);
- bu.addOp(rsrc.getChange().getId(), op);
- bu.execute();
- return Response.ok(
- commentJson.get().setFillAccounts(false).newHumanCommentFormatter().format(op.comment));
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
+ Op op = new Op(rsrc.getComment().key, in);
+ bu.addOp(rsrc.getChange().getId(), op);
+ bu.execute();
+ return Response.ok(
+ commentJson.get().setFillAccounts(false).newHumanCommentFormatter().format(op.comment));
+ }
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutMessage.java b/java/com/google/gerrit/server/restapi/change/PutMessage.java
index f898dca..4a4f546 100644
--- a/java/com/google/gerrit/server/restapi/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.entities.PatchSet;
@@ -41,6 +42,7 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -127,21 +129,24 @@
}
Instant ts = TimeUtil.now();
- try (BatchUpdate bu =
- updateFactory.create(resource.getChange().getProject(), userProvider.get(), ts)) {
- // Ensure that BatchUpdate will update the same repo
- bu.setRepository(repository, new RevWalk(objectInserter.newReader()), objectInserter);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(resource.getChange().getProject(), userProvider.get(), ts)) {
+ // Ensure that BatchUpdate will update the same repo
+ bu.setRepository(repository, new RevWalk(objectInserter.newReader()), objectInserter);
- PatchSet.Id psId = ChangeUtil.nextPatchSetId(repository, ps.id());
- ObjectId newCommit =
- createCommit(objectInserter, patchSetCommit, sanitizedCommitMessage, ts);
- PatchSetInserter inserter = psInserterFactory.create(resource.getNotes(), psId, newCommit);
- inserter.setMessage(
- String.format("Patch Set %s: Commit message was updated.", psId.getId()));
- inserter.setDescription("Edit commit message");
- bu.setNotify(resolveNotify(input, resource));
- bu.addOp(resource.getChange().getId(), inserter);
- bu.execute();
+ PatchSet.Id psId = ChangeUtil.nextPatchSetId(repository, ps.id());
+ ObjectId newCommit =
+ createCommit(objectInserter, patchSetCommit, sanitizedCommitMessage, ts);
+ PatchSetInserter inserter =
+ psInserterFactory.create(resource.getNotes(), psId, newCommit);
+ inserter.setMessage(
+ String.format("Patch Set %s: Commit message was updated.", psId.getId()));
+ inserter.setDescription("Edit commit message");
+ bu.setNotify(resolveNotify(input, resource));
+ bu.addOp(resource.getChange().getId(), inserter);
+ bu.execute();
+ }
}
}
return Response.ok("ok");
diff --git a/java/com/google/gerrit/server/restapi/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
index c9b436e..b1e5d5a 100644
--- a/java/com/google/gerrit/server/restapi/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.common.base.Strings;
import com.google.gerrit.extensions.api.changes.TopicInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -28,6 +30,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -62,10 +65,12 @@
}
SetTopicOp op = topicOpFactory.create(sanitizedInput.topic);
- try (BatchUpdate u =
- updateFactory.create(req.getChange().getProject(), req.getUser(), TimeUtil.now())) {
- u.addOp(req.getId(), op);
- u.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u =
+ updateFactory.create(req.getChange().getProject(), req.getUser(), TimeUtil.now())) {
+ u.addOp(req.getId(), op);
+ u.execute();
+ }
}
if (Strings.isNullOrEmpty(sanitizedInput.topic)) {
diff --git a/java/com/google/gerrit/server/restapi/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
index 6535e42..fd51fbc 100644
--- a/java/com/google/gerrit/server/restapi/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -52,6 +53,7 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -124,30 +126,32 @@
.checkStatePermitsWrite();
Change change = rsrc.getChange();
- try (Repository repo = repoManager.openRepository(change.getProject());
- ObjectInserter oi = repo.newObjectInserter();
- ObjectReader reader = oi.newReader();
- RevWalk rw = CodeReviewCommit.newRevWalk(reader);
- BatchUpdate bu =
- updateFactory.create(change.getProject(), rsrc.getUser(), TimeUtil.now())) {
- rebaseUtil.verifyRebasePreconditions(rw, rsrc.getNotes(), rsrc.getPatchSet());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (Repository repo = repoManager.openRepository(change.getProject());
+ ObjectInserter oi = repo.newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ RevWalk rw = CodeReviewCommit.newRevWalk(reader);
+ BatchUpdate bu =
+ updateFactory.create(change.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ rebaseUtil.verifyRebasePreconditions(rw, rsrc.getNotes(), rsrc.getPatchSet());
- RebaseChangeOp rebaseOp =
- rebaseUtil.getRebaseOp(
- rsrc,
- input,
- rebaseUtil.parseOrFindBaseRevision(repo, rw, permissionBackend, rsrc, input, true));
+ RebaseChangeOp rebaseOp =
+ rebaseUtil.getRebaseOp(
+ rsrc,
+ input,
+ rebaseUtil.parseOrFindBaseRevision(repo, rw, permissionBackend, rsrc, input, true));
- // TODO(dborowitz): Why no notification? This seems wrong; dig up blame.
- bu.setNotify(NotifyResolver.Result.none());
- bu.setRepository(repo, rw, oi);
- bu.addOp(change.getId(), rebaseOp);
- bu.execute();
+ // TODO(dborowitz): Why no notification? This seems wrong; dig up blame.
+ bu.setNotify(NotifyResolver.Result.none());
+ bu.setRepository(repo, rw, oi);
+ bu.addOp(change.getId(), rebaseOp);
+ bu.execute();
- ChangeInfo changeInfo = json.create(OPTIONS).format(change.getProject(), change.getId());
- changeInfo.containsGitConflicts =
- !rebaseOp.getRebasedCommit().getFilesWithGitConflicts().isEmpty() ? true : null;
- return Response.ok(changeInfo);
+ ChangeInfo changeInfo = json.create(OPTIONS).format(change.getProject(), change.getId());
+ changeInfo.containsGitConflicts =
+ !rebaseOp.getRebasedCommit().getFilesWithGitConflicts().isEmpty() ? true : null;
+ return Response.ok(changeInfo);
+ }
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChain.java b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
index 5ae496f..34a2623 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChain.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChain.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -52,6 +53,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -128,66 +130,68 @@
List<Change.Id> upToDateAncestors = new ArrayList<>();
Map<Change.Id, RebaseChangeOp> rebaseOps = new LinkedHashMap<>();
- try (Repository repo = repoManager.openRepository(project);
- ObjectInserter oi = repo.newObjectInserter();
- ObjectReader reader = oi.newReader();
- RevWalk rw = CodeReviewCommit.newRevWalk(reader);
- BatchUpdate bu = updateFactory.create(project, user, TimeUtil.now())) {
- List<PatchSetData> chain = getChainForCurrentPatchSet(tipRsrc);
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (Repository repo = repoManager.openRepository(project);
+ ObjectInserter oi = repo.newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ RevWalk rw = CodeReviewCommit.newRevWalk(reader);
+ BatchUpdate bu = updateFactory.create(project, user, TimeUtil.now())) {
+ List<PatchSetData> chain = getChainForCurrentPatchSet(tipRsrc);
- boolean ancestorsAreUpToDate = true;
- for (int i = 0; i < chain.size(); i++) {
- ChangeData changeData = chain.get(i).data();
- PatchSet ps = patchSetUtil.current(changeData.notes());
- if (ps == null) {
- throw new IllegalStateException(
- "current revision is missing for change " + changeData.getId());
- }
+ boolean ancestorsAreUpToDate = true;
+ for (int i = 0; i < chain.size(); i++) {
+ ChangeData changeData = chain.get(i).data();
+ PatchSet ps = patchSetUtil.current(changeData.notes());
+ if (ps == null) {
+ throw new IllegalStateException(
+ "current revision is missing for change " + changeData.getId());
+ }
- RevisionResource revRsrc =
- new RevisionResource(changeResourceFactory.create(changeData, user), ps);
- revRsrc.permissions().check(ChangePermission.REBASE);
- rebaseUtil.verifyRebasePreconditions(rw, changeData.notes(), ps);
+ RevisionResource revRsrc =
+ new RevisionResource(changeResourceFactory.create(changeData, user), ps);
+ revRsrc.permissions().check(ChangePermission.REBASE);
+ rebaseUtil.verifyRebasePreconditions(rw, changeData.notes(), ps);
- boolean isUpToDate = false;
- RebaseChangeOp rebaseOp = null;
- if (i == 0) {
- ObjectId desiredBase =
- rebaseUtil.parseOrFindBaseRevision(
- repo, rw, permissionBackend, revRsrc, input, false);
- if (currentBase(rw, ps).equals(desiredBase)) {
- isUpToDate = true;
+ boolean isUpToDate = false;
+ RebaseChangeOp rebaseOp = null;
+ if (i == 0) {
+ ObjectId desiredBase =
+ rebaseUtil.parseOrFindBaseRevision(
+ repo, rw, permissionBackend, revRsrc, input, false);
+ if (currentBase(rw, ps).equals(desiredBase)) {
+ isUpToDate = true;
+ } else {
+ rebaseOp = rebaseUtil.getRebaseOp(revRsrc, input, desiredBase);
+ }
} else {
- rebaseOp = rebaseUtil.getRebaseOp(revRsrc, input, desiredBase);
+ if (ancestorsAreUpToDate) {
+ ObjectId latestCommittedBase =
+ PatchSetUtil.getCurrentCommittedRevCommit(
+ project, rw, notesFactory, chain.get(i - 1).id());
+ isUpToDate = currentBase(rw, ps).equals(latestCommittedBase);
+ }
+ if (!isUpToDate) {
+ rebaseOp = rebaseUtil.getRebaseOp(revRsrc, input, chain.get(i - 1).id());
+ }
}
- } else {
- if (ancestorsAreUpToDate) {
- ObjectId latestCommittedBase =
- PatchSetUtil.getCurrentCommittedRevCommit(
- project, rw, notesFactory, chain.get(i - 1).id());
- isUpToDate = currentBase(rw, ps).equals(latestCommittedBase);
+
+ if (isUpToDate) {
+ upToDateAncestors.add(changeData.getId());
+ continue;
}
- if (!isUpToDate) {
- rebaseOp = rebaseUtil.getRebaseOp(revRsrc, input, chain.get(i - 1).id());
- }
+ ancestorsAreUpToDate = false;
+ bu.addOp(revRsrc.getChange().getId(), rebaseOp);
+ rebaseOps.put(revRsrc.getChange().getId(), rebaseOp);
}
- if (isUpToDate) {
- upToDateAncestors.add(changeData.getId());
- continue;
+ if (ancestorsAreUpToDate) {
+ throw new ResourceConflictException("The whole chain is already up to date.");
}
- ancestorsAreUpToDate = false;
- bu.addOp(revRsrc.getChange().getId(), rebaseOp);
- rebaseOps.put(revRsrc.getChange().getId(), rebaseOp);
- }
- if (ancestorsAreUpToDate) {
- throw new ResourceConflictException("The whole chain is already up to date.");
+ bu.setNotify(NotifyResolver.Result.none());
+ bu.setRepository(repo, rw, oi);
+ bu.execute();
}
-
- bu.setNotify(NotifyResolver.Result.none());
- bu.setRepository(repo, rw, oi);
- bu.execute();
}
RebaseChainInfo res = new RebaseChainInfo();
@@ -241,6 +245,9 @@
try (Repository repo = repoManager.openRepository(tipRsrc.getProject());
RevWalk rw = new RevWalk(repo)) {
List<PatchSetData> chain = getChainForCurrentPatchSet(tipRsrc);
+ if (chain.size() <= 1) {
+ return description;
+ }
PatchSetData oldestAncestor = chain.get(0);
if (rebaseUtil.canRebase(
oldestAncestor.patchSet(), oldestAncestor.data().change().getDest(), repo, rw)) {
diff --git a/java/com/google/gerrit/server/restapi/change/RemoveFromAttentionSet.java b/java/com/google/gerrit/server/restapi/change/RemoveFromAttentionSet.java
index bd3e8ec..d761fa7 100644
--- a/java/com/google/gerrit/server/restapi/change/RemoveFromAttentionSet.java
+++ b/java/com/google/gerrit/server/restapi/change/RemoveFromAttentionSet.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+
import com.google.common.base.Strings;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.changes.AttentionSetInput;
@@ -30,6 +32,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AttentionSetUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -79,16 +82,18 @@
}
}
ChangeResource changeResource = attentionResource.getChangeResource();
- try (BatchUpdate bu =
- updateFactory.create(
- changeResource.getProject(), changeResource.getUser(), TimeUtil.now())) {
- RemoveFromAttentionSetOp op =
- opFactory.create(attentionResource.getAccountId(), input.reason, true);
- bu.addOp(changeResource.getId(), op);
- NotifyHandling notify = input.notify == null ? NotifyHandling.OWNER : input.notify;
- NotifyResolver.Result notifyResult = notifyResolver.resolve(notify, input.notifyDetails);
- bu.setNotify(notifyResult);
- bu.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(
+ changeResource.getProject(), changeResource.getUser(), TimeUtil.now())) {
+ RemoveFromAttentionSetOp op =
+ opFactory.create(attentionResource.getAccountId(), input.reason, true);
+ bu.addOp(changeResource.getId(), op);
+ NotifyHandling notify = input.notify == null ? NotifyHandling.OWNER : input.notify;
+ NotifyResolver.Result notifyResult = notifyResolver.resolve(notify, input.notifyDetails);
+ bu.setNotify(notifyResult);
+ bu.execute();
+ }
}
return Response.none();
}
diff --git a/java/com/google/gerrit/server/restapi/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
index 19d0677..6ac9c21 100644
--- a/java/com/google/gerrit/server/restapi/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
@@ -47,6 +48,7 @@
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.PostUpdateContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -99,11 +101,13 @@
.checkStatePermitsWrite();
Op op = new Op(input);
- try (BatchUpdate u =
- updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
- u.addOp(rsrc.getId(), op).execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate u =
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.now())) {
+ u.addOp(rsrc.getId(), op).execute();
+ }
+ return Response.ok(json.noOptions().format(op.change));
}
- return Response.ok(json.noOptions().format(op.change));
}
private class Op implements BatchUpdateOp {
diff --git a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
index 62fdcbb..4c7c352 100644
--- a/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
+++ b/java/com/google/gerrit/server/restapi/change/RevertSubmission.java
@@ -19,6 +19,7 @@
import static com.google.gerrit.server.permissions.ChangePermission.REVERT;
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
@@ -70,6 +71,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
@@ -306,24 +308,26 @@
cherryPickInput.message = revertInput.message;
ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
Change.Id cherryPickRevertChangeId = Change.id(seq.nextChangeId());
- try (BatchUpdate bu = updateFactory.create(project, user.get(), TimeUtil.now())) {
- bu.setNotify(
- notifyResolver.resolve(
- firstNonNull(cherryPickInput.notify, NotifyHandling.ALL),
- cherryPickInput.notifyDetails));
- bu.addOp(
- changeNotes.getChange().getId(),
- new CreateCherryPickOp(
- revCommitId,
- generatedChangeId,
- cherryPickRevertChangeId,
- timestamp,
- revertInput.workInProgress));
- if (!revertInput.workInProgress) {
- commitUtil.addChangeRevertedNotificationOps(
- bu, changeNotes.getChangeId(), cherryPickRevertChangeId, generatedChangeId.name());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu = updateFactory.create(project, user.get(), TimeUtil.now())) {
+ bu.setNotify(
+ notifyResolver.resolve(
+ firstNonNull(cherryPickInput.notify, NotifyHandling.ALL),
+ cherryPickInput.notifyDetails));
+ bu.addOp(
+ changeNotes.getChange().getId(),
+ new CreateCherryPickOp(
+ revCommitId,
+ generatedChangeId,
+ cherryPickRevertChangeId,
+ timestamp,
+ revertInput.workInProgress));
+ if (!revertInput.workInProgress) {
+ commitUtil.addChangeRevertedNotificationOps(
+ bu, changeNotes.getChangeId(), cherryPickRevertChangeId, generatedChangeId.name());
+ }
+ bu.execute();
}
- bu.execute();
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
index a587ecc..7d3fe98 100644
--- a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
+++ b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -34,6 +35,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -68,16 +70,18 @@
if (!change.isWorkInProgress()) {
throw new ResourceConflictException("change is not work in progress");
}
-
- try (BatchUpdate bu = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.ALL)));
- bu.addOp(rsrc.getChange().getId(), opFactory.create(false, input));
- if (change.getRevertOf() != null) {
- commitUtil.addChangeRevertedNotificationOps(
- bu, change.getRevertOf(), change.getId(), change.getKey().get());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.ALL)));
+ bu.addOp(rsrc.getChange().getId(), opFactory.create(false, input));
+ if (change.getRevertOf() != null) {
+ commitUtil.addChangeRevertedNotificationOps(
+ bu, change.getRevertOf(), change.getId(), change.getKey().get());
+ }
+ bu.execute();
+ return Response.ok();
}
- bu.execute();
- return Response.ok();
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
index 0ad5180..306aeea 100644
--- a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
+++ b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.gerrit.entities.Change;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -33,6 +34,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -62,12 +64,14 @@
if (change.isWorkInProgress()) {
throw new ResourceConflictException("change is already work in progress");
}
-
- try (BatchUpdate bu = updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
- bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.NONE)));
- bu.addOp(rsrc.getChange().getId(), opFactory.create(true, input));
- bu.execute();
- return Response.ok();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (BatchUpdate bu =
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.now())) {
+ bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.NONE)));
+ bu.addOp(rsrc.getChange().getId(), opFactory.create(true, input));
+ bu.execute();
+ return Response.ok();
+ }
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index 977bfdb..65182db 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -45,6 +46,7 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -142,24 +144,26 @@
md.setMessage("Review access change");
md.setInsertChangeId(true);
Change.Id changeId = Change.id(seq.nextChangeId());
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ RevCommit commit =
+ config.commitToNewRef(
+ md, PatchSet.id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
- RevCommit commit =
- config.commitToNewRef(md, PatchSet.id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
+ if (commit.name().equals(oldCommitSha1)) {
+ throw new BadRequestException("no change");
+ }
- if (commit.name().equals(oldCommitSha1)) {
- throw new BadRequestException("no change");
- }
-
- try (ObjectInserter objInserter = md.getRepository().newObjectInserter();
- ObjectReader objReader = objInserter.newReader();
- RevWalk rw = new RevWalk(objReader);
- BatchUpdate bu =
- updateFactory.create(rsrc.getNameKey(), rsrc.getUser(), TimeUtil.now())) {
- bu.setRepository(md.getRepository(), rw, objInserter);
- ChangeInserter ins = newInserter(changeId, commit);
- bu.insertChange(ins);
- bu.execute();
- return Response.created(jsonFactory.noOptions().format(ins.getChange()));
+ try (ObjectInserter objInserter = md.getRepository().newObjectInserter();
+ ObjectReader objReader = objInserter.newReader();
+ RevWalk rw = new RevWalk(objReader);
+ BatchUpdate bu =
+ updateFactory.create(rsrc.getNameKey(), rsrc.getUser(), TimeUtil.now())) {
+ bu.setRepository(md.getRepository(), rw, objInserter);
+ ChangeInserter ins = newInserter(changeId, commit);
+ bu.insertChange(ins);
+ bu.execute();
+ return Response.created(jsonFactory.noOptions().format(ins.getChange()));
+ }
}
} catch (InvalidNameException e) {
throw new BadRequestException(e.toString());
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
index 17fc6db..412559b 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.entities.RefNames.isConfigRef;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
import com.google.common.base.Strings;
import com.google.gerrit.entities.BranchNameKey;
@@ -42,6 +43,7 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.RefUtil;
import com.google.gerrit.server.project.RefValidationHelper;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.MagicBranch;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -87,130 +89,132 @@
throws BadRequestException, AuthException, ResourceConflictException,
UnprocessableEntityException, IOException, PermissionBackendException,
NoSuchProjectException {
- String ref = id.get();
- if (input == null) {
- input = new BranchInput();
- }
- if (input.ref != null && !ref.equals(input.ref)) {
- throw new BadRequestException("ref must match URL");
- }
- if (input.revision != null) {
- input.revision = input.revision.trim();
- }
- if (Strings.isNullOrEmpty(input.revision)) {
- input.revision = Constants.HEAD;
- }
- while (ref.startsWith("/")) {
- ref = ref.substring(1);
- }
- ref = RefNames.fullName(ref);
- if (!Repository.isValidRefName(ref)) {
- throw new BadRequestException("invalid branch name \"" + ref + "\"");
- }
- if (MagicBranch.isMagicBranch(ref)) {
- throw new BadRequestException(
- "not allowed to create branches under \""
- + MagicBranch.getMagicRefNamePrefix(ref)
- + "\"");
- }
- if (!isBranchAllowed(ref)) {
- throw new BadRequestException(
- "Cannot create a branch with name \""
- + ref
- + "\". Not allowed to create branches under Gerrit internal or tags refs.");
- }
-
- BranchNameKey name = BranchNameKey.create(rsrc.getNameKey(), ref);
- try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
- ObjectId revid = RefUtil.parseBaseRevision(repo, input.revision);
- RevWalk rw = RefUtil.verifyConnected(repo, revid);
- RevObject object = rw.parseAny(revid);
-
- if (ref.startsWith(Constants.R_HEADS)) {
- // Ensure that what we start the branch from is a commit. If we
- // were given a tag, dereference to the commit instead.
- //
- object = rw.parseCommit(object);
+ try (RefUpdateContext ctx = RefUpdateContext.open(BRANCH_MODIFICATION)) {
+ String ref = id.get();
+ if (input == null) {
+ input = new BranchInput();
+ }
+ if (input.ref != null && !ref.equals(input.ref)) {
+ throw new BadRequestException("ref must match URL");
+ }
+ if (input.revision != null) {
+ input.revision = input.revision.trim();
+ }
+ if (Strings.isNullOrEmpty(input.revision)) {
+ input.revision = Constants.HEAD;
+ }
+ while (ref.startsWith("/")) {
+ ref = ref.substring(1);
+ }
+ ref = RefNames.fullName(ref);
+ if (!Repository.isValidRefName(ref)) {
+ throw new BadRequestException("invalid branch name \"" + ref + "\"");
+ }
+ if (MagicBranch.isMagicBranch(ref)) {
+ throw new BadRequestException(
+ "not allowed to create branches under \""
+ + MagicBranch.getMagicRefNamePrefix(ref)
+ + "\"");
+ }
+ if (!isBranchAllowed(ref)) {
+ throw new BadRequestException(
+ "Cannot create a branch with name \""
+ + ref
+ + "\". Not allowed to create branches under Gerrit internal or tags refs.");
}
- Ref sourceRef = repo.exactRef(input.revision);
- if (sourceRef == null) {
- createRefControl.checkCreateRef(identifiedUser, repo, name, object, /* forPush= */ false);
- } else {
- if (sourceRef.isSymbolic()) {
- sourceRef = sourceRef.getTarget();
+ BranchNameKey name = BranchNameKey.create(rsrc.getNameKey(), ref);
+ try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
+ ObjectId revid = RefUtil.parseBaseRevision(repo, input.revision);
+ RevWalk rw = RefUtil.verifyConnected(repo, revid);
+ RevObject object = rw.parseAny(revid);
+
+ if (ref.startsWith(Constants.R_HEADS)) {
+ // Ensure that what we start the branch from is a commit. If we
+ // were given a tag, dereference to the commit instead.
+ //
+ object = rw.parseCommit(object);
}
- createRefControl.checkCreateRef(
- identifiedUser,
- repo,
- name,
- object,
- /* forPush= */ false,
- BranchNameKey.create(rsrc.getNameKey(), sourceRef.getName()));
- }
- RefUpdate u = repo.updateRef(ref);
- u.setExpectedOldObjectId(ObjectId.zeroId());
- u.setNewObjectId(object.copy());
- u.setRefLogIdent(identifiedUser.get().newRefLogIdent());
- u.setRefLogMessage("created via REST from " + input.revision, false);
- refCreationValidator.validateRefOperation(
- rsrc.getName(),
- identifiedUser.get(),
- u,
- ValidationOptionsUtil.getValidateOptionsAsMultimap(input.validationOptions));
- RefUpdate.Result result = u.update(rw);
- switch (result) {
- case FAST_FORWARD:
- case NEW:
- case NO_CHANGE:
- referenceUpdated.fire(
- name.project(), u, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
- break;
- case LOCK_FAILURE:
- if (repo.getRefDatabase().exactRef(ref) != null) {
- throw new ResourceConflictException("branch \"" + ref + "\" already exists");
+ Ref sourceRef = repo.exactRef(input.revision);
+ if (sourceRef == null) {
+ createRefControl.checkCreateRef(identifiedUser, repo, name, object, /* forPush= */ false);
+ } else {
+ if (sourceRef.isSymbolic()) {
+ sourceRef = sourceRef.getTarget();
}
- String refPrefix = RefUtil.getRefPrefix(ref);
- while (!Constants.R_HEADS.equals(refPrefix)) {
- if (repo.getRefDatabase().exactRef(refPrefix) != null) {
- throw new ResourceConflictException(
- "Cannot create branch \""
- + ref
- + "\" since it conflicts with branch \""
- + refPrefix
- + "\".");
+ createRefControl.checkCreateRef(
+ identifiedUser,
+ repo,
+ name,
+ object,
+ /* forPush= */ false,
+ BranchNameKey.create(rsrc.getNameKey(), sourceRef.getName()));
+ }
+
+ RefUpdate u = repo.updateRef(ref);
+ u.setExpectedOldObjectId(ObjectId.zeroId());
+ u.setNewObjectId(object.copy());
+ u.setRefLogIdent(identifiedUser.get().newRefLogIdent());
+ u.setRefLogMessage("created via REST from " + input.revision, false);
+ refCreationValidator.validateRefOperation(
+ rsrc.getName(),
+ identifiedUser.get(),
+ u,
+ ValidationOptionsUtil.getValidateOptionsAsMultimap(input.validationOptions));
+ RefUpdate.Result result = u.update(rw);
+ switch (result) {
+ case FAST_FORWARD:
+ case NEW:
+ case NO_CHANGE:
+ referenceUpdated.fire(
+ name.project(), u, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
+ break;
+ case LOCK_FAILURE:
+ if (repo.getRefDatabase().exactRef(ref) != null) {
+ throw new ResourceConflictException("branch \"" + ref + "\" already exists");
}
- refPrefix = RefUtil.getRefPrefix(refPrefix);
- }
- throw new LockFailureException(String.format("Failed to create %s", ref), u);
- case FORCED:
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new IOException(String.format("Failed to create %s: %s", ref, result.name()));
- }
+ String refPrefix = RefUtil.getRefPrefix(ref);
+ while (!Constants.R_HEADS.equals(refPrefix)) {
+ if (repo.getRefDatabase().exactRef(refPrefix) != null) {
+ throw new ResourceConflictException(
+ "Cannot create branch \""
+ + ref
+ + "\" since it conflicts with branch \""
+ + refPrefix
+ + "\".");
+ }
+ refPrefix = RefUtil.getRefPrefix(refPrefix);
+ }
+ throw new LockFailureException(String.format("Failed to create %s", ref), u);
+ case FORCED:
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new IOException(String.format("Failed to create %s: %s", ref, result.name()));
+ }
- BranchInfo info = new BranchInfo();
- info.ref = ref;
- info.revision = revid.getName();
+ BranchInfo info = new BranchInfo();
+ info.ref = ref;
+ info.revision = revid.getName();
- if (isConfigRef(name.branch())) {
- // Never allow to delete the meta config branch.
- info.canDelete = null;
- } else {
- info.canDelete =
- permissionBackend.currentUser().ref(name).testOrFalse(RefPermission.DELETE)
- && rsrc.getProjectState().statePermitsWrite()
- ? true
- : null;
+ if (isConfigRef(name.branch())) {
+ // Never allow to delete the meta config branch.
+ info.canDelete = null;
+ } else {
+ info.canDelete =
+ permissionBackend.currentUser().ref(name).testOrFalse(RefPermission.DELETE)
+ && rsrc.getProjectState().statePermitsWrite()
+ ? true
+ : null;
+ }
+ return Response.created(info);
}
- return Response.created(info);
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
index 63734bb..34c3ff7 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.project;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.TAG_MODIFICATION;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.common.base.Strings;
@@ -39,6 +40,7 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.RefUtil;
import com.google.gerrit.server.project.TagResource;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -97,64 +99,66 @@
ref = RefUtil.normalizeTagRef(ref);
PermissionBackend.ForRef perm =
permissionBackend.currentUser().project(resource.getNameKey()).ref(ref);
+ try (RefUpdateContext ctx = RefUpdateContext.open(TAG_MODIFICATION)) {
+ try (Repository repo = repoManager.openRepository(resource.getNameKey())) {
+ ObjectId revid = RefUtil.parseBaseRevision(repo, input.revision);
+ RevWalk rw = RefUtil.verifyConnected(repo, revid);
+ // Reachability through tags does not influence a commit's visibility, so no need to check
+ // for
+ // visibility.
+ RevObject object = rw.parseAny(revid);
+ rw.reset();
+ boolean isAnnotated = Strings.emptyToNull(input.message) != null;
+ boolean isSigned = isAnnotated && input.message.contains("-----BEGIN PGP SIGNATURE-----\n");
+ if (isSigned) {
+ throw new MethodNotAllowedException("Cannot create signed tag \"" + ref + "\"");
+ } else if (isAnnotated) {
+ if (!check(perm, RefPermission.CREATE_TAG)) {
+ throw new AuthException("Cannot create annotated tag \"" + ref + "\"");
+ }
- try (Repository repo = repoManager.openRepository(resource.getNameKey())) {
- ObjectId revid = RefUtil.parseBaseRevision(repo, input.revision);
- RevWalk rw = RefUtil.verifyConnected(repo, revid);
- // Reachability through tags does not influence a commit's visibility, so no need to check for
- // visibility.
- RevObject object = rw.parseAny(revid);
- rw.reset();
- boolean isAnnotated = Strings.emptyToNull(input.message) != null;
- boolean isSigned = isAnnotated && input.message.contains("-----BEGIN PGP SIGNATURE-----\n");
- if (isSigned) {
- throw new MethodNotAllowedException("Cannot create signed tag \"" + ref + "\"");
- } else if (isAnnotated) {
- if (!check(perm, RefPermission.CREATE_TAG)) {
- throw new AuthException("Cannot create annotated tag \"" + ref + "\"");
+ } else {
+ perm.check(RefPermission.CREATE);
+ }
+ if (repo.getRefDatabase().exactRef(ref) != null) {
+ throw new ResourceConflictException("tag \"" + ref + "\" already exists");
}
- } else {
- perm.check(RefPermission.CREATE);
- }
- if (repo.getRefDatabase().exactRef(ref) != null) {
- throw new ResourceConflictException("tag \"" + ref + "\" already exists");
- }
+ try (Git git = new Git(repo)) {
+ TagCommand tag =
+ git.tag()
+ .setObjectId(object)
+ .setName(ref.substring(R_TAGS.length()))
+ .setAnnotated(isAnnotated)
+ .setSigned(isSigned);
- try (Git git = new Git(repo)) {
- TagCommand tag =
- git.tag()
- .setObjectId(object)
- .setName(ref.substring(R_TAGS.length()))
- .setAnnotated(isAnnotated)
- .setSigned(isSigned);
+ if (isAnnotated) {
+ tag.setMessage(input.message)
+ .setTagger(
+ resource
+ .getUser()
+ .asIdentifiedUser()
+ .newCommitterIdent(TimeUtil.now(), ZoneId.systemDefault()));
+ }
- if (isAnnotated) {
- tag.setMessage(input.message)
- .setTagger(
- resource
- .getUser()
- .asIdentifiedUser()
- .newCommitterIdent(TimeUtil.now(), ZoneId.systemDefault()));
+ Ref result = tag.call();
+ tagCache.updateFastForward(
+ resource.getNameKey(), ref, ObjectId.zeroId(), result.getObjectId());
+ referenceUpdated.fire(
+ resource.getNameKey(),
+ ref,
+ ObjectId.zeroId(),
+ result.getObjectId(),
+ resource.getUser().asIdentifiedUser().state());
+ try (RevWalk w = new RevWalk(repo)) {
+ return Response.created(
+ ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
+ }
}
-
- Ref result = tag.call();
- tagCache.updateFastForward(
- resource.getNameKey(), ref, ObjectId.zeroId(), result.getObjectId());
- referenceUpdated.fire(
- resource.getNameKey(),
- ref,
- ObjectId.zeroId(),
- result.getObjectId(),
- resource.getUser().asIdentifiedUser().state());
- try (RevWalk w = new RevWalk(repo)) {
- return Response.created(
- ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
- }
+ } catch (GitAPIException e) {
+ logger.atSevere().withCause(e).log("Cannot create tag \"%s\"", ref);
+ throw new IOException(e);
}
- } catch (GitAPIException e) {
- logger.atSevere().withCause(e).log("Cannot create tag \"%s\"", ref);
- throw new IOException(e);
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
index 6248a61..227a01b0 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.entities.RefNames.isConfigRef;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.gerrit.entities.RefNames;
@@ -27,6 +28,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.BranchResource;
import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -58,8 +60,9 @@
if (!queryProvider.get().setLimit(1).byBranchOpen(rsrc.getBranchKey()).isEmpty()) {
throw new ResourceConflictException("branch " + rsrc.getBranchKey() + " has open changes");
}
-
- deleteRef.deleteSingleRef(rsrc.getProjectState(), rsrc.getRef(), R_HEADS);
+ try (RefUpdateContext ctx = RefUpdateContext.open(BRANCH_MODIFICATION)) {
+ deleteRef.deleteSingleRef(rsrc.getProjectState(), rsrc.getRef(), R_HEADS);
+ }
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
index ca5962e..a1b5f81e 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.project;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.common.collect.ImmutableSet;
@@ -26,6 +27,7 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -52,9 +54,10 @@
// Never allow to delete the meta config branch.
throw new MethodNotAllowedException("not allowed to delete branch " + RefNames.REFS_CONFIG);
}
-
- deleteRef.deleteMultipleRefs(
- project.getProjectState(), ImmutableSet.copyOf(input.branches), R_HEADS);
+ try (RefUpdateContext ctx = RefUpdateContext.open(BRANCH_MODIFICATION)) {
+ deleteRef.deleteMultipleRefs(
+ project.getProjectState(), ImmutableSet.copyOf(input.branches), R_HEADS);
+ }
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
index 5a84f69..b7fe46e 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -16,6 +16,7 @@
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.gerrit.entities.RefNames.isConfigRef;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
import static java.lang.String.format;
import static org.eclipse.jgit.lib.Constants.R_REFS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -41,6 +42,7 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefValidationHelper;
import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -102,56 +104,58 @@
*/
public void deleteSingleRef(ProjectState projectState, String ref, @Nullable String prefix)
throws IOException, ResourceConflictException, AuthException, PermissionBackendException {
- if (prefix != null && !ref.startsWith(R_REFS)) {
- ref = prefix + ref;
- }
+ try (RefUpdateContext ctx = RefUpdateContext.open(BRANCH_MODIFICATION)) {
+ if (prefix != null && !ref.startsWith(R_REFS)) {
+ ref = prefix + ref;
+ }
- projectState.checkStatePermitsWrite();
- permissionBackend
- .currentUser()
- .project(projectState.getNameKey())
- .ref(ref)
- .check(RefPermission.DELETE);
+ projectState.checkStatePermitsWrite();
+ permissionBackend
+ .currentUser()
+ .project(projectState.getNameKey())
+ .ref(ref)
+ .check(RefPermission.DELETE);
- try (Repository repository = repoManager.openRepository(projectState.getNameKey())) {
- RefUpdate.Result result;
- RefUpdate u = repository.updateRef(ref);
- u.setExpectedOldObjectId(repository.exactRef(ref).getObjectId());
- u.setNewObjectId(ObjectId.zeroId());
- u.setForceUpdate(true);
- refDeletionValidator.validateRefOperation(
- projectState.getName(),
- identifiedUser.get(),
- u,
- /* pushOptions */ ImmutableListMultimap.of());
- result = u.delete();
+ try (Repository repository = repoManager.openRepository(projectState.getNameKey())) {
+ RefUpdate.Result result;
+ RefUpdate u = repository.updateRef(ref);
+ u.setExpectedOldObjectId(repository.exactRef(ref).getObjectId());
+ u.setNewObjectId(ObjectId.zeroId());
+ u.setForceUpdate(true);
+ refDeletionValidator.validateRefOperation(
+ projectState.getName(),
+ identifiedUser.get(),
+ u,
+ /* pushOptions */ ImmutableListMultimap.of());
+ result = u.delete();
- switch (result) {
- case NEW:
- case NO_CHANGE:
- case FAST_FORWARD:
- case FORCED:
- referenceUpdated.fire(
- projectState.getNameKey(),
- u,
- ReceiveCommand.Type.DELETE,
- identifiedUser.get().state());
- break;
+ switch (result) {
+ case NEW:
+ case NO_CHANGE:
+ case FAST_FORWARD:
+ case FORCED:
+ referenceUpdated.fire(
+ projectState.getNameKey(),
+ u,
+ ReceiveCommand.Type.DELETE,
+ identifiedUser.get().state());
+ break;
- case REJECTED_CURRENT_BRANCH:
- logger.atFine().log("Cannot delete current branch %s: %s", ref, result.name());
- throw new ResourceConflictException("cannot delete current branch");
+ case REJECTED_CURRENT_BRANCH:
+ logger.atFine().log("Cannot delete current branch %s: %s", ref, result.name());
+ throw new ResourceConflictException("cannot delete current branch");
- case LOCK_FAILURE:
- throw new LockFailureException(String.format("Cannot delete %s", ref), u);
- case IO_FAILURE:
- case NOT_ATTEMPTED:
- case REJECTED:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new StorageException(String.format("Cannot delete %s: %s", ref, result.name()));
+ case LOCK_FAILURE:
+ throw new LockFailureException(String.format("Cannot delete %s", ref), u);
+ case IO_FAILURE:
+ case NOT_ATTEMPTED:
+ case REJECTED:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new StorageException(String.format("Cannot delete %s: %s", ref, result.name()));
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTag.java b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
index 8d0a3d3..e22c90f 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTag.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.restapi.project;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.TAG_MODIFICATION;
+
import com.google.common.base.Preconditions;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
@@ -22,6 +24,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.RefUtil;
import com.google.gerrit.server.project.TagResource;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -44,7 +47,9 @@
Preconditions.checkState(tag.startsWith(Constants.R_TAGS));
- deleteRef.deleteSingleRef(resource.getProjectState(), tag);
+ try (RefUpdateContext ctx = RefUpdateContext.open(TAG_MODIFICATION)) {
+ deleteRef.deleteSingleRef(resource.getProjectState(), tag);
+ }
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTags.java b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
index 7ac3aff..a015d2b 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTags.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.project;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.TAG_MODIFICATION;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.common.collect.ImmutableSet;
@@ -24,6 +25,7 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -43,12 +45,14 @@
if (input == null || input.tags == null || input.tags.isEmpty()) {
throw new BadRequestException("tags must be specified");
}
-
- // If input.tags = ["refs/heads/bla"], this will actually delete the 'ref/heads/bla' branch,
- // rather than refs/tags/refs/heads/bla.
- // Since this is checked against DELETE permissions for refs/heads/bla, we'll let it go through.
- deleteRef.deleteMultipleRefs(
- project.getProjectState(), ImmutableSet.copyOf(input.tags), R_TAGS);
+ try (RefUpdateContext ctx = RefUpdateContext.open(TAG_MODIFICATION)) {
+ // If input.tags = ["refs/heads/bla"], this will actually delete the 'ref/heads/bla' branch,
+ // rather than refs/tags/refs/heads/bla.
+ // Since this is checked against DELETE permissions for refs/heads/bla, we'll let it go
+ // through.
+ deleteRef.deleteMultipleRefs(
+ project.getProjectState(), ImmutableSet.copyOf(input.tags), R_TAGS);
+ }
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 09f142b..dc83d4a 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -21,6 +21,7 @@
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.schema.AclUtil.grant;
import static com.google.gerrit.server.schema.AclUtil.rule;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.INIT_REPO;
import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.GlobalCapability;
@@ -39,6 +40,7 @@
import com.google.gerrit.server.notedb.RepoSequence;
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.project.ProjectConfig;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -84,18 +86,20 @@
}
public void create(AllProjectsInput input) throws IOException, ConfigInvalidException {
- try (Repository git = repositoryManager.openRepository(allProjectsName)) {
- initAllProjects(git, input);
- } catch (RepositoryNotFoundException notFound) {
- // A repository may be missing if this project existed only to store
- // inheritable permissions. For example 'All-Projects'.
- try (Repository git = repositoryManager.createRepository(allProjectsName)) {
+ try (RefUpdateContext updCtx = RefUpdateContext.open(INIT_REPO)) {
+ try (Repository git = repositoryManager.openRepository(allProjectsName)) {
initAllProjects(git, input);
- RefUpdate u = git.updateRef(Constants.HEAD);
- u.link(RefNames.REFS_CONFIG);
- } catch (RepositoryNotFoundException err) {
- String name = allProjectsName.get();
- throw new IOException("Cannot create repository " + name, err);
+ } catch (RepositoryNotFoundException notFound) {
+ // A repository may be missing if this project existed only to store
+ // inheritable permissions. For example 'All-Projects'.
+ try (Repository git = repositoryManager.createRepository(allProjectsName)) {
+ initAllProjects(git, input);
+ RefUpdate u = git.updateRef(Constants.HEAD);
+ u.link(RefNames.REFS_CONFIG);
+ } catch (RepositoryNotFoundException err) {
+ String name = allProjectsName.get();
+ throw new IOException("Cannot create repository " + name, err);
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/schema/AllUsersCreator.java b/java/com/google/gerrit/server/schema/AllUsersCreator.java
index f2fe7f6..63fbaf9 100644
--- a/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -18,6 +18,7 @@
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.schema.AclUtil.grant;
import static com.google.gerrit.server.schema.AllProjectsInput.getDefaultCodeReviewLabel;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.INIT_REPO;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
@@ -35,6 +36,7 @@
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.RefPattern;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -90,16 +92,18 @@
}
public void create() throws IOException, ConfigInvalidException {
- try (Repository git = mgr.openRepository(allUsersName)) {
- initAllUsers(git);
- } catch (RepositoryNotFoundException notFound) {
- try (Repository git = mgr.createRepository(allUsersName)) {
+ try (RefUpdateContext ctx = RefUpdateContext.open(INIT_REPO)) {
+ try (Repository git = mgr.openRepository(allUsersName)) {
initAllUsers(git);
- RefUpdate u = git.updateRef(Constants.HEAD);
- u.link(RefNames.REFS_CONFIG);
- } catch (RepositoryNotFoundException err) {
- String name = allUsersName.get();
- throw new IOException("Cannot create repository " + name, err);
+ } catch (RepositoryNotFoundException notFound) {
+ try (Repository git = mgr.createRepository(allUsersName)) {
+ initAllUsers(git);
+ RefUpdate u = git.updateRef(Constants.HEAD);
+ u.link(RefNames.REFS_CONFIG);
+ } catch (RepositoryNotFoundException err) {
+ String name = allUsersName.get();
+ throw new IOException("Cannot create repository " + name, err);
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
index 0e22af9..57ec7ef 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.schema;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.OFFLINE_OPERATION;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
@@ -26,6 +27,7 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.stream.IntStream;
@@ -87,15 +89,16 @@
// seeded refs/meta/version during AllProjectsCreator, so it won't hit this block.
checkNoteDbConfigFor216();
}
-
- for (int nextVersion : requiredUpgrades(currentVersion, schemaVersions.keySet())) {
- try {
- ui.message(String.format("Migrating data to schema %d ...", nextVersion));
- NoteDbSchemaVersions.get(schemaVersions, nextVersion).upgrade(args, ui);
- versionManager.increment(nextVersion - 1);
- } catch (Exception e) {
- throw new StorageException(
- String.format("Failed to upgrade to schema version %d", nextVersion), e);
+ try (RefUpdateContext ctx = RefUpdateContext.open(OFFLINE_OPERATION)) {
+ for (int nextVersion : requiredUpgrades(currentVersion, schemaVersions.keySet())) {
+ try {
+ ui.message(String.format("Migrating data to schema %d ...", nextVersion));
+ NoteDbSchemaVersions.get(schemaVersions, nextVersion).upgrade(args, ui);
+ versionManager.increment(nextVersion - 1);
+ } catch (Exception e) {
+ throw new StorageException(
+ String.format("Failed to upgrade to schema version %d", nextVersion), e);
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
index 26ae4a8..38e45ab 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -38,6 +38,8 @@
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.update.context.RefUpdateContext;
+import com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -91,31 +93,33 @@
@Override
public void create() throws IOException, ConfigInvalidException {
- GroupReference admins = createGroupReference("Administrators");
- GroupReference serviceUsers = createGroupReference(ServiceUserClassifier.SERVICE_USERS);
+ try (RefUpdateContext ctx = RefUpdateContext.open(RefUpdateType.INIT_REPO)) {
+ GroupReference admins = createGroupReference("Administrators");
+ GroupReference serviceUsers = createGroupReference(ServiceUserClassifier.SERVICE_USERS);
- AllProjectsInput allProjectsInput =
- AllProjectsInput.builder()
- .administratorsGroup(admins)
- .serviceUsersGroup(serviceUsers)
- .build();
- allProjectsCreator.create(allProjectsInput);
- // We have to create the All-Users repository before we can use it to store the groups in it.
- allUsersCreator.setAdministrators(admins).create();
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builder()
+ .administratorsGroup(admins)
+ .serviceUsersGroup(serviceUsers)
+ .build();
+ allProjectsCreator.create(allProjectsInput);
+ // We have to create the All-Users repository before we can use it to store the groups in it.
+ allUsersCreator.setAdministrators(admins).create();
- // Don't rely on injection to construct Sequences, as the default GitReferenceUpdated has a
- // thick dependency stack which may not all be available at schema creation time.
- Sequences seqs =
- new Sequences(
- config,
- repoManager,
- GitReferenceUpdated.DISABLED,
- allProjectsName,
- allUsersName,
- metricMaker);
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- createAdminsGroup(seqs, allUsersRepo, admins);
- createBatchUsersGroup(seqs, allUsersRepo, serviceUsers, admins.getUUID());
+ // Don't rely on injection to construct Sequences, as the default GitReferenceUpdated has a
+ // thick dependency stack which may not all be available at schema creation time.
+ Sequences seqs =
+ new Sequences(
+ config,
+ repoManager,
+ GitReferenceUpdated.DISABLED,
+ allProjectsName,
+ allUsersName,
+ metricMaker);
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ createAdminsGroup(seqs, allUsersRepo, admins);
+ createBatchUsersGroup(seqs, allUsersRepo, serviceUsers, admins.getUUID());
+ }
}
}
diff --git a/java/com/google/gerrit/server/schema/Schema_184.java b/java/com/google/gerrit/server/schema/Schema_184.java
index 436c57b..a7e9506 100644
--- a/java/com/google/gerrit/server/schema/Schema_184.java
+++ b/java/com/google/gerrit/server/schema/Schema_184.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.schema;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.OFFLINE_OPERATION;
+
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.GroupReference;
@@ -29,6 +31,7 @@
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.gerrit.server.index.group.GroupIndexCollection;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import java.io.IOException;
import java.util.Optional;
import org.eclipse.jgit.lib.BatchRefUpdate;
@@ -84,17 +87,19 @@
GroupConfig groupConfig,
GroupNameNotes groupNameNotes)
throws IOException {
- BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
- try (MetaDataUpdate metaDataUpdate =
- createMetaDataUpdate(allUsersName, serverUser, allUsersRepo, batchRefUpdate)) {
- groupConfig.commit(metaDataUpdate);
+ try (RefUpdateContext ctx = RefUpdateContext.open(OFFLINE_OPERATION)) {
+ BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
+ try (MetaDataUpdate metaDataUpdate =
+ createMetaDataUpdate(allUsersName, serverUser, allUsersRepo, batchRefUpdate)) {
+ groupConfig.commit(metaDataUpdate);
+ }
+ // MetaDataUpdates unfortunately can't be reused. -> Create a new one.
+ try (MetaDataUpdate metaDataUpdate =
+ createMetaDataUpdate(allUsersName, serverUser, allUsersRepo, batchRefUpdate)) {
+ groupNameNotes.commit(metaDataUpdate);
+ }
+ RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
}
- // MetaDataUpdates unfortunately can't be reused. -> Create a new one.
- try (MetaDataUpdate metaDataUpdate =
- createMetaDataUpdate(allUsersName, serverUser, allUsersRepo, batchRefUpdate)) {
- groupNameNotes.commit(metaDataUpdate);
- }
- RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
}
private MetaDataUpdate createMetaDataUpdate(
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 14a636f..1d3ec73 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.MERGE_CHANGE;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
@@ -84,6 +85,7 @@
import com.google.gerrit.server.update.SubmissionListener;
import com.google.gerrit.server.update.SuperprojectUpdateOnSubmission;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -605,95 +607,98 @@
private void integrateIntoHistory(
ChangeSet cs, SubmissionExecutor submissionExecutor, boolean checkSubmitRules)
throws RestApiException, UpdateException {
- checkArgument(!cs.furtherHiddenChanges(), "cannot integrate hidden changes into history");
- logger.atFine().log("Beginning merge attempt on %s", cs);
- Map<BranchNameKey, BranchBatch> toSubmit = new HashMap<>();
+ try (RefUpdateContext ctx = RefUpdateContext.open(MERGE_CHANGE)) {
+ checkArgument(!cs.furtherHiddenChanges(), "cannot integrate hidden changes into history");
+ logger.atFine().log("Beginning merge attempt on %s", cs);
+ Map<BranchNameKey, BranchBatch> toSubmit = new HashMap<>();
- ListMultimap<BranchNameKey, ChangeData> cbb;
- try {
- cbb = cs.changesByBranch();
- } catch (StorageException e) {
- throw new StorageException("Error reading changes to submit", e);
- }
- Set<BranchNameKey> branches = cbb.keySet();
-
- for (BranchNameKey branch : branches) {
- OpenRepo or = openRepo(branch.project());
- if (or != null) {
- toSubmit.put(branch, validateChangeList(or, cbb.get(branch)));
- }
- }
-
- // Done checks that don't involve running submit strategies.
- commitStatus.maybeFailVerbose();
-
- try {
- SubscriptionGraph subscriptionGraph = subscriptionGraphFactory.compute(branches, orm);
- SubmoduleCommits submoduleCommits = submoduleCommitsFactory.create(orm);
- UpdateOrderCalculator updateOrderCalculator = new UpdateOrderCalculator(subscriptionGraph);
- List<SubmitStrategy> strategies =
- getSubmitStrategies(
- toSubmit, updateOrderCalculator, submoduleCommits, subscriptionGraph, dryrun);
- this.projects = updateOrderCalculator.getProjectsInOrder();
- List<BatchUpdate> batchUpdates =
- orm.batchUpdates(
- projects, /* refLogMessage= */ checkSubmitRules ? "merged" : "forced-merge");
- // Group batch updates by project
- Map<Project.NameKey, BatchUpdate> batchUpdatesByProject =
- batchUpdates.stream().collect(Collectors.toMap(b -> b.getProject(), Function.identity()));
- for (Map.Entry<Change.Id, ChangeData> entry : cs.changesById().entrySet()) {
- Project.NameKey project = entry.getValue().project();
- Change.Id changeId = entry.getKey();
- ChangeData cd = entry.getValue();
- batchUpdatesByProject
- .get(project)
- .addOp(
- changeId,
- storeSubmitRequirementsOpFactory.create(
- cd.submitRequirementsIncludingLegacy().values(), cd));
- }
+ ListMultimap<BranchNameKey, ChangeData> cbb;
try {
- submissionExecutor.setAdditionalBatchUpdateListeners(
- ImmutableList.of(new SubmitStrategyListener(submitInput, strategies, commitStatus)));
- submissionExecutor.execute(batchUpdates);
- } finally {
- // If the BatchUpdate fails it can be that merging some of the changes was actually
- // successful. This is why we must to collect the updated changes also when an
- // exception was thrown.
- strategies.forEach(s -> updatedChanges.putAll(s.getUpdatedChanges()));
+ cbb = cs.changesByBranch();
+ } catch (StorageException e) {
+ throw new StorageException("Error reading changes to submit", e);
+ }
+ Set<BranchNameKey> branches = cbb.keySet();
- // Do not leave executed BatchUpdates in the OpenRepos
- if (!dryrun) {
- orm.resetUpdates(ImmutableSet.copyOf(this.projects));
+ for (BranchNameKey branch : branches) {
+ OpenRepo or = openRepo(branch.project());
+ if (or != null) {
+ toSubmit.put(branch, validateChangeList(or, cbb.get(branch)));
}
}
- } catch (NoSuchProjectException e) {
- throw new ResourceNotFoundException(e.getMessage());
- } catch (IOException e) {
- throw new StorageException(e);
- } catch (SubmoduleConflictException e) {
- throw new IntegrationConflictException(e.getMessage(), e);
- } catch (UpdateException e) {
- if (e.getCause() instanceof LockFailureException) {
- // Lock failures are a special case: RetryHelper depends on this specific causal chain in
- // order to trigger a retry. The downside of throwing here is we will not get the nicer
- // error message constructed below, in the case where this is the final attempt and the
- // operation is not retried further. This is not a huge downside, and is hopefully so rare
- // as to be unnoticeable, assuming RetryHelper is retrying sufficiently.
- throw e;
- }
- // BatchUpdate may have inadvertently wrapped an IntegrationConflictException
- // thrown by some legacy SubmitStrategyOp code that intended the error
- // message to be user-visible. Copy the message from the wrapped
- // exception.
- //
- // If you happen across one of these, the correct fix is to convert the
- // inner IntegrationConflictException to a ResourceConflictException.
- if (e.getCause() instanceof IntegrationConflictException) {
- throw (IntegrationConflictException) e.getCause();
+ // Done checks that don't involve running submit strategies.
+ commitStatus.maybeFailVerbose();
+
+ try {
+ SubscriptionGraph subscriptionGraph = subscriptionGraphFactory.compute(branches, orm);
+ SubmoduleCommits submoduleCommits = submoduleCommitsFactory.create(orm);
+ UpdateOrderCalculator updateOrderCalculator = new UpdateOrderCalculator(subscriptionGraph);
+ List<SubmitStrategy> strategies =
+ getSubmitStrategies(
+ toSubmit, updateOrderCalculator, submoduleCommits, subscriptionGraph, dryrun);
+ this.projects = updateOrderCalculator.getProjectsInOrder();
+ List<BatchUpdate> batchUpdates =
+ orm.batchUpdates(
+ projects, /* refLogMessage= */ checkSubmitRules ? "merged" : "forced-merge");
+ // Group batch updates by project
+ Map<Project.NameKey, BatchUpdate> batchUpdatesByProject =
+ batchUpdates.stream()
+ .collect(Collectors.toMap(b -> b.getProject(), Function.identity()));
+ for (Map.Entry<Change.Id, ChangeData> entry : cs.changesById().entrySet()) {
+ Project.NameKey project = entry.getValue().project();
+ Change.Id changeId = entry.getKey();
+ ChangeData cd = entry.getValue();
+ batchUpdatesByProject
+ .get(project)
+ .addOp(
+ changeId,
+ storeSubmitRequirementsOpFactory.create(
+ cd.submitRequirementsIncludingLegacy().values(), cd));
+ }
+ try {
+ submissionExecutor.setAdditionalBatchUpdateListeners(
+ ImmutableList.of(new SubmitStrategyListener(submitInput, strategies, commitStatus)));
+ submissionExecutor.execute(batchUpdates);
+ } finally {
+ // If the BatchUpdate fails it can be that merging some of the changes was actually
+ // successful. This is why we must to collect the updated changes also when an
+ // exception was thrown.
+ strategies.forEach(s -> updatedChanges.putAll(s.getUpdatedChanges()));
+
+ // Do not leave executed BatchUpdates in the OpenRepos
+ if (!dryrun) {
+ orm.resetUpdates(ImmutableSet.copyOf(this.projects));
+ }
+ }
+ } catch (NoSuchProjectException e) {
+ throw new ResourceNotFoundException(e.getMessage());
+ } catch (IOException e) {
+ throw new StorageException(e);
+ } catch (SubmoduleConflictException e) {
+ throw new IntegrationConflictException(e.getMessage(), e);
+ } catch (UpdateException e) {
+ if (e.getCause() instanceof LockFailureException) {
+ // Lock failures are a special case: RetryHelper depends on this specific causal chain in
+ // order to trigger a retry. The downside of throwing here is we will not get the nicer
+ // error message constructed below, in the case where this is the final attempt and the
+ // operation is not retried further. This is not a huge downside, and is hopefully so rare
+ // as to be unnoticeable, assuming RetryHelper is retrying sufficiently.
+ throw e;
+ }
+
+ // BatchUpdate may have inadvertently wrapped an IntegrationConflictException
+ // thrown by some legacy SubmitStrategyOp code that intended the error
+ // message to be user-visible. Copy the message from the wrapped
+ // exception.
+ //
+ // If you happen across one of these, the correct fix is to convert the
+ // inner IntegrationConflictException to a ResourceConflictException.
+ if (e.getCause() instanceof IntegrationConflictException) {
+ throw (IntegrationConflictException) e.getCause();
+ }
+ throw new MergeUpdateException(genericMergeError(cs), e);
}
- throw new MergeUpdateException(genericMergeError(cs), e);
}
}
diff --git a/java/com/google/gerrit/server/submit/SubmoduleOp.java b/java/com/google/gerrit/server/submit/SubmoduleOp.java
index ba736fa..cebb5e3 100644
--- a/java/com/google/gerrit/server/submit/SubmoduleOp.java
+++ b/java/com/google/gerrit/server/submit/SubmoduleOp.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.submit;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.UPDATE_SUPERPROJECT;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.BranchNameKey;
@@ -25,6 +27,7 @@
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -103,10 +106,12 @@
}
}
}
- BatchUpdate.execute(
- orm.batchUpdates(superProjects, /* refLogMessage= */ "merged"),
- ImmutableList.of(),
- dryrun);
+ try (RefUpdateContext ctx = RefUpdateContext.open(UPDATE_SUPERPROJECT)) {
+ BatchUpdate.execute(
+ orm.batchUpdates(superProjects, /* refLogMessage= */ "merged"),
+ ImmutableList.of(),
+ dryrun);
+ }
} catch (UpdateException | IOException | NoSuchProjectException e) {
throw new StorageException("Cannot update gitlinks", e);
}
diff --git a/java/com/google/gerrit/server/update/context/RefUpdateContext.java b/java/com/google/gerrit/server/update/context/RefUpdateContext.java
new file mode 100644
index 0000000..1144e4f
--- /dev/null
+++ b/java/com/google/gerrit/server/update/context/RefUpdateContext.java
@@ -0,0 +1,176 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.update.context;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * Passes additional information about an operation to the {@code BatchRefUpdate#execute} method.
+ *
+ * <p>To pass the additional information {@link RefUpdateContext}, wraps a code into an open
+ * RefUpdateContext, e.g.:
+ *
+ * <pre>{@code
+ * try(RefUpdateContext ctx = RefUpdateContext.open(RefUpdateType.CHANGE_MODIFICATION)) {
+ * ...
+ * // some code which modifies a ref using BatchRefUpdate.execute method
+ * }
+ * }</pre>
+ *
+ * When the {@code BatchRefUpdate#execute} method is executed, it can get all opened contexts and
+ * use it for an additional actions, e.g. it can put it in the reflog.
+ *
+ * <p>The information provided by this class is used internally in google.
+ *
+ * <p>The InMemoryRepositoryManager file makes some validation to ensure that RefUpdateContext is
+ * used correctly within the code (see thee validateRefUpdateContext method).
+ *
+ * <p>The class includes only operations from open-source gerrit and can be extended (see {@code
+ * TestActionRefUpdateContext} for example how to extend it).
+ */
+public class RefUpdateContext implements AutoCloseable {
+ private static final ThreadLocal<Deque<RefUpdateContext>> current = new ThreadLocal<>();
+
+ /**
+ * List of possible ref-update types.
+ *
+ * <p>Items in this enum are not fine-grained; different actions are shared the same type (e.g.
+ * {@link #CHANGE_MODIFICATION} includes posting comments, change edits and attention set update).
+ *
+ * <p>It is expected, that each type of operation should include only specific ref(s); check the
+ * validateRefUpdateContext in InMemoryRepositoryManager for relation between RefUpdateType and
+ * ref name.
+ */
+ public enum RefUpdateType {
+ /**
+ * Indicates that the context is implemented as a descendant of the {@link RefUpdateContext} .
+ *
+ * <p>The {@link #getUpdateType()} returns this type for all descendant of {@link
+ * RefUpdateContext}. This type is never returned if the context is exactly {@link
+ * RefUpdateContext}.
+ */
+ OTHER,
+ /**
+ * A ref is updated as a part of change-related operation.
+ *
+ * <p>This covers multiple different cases - creating and uploading changes and patchsets,
+ * comments operations, change edits, etc...
+ */
+ CHANGE_MODIFICATION,
+ /** A ref is updated during merge-change operation. */
+ MERGE_CHANGE,
+ /** A ref is updated as a part of a repo sequence operation. */
+ REPO_SEQ,
+ /** A ref is updated as a part of a repo initialization. */
+ INIT_REPO,
+ /** A ref is udpated as a part of gpg keys modification. */
+ GPG_KEYS_MODIFICATION,
+ /** A ref is updated as a part of group(s) update */
+ GROUPS_UPDATE,
+ /** A ref is updated as a part of account(s) update. */
+ ACCOUNTS_UPDATE,
+ /** A ref is updated as a part of direct push. */
+ DIRECT_PUSH,
+ /** A ref is updated as a part of explicit branch or ref update operation. */
+ BRANCH_MODIFICATION,
+ /** A ref is updated as a part of explicit tag update operation. */
+ TAG_MODIFICATION,
+ /**
+ * A tag is updated as a part of an offline operation.
+ *
+ * <p>Offline operation - an operation which is executed separately from the gerrit server and
+ * can't be triggered by any gerrit API. E.g. schema update.
+ */
+ OFFLINE_OPERATION,
+ /** A tag is updated as a part of an update-superproject flow. */
+ UPDATE_SUPERPROJECT,
+ /** A ref is updated as a part of explicit HEAD update operation. */
+ HEAD_MODIFICATION,
+ /** A ref is updated as a part of versioned meta data change. */
+ VERSIONED_META_DATA_CHANGE,
+ /** A ref is updated as a part of commit-ban operation. */
+ BAN_COMMIT,
+ /**
+ * A ref is updated inside a plugin.
+ *
+ * <p>If a plugin updates one of a special refs - it must also open a nested context.
+ */
+ PLUGIN,
+ }
+
+ /** Opens a provided context. */
+ protected static <T extends RefUpdateContext> T open(T ctx) {
+ getCurrent().addLast(ctx);
+ return ctx;
+ }
+
+ /** Opens a context of a give type. */
+ public static RefUpdateContext open(RefUpdateType updateType) {
+ checkArgument(updateType != RefUpdateType.OTHER, "The OTHER type is for internal use only.");
+ return open(new RefUpdateContext(updateType));
+ }
+
+ /** Returns the list of opened contexts; the first element is the outermost context. */
+ public static ImmutableList<RefUpdateContext> getOpenedContexts() {
+ return ImmutableList.copyOf(getCurrent());
+ }
+
+ /** Checks if there is an open context of the given type. */
+ public static boolean hasOpen(RefUpdateType type) {
+ return getCurrent().stream().anyMatch(ctx -> ctx.getUpdateType() == type);
+ }
+
+ private final RefUpdateType updateType;
+
+ private RefUpdateContext(RefUpdateType updateType) {
+ this.updateType = updateType;
+ }
+
+ protected RefUpdateContext() {
+ this(RefUpdateType.OTHER);
+ }
+
+ protected static final Deque<RefUpdateContext> getCurrent() {
+ Deque<RefUpdateContext> result = current.get();
+ if (result == null) {
+ result = new ArrayDeque<>();
+ current.set(result);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the type of {@link RefUpdateContext}.
+ *
+ * <p>For descendants, always return {@link RefUpdateType#OTHER}
+ */
+ public final RefUpdateType getUpdateType() {
+ return updateType;
+ }
+
+ /** Closes the current context. */
+ @Override
+ public void close() {
+ Deque<RefUpdateContext> openedContexts = getCurrent();
+ checkState(
+ openedContexts.peekLast() == this, "The current context is different from this context.");
+ openedContexts.removeLast();
+ }
+}
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index e5234fe..fb9e64e 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -5,7 +5,10 @@
testonly = True,
srcs = glob(
["**/*.java"],
- exclude = ["AssertableExecutorService.java"],
+ exclude = [
+ "AssertableExecutorService.java",
+ "TestActionRefUpdateContext.java",
+ ],
),
visibility = ["//visibility:public"],
exports = [
@@ -40,6 +43,7 @@
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:guava",
"//lib:h2",
"//lib:jgit",
@@ -66,3 +70,14 @@
"//lib/truth",
],
)
+
+java_library(
+ name = "test-ref-update-context",
+ testonly = True,
+ srcs = ["TestActionRefUpdateContext.java"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/server",
+ "//lib/errorprone:annotations",
+ ],
+)
diff --git a/java/com/google/gerrit/testing/InMemoryRepositoryManager.java b/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
index 2051ae3..8d1130c 100644
--- a/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
+++ b/java/com/google/gerrit/testing/InMemoryRepositoryManager.java
@@ -14,20 +14,56 @@
package com.google.gerrit.testing;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.ACCOUNTS_UPDATE;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BAN_COMMIT;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.BRANCH_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.DIRECT_PUSH;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.GPG_KEYS_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.GROUPS_UPDATE;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.HEAD_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.INIT_REPO;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.MERGE_CHANGE;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.OFFLINE_OPERATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.REPO_SEQ;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.TAG_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.VERSIONED_META_DATA_CHANGE;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.gpg.PublicKeyStore;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.RepositoryCaseMismatchException;
+import com.google.gerrit.server.update.context.RefUpdateContext;
+import com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType;
import com.google.inject.Inject;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.NavigableSet;
+import java.util.Optional;
+import java.util.function.Predicate;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase;
+import org.eclipse.jgit.internal.storage.dfs.DfsReftableBatchRefUpdate;
import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
/** Repository manager that uses in-memory repositories. */
public class InMemoryRepositoryManager implements GitRepositoryManager {
@@ -56,6 +92,137 @@
setPerformsAtomicTransactions(true);
}
+ /** Validates that a given ref is updated within the expected context. */
+ private static class RefUpdateContextValidator {
+ /**
+ * A configured singleton for ref context validation.
+ *
+ * <p>Each ref must match no more than 1 special ref from the list below. If ref is not
+ * matched to any special ref predicate, then it is checked against the standard rules - check
+ * the code of the {@link #validateRefUpdateContext} for details.
+ */
+ public static final RefUpdateContextValidator INSTANCE =
+ new RefUpdateContextValidator()
+ .addSpecialRef(RefNames::isSequenceRef, REPO_SEQ)
+ .addSpecialRef(RefNames.HEAD::equals, HEAD_MODIFICATION)
+ .addSpecialRef(RefNames::isRefsChanges, CHANGE_MODIFICATION, MERGE_CHANGE)
+ .addSpecialRef(RefNames::isAutoMergeRef, CHANGE_MODIFICATION)
+ .addSpecialRef(RefNames::isRefsEdit, CHANGE_MODIFICATION, MERGE_CHANGE)
+ .addSpecialRef(RefNames::isTagRef, TAG_MODIFICATION)
+ .addSpecialRef(RefNames::isRejectCommitsRef, BAN_COMMIT)
+ .addSpecialRef(
+ name -> RefNames.isRefsUsers(name) && !RefNames.isRefsEdit(name),
+ VERSIONED_META_DATA_CHANGE,
+ ACCOUNTS_UPDATE,
+ MERGE_CHANGE)
+ .addSpecialRef(
+ RefNames::isConfigRef,
+ VERSIONED_META_DATA_CHANGE,
+ BRANCH_MODIFICATION,
+ MERGE_CHANGE)
+ .addSpecialRef(RefNames::isExternalIdRef, VERSIONED_META_DATA_CHANGE, ACCOUNTS_UPDATE)
+ .addSpecialRef(PublicKeyStore.REFS_GPG_KEYS::equals, GPG_KEYS_MODIFICATION)
+ .addSpecialRef(RefNames::isRefsDraftsComments, CHANGE_MODIFICATION)
+ .addSpecialRef(RefNames::isRefsStarredChanges, CHANGE_MODIFICATION)
+ // A user can create a change for updating a group and then merge it.
+ // The GroupsIT#pushToGroupBranchForReviewForNonAllUsersRepoAndSubmit test verifies
+ // this scenario.
+ .addSpecialRef(RefNames::isGroupRef, GROUPS_UPDATE, MERGE_CHANGE);
+
+ private List<Entry<Predicate<String>, ImmutableList<RefUpdateType>>> specialRefs =
+ new ArrayList<>();
+
+ private RefUpdateContextValidator() {}
+
+ public void validateRefUpdateContext(ReceiveCommand cmd) {
+ if (TestActionRefUpdateContext.isOpen()
+ || RefUpdateContext.hasOpen(OFFLINE_OPERATION)
+ || RefUpdateContext.hasOpen(INIT_REPO)
+ || RefUpdateContext.hasOpen(DIRECT_PUSH)) {
+ // The action can touch any refs in these contexts.
+ return;
+ }
+
+ String refName = cmd.getRefName();
+
+ Optional<ImmutableList<RefUpdateType>> allowedRefUpdateTypes =
+ RefUpdateContextValidator.INSTANCE.getAllowedRefUpdateTypes(refName);
+
+ if (allowedRefUpdateTypes.isPresent()) {
+ checkState(
+ allowedRefUpdateTypes.get().stream().anyMatch(RefUpdateContext::hasOpen)
+ || isTestRepoCall(),
+ "Special ref '%s' is updated outside of the expected operation. Wrap code in the correct RefUpdateContext or fix allowed update types",
+ refName);
+ return;
+ }
+ // It is not one of the special ref - update is possible only within specific contexts.
+ checkState(
+ RefUpdateContext.hasOpen(MERGE_CHANGE)
+ || RefUpdateContext.hasOpen(RefUpdateType.BRANCH_MODIFICATION)
+ || RefUpdateContext.hasOpen(RefUpdateType.UPDATE_SUPERPROJECT)
+ // Plugin can update any ref
+ || RefUpdateContext.hasOpen(PLUGIN)
+ || isTestRepoCall(),
+ "Ordinary ref '%s' is updated outside of the expected operation. Wrap code in the correct RefUpdateContext or add the ref as a special ref.",
+ refName);
+ }
+
+ private RefUpdateContextValidator addSpecialRef(
+ Predicate<String> refNamePredicate, RefUpdateType... validRefUpdateTypes) {
+ specialRefs.add(
+ new SimpleImmutableEntry<>(
+ refNamePredicate, ImmutableList.copyOf(validRefUpdateTypes)));
+ return this;
+ }
+
+ private Optional<ImmutableList<RefUpdateType>> getAllowedRefUpdateTypes(String refName) {
+ List<ImmutableList<RefUpdateType>> allowedTypes =
+ specialRefs.stream()
+ .filter(entry -> entry.getKey().test(refName))
+ .map(Entry::getValue)
+ .collect(toList());
+ checkState(
+ allowedTypes.size() <= 1,
+ "refName matches more than 1 predicate. Please fix the specialRefs list, so each reference has no more than one match.");
+ if (allowedTypes.size() == 0) {
+ return Optional.empty();
+ }
+ return Optional.of(allowedTypes.get(0));
+ }
+
+ /**
+ * Returns true if a ref is updated using one of the method in {@link
+ * org.eclipse.jgit.junit.TestRepository}.
+ *
+ * <p>The {@link org.eclipse.jgit.junit.TestRepository} used only in tests and allows to
+ * change refs directly. Wrapping each usage in a test context requires a lot of modification,
+ * so instead we allow any ref updates, which are made using through this class.
+ */
+ private boolean isTestRepoCall() {
+ return Arrays.stream(Thread.currentThread().getStackTrace())
+ .anyMatch(elem -> elem.getClassName().equals("org.eclipse.jgit.junit.TestRepository"));
+ }
+ }
+
+ @Override
+ protected MemRefDatabase createRefDatabase() {
+ return new MemRefDatabase() {
+ @Override
+ public BatchRefUpdate newBatchUpdate() {
+ DfsObjDatabase odb = getRepository().getObjectDatabase();
+ return new DfsReftableBatchRefUpdate(this, odb) {
+ @Override
+ public void execute(RevWalk rw, ProgressMonitor pm, List<String> options) {
+ getCommands().stream()
+ .forEach(RefUpdateContextValidator.INSTANCE::validateRefUpdateContext);
+ super.execute(rw, pm, options);
+ }
+ };
+ }
+ };
+ }
+
@Override
public Description getDescription() {
return (Description) super.getDescription();
diff --git a/java/com/google/gerrit/testing/TestActionRefUpdateContext.java b/java/com/google/gerrit/testing/TestActionRefUpdateContext.java
new file mode 100644
index 0000000..23ec9aa
--- /dev/null
+++ b/java/com/google/gerrit/testing/TestActionRefUpdateContext.java
@@ -0,0 +1,73 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.testing;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.gerrit.server.update.context.RefUpdateContext;
+
+/**
+ * Marks ref updates as a test actions.
+ *
+ * <p>This class should be used in tests only to wrap a portion of test code which directly modifies
+ * references. Usage:
+ *
+ * <pre>{@code
+ * import static ...TestActionRefUpdateContext.openTestRefUpdateContext();
+ *
+ * try(RefUpdateContext ctx=openTestRefUpdateContext()) {
+ * // Some test code, which modifies a reference.
+ * }
+ * }</pre>
+ *
+ * or
+ *
+ * <pre>{@code
+ * import static ...TestActionRefUpdateContext.testRefAction;
+ *
+ * testRefAction(() -> {doSomethingWithRef()});
+ * T result = testRefAction(() -> { return doSomethingWithRef()});
+ * }</pre>
+ */
+public final class TestActionRefUpdateContext extends RefUpdateContext {
+ public static boolean isOpen() {
+ return getCurrent().stream().anyMatch(ctx -> ctx instanceof TestActionRefUpdateContext);
+ }
+
+ public static TestActionRefUpdateContext openTestRefUpdateContext() {
+ return open(new TestActionRefUpdateContext());
+ }
+
+ @CanIgnoreReturnValue
+ public static <V, E extends Exception> V testRefAction(CallableWithException<V, E> c) throws E {
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ return c.call();
+ }
+ }
+
+ public static <E extends Exception> void testRefAction(RunnableWithException<E> c) throws E {
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ c.run();
+ }
+ }
+
+ public interface CallableWithException<V, E extends Exception> {
+ V call() throws E;
+ }
+
+ @FunctionalInterface
+ public interface RunnableWithException<E extends Exception> {
+ void run() throws E;
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java b/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java
index b6e5b74..33e6692 100644
--- a/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java
+++ b/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
@@ -32,6 +33,7 @@
import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.TestTimeUtil;
@@ -286,14 +288,16 @@
}
private Ref createRef(Repository repo, String ref) throws IOException {
- try (ObjectInserter oi = repo.newObjectInserter();
- RevWalk rw = new RevWalk(repo)) {
- ObjectId emptyCommit = createCommit(repo);
- RefUpdate updateRef = repo.updateRef(ref);
- updateRef.setExpectedOldObjectId(ObjectId.zeroId());
- updateRef.setNewObjectId(emptyCommit);
- assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.NEW);
- return repo.exactRef(ref);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (ObjectInserter oi = repo.newObjectInserter();
+ RevWalk rw = new RevWalk(repo)) {
+ ObjectId emptyCommit = createCommit(repo);
+ RefUpdate updateRef = repo.updateRef(ref);
+ updateRef.setExpectedOldObjectId(ObjectId.zeroId());
+ updateRef.setNewObjectId(emptyCommit);
+ assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.NEW);
+ return repo.exactRef(ref);
+ }
}
}
@@ -302,17 +306,19 @@
}
private Ref updateRef(Repository repo, Ref ref) throws IOException {
- try (ObjectInserter oi = repo.newObjectInserter();
- RevWalk rw = new RevWalk(repo)) {
- ObjectId emptyCommit = createCommit(repo);
- RefUpdate updateRef = repo.updateRef(ref.getName());
- updateRef.setExpectedOldObjectId(ref.getObjectId());
- updateRef.setNewObjectId(emptyCommit);
- updateRef.setForceUpdate(true);
- assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.FORCED);
- Ref updatedRef = repo.exactRef(ref.getName());
- assertThat(updatedRef.getObjectId()).isNotEqualTo(ref.getObjectId());
- return updatedRef;
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (ObjectInserter oi = repo.newObjectInserter();
+ RevWalk rw = new RevWalk(repo)) {
+ ObjectId emptyCommit = createCommit(repo);
+ RefUpdate updateRef = repo.updateRef(ref.getName());
+ updateRef.setExpectedOldObjectId(ref.getObjectId());
+ updateRef.setNewObjectId(emptyCommit);
+ updateRef.setForceUpdate(true);
+ assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.FORCED);
+ Ref updatedRef = repo.exactRef(ref.getName());
+ assertThat(updatedRef.getObjectId()).isNotEqualTo(ref.getObjectId());
+ return updatedRef;
+ }
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 111f8c9..2c1739d 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -42,6 +42,7 @@
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.project.ProjectCache.illegalState;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.gerrit.truth.ConfigSubject.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
@@ -264,7 +265,7 @@
if (ref != null) {
RefUpdate ru = repo.updateRef(REFS_GPG_KEYS);
ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
}
}
@@ -1888,7 +1889,7 @@
// Mark first key as invalid
assertThat(info.get(0).valid).isTrue();
- authorizedKeys.markKeyInvalid(admin.id(), 1);
+ testRefAction(() -> authorizedKeys.markKeyInvalid(admin.id(), 1));
info = gApi.accounts().self().listSshKeys();
assertThat(info).hasSize(2);
assertThat(info.get(0).seq).isEqualTo(1);
@@ -2434,79 +2435,88 @@
// Manually updating the user ref makes the index document stale.
String userRef = RefNames.refsUsers(accountId);
- try (Repository repo = repoManager.openRepository(allUsers);
- ObjectInserter oi = repo.newObjectInserter();
- RevWalk rw = new RevWalk(repo)) {
- RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
+ testRefAction(
+ () -> {
+ try (Repository repo = repoManager.openRepository(allUsers);
+ ObjectInserter oi = repo.newObjectInserter();
+ RevWalk rw = new RevWalk(repo)) {
+ RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
- PersonIdent ident = new PersonIdent(serverIdent.get(), TimeUtil.now());
- CommitBuilder cb = new CommitBuilder();
- cb.setTreeId(commit.getTree());
- cb.setCommitter(ident);
- cb.setAuthor(ident);
- cb.setMessage(commit.getFullMessage());
- ObjectId emptyCommit = oi.insert(cb);
- oi.flush();
+ PersonIdent ident = new PersonIdent(serverIdent.get(), TimeUtil.now());
+ CommitBuilder cb = new CommitBuilder();
+ cb.setTreeId(commit.getTree());
+ cb.setCommitter(ident);
+ cb.setAuthor(ident);
+ cb.setMessage(commit.getFullMessage());
+ ObjectId emptyCommit = oi.insert(cb);
+ oi.flush();
- RefUpdate updateRef = repo.updateRef(userRef);
- updateRef.setExpectedOldObjectId(commit.toObjectId());
- updateRef.setNewObjectId(emptyCommit);
- assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
- }
+ RefUpdate updateRef = repo.updateRef(userRef);
+ updateRef.setExpectedOldObjectId(commit.toObjectId());
+ updateRef.setNewObjectId(emptyCommit);
+ assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
+ }
+ });
assertStaleAccountAndReindex(accountId);
// Manually inserting/updating/deleting an external ID of the user makes the index document
// stale.
try (Repository repo = repoManager.openRepository(allUsers)) {
- ExternalIdNotes extIdNotes =
- ExternalIdNotes.load(
- allUsers,
- repo,
- externalIdFactory,
- authConfig.isUserNameCaseInsensitiveMigrationMode());
+ testRefAction(
+ () -> {
+ ExternalIdNotes extIdNotes =
+ ExternalIdNotes.load(
+ allUsers,
+ repo,
+ externalIdFactory,
+ authConfig.isUserNameCaseInsensitiveMigrationMode());
- ExternalId.Key key = externalIdKeyFactory.create("foo", "foo");
- extIdNotes.insert(externalIdFactory.create(key, accountId));
- try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
- extIdNotes.commit(update);
- }
- assertStaleAccountAndReindex(accountId);
+ ExternalId.Key key = externalIdKeyFactory.create("foo", "foo");
+ extIdNotes.insert(externalIdFactory.create(key, accountId));
+ try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
+ extIdNotes.commit(update);
+ }
+ assertStaleAccountAndReindex(accountId);
- extIdNotes =
- ExternalIdNotes.load(
- allUsers,
- repo,
- externalIdFactory,
- authConfig.isUserNameCaseInsensitiveMigrationMode());
- extIdNotes.upsert(externalIdFactory.createWithEmail(key, accountId, "foo@example.com"));
- try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
- extIdNotes.commit(update);
- }
- assertStaleAccountAndReindex(accountId);
+ extIdNotes =
+ ExternalIdNotes.load(
+ allUsers,
+ repo,
+ externalIdFactory,
+ authConfig.isUserNameCaseInsensitiveMigrationMode());
+ extIdNotes.upsert(externalIdFactory.createWithEmail(key, accountId, "foo@example.com"));
+ try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
+ extIdNotes.commit(update);
+ }
+ assertStaleAccountAndReindex(accountId);
- extIdNotes =
- ExternalIdNotes.load(
- allUsers,
- repo,
- externalIdFactory,
- authConfig.isUserNameCaseInsensitiveMigrationMode());
- extIdNotes.delete(accountId, key);
- try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
- extIdNotes.commit(update);
- }
+ extIdNotes =
+ ExternalIdNotes.load(
+ allUsers,
+ repo,
+ externalIdFactory,
+ authConfig.isUserNameCaseInsensitiveMigrationMode());
+ extIdNotes.delete(accountId, key);
+ try (MetaDataUpdate update = metaDataUpdateFactory.create(allUsers)) {
+ extIdNotes.commit(update);
+ }
+ });
assertStaleAccountAndReindex(accountId);
}
// Manually delete account
- try (Repository repo = repoManager.openRepository(allUsers);
- RevWalk rw = new RevWalk(repo)) {
- RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
- RefUpdate updateRef = repo.updateRef(userRef);
- updateRef.setExpectedOldObjectId(commit.toObjectId());
- updateRef.setNewObjectId(ObjectId.zeroId());
- updateRef.setForceUpdate(true);
- assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED);
- }
+ testRefAction(
+ () -> {
+ try (Repository repo = repoManager.openRepository(allUsers);
+ RevWalk rw = new RevWalk(repo)) {
+ RevCommit commit = rw.parseCommit(repo.exactRef(userRef).getObjectId());
+ RefUpdate updateRef = repo.updateRef(userRef);
+ updateRef.setExpectedOldObjectId(commit.toObjectId());
+ updateRef.setNewObjectId(ObjectId.zeroId());
+ updateRef.setForceUpdate(true);
+ assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ }
+ });
assertStaleAccountAndReindex(accountId);
}
@@ -3385,16 +3395,19 @@
}
private Map<String, GpgKeyInfo> addGpgKey(TestAccount account, String armored) throws Exception {
- AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
- try (Registration registration =
- extensionRegistry.newRegistration().add(accountIndexedCounter)) {
- Map<String, GpgKeyInfo> gpgKeys =
- gApi.accounts()
- .id(account.username())
- .putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of());
- accountIndexedCounter.assertReindexOf(gApi.accounts().id(account.username()).get());
- return gpgKeys;
- }
+ return testRefAction(
+ () -> {
+ AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(accountIndexedCounter)) {
+ Map<String, GpgKeyInfo> gpgKeys =
+ gApi.accounts()
+ .id(account.username())
+ .putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of());
+ accountIndexedCounter.assertReindexOf(gApi.accounts().id(account.username()).get());
+ return gpgKeys;
+ }
+ });
}
private Map<String, GpgKeyInfo> addGpgKeyNoReindex(String armored) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
index d1258fc..1693411 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
@@ -16,6 +16,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.api.GerritApi;
@@ -32,6 +33,7 @@
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.gerrit.server.query.account.InternalAccountQuery;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -141,16 +143,19 @@
private void updateAccountWithoutCacheOrIndex(Account.Id accountId, AccountDelta accountDelta)
throws IOException, ConfigInvalidException {
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName);
- MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo)) {
- PersonIdent ident = serverIdent.get();
- md.getCommitBuilder().setAuthor(ident);
- md.getCommitBuilder().setCommitter(ident);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName);
+ MetaDataUpdate md =
+ new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo)) {
+ PersonIdent ident = serverIdent.get();
+ md.getCommitBuilder().setAuthor(ident);
+ md.getCommitBuilder().setCommitter(ident);
- AccountConfig accountConfig = new AccountConfig(accountId, allUsersName, allUsersRepo).load();
- accountConfig.setAccountDelta(accountDelta);
- accountConfig.commit(md);
+ AccountConfig accountConfig =
+ new AccountConfig(accountId, allUsersName, allUsersRepo).load();
+ accountConfig.setAccountDelta(accountDelta);
+ accountConfig.commit(md);
+ }
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index 7e23f0e..875b520 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableSet;
@@ -45,6 +46,7 @@
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.ssh.SshKeyCache;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.util.Providers;
import java.util.Optional;
@@ -285,11 +287,13 @@
// Create orphaned SCHEME_GERRIT external ID.
Account.Id accountId = Account.id(seq.nextAccountId());
ExternalId gerritExtId = externalIdFactory.create(gerritExtIdKey, accountId);
- try (Repository allUsersRepo = repoManager.openRepository(allUsers);
- MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
- ExternalIdNotes extIdNotes = extIdNotesFactory.load(allUsersRepo);
- extIdNotes.insert(gerritExtId);
- extIdNotes.commit(md);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = extIdNotesFactory.load(allUsersRepo);
+ extIdNotes.insert(gerritExtId);
+ extIdNotes.commit(md);
+ }
}
AuthRequest who = authRequestFactory.createForUser(username);
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
index 3c605e1..c441402 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
@@ -13,6 +13,7 @@
"//java/com/google/gerrit/git",
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java b/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java
index 898e1ff..0b55563 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ApplyPatchIT.java
@@ -213,6 +213,29 @@
}
@Test
+ public void applyGerritBasedPatchUsingRestWithEncodedPatch_success() throws Exception {
+ String head = getHead(repo(), HEAD).name();
+ createBranchWithRevision(BranchNameKey.create(project, "branch"), head);
+ PushOneCommit.Result baseCommit = createChange("Add file", ADDED_FILE_NAME, ADDED_FILE_CONTENT);
+ baseCommit.assertOkStatus();
+ createBranchWithRevision(BranchNameKey.create(project, DESTINATION_BRANCH), head);
+ RestResponse patchResp =
+ userRestSession.get("/changes/" + baseCommit.getChangeId() + "/revisions/current/patch");
+ patchResp.assertOK();
+ String originalEncodedPatch = patchResp.getEntityContent();
+ String originalDecodedPatch = new String(Base64.decode(patchResp.getEntityContent()), UTF_8);
+ ApplyPatchPatchSetInput in = buildInput(originalEncodedPatch);
+ PushOneCommit.Result destChange = createChange();
+
+ RestResponse resp =
+ adminRestSession.post("/changes/" + destChange.getChangeId() + "/patch:apply", in);
+
+ resp.assertOK();
+ BinaryResult resultPatch = gApi.changes().id(destChange.getChangeId()).current().patch();
+ assertThat(removeHeader(resultPatch)).isEqualTo(removeHeader(originalDecodedPatch));
+ }
+
+ @Test
public void applyPatchWithConflict_fails() throws Exception {
initBaseWithFile(MODIFIED_FILE_NAME, "Unexpected base content");
ApplyPatchPatchSetInput in = buildInput(MODIFIED_FILE_DIFF);
@@ -404,6 +427,6 @@
}
private String removeHeader(String s) {
- return s.substring(s.indexOf("\ndiff --git"), s.length() - 1);
+ return s.substring(s.lastIndexOf("\ndiff --git"), s.length() - 1);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 215d1e8..e3d69e1 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -56,6 +56,7 @@
import static com.google.gerrit.server.project.testing.TestLabels.label;
import static com.google.gerrit.server.project.testing.TestLabels.value;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import static com.google.gerrit.truth.CacheStatsSubject.assertThat;
import static com.google.gerrit.truth.CacheStatsSubject.cloneStats;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -181,6 +182,7 @@
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AccountTemplateUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.FakeEmailSender.Message;
@@ -2908,9 +2910,11 @@
@Test
public void submitToSymref() throws Exception {
// Create symref in the origin repository (testRepo references to a local repository)
- try (Repository repo = repoManager.openRepository(project)) {
- RefUpdate u = repo.updateRef("refs/heads/master_symref");
- assertThat(u.link("refs/heads/master")).isEqualTo(Result.NEW);
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (Repository repo = repoManager.openRepository(project)) {
+ RefUpdate u = repo.updateRef("refs/heads/master_symref");
+ assertThat(u.link("refs/heads/master")).isEqualTo(Result.NEW);
+ }
}
PushOneCommit.Result r = createChange("refs/for/master_symref");
@@ -4231,10 +4235,12 @@
}
private void setChangeStatus(Change.Id id, Change.Status newStatus) throws Exception {
- try (BatchUpdate batchUpdate =
- batchUpdateFactory.create(project, atrScope.get().getUser(), TimeUtil.now())) {
- batchUpdate.addOp(id, new ChangeStatusUpdateOp(newStatus));
- batchUpdate.execute();
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (BatchUpdate batchUpdate =
+ batchUpdateFactory.create(project, atrScope.get().getUser(), TimeUtil.now())) {
+ batchUpdate.addOp(id, new ChangeStatusUpdateOp(newStatus));
+ batchUpdate.execute();
+ }
}
ChangeStatus changeStatus = gApi.changes().id(id.get()).get().status;
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
index 267f5a7..519c1dc 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
@@ -18,6 +18,7 @@
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -252,20 +253,22 @@
try (BatchUpdate u =
batchUpdateFactory.create(
project, identifiedUserFactory.create(admin.id()), TimeUtil.now())) {
- u.addOp(
- changeId,
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) {
- ctx.getChange().setPrivate(true);
- ChangeUpdate update = ctx.getUpdate(ctx.getChange().currentPatchSetId());
- ctx.getChange().setPrivate(true);
- ctx.getChange().setLastUpdatedOn(ctx.getWhen());
- update.setPrivate(true);
- return true;
- }
- })
- .execute();
+ testRefAction(
+ () ->
+ u.addOp(
+ changeId,
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getChange().setPrivate(true);
+ ChangeUpdate update = ctx.getUpdate(ctx.getChange().currentPatchSetId());
+ ctx.getChange().setPrivate(true);
+ ctx.getChange().setLastUpdatedOn(ctx.getWhen());
+ update.setPrivate(true);
+ return true;
+ }
+ })
+ .execute());
}
assertThat(gApi.changes().id(changeId.get()).get().isPrivate).isTrue();
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java
index 56e23a4..2fe7038 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitRequirementPredicateIT.java
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.server.project.testing.TestLabels.codeReview;
import static com.google.gerrit.server.project.testing.TestLabels.label;
import static com.google.gerrit.server.project.testing.TestLabels.value;
import static org.eclipse.jgit.lib.Constants.HEAD;
@@ -26,6 +28,8 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.UseTimezone;
import com.google.gerrit.acceptance.VerifyNoPiiInChangeNotes;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
@@ -34,15 +38,21 @@
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.LabelType;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.SubmitRequirementExpression;
import com.google.gerrit.entities.SubmitRequirementExpressionResult;
import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.server.project.SubmitRequirementsEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -294,6 +304,162 @@
"unexpected base value format");
}
+ @Test
+ public void nonContributorLabelVote_match() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ TestRepository<InMemoryRepository> clonedRepo = cloneProject(project, user);
+ PushOneCommit.Result r1 =
+ pushFactory
+ .create(user.newIdent(), clonedRepo, "Subject", "file.txt", "text")
+ .to("refs/for/master");
+
+ Change.Id cId = r1.getChange().getId();
+
+ ChangeInfo changeInfo = gApi.changes().id(r1.getChangeId()).get();
+
+ // Assert on uploader, committer and author
+ assertUploader(changeInfo, user.email());
+ assertCommitter(changeInfo, user.email());
+ assertAuthor(changeInfo, user.email());
+
+ // Vote from admin (a.k.a. non uploader/committer/author) matches
+ requestScopeOperations.setApiUser(admin.id());
+ approve(cId.toString());
+ assertMatching("label:Code-Review=+2,user=non_contributor", cId);
+ // Also make sure magic label votes and > operator work
+ assertMatching("label:Code-Review=MAX,user=non_contributor", cId);
+ assertMatching("label:Code-Review>+1,user=non_contributor", cId);
+ }
+
+ @Test
+ public void nonContributorLabelVote_voteFromUploader_doesNotMatch() throws Exception {
+ PushOneCommit.Result r1 = createNormalCommit(user.newIdent(), "refs/for/master", "file1");
+
+ ChangeInfo changeInfo = gApi.changes().id(r1.getChangeId()).get();
+ assertUploader(changeInfo, admin.email());
+
+ // Vote from admin (a.k.a. uploader) does not match
+ requestScopeOperations.setApiUser(admin.id());
+ approve(r1.getChangeId());
+ assertNotMatching("label:Code-Review=+2,user=non_contributor", r1.getChange().getId());
+ }
+
+ @Test
+ @Sandboxed
+ public void nonContributorLabelVote_voteFromAuthor_doesNotMatch() throws Exception {
+ Account.Id authorId =
+ accountOperations
+ .newAccount()
+ .fullname("author")
+ .preferredEmail("authoremail@example.com")
+ .create();
+ Account.Id committerId =
+ accountOperations
+ .newAccount()
+ .fullname("committer")
+ .preferredEmail("committeremail@example.com")
+ .create();
+
+ Change.Id changeId =
+ changeOperations.newChange().author(authorId).committer(committerId).create();
+ ChangeInfo changeInfo = gApi.changes().id(changeId.get()).get();
+ assertAuthor(changeInfo, "authoremail@example.com");
+
+ allowLabelPermission(
+ codeReview().getName(), RefNames.REFS_HEADS + "*", REGISTERED_USERS, -2, +2);
+
+ // Vote from author does not match
+ requestScopeOperations.setApiUser(authorId);
+ approve(changeId.toString());
+ assertNotMatching("label:Code-Review=+2,user=non_contributor", changeId);
+ }
+
+ @Test
+ public void nonContributorLabelVote_voteFromCommitter_doesNotMatch() throws Exception {
+ Account.Id authorId =
+ accountOperations
+ .newAccount()
+ .fullname("author")
+ .preferredEmail("authoremail@example.com")
+ .create();
+ Account.Id committerId =
+ accountOperations
+ .newAccount()
+ .fullname("committer")
+ .preferredEmail("committeremail@example.com")
+ .create();
+
+ Change.Id changeId =
+ changeOperations.newChange().author(authorId).committer(committerId).create();
+ ChangeInfo changeInfo = gApi.changes().id(changeId.get()).get();
+ assertCommitter(changeInfo, "committeremail@example.com");
+
+ allowLabelPermission(
+ codeReview().getName(), RefNames.REFS_HEADS + "*", REGISTERED_USERS, -2, +2);
+
+ // Vote from committer does not match
+ requestScopeOperations.setApiUser(committerId);
+ approve(changeId.toString());
+ assertNotMatching("label:Code-Review=+2,user=non_contributor", changeId);
+ }
+
+ @Test
+ public void nonContributorLabelVote_uploaderAndAuthorDifferent() throws Exception {
+ TestRepository<InMemoryRepository> clonedRepo = cloneProject(project, admin);
+ PushOneCommit.Result r1 =
+ pushFactory
+ .create(user.newIdent(), clonedRepo, "Subject", "file.txt", "text")
+ .to("refs/for/master");
+
+ requestScopeOperations.setApiUser(admin.id());
+ ChangeInfo changeInfo = gApi.changes().id(r1.getChangeId()).get();
+ assertUploader(changeInfo, admin.email());
+ assertAuthor(changeInfo, user.email());
+
+ allowLabelPermission(
+ codeReview().getName(), RefNames.REFS_HEADS + "*", REGISTERED_USERS, -2, +2);
+
+ // Vote from admin (a.k.a. uploader) does not match
+ requestScopeOperations.setApiUser(user.id());
+ approve(r1.getChangeId());
+ assertNotMatching("label:Code-Review=+2,user=non_contributor", r1.getChange().getId());
+
+ // Vote from user (a.k.a. author) does not match
+ requestScopeOperations.setApiUser(admin.id());
+ approve(r1.getChangeId());
+ assertNotMatching("label:Code-Review=+2,user=non_contributor", r1.getChange().getId());
+
+ // Vote from user2 (a.k.a. non-author and non-uploader) matches
+ TestAccount user2 = accountCreator.create();
+ requestScopeOperations.setApiUser(user2.id());
+ approve(r1.getChangeId());
+ assertMatching("label:Code-Review=+2,user=non_contributor", r1.getChange().getId());
+ }
+
+ private static void assertUploader(ChangeInfo changeInfo, String email) {
+ assertThat(changeInfo.revisions.get(changeInfo.currentRevision).uploader.email)
+ .isEqualTo(email);
+ }
+
+ private static void assertCommitter(ChangeInfo changeInfo, String email) {
+ assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.committer.email)
+ .isEqualTo(email);
+ }
+
+ private static void assertAuthor(ChangeInfo changeInfo, String email) {
+ assertThat(changeInfo.revisions.get(changeInfo.currentRevision).commit.author.email)
+ .isEqualTo(email);
+ }
+
+ private void allowLabelPermission(
+ String labelName, String refPattern, AccountGroup.UUID group, int minVote, int maxVote) {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(allowLabel(labelName).ref(refPattern).group(group).range(minVote, maxVote))
+ .update();
+ }
+
private PushOneCommit.Result createGitSubmoduleCommit(String ref) throws Exception {
return pushFactory
.create(admin.newIdent(), testRepo, "subject", ImmutableMap.of())
@@ -302,6 +468,13 @@
.to(ref);
}
+ private PushOneCommit.Result createNormalCommit(
+ PersonIdent personIdent, String ref, String fileName) throws Exception {
+ return pushFactory
+ .create(personIdent, testRepo, "subject", ImmutableMap.of(fileName, fileName))
+ .to(ref);
+ }
+
private PushOneCommit.Result createNormalCommit(String ref, String fileName) throws Exception {
return pushFactory
.create(admin.newIdent(), testRepo, "subject", ImmutableMap.of(fileName, fileName))
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
index e6c3919..1607f09 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
@@ -90,7 +91,7 @@
try (Repository repo = repoManager.openRepository(allUsers)) {
RefUpdate ru = repo.updateRef(RefNames.REFS_GROUPNAMES);
ru.setForceUpdate(true);
- RefUpdate.Result result = ru.delete();
+ RefUpdate.Result result = testRefAction(() -> ru.delete());
assertThat(result).isEqualTo(Result.FORCED);
}
@@ -103,7 +104,7 @@
try (Repository repo = repoManager.openRepository(allUsers)) {
RefUpdate ru = repo.updateRef(RefNames.refsGroups(AccountGroup.uuid(g1.id)));
ru.setForceUpdate(true);
- RefUpdate.Result result = ru.delete();
+ RefUpdate.Result result = testRefAction(() -> ru.delete());
assertThat(result).isEqualTo(Result.FORCED);
}
@@ -117,7 +118,7 @@
RefRename ru =
repo.renameRef(
RefNames.refsGroups(AccountGroup.uuid(g1.id)), RefNames.REFS_GROUPS + BOGUS_UUID);
- RefUpdate.Result result = ru.rename();
+ RefUpdate.Result result = testRefAction(() -> ru.rename());
assertThat(result).isEqualTo(Result.RENAMED);
}
@@ -132,7 +133,7 @@
repo.renameRef(
RefNames.refsGroups(AccountGroup.uuid(g1.id)),
RefNames.refsGroups(AccountGroup.uuid(BOGUS_UUID)));
- RefUpdate.Result result = ru.rename();
+ RefUpdate.Result result = testRefAction(() -> ru.rename());
assertThat(result).isEqualTo(Result.RENAMED);
}
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 04bdf15..142a45c 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -28,6 +28,7 @@
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.gerrit.truth.MapSubject.assertThatMap;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -1147,7 +1148,7 @@
RefUpdate ru = repo.updateRef(RefNames.refsGroups(uuid));
ru.setForceUpdate(true);
ru.setNewObjectId(ObjectId.zeroId());
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
// Reindex the group.
@@ -1349,7 +1350,7 @@
updateRef.setExpectedOldObjectId(commit.toObjectId());
updateRef.setNewObjectId(ObjectId.zeroId());
updateRef.setForceUpdate(true);
- assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
// refs/meta/group-names is only visible with ACCESS_DATABASE
@@ -1449,7 +1450,7 @@
RefUpdate updateRef = repo.updateRef(groupRef);
updateRef.setExpectedOldObjectId(commit.toObjectId());
updateRef.setNewObjectId(emptyCommit);
- assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(updateRef.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED));
}
assertStaleGroupAndReindex(groupUuid);
@@ -1461,7 +1462,7 @@
updateRef.setExpectedOldObjectId(commit.toObjectId());
updateRef.setNewObjectId(ObjectId.zeroId());
updateRef.setForceUpdate(true);
- assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(updateRef.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
assertStaleGroupAndReindex(groupUuid);
}
@@ -1506,13 +1507,15 @@
// then run the reindexer -> only the new group is reindexed.
String groupName = "foo";
AccountGroup.UUID groupUuid = AccountGroup.uuid(groupName + "-UUID");
- groupsUpdate.createGroupInNoteDb(
- InternalGroupCreation.builder()
- .setGroupUUID(groupUuid)
- .setNameKey(AccountGroup.nameKey(groupName))
- .setId(AccountGroup.id(seq.nextGroupId()))
- .build(),
- GroupDelta.builder().build());
+ testRefAction(
+ () ->
+ groupsUpdate.createGroupInNoteDb(
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(AccountGroup.nameKey(groupName))
+ .setId(AccountGroup.id(seq.nextGroupId()))
+ .build(),
+ GroupDelta.builder().build()));
slaveGroupIndexer.run();
groupIndexedCounter.assertReindexOf(groupUuid);
@@ -1528,7 +1531,7 @@
try (Repository repo = repoManager.openRepository(allUsers)) {
RefUpdate u = repo.updateRef(RefNames.refsGroups(groupUuid));
u.setForceUpdate(true);
- assertThat(u.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(u.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
slaveGroupIndexer.run();
groupIndexedCounter.assertReindexOf(groupUuid);
@@ -1613,7 +1616,7 @@
RefUpdate updateRef = r.updateRef(ref);
updateRef.setExpectedOldObjectId(ObjectId.zeroId());
updateRef.setNewObjectId(emptyCommit);
- assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.NEW));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/AccessIT.java
index 6bd2b68..a2f1f46 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/AccessIT.java
@@ -22,6 +22,7 @@
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.schema.AclUtil.grant;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.gerrit.truth.ConfigSubject.assertThat;
import static com.google.gerrit.truth.MapSubject.assertThatMap;
import static java.util.Arrays.asList;
@@ -240,7 +241,7 @@
Registration registration = newFileHistoryWebLink()) {
RefUpdate u = repo.updateRef(RefNames.REFS_CONFIG);
u.setForceUpdate(true);
- assertThat(u.delete()).isEqualTo(Result.FORCED);
+ testRefAction(() -> assertThat(u.delete()).isEqualTo(Result.FORCED));
// This should not crash.
pApi().access();
@@ -442,7 +443,7 @@
try (Repository repo = repoManager.openRepository(newProjectName)) {
RefUpdate ru = repo.updateRef(RefNames.REFS_CONFIG);
ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(Result.FORCED);
+ testRefAction(() -> assertThat(ru.delete()).isEqualTo(Result.FORCED));
ProjectAccessInput accessInput = newProjectAccessInput();
AccessSectionInfo accessSection = newAccessSectionInfo();
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index b0de1c1..5c46fec 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -21,6 +21,7 @@
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -388,7 +389,7 @@
try (Repository repo = repoManager.openRepository(normalProject)) {
RefUpdate u = repo.updateRef(RefNames.REFS_HEADS + "master");
u.setForceUpdate(true);
- assertThat(u.delete()).isEqualTo(Result.FORCED);
+ testRefAction(() -> assertThat(u.delete()).isEqualTo(Result.FORCED));
}
AccessCheckInput input = new AccessCheckInput();
input.account = privilegedUser.email();
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index b738324..e120f97 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -41,6 +41,7 @@
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.project.testing.TestLabels.label;
import static com.google.gerrit.server.project.testing.TestLabels.value;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
@@ -335,7 +336,7 @@
RefUpdate u = repo.updateRef(RefNames.REFS_CONFIG);
u.setForceUpdate(true);
u.setExpectedOldObjectId(repo.resolve(RefNames.REFS_CONFIG));
- assertThat(u.delete(rw)).isEqualTo(Result.FORCED);
+ testRefAction(() -> assertThat(u.delete(rw)).isEqualTo(Result.FORCED));
}
RevCommit c =
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
index c3bcbd3..206a9d5 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
@@ -511,7 +512,7 @@
RefUpdate ru = serverRepo.updateRef(refName);
ru.setExpectedOldObjectId(oldCommitId);
ru.setNewObjectId(newCommitId);
- assertThat(ru.update()).isEqualTo(RefUpdate.Result.FAST_FORWARD);
+ testRefAction(() -> assertThat(ru.update()).isEqualTo(RefUpdate.Result.FAST_FORWARD));
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AutoMergeIT.java b/javatests/com/google/gerrit/acceptance/git/AutoMergeIT.java
index b16394d..3b158a9 100644
--- a/javatests/com/google/gerrit/acceptance/git/AutoMergeIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/AutoMergeIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static org.eclipse.jgit.lib.Constants.HEAD;
import com.google.common.collect.ImmutableList;
@@ -201,7 +202,7 @@
try (Repository repo = repoManager.openRepository(project)) {
RefUpdate ru = repo.updateRef(RefNames.refsCacheAutomerge(mergeCommit.name()));
ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
assertNoAutoMergeCreated(mergeCommit);
}
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index f58f81c..9e85d8c 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -24,6 +24,7 @@
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
@@ -217,7 +218,7 @@
RefUpdate mtu = repo.updateRef("refs/tags/master-tag");
mtu.setExpectedOldObjectId(ObjectId.zeroId());
mtu.setNewObjectId(repo.exactRef("refs/heads/master").getObjectId());
- assertThat(mtu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(mtu.update()).isEqualTo(RefUpdate.Result.NEW));
// rcMaster (c1 master master-tag) <-- rcBranch (c2 branch branch-tag)
// \ \
@@ -225,14 +226,14 @@
RefUpdate btu = repo.updateRef("refs/tags/branch-tag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(repo.exactRef("refs/heads/branch").getObjectId());
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW));
// Create a tag for the tree of the commit on 'master'
// tree-tag -> master.tree
RefUpdate ttu = repo.updateRef("refs/tags/tree-tag");
ttu.setExpectedOldObjectId(ObjectId.zeroId());
ttu.setNewObjectId(rcMaster.getTree().toObjectId());
- assertThat(ttu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(ttu.update()).isEqualTo(RefUpdate.Result.NEW));
}
}
@@ -588,14 +589,17 @@
.forUpdate()
.add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS))
.update();
- // Create a tag for the pending change on 'branch' so that the tag is orphaned
- try (Repository repo = repoManager.openRepository(project)) {
- // change4-tag -> psRef4
- RefUpdate ctu = repo.updateRef("refs/tags/change4-tag");
- ctu.setExpectedOldObjectId(ObjectId.zeroId());
- ctu.setNewObjectId(repo.exactRef(psRef4).getObjectId());
- assertThat(ctu.update()).isEqualTo(RefUpdate.Result.NEW);
- }
+ testRefAction(
+ () -> {
+ // Create a tag for the pending change on 'branch' so that the tag is orphaned
+ try (Repository repo = repoManager.openRepository(project)) {
+ // change4-tag -> psRef4
+ RefUpdate ctu = repo.updateRef("refs/tags/change4-tag");
+ ctu.setExpectedOldObjectId(ObjectId.zeroId());
+ ctu.setNewObjectId(repo.exactRef(psRef4).getObjectId());
+ assertThat(ctu.update()).isEqualTo(RefUpdate.Result.NEW);
+ }
+ });
requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
@@ -641,7 +645,7 @@
RefUpdate btu = repo.updateRef("refs/tags/master-newtag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(r.getCommit());
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW));
}
assertUploadPackRefs(
@@ -695,7 +699,7 @@
RefUpdate btu = repo.updateRef("refs/tags/branch-newtag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(tagRc);
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW));
}
assertUploadPackRefs(
@@ -751,7 +755,7 @@
RefUpdate btu = repo.updateRef("refs/tags/branch-newtag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(tagRc);
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW));
}
assertUploadPackRefs(
@@ -794,10 +798,11 @@
RevCommit bRc = r.getCommit();
// rcBranch (c2) <- newcommit1 (branch-oldtag) <- newcommit2 (branch)
- RefUpdate btu = repo.updateRef("refs/tags/branch-oldtag");
- btu.setExpectedOldObjectId(ObjectId.zeroId());
- btu.setNewObjectId(tagRc);
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ RefUpdate btu1 = repo.updateRef("refs/tags/branch-oldtag");
+
+ btu1.setExpectedOldObjectId(ObjectId.zeroId());
+ btu1.setNewObjectId(tagRc);
+ testRefAction(() -> assertThat(btu1.update()).isEqualTo(RefUpdate.Result.NEW));
assertUploadPackRefs(
psRef2,
@@ -811,11 +816,11 @@
"refs/tags/master-tag");
// rcBranch (c2 branch) <- newcommit1 (branch-oldtag) <- newcommit2
- btu = repo.updateRef("refs/heads/branch");
- btu.setExpectedOldObjectId(bRc);
- btu.setNewObjectId(rcBranch);
- btu.setForceUpdate(true);
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.FORCED);
+ RefUpdate btu2 = repo.updateRef("refs/heads/branch");
+ btu2.setExpectedOldObjectId(bRc);
+ btu2.setNewObjectId(rcBranch);
+ btu2.setForceUpdate(true);
+ testRefAction(() -> assertThat(btu2.update()).isEqualTo(RefUpdate.Result.FORCED));
}
assertUploadPackRefs(
@@ -907,7 +912,7 @@
btu.setExpectedOldObjectId(tagRc);
btu.setNewObjectId(rcBranch);
btu.setForceUpdate(true);
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.FORCED));
}
assertUploadPackRefs(
@@ -939,7 +944,7 @@
RefUpdate btu = repo.updateRef("refs/tags/updated-tag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(rcBranch);
- assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
+ testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW));
assertUploadPackRefs(
psRef2,
@@ -995,13 +1000,16 @@
"refs/tags/master-tag");
// rcBranch (c2 branch)
- try (Repository repo = repoManager.openRepository(project)) {
- RefUpdate btu = repo.updateRef("refs/tags/branch-tag");
- btu.setExpectedOldObjectId(rcBranch);
- btu.setNewObjectId(ObjectId.zeroId());
- btu.setForceUpdate(true);
- assertThat(btu.delete()).isEqualTo(RefUpdate.Result.FORCED);
- }
+ testRefAction(
+ () -> {
+ try (Repository repo = repoManager.openRepository(project)) {
+ RefUpdate btu = repo.updateRef("refs/tags/branch-tag");
+ btu.setExpectedOldObjectId(rcBranch);
+ btu.setNewObjectId(ObjectId.zeroId());
+ btu.setForceUpdate(true);
+ assertThat(btu.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ }
+ });
assertUploadPackRefs(
psRef2, metaRef2, psRef4, metaRef4, "refs/heads/branch", "refs/tags/master-tag");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 0e4f212..f9fb92c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -95,6 +95,8 @@
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
+import com.google.gerrit.server.update.context.RefUpdateContext;
+import com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.server.validators.ValidationException;
import com.google.gerrit.testing.ConfigSuite;
@@ -1125,20 +1127,22 @@
}
private void setChangeStatusToNew(PushOneCommit.Result... changes) throws Throwable {
- for (PushOneCommit.Result change : changes) {
- try (BatchUpdate bu =
- batchUpdateFactory.create(project, userFactory.create(admin.id()), TimeUtil.now())) {
- bu.addOp(
- change.getChange().getId(),
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) {
- ctx.getChange().setStatus(Change.Status.NEW);
- ctx.getUpdate(ctx.getChange().currentPatchSetId()).setStatus(Change.Status.NEW);
- return true;
- }
- });
- bu.execute();
+ try (RefUpdateContext ctx = RefUpdateContext.open(RefUpdateType.CHANGE_MODIFICATION)) {
+ for (PushOneCommit.Result change : changes) {
+ try (BatchUpdate bu =
+ batchUpdateFactory.create(project, userFactory.create(admin.id()), TimeUtil.now())) {
+ bu.addOp(
+ change.getChange().getId(),
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getChange().setStatus(Change.Status.NEW);
+ ctx.getUpdate(ctx.getChange().currentPatchSetId()).setStatus(Change.Status.NEW);
+ return true;
+ }
+ });
+ bu.execute();
+ }
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 079f84e..d08a7db 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -27,6 +27,7 @@
import static com.google.gerrit.git.ObjectIds.abbreviateName;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.SIGNED_OFF_BY_TAG;
@@ -116,16 +117,19 @@
@Before
public void addNonCommitHead() throws Exception {
- try (Repository repo = repoManager.openRepository(project);
- ObjectInserter ins = repo.newObjectInserter()) {
- ObjectId answer = ins.insert(Constants.OBJ_BLOB, new byte[] {42});
- ins.flush();
- ins.close();
+ testRefAction(
+ () -> {
+ try (Repository repo = repoManager.openRepository(project);
+ ObjectInserter ins = repo.newObjectInserter()) {
+ ObjectId answer = ins.insert(Constants.OBJ_BLOB, new byte[] {42});
+ ins.flush();
+ ins.close();
- RefUpdate update = repo.getRefDatabase().newUpdate("refs/heads/answer", false);
- update.setNewObjectId(answer);
- assertThat(update.forceUpdate()).isEqualTo(RefUpdate.Result.NEW);
- }
+ RefUpdate update = repo.getRefDatabase().newUpdate("refs/heads/answer", false);
+ update.setNewObjectId(answer);
+ assertThat(update.forceUpdate()).isEqualTo(RefUpdate.Result.NEW);
+ }
+ });
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java
index 8dce9c3..8c8f267 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/FileBranchIT.java
@@ -16,6 +16,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
@@ -58,7 +59,7 @@
@Test
public void getFileFromSymbolicRefPointingToAnUnbornBranch() throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
- repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+ testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
}
RestResponse response =
adminRestSession.get(String.format("/projects/%s/branches/HEAD/files/path", project.get()));
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index bcde618..55f102f 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.extensions.common.ProblemInfo.Status.FIXED;
import static com.google.gerrit.extensions.common.ProblemInfo.Status.FIX_FAILED;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.gerrit.testing.TestChanges.newPatchSet;
import static java.util.Objects.requireNonNull;
@@ -47,6 +49,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.RepoContext;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.TestChanges;
import com.google.inject.Inject;
@@ -297,7 +300,7 @@
serverSideTestRepo.reset(serverSideTestRepo.getRepository().exactRef(ref).getObjectId());
RefUpdate ru = serverSideTestRepo.getRepository().updateRef(ref);
ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED));
assertProblems(notes, null, problem("Destination ref not found (may be new branch): " + ref));
}
@@ -305,20 +308,21 @@
@Test
public void mergedChangeIsNotMerged() throws Exception {
ChangeNotes notes = insertChange();
-
- try (BatchUpdate bu = newUpdate(adminId)) {
- bu.addOp(
- notes.getChangeId(),
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) {
- ctx.getChange().setStatus(Change.Status.MERGED);
- ctx.getUpdate(ctx.getChange().currentPatchSetId())
- .fixStatusToMerged(new SubmissionId(ctx.getChange()));
- return true;
- }
- });
- bu.execute();
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (BatchUpdate bu = newUpdate(adminId)) {
+ bu.addOp(
+ notes.getChangeId(),
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getChange().setStatus(Change.Status.MERGED);
+ ctx.getUpdate(ctx.getChange().currentPatchSetId())
+ .fixStatusToMerged(new SubmissionId(ctx.getChange()));
+ return true;
+ }
+ });
+ bu.execute();
+ }
}
notes = reload(notes);
@@ -745,19 +749,22 @@
private ChangeNotes insertChange(TestAccount owner, String dest) throws Exception {
Change.Id id = Change.id(sequences.nextChangeId());
- ChangeInserter ins;
- try (BatchUpdate bu = newUpdate(owner.id())) {
- RevCommit commit = patchSetCommit(PatchSet.id(id, 1));
- bu.setNotify(NotifyResolver.Result.none());
- ins =
- changeInserterFactory
- .create(id, commit, dest)
- .setValidate(false)
- .setFireRevisionCreated(false)
- .setSendMail(false);
- bu.insertChange(ins).execute();
- }
- return changeNotesFactory.create(project, ins.getChange().getId());
+ return testRefAction(
+ () -> {
+ ChangeInserter ins;
+ try (BatchUpdate bu = newUpdate(owner.id())) {
+ RevCommit commit = patchSetCommit(PatchSet.id(id, 1));
+ bu.setNotify(NotifyResolver.Result.none());
+ ins =
+ changeInserterFactory
+ .create(id, commit, dest)
+ .setValidate(false)
+ .setFireRevisionCreated(false)
+ .setSendMail(false);
+ bu.insertChange(ins).execute();
+ }
+ return changeNotesFactory.create(project, ins.getChange().getId());
+ });
}
private PatchSet.Id nextPatchSetId(ChangeNotes notes) throws Exception {
@@ -770,17 +777,20 @@
}
private ChangeNotes incrementPatchSet(ChangeNotes notes, RevCommit commit) throws Exception {
- PatchSetInserter ins;
- try (BatchUpdate bu = newUpdate(notes.getChange().getOwner())) {
- bu.setNotify(NotifyResolver.Result.none());
- ins =
- patchSetInserterFactory
- .create(notes, nextPatchSetId(notes), commit)
- .setValidate(false)
- .setFireRevisionCreated(false);
- bu.addOp(notes.getChangeId(), ins).execute();
- }
- return reload(notes);
+ return testRefAction(
+ () -> {
+ PatchSetInserter ins;
+ try (BatchUpdate bu = newUpdate(notes.getChange().getOwner())) {
+ bu.setNotify(NotifyResolver.Result.none());
+ ins =
+ patchSetInserterFactory
+ .create(notes, nextPatchSetId(notes), commit)
+ .setValidate(false)
+ .setFireRevisionCreated(false);
+ bu.addOp(notes.getChangeId(), ins).execute();
+ }
+ return reload(notes);
+ });
}
private ChangeNotes reload(ChangeNotes notes) throws Exception {
@@ -822,7 +832,7 @@
private void deleteRef(String refName) throws Exception {
RefUpdate ru = serverSideTestRepo.getRepository().updateRef(refName, true);
ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ testRefAction(() -> assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED));
}
private void addNoteDbCommit(Change.Id id, String commitMessage) throws Exception {
@@ -847,30 +857,33 @@
}
private ChangeNotes mergeChange(ChangeNotes notes) throws Exception {
- ObjectId oldId = getDestRef(notes);
- ObjectId newId = psUtil.current(notes).commitId();
- String dest = notes.getChange().getDest().branch();
+ return testRefAction(
+ () -> {
+ ObjectId oldId = getDestRef(notes);
+ ObjectId newId = psUtil.current(notes).commitId();
+ String dest = notes.getChange().getDest().branch();
- try (BatchUpdate bu = newUpdate(adminId)) {
- bu.addOp(
- notes.getChangeId(),
- new BatchUpdateOp() {
- @Override
- public void updateRepo(RepoContext ctx) throws IOException {
- ctx.addRefUpdate(oldId, newId, dest);
- }
+ try (BatchUpdate bu = newUpdate(adminId)) {
+ bu.addOp(
+ notes.getChangeId(),
+ new BatchUpdateOp() {
+ @Override
+ public void updateRepo(RepoContext ctx) throws IOException {
+ ctx.addRefUpdate(oldId, newId, dest);
+ }
- @Override
- public boolean updateChange(ChangeContext ctx) {
- ctx.getChange().setStatus(Change.Status.MERGED);
- ctx.getUpdate(ctx.getChange().currentPatchSetId())
- .fixStatusToMerged(new SubmissionId(ctx.getChange()));
- return true;
- }
- });
- bu.execute();
- }
- return reload(notes);
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getChange().setStatus(Change.Status.MERGED);
+ ctx.getUpdate(ctx.getChange().currentPatchSetId())
+ .fixStatusToMerged(new SubmissionId(ctx.getChange()));
+ return true;
+ }
+ });
+ bu.execute();
+ }
+ return reload(notes);
+ });
}
private static ProblemInfo problem(String message) {
@@ -911,7 +924,7 @@
ru.setExpectedOldObjectId(ref.getObjectId());
ru.setNewObjectId(ObjectId.zeroId());
ru.setForceUpdate(true);
- Result result = ru.delete();
+ Result result = testRefAction(() -> ru.delete());
if (result != Result.FORCED) {
throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
}
diff --git a/javatests/com/google/gerrit/acceptance/server/change/DeleteZombieDraftIT.java b/javatests/com/google/gerrit/acceptance/server/change/DeleteZombieDraftIT.java
index 1eef944..107b777 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/DeleteZombieDraftIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/DeleteZombieDraftIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.server.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
@@ -30,6 +31,7 @@
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.server.notedb.ChangeNoteJson;
import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gson.JsonParser;
import com.google.inject.Inject;
@@ -191,10 +193,12 @@
}
private void restoreRef(String refName, ObjectId id) throws Exception {
- try (Repository allUsersRepo = repoManager.openRepository(allUsers)) {
- RefUpdate u = allUsersRepo.updateRef(refName);
- u.setNewObjectId(id);
- u.forceUpdate();
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers)) {
+ RefUpdate u = allUsersRepo.updateRef(refName);
+ u.setNewObjectId(id);
+ u.forceUpdate();
+ }
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index 21db45c..3e03b2a 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -20,6 +20,7 @@
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -55,6 +56,7 @@
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Inject;
@@ -772,17 +774,19 @@
}
private void clearGroups(PatchSet.Id psId) throws Exception {
- try (BatchUpdate bu = batchUpdateFactory.create(project, user(user), TimeUtil.now())) {
- bu.addOp(
- psId.changeId(),
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) {
- ctx.getUpdate(psId).setGroups(ImmutableList.of());
- return true;
- }
- });
- bu.execute();
+ try (RefUpdateContext ctx = openTestRefUpdateContext()) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user(user), TimeUtil.now())) {
+ bu.addOp(
+ psId.changeId(),
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getUpdate(psId).setGroups(ImmutableList.of());
+ return true;
+ }
+ });
+ bu.execute();
+ }
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 45a471b..f728995 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -15,16 +15,25 @@
package com.google.gerrit.acceptance.server.mail;
import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.entities.EmailHeader;
import com.google.gerrit.entities.EmailHeader.StringEmailHeader;
+import com.google.gerrit.server.config.SitePaths;
import java.net.URI;
+import java.nio.file.Files;
import java.util.Map;
+import javax.inject.Inject;
import org.junit.Test;
+@UseLocalDisk
public class MailSenderIT extends AbstractMailIT {
+ @Inject private SitePaths sitePaths;
+
@Test
@GerritConfig(name = "sendemail.replyToAddress", value = "custom@gerritcodereview.com")
@GerritConfig(name = "receiveemail.protocol", value = "POP3")
@@ -63,6 +72,20 @@
assertThat(headerString(headers, "In-Reply-To")).isEqualTo(threadId);
}
+ @Test
+ @Sandboxed
+ public void useCustomTemplates() throws Exception {
+ String customTemplate =
+ "{namespace com.google.gerrit.server.mail.template.ChangeSubject}\n"
+ + "\n"
+ + "{template ChangeSubject kind=\"text\"}CUSTOM-TEMPLATE{/template}\n";
+ Files.write(sitePaths.mail_dir.resolve("ChangeSubject.soy"), customTemplate.getBytes(UTF_8));
+
+ createChangeWithReview(user);
+ String subject = headerString(sender.getMessages().iterator().next().headers(), "Subject");
+ assertThat(subject).isEqualTo("CUSTOM-TEMPLATE");
+ }
+
private String headerString(Map<String, EmailHeader> headers, String name) {
EmailHeader header = headers.get(name);
assertThat(header).isInstanceOf(StringEmailHeader.class);
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index ab5e1d8..fc746ad 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
@@ -100,10 +101,13 @@
}
};
- try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
- bu.addOp(id, backupMasterOp);
- bu.execute();
- }
+ testRefAction(
+ () -> {
+ try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
+ bu.addOp(id, backupMasterOp);
+ bu.execute();
+ }
+ });
// Ensure backupMasterOp worked.
assertThat(getRef(backup)).hasValue(master1);
@@ -158,13 +162,16 @@
.changeUpdate(
"testUpdateRefAndAddMessageOp",
batchUpdateFactory -> {
- try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
- bu.addOp(
- id,
- new UpdateRefAndAddMessageOp(
- updateRepoCalledCount, updateChangeCalledCount));
- bu.execute(new ConcurrentWritingListener(afterUpdateReposCalledCount));
- }
+ testRefAction(
+ () -> {
+ try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
+ bu.addOp(
+ id,
+ new UpdateRefAndAddMessageOp(
+ updateRepoCalledCount, updateChangeCalledCount));
+ bu.execute(new ConcurrentWritingListener(afterUpdateReposCalledCount));
+ }
+ });
return "Done";
})
.call();
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index c694a87..b5c6149 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -71,6 +71,7 @@
"//java/com/google/gerrit/sshd",
"//java/com/google/gerrit/testing:assertable-executor",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//java/com/google/gerrit/truth",
"//lib:gson",
"//lib:guava",
diff --git a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
index 6bdf80f..0112f88 100644
--- a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
+++ b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
@@ -16,6 +16,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.entities.Account;
@@ -166,7 +167,7 @@
new ReceiveCommand(ObjectId.zeroId(), commitId, refName, ReceiveCommand.Type.CREATE));
refNames.add(refName);
}
- RefUpdateUtil.executeChecked(bru, usersRepo);
+ testRefAction(() -> RefUpdateUtil.executeChecked(bru, usersRepo));
return refNames;
}
@@ -201,7 +202,7 @@
RefUpdate update = repo.updateRef(refName);
update.setNewObjectId(commitId);
update.setForceUpdate(true);
- update.update();
+ testRefAction(() -> update.update());
return repo.exactRef(refName);
}
diff --git a/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java b/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
index a764654..b2a6790 100644
--- a/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
+++ b/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.group.db;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -236,34 +237,40 @@
private InternalGroup createGroup(
int next, String groupName, PersonIdent authorIdent, Account.Id authorId) throws Exception {
- InternalGroupCreation groupCreation =
- InternalGroupCreation.builder()
- .setGroupUUID(GroupUuid.make(groupName, serverIdent))
- .setNameKey(AccountGroup.nameKey(groupName))
- .setId(AccountGroup.id(next))
- .build();
- GroupDelta groupDelta =
- authorIdent.equals(serverIdent)
- ? GroupDelta.builder().setDescription("Groups").build()
- : GroupDelta.builder()
- .setDescription("Groups")
- .setMemberModification(members -> ImmutableSet.of(authorId))
- .build();
+ return testRefAction(
+ () -> {
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(GroupUuid.make(groupName, serverIdent))
+ .setNameKey(AccountGroup.nameKey(groupName))
+ .setId(AccountGroup.id(next))
+ .build();
+ GroupDelta groupDelta =
+ authorIdent.equals(serverIdent)
+ ? GroupDelta.builder().setDescription("Groups").build()
+ : GroupDelta.builder()
+ .setDescription("Groups")
+ .setMemberModification(members -> ImmutableSet.of(authorId))
+ .build();
- GroupConfig groupConfig =
- GroupConfig.createForNewGroup(allUsersName, allUsersRepo, groupCreation);
- groupConfig.setGroupDelta(groupDelta, getAuditLogFormatter());
+ GroupConfig groupConfig =
+ GroupConfig.createForNewGroup(allUsersName, allUsersRepo, groupCreation);
+ groupConfig.setGroupDelta(groupDelta, getAuditLogFormatter());
- groupConfig.commit(createMetaDataUpdate(authorIdent));
- return groupConfig
- .getLoadedGroup()
- .orElseThrow(() -> new IllegalStateException("create group failed"));
+ groupConfig.commit(createMetaDataUpdate(authorIdent));
+ return groupConfig
+ .getLoadedGroup()
+ .orElseThrow(() -> new IllegalStateException("create group failed"));
+ });
}
private void updateGroup(AccountGroup.UUID uuid, GroupDelta groupDelta) throws Exception {
- GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersName, allUsersRepo, uuid);
- groupConfig.setGroupDelta(groupDelta, getAuditLogFormatter());
- groupConfig.commit(createMetaDataUpdate(userIdent));
+ testRefAction(
+ () -> {
+ GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersName, allUsersRepo, uuid);
+ groupConfig.setGroupDelta(groupDelta, getAuditLogFormatter());
+ groupConfig.commit(createMetaDataUpdate(userIdent));
+ });
}
private void addMembers(AccountGroup.UUID groupUuid, Set<Account.Id> ids) throws Exception {
diff --git a/javatests/com/google/gerrit/server/group/db/BUILD b/javatests/com/google/gerrit/server/group/db/BUILD
index 9f9f459..47550bb 100644
--- a/javatests/com/google/gerrit/server/group/db/BUILD
+++ b/javatests/com/google/gerrit/server/group/db/BUILD
@@ -17,6 +17,7 @@
"//java/com/google/gerrit/server/group/testing",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//java/com/google/gerrit/truth",
"//lib:guava",
"//lib:jgit",
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index 1e6ba3a..bf6839d 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.notedb;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.inject.Scopes.SINGLETON;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -247,13 +248,16 @@
}
protected Change newChange(Injector injector, boolean workInProgress) throws Exception {
- Change c = TestChanges.newChange(project, changeOwner.getAccountId());
- ChangeUpdate u = newUpdate(injector, c, changeOwner, false);
- u.setChangeId(c.getKey().get());
- u.setBranch(c.getDest().branch());
- u.setWorkInProgress(workInProgress);
- u.commit();
- return c;
+ return testRefAction(
+ () -> {
+ Change c = TestChanges.newChange(project, changeOwner.getAccountId());
+ ChangeUpdate u = newUpdate(injector, c, changeOwner, false);
+ u.setChangeId(c.getKey().get());
+ u.setBranch(c.getDest().branch());
+ u.setWorkInProgress(workInProgress);
+ u.commit();
+ return c;
+ });
}
protected Change newWorkInProgressChange() throws Exception {
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index cf739f6..15eefcd 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -25,6 +25,7 @@
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REMOVED;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Comparator.comparing;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -1113,7 +1114,7 @@
ChangeUpdate update = newUpdate(c, changeOwner);
update.putReviewer(changeOwner.getAccount().id(), REVIEWER);
update.putReviewer(otherUser.getAccount().id(), CC);
- update.commit();
+ testRefAction(() -> update.commit());
ChangeNotes notes = newNotes(c);
Instant ts = update.getWhen();
@@ -1936,7 +1937,7 @@
try (NoteDbUpdateManager updateManager = updateManagerFactory.create(project)) {
updateManager.add(update1);
updateManager.add(update2);
- updateManager.execute();
+ testRefAction(() -> updateManager.execute());
}
ChangeNotes notes = newNotes(c);
@@ -1985,7 +1986,7 @@
update2.putApproval(LabelId.CODE_REVIEW, (short) 2);
updateManager.add(update2);
- updateManager.execute();
+ testRefAction(() -> updateManager.execute());
}
ChangeNotes notes = newNotes(c);
@@ -2047,7 +2048,7 @@
try (NoteDbUpdateManager updateManager = updateManagerFactory.create(project)) {
updateManager.add(update1);
updateManager.add(update2);
- updateManager.execute();
+ testRefAction(() -> updateManager.execute());
}
Ref ref1 = repo.exactRef(update1.getRefName());
@@ -3362,7 +3363,7 @@
draftUpdate.putComment(comment2);
try (NoteDbUpdateManager manager = updateManagerFactory.create(c.getProject())) {
manager.add(draftUpdate);
- manager.execute();
+ testRefAction(() -> manager.execute());
}
// Looking at drafts directly shows the zombie comment.
@@ -3426,7 +3427,7 @@
try (NoteDbUpdateManager manager = updateManagerFactory.create(project)) {
manager.add(update1);
manager.add(update2);
- manager.execute();
+ testRefAction(() -> manager.execute());
}
ChangeNotes notes = newNotes(c);
diff --git a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
index 4f4911a..d13ccdd 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
@@ -21,6 +21,7 @@
import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REMOVED;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableList;
@@ -90,7 +91,7 @@
bru.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName()));
}
- RefUpdateUtil.executeChecked(bru, repo);
+ testRefAction(() -> RefUpdateUtil.executeChecked(bru, repo));
}
@Test
diff --git a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
index 0c9f731..1b2d906 100644
--- a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
+++ b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -385,7 +386,9 @@
ins.flush();
RefUpdate ru = repo.updateRef(refName);
ru.setNewObjectId(newId);
- assertThat(ru.forceUpdate()).isAnyOf(RefUpdate.Result.NEW, RefUpdate.Result.FORCED);
+ testRefAction(
+ () ->
+ assertThat(ru.forceUpdate()).isAnyOf(RefUpdate.Result.NEW, RefUpdate.Result.FORCED));
return newId;
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 96a8dea..9070ffc 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -26,6 +26,7 @@
import static com.google.gerrit.server.project.testing.TestLabels.label;
import static com.google.gerrit.server.project.testing.TestLabels.value;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -1435,6 +1436,15 @@
// "count" and "group" args cannot be used simultaneously.
assertThrows(
BadRequestException.class, () -> assertQuery("label:Code-Review=+1,group=gerrit,count=2"));
+
+ // "non_contributor arg for the label operator is not allowed in change queries
+ thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> assertQuery("label:Code-Review=+2,user=non_contributor"));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo("non_contributor arg is not allowed in change queries");
}
@Test
@@ -4161,11 +4171,14 @@
Project.NameKey project = Project.nameKey(repoName);
Account.Id ownerId = owner != null ? owner : userId;
IdentifiedUser user = userFactory.create(ownerId);
- try (BatchUpdate bu = updateFactory.create(project, user, createdOn)) {
- bu.insertChange(ins);
- bu.execute();
- return ins.getChange();
- }
+ return testRefAction(
+ () -> {
+ try (BatchUpdate bu = updateFactory.create(project, user, createdOn)) {
+ bu.insertChange(ins);
+ bu.execute();
+ return ins.getChange();
+ }
+ });
}
protected Change newPatchSet(
@@ -4187,15 +4200,18 @@
.create(changeNotesFactory.createChecked(c), PatchSet.id(c.getId(), n), commit)
.setFireRevisionCreated(false)
.setValidate(false);
- try (BatchUpdate bu = updateFactory.create(c.getProject(), user, TimeUtil.now());
- ObjectInserter oi = repo.getRepository().newObjectInserter();
- ObjectReader reader = oi.newReader();
- RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo.getRepository(), rw, oi);
- bu.setNotify(NotifyResolver.Result.none());
- bu.addOp(c.getId(), inserter);
- bu.execute();
- }
+ testRefAction(
+ () -> {
+ try (BatchUpdate bu = updateFactory.create(c.getProject(), user, TimeUtil.now());
+ ObjectInserter oi = repo.getRepository().newObjectInserter();
+ ObjectReader reader = oi.newReader();
+ RevWalk rw = new RevWalk(reader)) {
+ bu.setRepository(repo.getRepository(), rw, oi);
+ bu.setNotify(NotifyResolver.Result.none());
+ bu.addOp(c.getId(), inserter);
+ bu.execute();
+ }
+ });
return inserter.getChange();
}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java
index a5fd4a2..d2ccaa9 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionCheckTest.java
@@ -16,6 +16,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.SitePaths;
@@ -38,7 +39,7 @@
GitRepositoryManager repoManager = new InMemoryRepositoryManager();
repoManager.createRepository(allProjectsName);
versionManager = new NoteDbSchemaVersionManager(allProjectsName, repoManager);
- versionManager.init();
+ testRefAction(() -> versionManager.init());
sitePaths = new SitePaths(Paths.get("/tmp/foo"));
}
@@ -51,7 +52,7 @@
@Test
public void shouldFailIfCurrentVersionIsOneMoreThanExpected() throws IOException {
- versionManager.increment(NoteDbSchemaVersions.LATEST);
+ testRefAction(() -> versionManager.increment(NoteDbSchemaVersions.LATEST));
ProvisionException e =
assertThrows(
@@ -69,7 +70,7 @@
throws IOException {
Config gerritConfig = new Config();
gerritConfig.setBoolean("gerrit", null, "experimentalRollingUpgrade", true);
- versionManager.increment(NoteDbSchemaVersions.LATEST);
+ testRefAction(() -> versionManager.increment(NoteDbSchemaVersions.LATEST));
NoteDbSchemaVersionCheck versionCheck =
new NoteDbSchemaVersionCheck(versionManager, sitePaths, gerritConfig);
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
index 38e19f7..3a1ea12 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.entities.RefNames.REFS_VERSION;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.AllProjectsName;
@@ -62,14 +63,14 @@
@Test
public void incrementFromMissing() throws Exception {
- manager.increment(123);
+ testRefAction(() -> manager.increment(123));
assertThat(manager.read()).isEqualTo(124);
}
@Test
public void increment() throws Exception {
tr.update(REFS_VERSION, tr.blob("123"));
- manager.increment(123);
+ testRefAction(() -> manager.increment(123));
assertThat(manager.read()).isEqualTo(124);
}
diff --git a/javatests/com/google/gerrit/server/submit/BUILD b/javatests/com/google/gerrit/server/submit/BUILD
index 7425bc8..01acb72 100644
--- a/javatests/com/google/gerrit/server/submit/BUILD
+++ b/javatests/com/google/gerrit/server/submit/BUILD
@@ -11,6 +11,7 @@
"//java/com/google/gerrit/entities",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:jgit",
"//lib/mockito",
"//lib/truth",
diff --git a/javatests/com/google/gerrit/server/submit/SubmoduleCommitsTest.java b/javatests/com/google/gerrit/server/submit/SubmoduleCommitsTest.java
index 313e697..a391c03 100644
--- a/javatests/com/google/gerrit/server/submit/SubmoduleCommitsTest.java
+++ b/javatests/com/google/gerrit/server/submit/SubmoduleCommitsTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@@ -197,7 +198,7 @@
RefUpdate ru = serverRepo.updateRef(refName);
ru.setExpectedOldObjectId(oldCommitId);
ru.setNewObjectId(newCommitId);
- assertThat(ru.update()).isEqualTo(RefUpdate.Result.FAST_FORWARD);
+ testRefAction(() -> assertThat(ru.update()).isEqualTo(RefUpdate.Result.FAST_FORWARD));
return rw.parseCommit(newCommitId);
}
diff --git a/javatests/com/google/gerrit/server/update/BUILD b/javatests/com/google/gerrit/server/update/BUILD
index 6d96c10..345681d 100644
--- a/javatests/com/google/gerrit/server/update/BUILD
+++ b/javatests/com/google/gerrit/server/update/BUILD
@@ -15,6 +15,7 @@
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
"//lib:guava",
"//lib:jgit",
"//lib:jgit-junit",
diff --git a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
index 91c8371..07159b7 100644
--- a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -18,6 +18,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.openTestRefUpdateContext;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -48,6 +49,7 @@
import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.patch.DiffSummary;
import com.google.gerrit.server.patch.DiffSummaryKey;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.inject.Inject;
@@ -58,6 +60,7 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -106,12 +109,21 @@
private Project.NameKey project;
private TestRepository<Repository> repo;
+ private RefUpdateContext testRefUpdateContext;
+
@Before
public void setUp() throws Exception {
project = Project.nameKey("test");
Repository inMemoryRepo = repoManager.createRepository(project);
repo = new TestRepository<>(inMemoryRepo);
+ // All tests here are low level. Open context here to avoid repeated code in multiple tests.
+ testRefUpdateContext = openTestRefUpdateContext();
+ }
+
+ @After
+ public void tearDown() {
+ testRefUpdateContext.close();
}
@Test
@@ -129,7 +141,6 @@
});
bu.execute();
}
-
assertThat(repo.getRepository().exactRef("refs/heads/master").getObjectId())
.isEqualTo(branchCommit.getId());
}
@@ -341,7 +352,8 @@
int cacheSizeBefore = diffSummaryCache.asMap().size();
- // We don't want to depend on the test helper used above so we perform an explicit commit here.
+ // We don't want to depend on the test helper used above so we perform an explicit commit
+ // here.
try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.now())) {
ObjectId commitId =
repo.amend(notes.getCurrentPatchSet().commitId())
diff --git a/javatests/com/google/gerrit/server/update/RepoViewTest.java b/javatests/com/google/gerrit/server/update/RepoViewTest.java
index b37e302..b118c9f 100644
--- a/javatests/com/google/gerrit/server/update/RepoViewTest.java
+++ b/javatests/com/google/gerrit/server/update/RepoViewTest.java
@@ -16,6 +16,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.gerrit.entities.Project;
@@ -43,8 +44,14 @@
InMemoryRepositoryManager repoManager = new InMemoryRepositoryManager();
Project.NameKey project = Project.nameKey("project");
repo = repoManager.createRepository(project);
- tr = new TestRepository<>(repo);
- tr.branch(MASTER).commit().create();
+ tr =
+ testRefAction(
+ () -> {
+ TestRepository<?> testRepo = new TestRepository<>(repo);
+ testRepo.branch(MASTER).commit().create();
+ return testRepo;
+ });
+
view = new RepoView(repoManager, project);
}
@@ -75,8 +82,11 @@
assertThat(view.getRef(MASTER)).hasValue(oldMaster);
assertThat(view.getRef(BRANCH)).isEmpty();
- tr.branch(MASTER).commit().create();
- tr.branch(BRANCH).commit().create();
+ testRefAction(
+ () -> {
+ tr.branch(MASTER).commit().create();
+ tr.branch(BRANCH).commit().create();
+ });
assertThat(repo.exactRef(MASTER).getObjectId()).isNotEqualTo(oldMaster);
assertThat(repo.exactRef(BRANCH)).isNotNull();
assertThat(view.getRef(MASTER)).hasValue(oldMaster);
@@ -88,7 +98,7 @@
ObjectId oldMaster = repo.exactRef(MASTER).getObjectId();
assertThat(view.getRefs(R_HEADS)).containsExactly("master", oldMaster);
- ObjectId newBranch = tr.branch(BRANCH).commit().create();
+ ObjectId newBranch = testRefAction(() -> tr.branch(BRANCH).commit().create());
assertThat(view.getRefs(R_HEADS)).containsExactly("master", oldMaster, "branch", newBranch);
}
@@ -99,17 +109,17 @@
assertThat(view.getRef(MASTER)).hasValue(master1);
// Doesn't reflect new value for master.
- ObjectId master2 = tr.branch(MASTER).commit().create();
+ ObjectId master2 = testRefAction(() -> tr.branch(MASTER).commit().create());
assertThat(repo.exactRef(MASTER).getObjectId()).isEqualTo(master2);
assertThat(view.getRefs(R_HEADS)).containsExactly("master", master1);
// Branch wasn't previously cached, so does reflect new value.
- ObjectId branch1 = tr.branch(BRANCH).commit().create();
+ ObjectId branch1 = testRefAction(() -> tr.branch(BRANCH).commit().create());
assertThat(view.getRefs(R_HEADS)).containsExactly("master", master1, "branch", branch1);
// Looking up branch causes it to be cached.
assertThat(view.getRef(BRANCH)).hasValue(branch1);
- ObjectId branch2 = tr.branch(BRANCH).commit().create();
+ ObjectId branch2 = testRefAction(() -> tr.branch(BRANCH).commit().create());
assertThat(repo.exactRef(BRANCH).getObjectId()).isEqualTo(branch2);
assertThat(view.getRefs(R_HEADS)).containsExactly("master", master1, "branch", branch1);
}
diff --git a/javatests/com/google/gerrit/server/update/context/BUILD b/javatests/com/google/gerrit/server/update/context/BUILD
new file mode 100644
index 0000000..e580595
--- /dev/null
+++ b/javatests/com/google/gerrit/server/update/context/BUILD
@@ -0,0 +1,14 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "update_context_tests",
+ size = "small",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/testing:test-ref-update-context",
+ "//lib/truth",
+ "//lib/truth:truth-java8-extension",
+ ],
+)
diff --git a/javatests/com/google/gerrit/server/update/context/RefUpdateContextTest.java b/javatests/com/google/gerrit/server/update/context/RefUpdateContextTest.java
new file mode 100644
index 0000000..178d67d
--- /dev/null
+++ b/javatests/com/google/gerrit/server/update/context/RefUpdateContextTest.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.update.context;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.GROUPS_UPDATE;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.INIT_REPO;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.After;
+import org.junit.Test;
+
+public class RefUpdateContextTest {
+ @After
+ public void tearDown() {
+ // Each test should close all opened context to avoid interference with other tests.
+ assertThat(RefUpdateContext.getOpenedContexts()).isEmpty();
+ }
+
+ @Test
+ public void contextNotOpen() {
+ assertThat(RefUpdateContext.getOpenedContexts()).isEmpty();
+ assertThat(RefUpdateContext.hasOpen(INIT_REPO)).isFalse();
+ assertThat(RefUpdateContext.hasOpen(GROUPS_UPDATE)).isFalse();
+ }
+
+ @Test
+ public void singleContext_openedAndClosedCorrectly() {
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ ImmutableList<RefUpdateContext> openedContexts = RefUpdateContext.getOpenedContexts();
+ assertThat(openedContexts).hasSize(1);
+ assertThat(openedContexts.get(0).getUpdateType()).isEqualTo(CHANGE_MODIFICATION);
+ assertThat(RefUpdateContext.hasOpen(CHANGE_MODIFICATION)).isTrue();
+ assertThat(RefUpdateContext.hasOpen(INIT_REPO)).isFalse();
+ }
+
+ assertThat(RefUpdateContext.getOpenedContexts()).isEmpty();
+ assertThat(RefUpdateContext.hasOpen(CHANGE_MODIFICATION)).isFalse();
+ }
+
+ @Test
+ public void nestedContext_openedAndClosedCorrectly() {
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (RefUpdateContext nestedCtx = RefUpdateContext.open(INIT_REPO)) {
+ ImmutableList<RefUpdateContext> nestedOpenedContexts = RefUpdateContext.getOpenedContexts();
+ assertThat(nestedOpenedContexts).hasSize(2);
+ assertThat(nestedOpenedContexts.get(0).getUpdateType()).isEqualTo(CHANGE_MODIFICATION);
+ assertThat(nestedOpenedContexts.get(1).getUpdateType()).isEqualTo(INIT_REPO);
+ assertThat(RefUpdateContext.hasOpen(CHANGE_MODIFICATION)).isTrue();
+ assertThat(RefUpdateContext.hasOpen(INIT_REPO)).isTrue();
+ assertThat(RefUpdateContext.hasOpen(GROUPS_UPDATE)).isFalse();
+ }
+ ImmutableList<RefUpdateContext> openedContexts = RefUpdateContext.getOpenedContexts();
+ assertThat(openedContexts).hasSize(1);
+ assertThat(openedContexts.get(0).getUpdateType()).isEqualTo(CHANGE_MODIFICATION);
+ assertThat(RefUpdateContext.hasOpen(CHANGE_MODIFICATION)).isTrue();
+ assertThat(RefUpdateContext.hasOpen(INIT_REPO)).isFalse();
+ assertThat(RefUpdateContext.hasOpen(GROUPS_UPDATE)).isFalse();
+ }
+
+ assertThat(RefUpdateContext.getOpenedContexts()).isEmpty();
+ assertThat(RefUpdateContext.hasOpen(CHANGE_MODIFICATION)).isFalse();
+ assertThat(RefUpdateContext.hasOpen(INIT_REPO)).isFalse();
+ assertThat(RefUpdateContext.hasOpen(GROUPS_UPDATE)).isFalse();
+ }
+
+ @Test
+ public void incorrectCloseOrder_exceptionThrown() {
+ try (RefUpdateContext ctx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
+ try (RefUpdateContext nestedCtx = RefUpdateContext.open(INIT_REPO)) {
+ assertThrows(Exception.class, () -> ctx.close());
+ ImmutableList<RefUpdateContext> openedContexts = RefUpdateContext.getOpenedContexts();
+ assertThat(openedContexts).hasSize(2);
+ assertThat(RefUpdateContext.hasOpen(CHANGE_MODIFICATION)).isTrue();
+ assertThat(RefUpdateContext.hasOpen(INIT_REPO)).isTrue();
+ }
+ }
+ }
+}
diff --git a/lib/bouncycastle/BUILD b/lib/bouncycastle/BUILD
index 43ba6e1..6a87d73 100644
--- a/lib/bouncycastle/BUILD
+++ b/lib/bouncycastle/BUILD
@@ -22,6 +22,13 @@
)
java_library(
+ name = "bcutil",
+ data = ["//lib:LICENSE-bouncycastle"],
+ visibility = ["//visibility:public"],
+ exports = ["@bcutil//jar"],
+)
+
+java_library(
name = "bcprov-neverlink",
data = ["//lib:LICENSE-bouncycastle"],
neverlink = 1,
@@ -44,3 +51,11 @@
visibility = ["//visibility:public"],
exports = ["@bcpkix//jar"],
)
+
+java_library(
+ name = "bcutil-neverlink",
+ data = ["//lib:LICENSE-bouncycastle"],
+ neverlink = 1,
+ visibility = ["//visibility:public"],
+ exports = ["@bcutil//jar"],
+)
diff --git a/polygerrit-ui/app/constants/constants.ts b/polygerrit-ui/app/constants/constants.ts
index f915432..b9ed56b 100644
--- a/polygerrit-ui/app/constants/constants.ts
+++ b/polygerrit-ui/app/constants/constants.ts
@@ -258,6 +258,8 @@
NONE = 'NONE',
}
+// These defaults should match the defaults in
+// java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
export function createDefaultPreferences(): PreferencesInfo {
return {
changes_per_page: 25,
@@ -265,8 +267,8 @@
size_bar_in_change_table: true,
my: [],
theme: AppTheme.AUTO,
- date_format: DateFormat.EURO,
- time_format: TimeFormat.HHMM_24,
+ date_format: DateFormat.STD,
+ time_format: TimeFormat.HHMM_12,
change_table: [],
email_strategy: EmailStrategy.ATTENTION_SET_ONLY,
default_base_for_merges: DefaultBase.AUTO_MERGE,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
index 342b876..19207bc 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.ts
@@ -42,6 +42,7 @@
import {classMap} from 'lit/directives/class-map.js';
import {createSearchUrl} from '../../../models/views/search';
import {createChangeUrl} from '../../../models/views/change';
+import {userModelToken} from '../../../models/user/user-model';
import {pluginLoaderToken} from '../../shared/gr-js-api-interface/gr-plugin-loader';
enum ChangeSize {
@@ -94,9 +95,6 @@
sectionName?: string;
@property({type: Boolean})
- showStar = false;
-
- @property({type: Boolean})
showNumber = false;
@property({type: String})
@@ -125,6 +123,10 @@
private readonly getNavigation = resolve(this, navigationToken);
+ private readonly getUserModel = resolve(this, userModelToken);
+
+ @state() private isLoggedIn = false;
+
constructor() {
super();
subscribe(
@@ -134,6 +136,11 @@
this.updateCheckedState(selectedChangeNums);
}
);
+ subscribe(
+ this,
+ () => this.getUserModel().loggedIn$,
+ isLoggedIn => (this.isLoggedIn = isLoggedIn)
+ );
}
override connectedCallback() {
@@ -332,6 +339,8 @@
}
private renderCellSelectionBox() {
+ if (!this.isLoggedIn) return;
+
return html`
<td class="cell selection">
<!--
@@ -352,7 +361,7 @@
}
private renderCellStar() {
- if (!this.showStar) return;
+ if (!this.isLoggedIn) return;
return html`
<td class="cell star">
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
index c7cb5b8..5e31cc8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.ts
@@ -8,9 +8,11 @@
import {
SubmitRequirementResultInfo,
NumericChangeId,
+ Timestamp,
} from '../../../api/rest-api';
import '../../../test/common-test-setup';
import {
+ createAccountWithEmail,
createAccountWithId,
createChange,
createSubmitRequirementExpressionInfo,
@@ -21,7 +23,6 @@
import {
query,
queryAndAssert,
- stubRestApi,
waitUntilObserved,
} from '../../../test/test-utils';
import {
@@ -43,6 +44,7 @@
bulkActionsModelToken,
BulkActionsModel,
} from '../../../models/bulk-actions/bulk-actions-model';
+import {UserModel, userModelToken} from '../../../models/user/user-model';
import {createTestAppContext} from '../../../test/test-app-context-init';
import {ColumnNames} from '../../../constants/constants';
import {testResolver} from '../../../test/common-test-setup';
@@ -58,13 +60,13 @@
let element: GrChangeListItem;
let bulkActionsModel: BulkActionsModel;
+ let userModel: UserModel;
setup(async () => {
- stubRestApi('getLoggedIn').returns(Promise.resolve(false));
-
bulkActionsModel = new BulkActionsModel(
createTestAppContext().restApiService
);
+ userModel = testResolver(userModelToken);
element = (
await fixture<DIProviderElement>(
wrapInProvider(
@@ -105,6 +107,10 @@
test('bulk actions checkboxes', async () => {
element.change = {...createChange(), _number: 1 as NumericChangeId};
bulkActionsModel.sync([element.change]);
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
await element.updateComplete;
const checkbox = queryAndAssert<HTMLInputElement>(
@@ -134,6 +140,10 @@
element.globalIndex = 5;
element.change = {...createChange(), _number: 1 as NumericChangeId};
bulkActionsModel.sync([element.change]);
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
await element.updateComplete;
const checkbox = queryAndAssert<HTMLInputElement>(
@@ -147,6 +157,10 @@
});
test('checkbox state updates with model updates', async () => {
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
element.requestUpdate();
await element.updateComplete;
@@ -168,6 +182,10 @@
});
test('checkbox state updates with change id update', async () => {
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
element.requestUpdate();
await element.updateComplete;
@@ -361,7 +379,10 @@
const change = createChange();
bulkActionsModel.sync([change]);
bulkActionsModel.addSelectedChangeNum(change._number);
- element.showStar = true;
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
element.showNumber = true;
element.account = createAccountWithId(1);
element.config = createServerInfo();
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section.ts
index 8227e11..61b276e 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section.ts
@@ -15,14 +15,15 @@
import {sharedStyles} from '../../../styles/shared-styles';
import {Metadata} from '../../../utils/change-metadata-util';
import {WAITING} from '../../../constants/constants';
-import {provide} from '../../../models/dependency';
+import {provide, resolve} from '../../../models/dependency';
import {
bulkActionsModelToken,
BulkActionsModel,
} from '../../../models/bulk-actions/bulk-actions-model';
+import {createSearchUrl} from '../../../models/views/search';
+import {userModelToken} from '../../../models/user/user-model';
import {subscribe} from '../../lit/subscription-controller';
import {classMap} from 'lit/directives/class-map.js';
-import {createSearchUrl} from '../../../models/views/search';
const NUMBER_FIXED_COLUMNS = 4;
const LABEL_PREFIX_INVALID_PROLOG = 'Invalid-Prolog-Rules-Label-Name--';
@@ -52,9 +53,6 @@
visibleChangeTableColumns?: string[];
@property({type: Boolean})
- showStar = false;
-
- @property({type: Boolean})
showNumber?: boolean; // No default value to prevent flickering.
@property({type: Number})
@@ -104,6 +102,10 @@
getAppContext().restApiService
);
+ private readonly getUserModel = resolve(this, userModelToken);
+
+ private isLoggedIn = false;
+
static override get styles() {
return [
changeListStyles,
@@ -156,6 +158,11 @@
() => this.bulkActionsModel.totalChangeCount$,
totalChangeCount => (this.totalChangeCount = totalChangeCount)
);
+ subscribe(
+ this,
+ () => this.getUserModel().loggedIn$,
+ isLoggedIn => (this.isLoggedIn = isLoggedIn)
+ );
}
override willUpdate(changedProperties: PropertyValues) {
@@ -189,8 +196,8 @@
<td class="leftPadding" aria-hidden="true"></td>
<td
class="star"
- ?aria-hidden=${!this.showStar}
- ?hidden=${!this.showStar}
+ ?aria-hidden=${!this.isLoggedIn}
+ ?hidden=${!this.isLoggedIn}
></td>
<td class="cell" colspan=${colSpan}>
${this.changeSection.emptyStateSlotName
@@ -213,7 +220,7 @@
<tbody>
<tr class="groupHeader">
<td aria-hidden="true" class="leftPadding"></td>
- <td aria-hidden="true" class="star" ?hidden=${!this.showStar}></td>
+ <td aria-hidden="true" class="star" ?hidden=${!this.isLoggedIn}></td>
<td class="cell" colspan=${colSpan}>
<h2 class="heading-3">
<a
@@ -248,7 +255,7 @@
: html` <td
class="star"
aria-label="Star status column"
- ?hidden=${!this.showStar}
+ ?hidden=${!this.isLoggedIn}
></td>
<td class="number" ?hidden=${!this.showNumber}>#</td>
${columns.map(item => this.renderHeaderCell(item))}
@@ -267,7 +274,7 @@
const indeterminate =
this.numSelected > 0 && this.numSelected !== this.totalChangeCount;
return html`
- <td class="selection">
+ <td class="selection" ?hidden=${!this.isLoggedIn}>
<!--
The .checked property must be used rather than the attribute because
the attribute only controls the default checked state and does not
@@ -322,7 +329,6 @@
.sectionName=${this.changeSection.name}
.visibleChangeTableColumns=${columns}
.showNumber=${this.showNumber}
- ?showStar=${this.showStar}
.usp=${this.usp}
.labelNames=${this.labelNames}
.globalIndex=${this.startIndex + index}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section_test.ts
index 8dfecdc..63552c7 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-section/gr-change-list-section_test.ts
@@ -13,9 +13,10 @@
import {
createChange,
createAccountDetailWithId,
+ createAccountWithEmail,
createServerInfo,
} from '../../../test/test-data-generators';
-import {NumericChangeId, ChangeInfoId} from '../../../api/rest-api';
+import {ChangeInfoId, NumericChangeId, Timestamp} from '../../../api/rest-api';
import {
queryAll,
query,
@@ -27,11 +28,15 @@
import {ChangeListSection} from '../gr-change-list/gr-change-list';
import {fixture, html, assert} from '@open-wc/testing';
import {ColumnNames} from '../../../constants/constants';
+import {testResolver} from '../../../test/common-test-setup';
+import {UserModel, userModelToken} from '../../../models/user/user-model';
suite('gr-change-list section', () => {
let element: GrChangeListSection;
+ let userModel: UserModel;
setup(async () => {
+ userModel = testResolver(userModelToken);
const changeSection: ChangeListSection = {
name: 'test',
query: 'test',
@@ -193,6 +198,10 @@
],
emptyStateSlotName: 'test',
};
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
await element.updateComplete;
let rows = queryAll(element, 'gr-change-list-item');
assert.lengthOf(rows, 2);
@@ -235,6 +244,10 @@
],
emptyStateSlotName: 'test',
};
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
await element.updateComplete;
const rows = queryAll(element, 'gr-change-list-item');
@@ -273,6 +286,31 @@
});
});
+ test('no checkbox when logged out', async () => {
+ element.changeSection = {
+ name: 'test',
+ query: 'test',
+ results: [
+ {
+ ...createChange(),
+ _number: 1 as NumericChangeId,
+ id: '1' as ChangeInfoId,
+ },
+ {
+ ...createChange(),
+ _number: 2 as NumericChangeId,
+ id: '2' as ChangeInfoId,
+ },
+ ],
+ emptyStateSlotName: 'test',
+ };
+ userModel.setAccount(undefined);
+ await element.updateComplete;
+ const rows = queryAll(element, 'gr-change-list-item');
+ assert.lengthOf(rows, 2);
+ assert.isUndefined(query<HTMLInputElement>(rows[0], 'input'));
+ });
+
test('colspans', async () => {
element.visibleChangeTableColumns = [];
element.changeSection = {results: [{...createChange()}]};
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
index d2ba2c9..1c86354 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
@@ -195,7 +195,6 @@
.account=${this.account}
.changes=${this.changes}
.preferences=${this.preferences}
- .showStar=${this.loggedIn}
@toggle-star=${(e: CustomEvent<ChangeStarToggleStarDetail>) => {
this.handleToggleStar(e);
}}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
index 4c43da5..748c2b8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
@@ -113,9 +113,6 @@
showNumber?: boolean; // No default value to prevent flickering.
@property({type: Boolean})
- showStar = false;
-
- @property({type: Boolean})
showReviewedState = false;
@property({type: Array})
@@ -270,7 +267,6 @@
sectionIndex,
this.sections
)}
- ?showStar=${this.showStar}
.showNumber=${this.showNumber}
.visibleChangeTableColumns=${this.visibleChangeTableColumns}
.usp=${this.usp}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.ts
index bef3166..7e9735f 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.ts
@@ -23,6 +23,7 @@
} from '../../../constants/constants';
import {AccountId, NumericChangeId} from '../../../types/common';
import {
+ createAccountWithEmail,
createChange,
createServerInfo,
createSubmitRequirementResultInfo,
@@ -32,12 +33,16 @@
import {fixture, assert} from '@open-wc/testing';
import {html} from 'lit';
import {testResolver} from '../../../test/common-test-setup';
+import {Timestamp} from '../../../api/rest-api';
+import {UserModel, userModelToken} from '../../../models/user/user-model';
suite('gr-change-list basic tests', () => {
let element: GrChangeList;
+ let userModel: UserModel;
setup(async () => {
element = await fixture(html`<gr-change-list></gr-change-list>`);
+ userModel = testResolver(userModelToken);
});
test('renders', async () => {
@@ -285,6 +290,12 @@
});
test('toggle checkbox keyboard shortcut', async () => {
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
+ await element.updateComplete;
+
const getCheckbox = (item: GrChangeListItem) =>
queryAndAssert<HTMLInputElement>(query(item, '.selection'), 'input');
@@ -515,7 +526,7 @@
assert.isTrue(element.isColumnEnabled('Subject'));
});
- test('showStar and showNumber', async () => {
+ test('loggedIn and showNumber', async () => {
element.sections = [{results: [{...createChange()}], name: 'a'}];
element.account = {_account_id: 1001 as AccountId};
element.preferences = {
@@ -534,6 +545,7 @@
],
};
element.config = createServerInfo();
+ userModel.setAccount(undefined);
await element.updateComplete;
const section = query<GrChangeListSection>(
element,
@@ -547,7 +559,10 @@
assert.isNotOk(query(query(section, 'gr-change-list-item'), '.star'));
assert.isNotOk(query(query(section, 'gr-change-list-item'), '.number'));
- element.showStar = true;
+ userModel.setAccount({
+ ...createAccountWithEmail('abc@def.com'),
+ registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
+ });
await element.updateComplete;
await section.updateComplete;
assert.isOk(query(query(section, 'gr-change-list-item'), '.star'));
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
index d013654..cd30440 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
@@ -284,7 +284,6 @@
${this.renderUserHeader()}
<h1 class="assistive-tech-only">Dashboard</h1>
<gr-change-list
- ?showStar=${true}
.account=${this.account}
.preferences=${this.preferences}
.sections=${this.results}
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts
index 17d7e95..84a3139 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts
@@ -82,7 +82,7 @@
<div class="loading" hidden="">Loading...</div>
<div>
<h1 class="assistive-tech-only">Dashboard</h1>
- <gr-change-list showstar="">
+ <gr-change-list>
<div id="emptyOutgoing" slot="outgoing-slot">No changes</div>
<div id="emptyYourTurn" slot="your-turn-slot">
<span> No changes need your attention  ðŸŽ‰ </span>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 72a6a3a..721d650 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -115,7 +115,6 @@
import {GrFileList} from '../gr-file-list/gr-file-list';
import {EditRevisionInfo, ParsedChangeInfo} from '../../../types/types';
import {
- CloseFixPreviewEvent,
EditableContentSaveEvent,
EventType,
OpenFixPreviewEvent,
@@ -590,8 +589,9 @@
this.addEventListener('editable-content-cancel', () =>
this.handleCommitMessageCancel()
);
- this.addEventListener('open-fix-preview', e => this.onOpenFixPreview(e));
- this.addEventListener('close-fix-preview', e => this.onCloseFixPreview(e));
+ this.addEventListener(EventType.OPEN_FIX_PREVIEW, e =>
+ this.onOpenFixPreview(e)
+ );
this.addEventListener(EventType.SHOW_TAB, e => this.setActiveTab(e));
this.addEventListener('reload', e => {
@@ -1675,10 +1675,6 @@
this.applyFixDialog.open(e);
}
- private onCloseFixPreview(e: CloseFixPreviewEvent) {
- if (e.detail.fixApplied) fireReload(this);
- }
-
// Private but used in tests.
handleToggleDiffMode() {
if (this.diffViewMode === DiffViewMode.SIDE_BY_SIDE) {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
index 6f8bd9a..c2739f3 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
@@ -218,7 +218,6 @@
<gr-autocomplete
id="parentInput"
.query=${this.query}
- no-debounce
.text=${this.text}
@text-changed=${(e: ValueChangedEvent) =>
(this.text = e.detail.value)}
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts
index 776e923..2644d81 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.ts
@@ -74,7 +74,6 @@
<gr-autocomplete
allow-non-suggested-values=""
id="parentInput"
- no-debounce=""
placeholder="Change number, ref, or commit hash"
>
</gr-autocomplete>
@@ -305,7 +304,6 @@
test('input text change triggers function', async () => {
const recentChangesSpy = sinon.spy(element, 'getRecentChanges');
- element.parentInput.noDebounce = true;
pressKey(
queryAndAssert(queryAndAssert(element, '#parentInput'), '#input'),
Key.ENTER
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index d4defcb..6eaf7ae 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -258,10 +258,6 @@
// Private but used in tests.
@state()
- displayLine?: boolean;
-
- // Private but used in tests.
- @state()
showSizeBars = true;
// For merge commits vs Auto Merge, an extra file row is shown detailing the
@@ -733,9 +729,6 @@
this.shortcutsController.addAbstract(Shortcut.TOGGLE_LEFT_PANE, _ =>
this.handleToggleLeftPane()
);
- this.shortcutsController.addGlobal({key: Key.ESC}, _ =>
- this.handleEscKey()
- );
this.shortcutsController.addAbstract(
Shortcut.EXPAND_ALL_COMMENT_THREADS,
_ => {}
@@ -1079,7 +1072,6 @@
<gr-diff-host
?noAutoRender=${true}
?showLoadFailure=${true}
- .displayLine=${this.displayLine}
.changeNum=${this.changeNum}
.change=${this.change}
.patchRange=${this.patchRange}
@@ -2032,7 +2024,6 @@
e.stopPropagation();
if (this.filesExpanded === FilesExpandedState.ALL) {
this.diffCursor?.moveDown();
- this.displayLine = true;
} else {
this.fileCursor.next({circular: true});
this.selectedIndex = this.fileCursor.index;
@@ -2052,7 +2043,6 @@
e.stopPropagation();
if (this.filesExpanded === FilesExpandedState.ALL) {
this.diffCursor?.moveUp();
- this.displayLine = true;
} else {
this.fileCursor.previous({circular: true});
this.selectedIndex = this.fileCursor.index;
@@ -2504,11 +2494,6 @@
return undefined;
}
- // Private but used in tests.
- handleEscKey() {
- this.displayLine = false;
- }
-
/**
* Compute size bar layout values from the file list.
* Private but used in tests.
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
index 1fbddc5..c79be96 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
@@ -83,9 +83,6 @@
stubRestApi('getDiffRobotComments').returns(Promise.resolve({}));
stubRestApi('getDiffDrafts').returns(Promise.resolve({}));
stubRestApi('getAccountCapabilities').returns(Promise.resolve({}));
- stubElement('gr-date-formatter', 'loadTimeFormat').callsFake(() =>
- Promise.resolve()
- );
stubElement('gr-diff-host', 'reload').callsFake(() => Promise.resolve());
stubElement('gr-diff-host', 'prefetchDiff').callsFake(() => {});
@@ -2071,9 +2068,6 @@
stubRestApi('getDiffComments').returns(Promise.resolve({}));
stubRestApi('getDiffRobotComments').returns(Promise.resolve({}));
stubRestApi('getDiffDrafts').returns(Promise.resolve({}));
- stubElement('gr-date-formatter', 'loadTimeFormat').callsFake(() =>
- Promise.resolve()
- );
stubRestApi('getDiff').callsFake(() => Promise.resolve(createDiff()));
stubElement('gr-diff-host', 'prefetchDiff').callsFake(() => {});
@@ -2305,22 +2299,6 @@
assert.isTrue(setUrlStub.calledOnce);
});
- test('displayLine', () => {
- element.filesExpanded = FilesExpandedState.ALL;
-
- element.displayLine = false;
- element.handleCursorNext(new KeyboardEvent('keydown'));
- assert.isTrue(element.displayLine);
-
- element.displayLine = false;
- element.handleCursorPrev(new KeyboardEvent('keydown'));
- assert.isTrue(element.displayLine);
-
- element.displayLine = true;
- element.handleEscKey();
- assert.isFalse(element.displayLine);
- });
-
suite('editMode behavior', () => {
test('reviewed checkbox', async () => {
reviewFileStub.restore();
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-page.ts b/polygerrit-ui/app/elements/core/gr-router/gr-page.ts
index 3af8207..1d2a272 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-page.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-page.ts
@@ -280,6 +280,7 @@
export class PageContext {
/**
* Includes everything: base, path, query and hash.
+ * NOT decoded.
*/
canonicalPath = '';
@@ -287,18 +288,21 @@
* Does not include base path.
* Does not include hash.
* Includes query string.
+ * NOT decoded.
*/
path = '';
- /** Does not include hash. */
+ /** Decoded. Does not include hash. */
querystring = '';
+ /** Decoded. */
hash = '';
/**
* Regular expression matches of capturing groups. The first entry params[0]
* corresponds to the first capturing group. The entire matched string is not
* returned in this array.
+ * Each param is double decoded.
*/
params: string[] = [];
@@ -346,17 +350,24 @@
replaceState() {
window.history.replaceState(this.state, this.title, this.canonicalPath);
}
+
+ match(re: RegExp) {
+ const qsIndex = this.path.indexOf('?');
+ const pathname = qsIndex !== -1 ? this.path.slice(0, qsIndex) : this.path;
+ const matches = re.exec(decodeURIComponent(pathname));
+ if (matches) {
+ this.params = matches
+ .slice(1)
+ .map(match => decodeURIComponentString(match));
+ }
+ return !!matches;
+ }
}
function createRoute(re: RegExp, fn: Function) {
return (ctx: PageContext, next: Function) => {
- const qsIndex = ctx.path.indexOf('?');
- const pathname = qsIndex !== -1 ? ctx.path.slice(0, qsIndex) : ctx.path;
- const matches = re.exec(decodeURIComponent(pathname));
+ const matches = ctx.match(re);
if (matches) {
- ctx.params = matches
- .slice(1)
- .map(match => decodeURIComponentString(match));
fn(ctx, next);
} else {
next();
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index 997d9d5..fe90471 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -14,7 +14,6 @@
import {assertIsDefined} from '../../../utils/common-util';
import {
BasePatchSetNum,
- DashboardId,
GroupId,
NumericChangeId,
RevisionPatchSetNum,
@@ -73,6 +72,7 @@
import {
DashboardViewModel,
DashboardViewState,
+ PROJECT_DASHBOARD_ROUTE,
} from '../../../models/views/dashboard';
import {
SettingsViewModel,
@@ -107,7 +107,6 @@
DASHBOARD: /^\/dashboard\/(.+)$/,
CUSTOM_DASHBOARD: /^\/dashboard\/?$/,
- PROJECT_DASHBOARD: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
LEGACY_PROJECT_DASHBOARD: /^\/projects\/(.+),dashboards\/(.+)/,
AGREEMENTS: /^\/settings\/agreements\/?/,
@@ -167,17 +166,12 @@
PLUGINS: /^\/plugins\/(.+)$/,
- // TODO: The first capturing group in the next 3 patterns works around a bug
- // in page.js that is fixed in version 1.11.6. Remove it when the new version
- // is being used at Google.
- // https://github.com/visionmedia/page.js/commit/60f764b0ca9ad55133bc373914e97a8927a8f2d5
-
// Matches /admin/plugins with optional filter and offset.
- PLUGIN_LIST: /^(\/admin\/plugins)\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
+ PLUGIN_LIST: /^\/admin\/plugins\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
// Matches /admin/groups with optional filter and offset.
- GROUP_LIST: /^(\/admin\/groups)\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
+ GROUP_LIST: /^\/admin\/groups\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
// Matches /admin/repos with optional filter and offset.
- REPO_LIST: /^(\/admin\/repos)\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
+ REPO_LIST: /^\/admin\/repos\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
// Matches /admin/repos/$REPO,branches with optional filter and offset.
BRANCH_LIST:
/^\/admin\/repos\/(.+),branches\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
@@ -635,10 +629,10 @@
ctx => this.handleCustomDashboardRoute(ctx)
);
- this.mapRoute(
- RoutePattern.PROJECT_DASHBOARD,
- 'handleProjectDashboardRoute',
- ctx => this.handleProjectDashboardRoute(ctx)
+ this.mapRouteState(
+ PROJECT_DASHBOARD_ROUTE,
+ this.dashboardViewModel,
+ 'handleProjectDashboardRoute'
);
this.mapRoute(
@@ -1008,19 +1002,6 @@
return Promise.resolve();
}
- handleProjectDashboardRoute(ctx: PageContext) {
- const project = ctx.params[0] as RepoName;
- const state: DashboardViewState = {
- view: GerritView.DASHBOARD,
- project,
- dashboard: decodeURIComponent(ctx.params[1]) as DashboardId,
- };
- // Note that router model view must be updated before view models.
- this.setState(state);
- this.dashboardViewModel.setState(state);
- this.reporting.setRepoName(project);
- }
-
handleLegacyProjectDashboardRoute(ctx: PageContext) {
this.redirect('/p/' + ctx.params[0] + '/+/dashboard/' + ctx.params[1]);
}
@@ -1070,10 +1051,10 @@
const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.GROUPS,
- offset: ctx.params[2] || '0',
- filter: ctx.params[1] ?? null,
+ offset: ctx.params[1] || '0',
+ filter: ctx.params[0] ?? null,
openCreateModal:
- !ctx.params[1] && !ctx.params[2] && ctx.hash === 'create',
+ !ctx.params[0] && !ctx.params[1] && ctx.hash === 'create',
};
// Note that router model view must be updated before view models.
this.setState(state);
@@ -1192,10 +1173,10 @@
const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.REPOS,
- offset: ctx.params[2] || '0',
- filter: ctx.params[1] ?? null,
+ offset: ctx.params[1] || '0',
+ filter: ctx.params[0] ?? null,
openCreateModal:
- !ctx.params[1] && !ctx.params[2] && ctx.hash === 'create',
+ !ctx.params[0] && !ctx.params[1] && ctx.hash === 'create',
};
// Note that router model view must be updated before view models.
this.setState(state);
@@ -1222,8 +1203,8 @@
const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.PLUGINS,
- offset: ctx.params[2] || '0',
- filter: ctx.params[1] ?? null,
+ offset: ctx.params[1] || '0',
+ filter: ctx.params[0] ?? null,
};
// Note that router model view must be updated before view models.
this.setState(state);
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
index 17edc19..314e126 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
@@ -198,6 +198,9 @@
return [
sharedStyles,
css`
+ gr-icon.searchIcon {
+ margin: 0 var(--spacing-xs);
+ }
form {
display: flex;
}
@@ -217,7 +220,6 @@
<gr-autocomplete
id="searchInput"
.label=${this.label}
- show-search-icon
.text=${this.inputVal}
.query=${this.query}
allow-non-suggested-values
@@ -232,6 +234,7 @@
this.handleSearchTextChanged(e);
}}
>
+ <gr-icon icon="search" class="searchIcon" slot="prefix"></gr-icon>
<a
class="help"
slot="suffix"
@@ -275,7 +278,7 @@
// fallback to gerrit's official doc
let baseUrl =
this.docsBaseUrl ||
- 'https://gerrit-review.googlesource.com/documentation/';
+ 'https://gerrit-review.googlesource.com/Documentation/';
if (baseUrl.endsWith('/')) {
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
}
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.ts b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.ts
index 01e2fb6..0694453 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.ts
@@ -69,9 +69,9 @@
allow-non-suggested-values=""
id="searchInput"
multi=""
- show-search-icon=""
tab-complete=""
>
+ <gr-icon icon="search" class="searchIcon" slot="prefix"></gr-icon>
<a
class="help"
href="https://mydocumentationurl.google.com/user-search.html"
@@ -319,7 +319,7 @@
await element.updateComplete;
assert.equal(
element.computeHelpDocLink(),
- 'https://gerrit-review.googlesource.com/documentation/' +
+ 'https://gerrit-review.googlesource.com/Documentation/' +
'user-search.html'
);
});
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
index b146e93..1608c22 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
@@ -20,7 +20,6 @@
import {PROVIDED_FIX_ID} from '../../../utils/comment-util';
import {OpenFixPreviewEvent} from '../../../types/events';
import {getAppContext} from '../../../services/app-context';
-import {fireCloseFixPreview} from '../../../utils/event-util';
import {DiffLayer, ParsedChangeInfo} from '../../../types/types';
import {GrButton} from '../../shared/gr-button/gr-button';
import {TokenHighlightLayer} from '../../../embed/diff/gr-diff-builder/token-highlight-layer';
@@ -37,6 +36,8 @@
import {GrSyntaxLayerWorker} from '../../../embed/diff/gr-syntax-layer/gr-syntax-layer-worker';
import {highlightServiceToken} from '../../../services/highlight/highlight-service';
import {anyLineTooLong} from '../../../embed/diff/gr-diff/gr-diff-utils';
+import {changeModelToken} from '../../../models/change/change-model';
+import {fireReload} from '../../../utils/event-util';
interface FilePreview {
filepath: string;
@@ -90,12 +91,20 @@
@state()
diffPrefs?: DiffPreferencesInfo;
+ @state()
+ isOwner = false;
+
+ @state()
+ onCloseFixPreviewCallbacks: ((fixapplied: boolean) => void)[] = [];
+
private readonly restApiService = getAppContext().restApiService;
private readonly getUserModel = resolve(this, userModelToken);
private readonly getNavigation = resolve(this, navigationToken);
+ private readonly getChangeModel = resolve(this, changeModelToken);
+
private readonly syntaxLayer = new GrSyntaxLayerWorker(
resolve(this, highlightServiceToken),
() => getAppContext().reportingService
@@ -105,6 +114,11 @@
super();
subscribe(
this,
+ () => this.getChangeModel().isOwner$,
+ x => (this.isOwner = x)
+ );
+ subscribe(
+ this,
() => this.getUserModel().preferences$,
preferences => {
const layers: DiffLayer[] = [this.syntaxLayer];
@@ -234,6 +248,7 @@
open(e: OpenFixPreviewEvent) {
this.patchNum = e.detail.patchNum;
this.fixSuggestions = e.detail.fixSuggestions;
+ this.onCloseFixPreviewCallbacks = e.detail.onCloseFixPreviewCallbacks;
assert(this.fixSuggestions.length > 0, 'no fix in the event');
this.selectedFixIdx = 0;
this.applyFixModal?.showModal();
@@ -319,12 +334,14 @@
this.currentPreviews = [];
this.isApplyFixLoading = false;
- fireCloseFixPreview(this, fixApplied);
+ this.onCloseFixPreviewCallbacks.forEach(fn => fn(fixApplied));
this.applyFixModal?.close();
+ if (fixApplied) fireReload(this);
}
private computeTooltip() {
if (!this.change || !this.patchNum) return '';
+ if (!this.isOwner) return 'Fix can only be applied by author';
const latestPatchNum =
this.change.revisions[this.change.current_revision]._number;
return latestPatchNum !== this.patchNum
@@ -334,6 +351,7 @@
private computeDisableApplyFixButton() {
if (!this.change || !this.patchNum) return true;
+ if (!this.isOwner) return true;
const latestPatchNum =
this.change.revisions[this.change.current_revision]._number;
return this.patchNum !== latestPatchNum || this.isApplyFixLoading;
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
index 24dadf7..9284fb2 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
@@ -17,11 +17,7 @@
} from '../../../test/test-data-generators';
import {createDefaultDiffPrefs} from '../../../constants/constants';
import {DiffInfo} from '../../../types/diff';
-import {
- CloseFixPreviewEventDetail,
- EventType,
- OpenFixPreviewEventDetail,
-} from '../../../types/events';
+import {EventType, OpenFixPreviewEventDetail} from '../../../types/events';
import {GrButton} from '../../shared/gr-button/gr-button';
import {fixture, html, assert} from '@open-wc/testing';
import {SinonStub} from 'sinon';
@@ -37,11 +33,13 @@
createFixSuggestionInfo('fix_1'),
createFixSuggestionInfo('fix_2'),
],
+ onCloseFixPreviewCallbacks: [],
};
const ONE_FIX: OpenFixPreviewEventDetail = {
patchNum: 2 as PatchSetNum,
fixSuggestions: [createFixSuggestionInfo('fix_1')],
+ onCloseFixPreviewCallbacks: [],
};
function getConfirmButton(): GrButton {
@@ -73,6 +71,7 @@
element.changeNum = change._number;
element.patchNum = change.revisions[change.current_revision]._number;
element.change = change;
+ element.isOwner = true;
element.diffPrefs = {
...createDefaultDiffPrefs(),
font_size: 12,
@@ -162,8 +161,22 @@
assert.equal(button.getAttribute('title'), '');
});
+ test('apply fix button is disabled for non-author', async () => {
+ element.isOwner = false;
+ await element.updateComplete;
+ await open(TWO_FIXES);
+ assert.equal(element.currentFix!.fix_id, 'fix_1');
+ assert.equal(element.currentPreviews.length, 2);
+ const button = getConfirmButton();
+ assert.isTrue(button.hasAttribute('disabled'));
+ assert.equal(
+ button.getAttribute('title'),
+ 'Fix can only be applied by author'
+ );
+ });
+
test('apply fix button is disabled on older patchset', async () => {
- element.change = element.change = {
+ element.change = {
...createParsedChange(),
revisions: createRevisions(2),
current_revision: getCurrentRevision(0),
@@ -246,11 +259,7 @@
element.currentFix = createFixSuggestionInfo('123');
const closeFixPreviewEventSpy = sinon.spy();
- // Element is recreated after each test, removeEventListener isn't required
- element.addEventListener(
- EventType.CLOSE_FIX_PREVIEW,
- closeFixPreviewEventSpy
- );
+ element.onCloseFixPreviewCallbacks.push(closeFixPreviewEventSpy);
await element.handleApplyFix(new CustomEvent('confirm'));
@@ -263,14 +272,7 @@
assert.isTrue(setUrlStub.called);
assert.equal(setUrlStub.lastCall.firstArg, '/c/test-project/+/42/2..edit');
- sinon.assert.calledOnceWithExactly(
- closeFixPreviewEventSpy,
- new CustomEvent<CloseFixPreviewEventDetail>(EventType.CLOSE_FIX_PREVIEW, {
- detail: {
- fixApplied: true,
- },
- })
- );
+ sinon.assert.calledOnceWithExactly(closeFixPreviewEventSpy, true);
// reset gr-apply-fix-dialog and close
assert.equal(element.currentFix, undefined);
assert.equal(element.currentPreviews.length, 0);
@@ -311,11 +313,7 @@
element.currentFix = createFixSuggestionInfo('fix_123');
const closeFixPreviewEventSpy = sinon.spy();
- // Element is recreated after each test, removeEventListener isn't required
- element.addEventListener(
- EventType.CLOSE_FIX_PREVIEW,
- closeFixPreviewEventSpy
- );
+ element.onCloseFixPreviewCallbacks.push(closeFixPreviewEventSpy);
let expectedError;
await element.handleApplyFix(new CustomEvent('click')).catch(e => {
@@ -328,19 +326,8 @@
test('onCancel fires close with correct parameters', () => {
const closeFixPreviewEventSpy = sinon.spy();
- // Element is recreated after each test, removeEventListener isn't required
- element.addEventListener(
- EventType.CLOSE_FIX_PREVIEW,
- closeFixPreviewEventSpy
- );
+ element.onCloseFixPreviewCallbacks.push(closeFixPreviewEventSpy);
element.onCancel(new CustomEvent('cancel'));
- sinon.assert.calledOnceWithExactly(
- closeFixPreviewEventSpy,
- new CustomEvent<CloseFixPreviewEventDetail>(EventType.CLOSE_FIX_PREVIEW, {
- detail: {
- fixApplied: false,
- },
- })
- );
+ sinon.assert.calledOnceWithExactly(closeFixPreviewEventSpy, false);
});
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
index 23dce97..fd0e5d2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
@@ -171,9 +171,6 @@
@property({type: String})
projectName?: RepoName;
- @property({type: Boolean})
- displayLine = false;
-
@state()
private _isImageDiff = false;
@@ -522,7 +519,6 @@
.noAutoRender=${this.noAutoRender}
.path=${this.path}
.prefs=${this.prefs}
- .displayLine=${this.displayLine}
.isImageDiff=${this.isImageDiff}
.noRenderOnPrefsChange=${this.noRenderOnPrefsChange}
.renderPrefs=${this.renderPrefs}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
index be03afd..7a32c27 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
@@ -774,14 +774,6 @@
assert.equal(element.diffElement.prefs, value);
});
- test('passes in displayLine', async () => {
- const value = true;
- element.displayLine = value;
- await element.updateComplete;
- assertIsDefined(element.diffElement);
- assert.equal(element.diffElement.displayLine, value);
- });
-
test('passes in hidden', async () => {
const value = true;
element.hidden = value;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index c7d6948..19186ec 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -56,7 +56,7 @@
import {OpenFixPreviewEvent, ValueChangedEvent} from '../../../types/events';
import {fireAlert, fireEvent, fireTitleChange} from '../../../utils/event-util';
import {assertIsDefined, queryAndAssert} from '../../../utils/common-util';
-import {Key, toggleClass, whenVisible} from '../../../utils/dom-util';
+import {toggleClass, whenVisible} from '../../../utils/dom-util';
import {CursorMoveResult} from '../../../api/core';
import {throttleWrap} from '../../../utils/async-util';
import {filter, take, switchMap} from 'rxjs/operators';
@@ -344,10 +344,6 @@
);
listen(Shortcut.EXPAND_ALL_COMMENT_THREADS, _ => {}); // docOnly
listen(Shortcut.COLLAPSE_ALL_COMMENT_THREADS, _ => {}); // docOnly
- this.shortcutsController.addGlobal({key: Key.ESC}, _ => {
- assertIsDefined(this.diffHost, 'diffHost');
- this.diffHost.displayLine = false;
- });
}
private setupSubscriptions() {
@@ -1067,7 +1063,6 @@
private handlePrevLine() {
assertIsDefined(this.diffHost, 'diffHost');
- this.diffHost.displayLine = true;
this.cursor?.moveUp();
}
@@ -1102,7 +1097,6 @@
private handleNextLine() {
assertIsDefined(this.diffHost, 'diffHost');
- this.diffHost.displayLine = true;
this.cursor?.moveDown();
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
index 889e9dd..6507046 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
@@ -61,7 +61,6 @@
import {GrDiffModeSelector} from '../../../embed/diff/gr-diff-mode-selector/gr-diff-mode-selector';
import {fixture, html, assert} from '@open-wc/testing';
import {EventType} from '../../../types/events';
-import {Key} from '../../../utils/dom-util';
import {GrButton} from '../../shared/gr-button/gr-button';
import {testResolver} from '../../../test/common-test-setup';
import {UserModel, userModelToken} from '../../../models/user/user-model';
@@ -459,15 +458,6 @@
element.diffHost.diffElement.viewMode,
DiffViewMode.SIDE_BY_SIDE
);
- assert.isTrue(element.diffHost.diffElement.displayLine);
-
- pressKey(element, Key.ESC);
- await element.updateComplete;
- assert.equal(
- element.diffHost.diffElement.viewMode,
- DiffViewMode.SIDE_BY_SIDE
- );
- assert.isFalse(element.diffHost.diffElement.displayLine);
const setReviewedStub = sinon.stub(element, 'setReviewed');
const handleToggleSpy = sinon.spy(element, 'handleToggleFileReviewed');
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts
index 31283ad..0729f21 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts
@@ -217,16 +217,20 @@
assert.isFalse(hideDialogStub.called);
queryAndAssert<GrButton>(element, '#open').click();
element.patchNum = 1 as RevisionPatchSetNum;
- await waitUntilVisible(element.modal!);
+ await showDialogSpy.lastCall.returnValue;
assert.isTrue(hideDialogStub.called);
assert.isTrue(element.openDialog!.disabled);
assert.isFalse(queryStub.called);
// Setup focused manually - in headless mode Chrome sometimes doesn't
// setup focus. waitEventLoop() doesn't help.
openAutoComplete.focused = true;
- openAutoComplete.noDebounce = true;
openAutoComplete.text = 'src/test.cpp';
+ // Focus happens after updateComplete, so we first wait for it explicitly.
+ await new Promise<void>(resolve => {
+ openAutoComplete.addEventListener('focus', () => resolve());
+ });
await element.updateComplete;
+ await openAutoComplete.latestSuggestionUpdateComplete;
assert.isTrue(queryStub.called);
await waitUntil(() => !element.openDialog!.disabled);
queryAndAssert<GrButton>(
@@ -242,7 +246,6 @@
queryAndAssert<GrButton>(element, '#open').click();
await waitUntilVisible(element.modal!);
assert.isTrue(element.openDialog!.disabled);
- openAutoComplete.noDebounce = true;
openAutoComplete.text = 'src/test.cpp';
await element.updateComplete;
await waitUntil(() => !element.openDialog!.disabled);
@@ -277,9 +280,13 @@
// Setup focused manually - in headless mode Chrome sometimes doesn't
// setup focus. waitEventLoop() doesn't help.
deleteAutocomplete.focused = true;
- deleteAutocomplete.noDebounce = true;
deleteAutocomplete.text = 'src/test.cpp';
+ // Focus happens after updateComplete, so we first wait for it explicitly.
+ await new Promise<void>(resolve => {
+ deleteAutocomplete.addEventListener('focus', () => resolve());
+ });
await element.updateComplete;
+ await deleteAutocomplete.latestSuggestionUpdateComplete;
assert.isTrue(queryStub.called);
await waitUntil(() => !element.deleteDialog!.disabled);
queryAndAssert<GrButton>(
@@ -304,9 +311,13 @@
// Setup focused manually - in headless mode Chrome sometimes doesn't
// setup focus. waitEventLoop() doesn't help.
deleteAutocomplete.focused = true;
- deleteAutocomplete.noDebounce = true;
deleteAutocomplete.text = 'src/test.cpp';
+ // Focus happens after updateComplete, so we first wait for it explicitly.
+ await new Promise<void>(resolve => {
+ deleteAutocomplete.addEventListener('focus', () => resolve());
+ });
await element.updateComplete;
+ await deleteAutocomplete.latestSuggestionUpdateComplete;
assert.isTrue(queryStub.called);
await waitUntil(() => !element.deleteDialog!.disabled);
queryAndAssert<GrButton>(
@@ -363,9 +374,13 @@
// Setup focused manually - in headless mode Chrome sometimes doesn't
// setup focus. waitEventLoop() doesn't help.
renameAutocomplete.focused = true;
- renameAutocomplete.noDebounce = true;
renameAutocomplete.text = 'src/test.cpp';
+ // Focus happens after updateComplete, so we first wait for it explicitly.
+ await new Promise<void>(resolve => {
+ renameAutocomplete.addEventListener('focus', () => resolve());
+ });
await element.updateComplete;
+ await renameAutocomplete.latestSuggestionUpdateComplete;
assert.isTrue(queryStub.called);
assert.isTrue(element.renameDialog!.disabled);
@@ -395,9 +410,13 @@
// Setup focused manually - in headless mode Chrome sometimes doesn't
// setup focus. waitEventLoop() doesn't help.
renameAutocomplete.focused = true;
- renameAutocomplete.noDebounce = true;
renameAutocomplete.text = 'src/test.cpp';
+ // Focus happens after updateComplete, so we first wait for it explicitly.
+ await new Promise<void>(resolve => {
+ renameAutocomplete.addEventListener('focus', () => resolve());
+ });
await element.updateComplete;
+ await renameAutocomplete.latestSuggestionUpdateComplete;
assert.isTrue(queryStub.called);
assert.isTrue(element.renameDialog!.disabled);
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index b096f76..e5991f6 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -500,11 +500,12 @@
}
private renderEditorView() {
- // The `cache()` is required for re-using the editor view when switching
- // back and forth between change, diff and editor views.
- return cache(
- this.isEditorView() ? html`<gr-editor-view></gr-editor-view>` : nothing
- );
+ // For some reason caching the editor view caused an issue (b/269308770).
+ // We did not bother to root cause that issue, but instead let's forgo
+ // caching of the editor view. It does not help much anyway.
+ return this.isEditorView()
+ ? html`<gr-editor-view></gr-editor-view>`
+ : nothing;
}
private isEditorView() {
diff --git a/polygerrit-ui/app/elements/gr-app-global-var-init.ts b/polygerrit-ui/app/elements/gr-app-global-var-init.ts
index da9c0c9..d66fec0 100644
--- a/polygerrit-ui/app/elements/gr-app-global-var-init.ts
+++ b/polygerrit-ui/app/elements/gr-app-global-var-init.ts
@@ -14,11 +14,27 @@
import {GrAnnotation} from '../embed/diff/gr-diff-highlight/gr-annotation';
import {GrPluginActionContext} from './shared/gr-js-api-interface/gr-plugin-action-context';
import {AppContext, injectAppContext} from '../services/app-context';
-import {Finalizable} from '../services/registry';
import {PluginLoader} from './shared/gr-js-api-interface/gr-plugin-loader';
+import {
+ initVisibilityReporter,
+ initPerformanceReporter,
+ initErrorReporter,
+ initWebVitals,
+} from '../services/gr-reporting/gr-reporting_impl';
+import {Finalizable} from '../services/registry';
-export function initGlobalVariables(appContext: AppContext & Finalizable) {
+export function initGlobalVariables(
+ appContext: AppContext & Finalizable,
+ initializeReporting: boolean
+) {
injectAppContext(appContext);
+ if (initializeReporting) {
+ const reportingService = appContext.reportingService;
+ initVisibilityReporter(reportingService);
+ initPerformanceReporter(reportingService);
+ initWebVitals(reportingService);
+ initErrorReporter(reportingService);
+ }
window.GrAnnotation = GrAnnotation;
window.GrPluginActionContext = GrPluginActionContext;
}
diff --git a/polygerrit-ui/app/elements/gr-app.ts b/polygerrit-ui/app/elements/gr-app.ts
index 645f94a..ded0626 100644
--- a/polygerrit-ui/app/elements/gr-app.ts
+++ b/polygerrit-ui/app/elements/gr-app.ts
@@ -37,12 +37,6 @@
createAppDependencies,
Creator,
} from '../services/app-context-init';
-import {
- initVisibilityReporter,
- initPerformanceReporter,
- initErrorReporter,
- initWebVitals,
-} from '../services/gr-reporting/gr-reporting_impl';
import {html, LitElement} from 'lit';
import {customElement} from 'lit/decorators.js';
import {
@@ -50,14 +44,9 @@
serviceWorkerInstallerToken,
} from '../services/service-worker-installer';
import {pluginLoaderToken} from './shared/gr-js-api-interface/gr-plugin-loader';
+import {getAppContext} from '../services/app-context';
-const appContext = createAppContext();
-initGlobalVariables(appContext);
-const reportingService = appContext.reportingService;
-initVisibilityReporter(reportingService);
-initPerformanceReporter(reportingService);
-initWebVitals(reportingService);
-initErrorReporter(reportingService);
+initGlobalVariables(createAppContext(), true);
installPolymerResin(safeTypesBridge);
@@ -97,7 +86,7 @@
};
for (const [token, creator] of createAppDependencies(
- appContext,
+ getAppContext(),
resolver
)) {
injectDependency(token, creator);
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts
index 6b4d670..cc2723e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts
@@ -401,8 +401,8 @@
);
input.text = 'newTest';
input.input!.focus();
- input.noDebounce = true;
await element.updateComplete;
+ await input.latestSuggestionUpdateComplete;
assert.isTrue(getSuggestionsStub.calledOnce);
assert.equal(getSuggestionsStub.lastCall.args[0], 'newTest');
await waitUntil(() => makeSuggestionItemSpy.getCalls().length === 2);
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts
index f7f8ea5..f8f7f9d 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.ts
@@ -6,11 +6,14 @@
import '@polymer/paper-input/paper-input';
import '../gr-autocomplete-dropdown/gr-autocomplete-dropdown';
import '../gr-cursor-manager/gr-cursor-manager';
-import '../gr-icon/gr-icon';
import '../../../styles/shared-styles';
import {GrAutocompleteDropdown} from '../gr-autocomplete-dropdown/gr-autocomplete-dropdown';
import {fire, fireEvent} from '../../../utils/event-util';
-import {debounce, DelayedTask} from '../../../utils/async-util';
+import {
+ debounce,
+ DelayedTask,
+ ResolvedDelayedTaskStatus,
+} from '../../../utils/async-util';
import {PropertyType} from '../../../types/common';
import {modifierPressed} from '../../../utils/dom-util';
import {sharedStyles} from '../../../styles/shared-styles';
@@ -108,9 +111,6 @@
@property({type: Boolean})
disabled = false;
- @property({type: Boolean, attribute: 'show-search-icon'})
- showSearchIcon = false;
-
/**
* Vertical offset needed for an element with 20px line-height, 4px
* padding and 1px border (30px height total). Plus 1px spacing between
@@ -154,12 +154,6 @@
@property({type: Boolean, attribute: 'warn-uncommitted'})
warnUncommitted = false;
- /**
- * When true, querying for suggestions is not debounced w/r/t keypresses
- */
- @property({type: Boolean, attribute: 'no-debounce'})
- noDebounce = false;
-
@property({type: Boolean, attribute: 'show-blue-focus-border'})
showBlueFocusBorder = false;
@@ -187,6 +181,15 @@
private updateSuggestionsTask?: DelayedTask;
+ /**
+ * @return Promise that resolves when suggestions are update.
+ */
+ get latestSuggestionUpdateComplete():
+ | Promise<ResolvedDelayedTaskStatus>
+ | undefined {
+ return this.updateSuggestionsTask?.promise;
+ }
+
get nativeInput() {
return (this.input!.inputElement as IronInputElement)
.inputElement as HTMLInputElement;
@@ -195,15 +198,6 @@
static override styles = [
sharedStyles,
css`
- .searchIcon {
- display: none;
- }
- .searchIcon.showSearchIcon {
- display: inline-block;
- }
- gr-icon {
- margin: 0 var(--spacing-xs);
- }
paper-input.borderless {
border: none;
padding: 0;
@@ -267,11 +261,7 @@
}
override willUpdate(changedProperties: PropertyValues) {
- if (
- changedProperties.has('text') ||
- changedProperties.has('threshold') ||
- changedProperties.has('noDebounce')
- ) {
+ if (changedProperties.has('text') || changedProperties.has('threshold')) {
this.updateSuggestions();
}
if (
@@ -307,12 +297,7 @@
.label=${this.label}
>
<div slot="prefix">
- <gr-icon
- icon="search"
- class="searchIcon ${this.computeShowSearchIconClass(
- this.showSearchIcon
- )}"
- ></gr-icon>
+ <slot name="prefix"></slot>
</div>
<div slot="suffix">
@@ -422,12 +407,7 @@
}
updateSuggestions() {
- if (
- this.text === undefined ||
- this.threshold === undefined ||
- this.noDebounce === undefined
- )
- return;
+ if (this.text === undefined || this.threshold === undefined) return;
// Reset suggestions for every update
// This will also prevent from carrying over suggestions:
@@ -455,10 +435,11 @@
return;
}
+ const requestText = this.text;
const update = () => {
query(this.text)
.then(suggestions => {
- if (this.text !== this.text) {
+ if (requestText !== this.text) {
// Late response.
return;
}
@@ -480,15 +461,11 @@
});
};
- if (this.noDebounce) {
- update();
- } else {
- this.updateSuggestionsTask = debounce(
- this.updateSuggestionsTask,
- update,
- DEBOUNCE_WAIT_MS
- );
- }
+ this.updateSuggestionsTask = debounce(
+ this.updateSuggestionsTask,
+ update,
+ DEBOUNCE_WAIT_MS
+ );
}
setFocus(focused: boolean) {
@@ -682,10 +659,6 @@
);
}
}
-
- computeShowSearchIconClass(showSearchIcon: boolean) {
- return showSearchIcon ? 'showSearchIcon' : '';
- }
}
/**
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.ts b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.ts
index e593c0d..81949c7 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.ts
@@ -46,7 +46,7 @@
tabindex="0"
>
<div slot="prefix">
- <gr-icon icon="search" class="searchIcon"></gr-icon>
+ <slot name="prefix"> </slot>
</div>
<div slot="suffix">
<slot name="suffix"> </slot>
@@ -96,7 +96,7 @@
tabindex="0"
>
<div slot="prefix">
- <gr-icon icon="search" class="searchIcon"></gr-icon>
+ <slot name="prefix"> </slot>
</div>
<div slot="suffix">
<slot name="suffix"> </slot>
@@ -135,7 +135,7 @@
tabindex="0"
>
<div slot="prefix">
- <gr-icon icon="search" class="searchIcon"></gr-icon>
+ <slot name="prefix"> </slot>
</div>
<div slot="suffix">
<slot name="suffix"> </slot>
@@ -391,7 +391,6 @@
element.query = queryStub;
await element.updateComplete;
- element.noDebounce = false;
focusOnInput();
element.text = 'a';
@@ -413,7 +412,6 @@
test('empty text results in no suggestions', async () => {
element.text = '';
element.threshold = 0;
- element.noDebounce = false;
await element.updateComplete;
assert.equal(element.suggestions.length, 0);
});
@@ -475,7 +473,6 @@
assert.equal(element.suggestions.length, 1);
element.text = '';
element.threshold = 0;
- element.noDebounce = false;
await element.updateComplete;
assert.equal(element.suggestions.length, 0);
});
@@ -494,7 +491,6 @@
await waitUntil(() => element.queryErrorMessage === 'Test error');
element.text = '';
element.threshold = 0;
- element.noDebounce = false;
await element.updateComplete;
assert.isUndefined(element.queryErrorMessage);
});
@@ -563,20 +559,6 @@
assert.isTrue(element.focused);
});
- test('search icon shows with showSearchIcon property', async () => {
- assert.equal(
- getComputedStyle(queryAndAssert(element, 'gr-icon')).display,
- 'none'
- );
- element.showSearchIcon = true;
- await element.updateComplete;
-
- assert.notEqual(
- getComputedStyle(queryAndAssert(element, 'gr-icon')).display,
- 'none'
- );
- });
-
test('vertical offset overridden by param if it exists', async () => {
assert.equal(suggestionsEl().verticalOffset, 31);
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 1ac89d6..c844d42 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -1053,6 +1053,11 @@
replacement
),
patchNum: this.comment.patch_set,
+ onCloseFixPreviewCallbacks: [
+ fixApplied => {
+ if (fixApplied) this.handleAppliedFix();
+ },
+ ],
};
}
if (isRobot(this.comment) && this.comment.fix_suggestions.length > 0) {
@@ -1065,6 +1070,7 @@
};
}),
patchNum: this.comment.patch_set,
+ onCloseFixPreviewCallbacks: [],
};
}
throw new Error('unable to create preview fix event');
@@ -1132,6 +1138,18 @@
fire(this, 'reply-to-comment', eventDetail);
}
+ private handleAppliedFix() {
+ const message = this.comment?.message;
+ assert(!!message, 'empty message');
+ const eventDetail: ReplyToCommentEventDetail = {
+ content: 'Fix applied.',
+ userWantsToEdit: false,
+ unresolved: false,
+ };
+ // Handled by <gr-comment-thread>.
+ fire(this, 'reply-to-comment', eventDetail);
+ }
+
private async handleShowFix() {
// Handled top-level in the diff and change view components.
fire(this, 'open-fix-preview', await this.createFixPreview());
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts
index 25ee130..05b7fb5 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts
@@ -18,8 +18,10 @@
} from '../../../utils/date-util';
import {TimeFormat, DateFormat} from '../../../constants/constants';
import {assertNever} from '../../../utils/common-util';
-import {Timestamp} from '../../../types/common';
-import {getAppContext} from '../../../services/app-context';
+import {PreferencesInfo, Timestamp} from '../../../types/common';
+import {resolve} from '../../../models/dependency';
+import {userModelToken} from '../../../models/user/user-model';
+import {subscribe} from '../../lit/subscription-controller';
const TimeFormats = {
TIME_12: 'h:mm A', // 2:14 PM
@@ -95,7 +97,7 @@
@state()
relative = false;
- private readonly restApiService = getAppContext().restApiService;
+ private readonly getUserModel = resolve(this, userModelToken);
static override get styles() {
return [
@@ -108,17 +110,30 @@
];
}
- override render() {
- if (!this.withTooltip) {
- return this.renderDateString();
- }
+ constructor() {
+ super();
+ subscribe(
+ this,
+ () => this.getUserModel().preferences$,
+ prefs => this.setPreferences(prefs)
+ );
+ }
- const fullDateStr = this.computeFullDateStr();
- if (!fullDateStr) {
- return this.renderDateString();
- }
+ // private but used by tests
+ setPreferences(prefs: PreferencesInfo) {
+ this.decideDateFormat(prefs.date_format);
+ this.decideTimeFormat(prefs.time_format);
+ this.relative =
+ this.forceRelative || Boolean(prefs?.relative_date_in_change_table);
+ }
+
+ override render() {
+ if (!this.withTooltip) return this.renderDateString();
+ const tooltip = this.computeFullDateStr();
+ if (!tooltip) return this.renderDateString();
+
return html`
- <gr-tooltip-content has-tooltip title=${fullDateStr}>
+ <gr-tooltip-content has-tooltip title=${tooltip}>
${this.renderDateString()}
</gr-tooltip-content>
`;
@@ -128,38 +143,11 @@
return html` <span>${this.computeDateStr()}</span>`;
}
- override connectedCallback() {
- super.connectedCallback();
- this.loadPreferences();
- }
-
// private but used by tests
- _getUtcOffsetString() {
+ getUtcOffsetString() {
return utcOffsetString();
}
- // private but used by tests
- async loadPreferences() {
- const loggedIn = await this.restApiService.getLoggedIn();
- if (!loggedIn) {
- this.timeFormat = TimeFormats.TIME_24;
- this.dateFormat = DateFormats.STD;
- this.relative = this.forceRelative;
- return;
- }
- await Promise.all([this.loadTimeFormat(), this.loadRelative()]);
- }
-
- // private but used in gr/file-list_test.ts
- async loadTimeFormat() {
- const preferences = await this.restApiService.getPreferences();
- if (!preferences) {
- throw Error('Preferences is not set');
- }
- this.decideTimeFormat(preferences.time_format);
- this.decideDateFormat(preferences.date_format);
- }
-
private decideTimeFormat(timeFormat: TimeFormat) {
switch (timeFormat) {
case TimeFormat.HHMM_12:
@@ -195,12 +183,6 @@
}
}
- private async loadRelative() {
- const prefs = await this.restApiService.getPreferences();
- this.relative =
- this.forceRelative || Boolean(prefs?.relative_date_in_change_table);
- }
-
private computeDateStr() {
if (!this.dateStr || !this.timeFormat || !this.dateFormat) {
return '';
@@ -222,33 +204,24 @@
if (isWithinHalfYear(now, date)) {
format = this.dateFormat.short;
}
- if (this.showDateAndTime || this.showDateAndTime) {
+ if (this.showDateAndTime) {
format = `${format} ${this.timeFormat}`;
}
}
return formatDate(date, format);
}
- private computeFullDateStr() {
- if (
- [this.dateStr, this.timeFormat].includes(undefined) ||
- !this.dateFormat
- ) {
- return undefined;
- }
-
- if (!this.dateStr) {
- return '';
- }
+ private computeFullDateStr(): string {
+ if (!this.dateStr) return '';
+ if (!this.timeFormat) return '';
+ if (!this.dateFormat) return '';
const date = parseDate(this.dateStr as Timestamp);
- if (!isValidDate(date)) {
- return '';
- }
- let format = this.dateFormat.full + ', ';
- format +=
+ if (!isValidDate(date)) return '';
+ const timeFormat =
this.timeFormat === TimeFormats.TIME_12
? TimeFormats.TIME_12_WITH_SEC
: TimeFormats.TIME_24_WITH_SEC;
- return formatDate(date, format) + this._getUtcOffsetString();
+ const format = `dddd, ${this.dateFormat.full}, ${timeFormat}`;
+ return formatDate(date, format) + this.getUtcOffsetString();
}
}
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.ts b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.ts
index d7c38df..98f2d82 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.ts
@@ -8,16 +8,15 @@
import {GrDateFormatter} from './gr-date-formatter';
import {parseDate} from '../../../utils/date-util';
import {fixture, html, assert} from '@open-wc/testing';
-import {query, queryAndAssert, stubRestApi} from '../../../test/test-utils';
+import {query, queryAndAssert} from '../../../test/test-utils';
import {GrTooltipContent} from '../gr-tooltip-content/gr-tooltip-content';
import {Timestamp} from '../../../api/rest-api';
-import {PreferencesInfo} from '../../../types/common';
-import {createPreferences} from '../../../test/test-data-generators';
import {
createDefaultPreferences,
DateFormat,
TimeFormat,
} from '../../../constants/constants';
+import {PreferencesInfo} from '../../../types/common';
const basicTemplate = html`
<gr-date-formatter withTooltip dateStr="2015-09-24 23:30:17.033000000">
@@ -41,6 +40,10 @@
return d;
}
+ function setPrefs(prefs: Partial<PreferencesInfo>) {
+ element.setPreferences({...createDefaultPreferences(), ...prefs});
+ }
+
async function testDates(
nowStr: string,
dateStr: string,
@@ -68,23 +71,11 @@
assert.equal(span.textContent?.trim(), expectedWithDateAndTime);
}
- function stubRestAPI(preferences?: PreferencesInfo) {
- stubRestApi('getLoggedIn').resolves(preferences !== undefined);
- stubRestApi('getPreferences').resolves(preferences);
- }
-
suite('STD + 24 hours time format preference', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_24,
- date_format: DateFormat.STD,
- relative_date_in_change_table: false,
- });
-
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.STD, time_format: TimeFormat.HHMM_24});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -93,7 +84,7 @@
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
- 'Jul 29, 2015, 15:34:14'
+ 'Wednesday, Jul 29, 2015, 15:34:14'
);
});
@@ -103,7 +94,7 @@
'2015-07-28 20:25:14.985000000',
'Jul 28',
'Jul 28 20:25',
- 'Jul 28, 2015, 20:25:14'
+ 'Tuesday, Jul 28, 2015, 20:25:14'
);
});
@@ -113,7 +104,7 @@
'2015-06-15 03:25:14.985000000',
'Jun 15',
'Jun 15 03:25',
- 'Jun 15, 2015, 03:25:14'
+ 'Monday, Jun 15, 2015, 03:25:14'
);
});
@@ -123,22 +114,16 @@
'2015-01-15 03:25:00.000000000',
'Jan 15, 2015',
'Jan 15, 2015 03:25',
- 'Jan 15, 2015, 03:25:00'
+ 'Thursday, Jan 15, 2015, 03:25:00'
);
});
});
suite('US + 24 hours time format preference', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_24,
- date_format: DateFormat.US,
- relative_date_in_change_table: false,
- });
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.US, time_format: TimeFormat.HHMM_24});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -147,7 +132,7 @@
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
- '07/29/15, 15:34:14'
+ 'Wednesday, 07/29/15, 15:34:14'
);
});
@@ -157,7 +142,7 @@
'2015-07-28 20:25:14.985000000',
'07/28',
'07/28 20:25',
- '07/28/15, 20:25:14'
+ 'Tuesday, 07/28/15, 20:25:14'
);
});
@@ -167,23 +152,16 @@
'2015-06-15 03:25:14.985000000',
'06/15',
'06/15 03:25',
- '06/15/15, 03:25:14'
+ 'Monday, 06/15/15, 03:25:14'
);
});
});
suite('ISO + 24 hours time format preference', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_24,
- date_format: DateFormat.ISO,
- relative_date_in_change_table: false,
- });
-
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.ISO, time_format: TimeFormat.HHMM_24});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -192,7 +170,7 @@
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
- '2015-07-29, 15:34:14'
+ 'Wednesday, 2015-07-29, 15:34:14'
);
});
@@ -202,7 +180,7 @@
'2015-07-28 20:25:14.985000000',
'07-28',
'07-28 20:25',
- '2015-07-28, 20:25:14'
+ 'Tuesday, 2015-07-28, 20:25:14'
);
});
@@ -212,23 +190,16 @@
'2015-06-15 03:25:14.985000000',
'06-15',
'06-15 03:25',
- '2015-06-15, 03:25:14'
+ 'Monday, 2015-06-15, 03:25:14'
);
});
});
suite('EURO + 24 hours time format preference', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_24,
- date_format: DateFormat.EURO,
- relative_date_in_change_table: false,
- });
-
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.EURO, time_format: TimeFormat.HHMM_24});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -237,7 +208,7 @@
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
- '29.07.2015, 15:34:14'
+ 'Wednesday, 29.07.2015, 15:34:14'
);
});
@@ -247,7 +218,7 @@
'2015-07-28 20:25:14.985000000',
'28. Jul',
'28. Jul 20:25',
- '28.07.2015, 20:25:14'
+ 'Tuesday, 28.07.2015, 20:25:14'
);
});
@@ -257,23 +228,16 @@
'2015-06-15 03:25:14.985000000',
'15. Jun',
'15. Jun 03:25',
- '15.06.2015, 03:25:14'
+ 'Monday, 15.06.2015, 03:25:14'
);
});
});
suite('UK + 24 hours time format preference', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_24,
- date_format: DateFormat.UK,
- relative_date_in_change_table: false,
- });
-
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.UK, time_format: TimeFormat.HHMM_24});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -282,7 +246,7 @@
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
- '29/07/2015, 15:34:14'
+ 'Wednesday, 29/07/2015, 15:34:14'
);
});
@@ -292,7 +256,7 @@
'2015-07-28 20:25:14.985000000',
'28/07',
'28/07 20:25',
- '28/07/2015, 20:25:14'
+ 'Tuesday, 28/07/2015, 20:25:14'
);
});
@@ -302,22 +266,16 @@
'2015-06-15 03:25:14.985000000',
'15/06',
'15/06 03:25',
- '15/06/2015, 03:25:14'
+ 'Monday, 15/06/2015, 03:25:14'
);
});
});
suite('STD + 12 hours time format preference', () => {
setup(async () => {
- // relative_date_in_change_table is not set when false.
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
- date_format: DateFormat.STD,
- });
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.STD, time_format: TimeFormat.HHMM_12});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -326,22 +284,16 @@
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
- 'Jul 29, 2015, 3:34:14 PM'
+ 'Wednesday, Jul 29, 2015, 3:34:14 PM'
);
});
});
suite('US + 12 hours time format preference', () => {
setup(async () => {
- // relative_date_in_change_table is not set when false.
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
- date_format: DateFormat.US,
- });
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.US, time_format: TimeFormat.HHMM_12});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -350,22 +302,16 @@
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
- '07/29/15, 3:34:14 PM'
+ 'Wednesday, 07/29/15, 3:34:14 PM'
);
});
});
suite('ISO + 12 hours time format preference', () => {
setup(async () => {
- // relative_date_in_change_table is not set when false.
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
- date_format: DateFormat.ISO,
- });
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.ISO, time_format: TimeFormat.HHMM_12});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -374,22 +320,16 @@
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
- '2015-07-29, 3:34:14 PM'
+ 'Wednesday, 2015-07-29, 3:34:14 PM'
);
});
});
suite('EURO + 12 hours time format preference', () => {
setup(async () => {
- // relative_date_in_change_table is not set when false.
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
- date_format: DateFormat.EURO,
- });
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.EURO, time_format: TimeFormat.HHMM_12});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -398,22 +338,16 @@
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
- '29.07.2015, 3:34:14 PM'
+ 'Wednesday, 29.07.2015, 3:34:14 PM'
);
});
});
suite('UK + 12 hours time format preference', () => {
setup(async () => {
- // relative_date_in_change_table is not set when false.
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
- date_format: DateFormat.UK,
- });
element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ setPrefs({date_format: DateFormat.UK, time_format: TimeFormat.HHMM_12});
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -422,22 +356,20 @@
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
- '29/07/2015, 3:34:14 PM'
+ 'Wednesday, 29/07/2015, 3:34:14 PM'
);
});
});
suite('relative date preference', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
+ element = await fixture(basicTemplate);
+ setPrefs({
date_format: DateFormat.STD,
+ time_format: TimeFormat.HHMM_12,
relative_date_in_change_table: true,
});
- element = await fixture(basicTemplate);
- sinon.stub(element, '_getUtcOffsetString').returns('');
- await element.loadPreferences();
+ sinon.stub(element, 'getUtcOffsetString').returns('');
});
test('Within 24 hours on same day', async () => {
@@ -446,7 +378,7 @@
'2015-07-29 15:34:14.985000000',
'5 hours ago',
'5 hours ago',
- 'Jul 29, 2015, 3:34:14 PM'
+ 'Wednesday, Jul 29, 2015, 3:34:14 PM'
);
});
@@ -456,21 +388,19 @@
'2015-01-15 03:25:00.000000000',
'8 months ago',
'8 months ago',
- 'Jan 15, 2015, 3:25:00 AM'
+ 'Thursday, Jan 15, 2015, 3:25:00 AM'
);
});
});
suite('logged in', () => {
setup(async () => {
- stubRestAPI({
- ...createPreferences(),
- time_format: TimeFormat.HHMM_12,
+ element = await fixture(basicTemplate);
+ setPrefs({
date_format: DateFormat.US,
+ time_format: TimeFormat.HHMM_12,
relative_date_in_change_table: true,
});
- element = await fixture(basicTemplate);
- await element.loadPreferences();
});
test('Preferences are respected', () => {
@@ -483,13 +413,12 @@
suite('logged out', () => {
setup(async () => {
- stubRestAPI(undefined);
element = await fixture(basicTemplate);
- await element.loadPreferences();
+ setPrefs({});
});
test('Default preferences are respected', () => {
- assert.equal(element.timeFormat, 'HH:mm');
+ assert.equal(element.timeFormat, 'h:mm A');
assert.equal(element.dateFormat?.short, 'MMM DD');
assert.equal(element.dateFormat?.full, 'MMM DD, YYYY');
assert.isFalse(element.relative);
@@ -498,9 +427,8 @@
suite('with tooltip', () => {
setup(async () => {
- stubRestAPI(createDefaultPreferences());
element = await fixture(basicTemplate);
- await element.loadPreferences();
+ setPrefs({});
await element.updateComplete;
});
@@ -515,9 +443,8 @@
suite('without tooltip', () => {
setup(async () => {
- stubRestAPI(createDefaultPreferences());
element = await fixture(lightTemplate);
- await element.loadPreferences();
+ setPrefs({});
await element.updateComplete;
});
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
index cf279ba..fcebeea 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.ts
@@ -76,12 +76,12 @@
element,
/* HTML */ `
<pre class="plaintext">
- http://google.com/<a
+ <a
href="http://google.com/LinkRewriteMe"
rel="noopener"
target="_blank"
>
- LinkRewriteMe
+ http://google.com/LinkRewriteMe
</a>
</pre>
`
@@ -159,8 +159,15 @@
element,
/* HTML */ `
<pre class="plaintext">
- text with plain link: http://google.com
- text with config link:
+ text with plain link:
+ <a
+ href="http://google.com"
+ rel="noopener"
+ target="_blank"
+ >
+ http://google.com
+ </a>
+ text with config link:
<a
href="http://google.com/LinkRewriteMe"
rel="noopener"
@@ -269,7 +276,14 @@
/* HTML */ `
<pre class="plaintext">
text
- text with plain link: http://google.com
+ text with plain link:
+ <a
+ href="http://google.com"
+ rel="noopener"
+ target="_blank"
+ >
+ http://google.com
+ </a>
text with config link:
<a
href="http://google.com/LinkRewriteMe"
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
index 13a6b00..b97ae59 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
@@ -160,6 +160,8 @@
${this.renderLineNumberCell(Side.RIGHT)}
${this.renderSignCell(Side.RIGHT)} ${this.renderContentCell(Side.RIGHT)}
</tr>
+ ${this.renderPostLineSlot(Side.LEFT)}
+ ${this.renderPostLineSlot(Side.RIGHT)}
`;
if (this.addTableWrapperForTesting) {
return html`<table>
@@ -456,6 +458,13 @@
id=${this.contentId(side)}
>${textElement}</div>`;
}
+
+ private renderPostLineSlot(side: Side) {
+ const lineNumber = this.lineNumber(side);
+ return lineNumber && Number.isInteger(lineNumber)
+ ? html`<slot name="post-${side}-line-${lineNumber}"></slot>`
+ : nothing;
+ }
}
declare global {
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts
index 1c7b311..42d30aa 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row_test.ts
@@ -87,6 +87,8 @@
</div>
</td>
</tr>
+ <slot name="post-left-line-1"></slot>
+ <slot name="post-right-line-1"></slot>
</tbody>
</table>
`
@@ -147,6 +149,8 @@
</div>
</td>
</tr>
+ <slot name="post-left-line-1"></slot>
+ <slot name="post-right-line-1"></slot>
</tbody>
</table>
`
@@ -201,6 +205,7 @@
<slot name="right-1"> </slot>
</div>
</td>
+ <slot name="post-right-line-1"></slot>
</tr>
</tbody>
</table>
@@ -257,6 +262,7 @@
<div class="contentText gr-diff" data-side="right"></div>
</td>
</tr>
+ <slot name="post-left-line-1"></slot>
</tbody>
</table>
`
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts
index b952a3d..d40fdda 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts
@@ -26,6 +26,7 @@
import '../gr-context-controls/gr-context-controls';
import '../gr-range-header/gr-range-header';
import './gr-diff-row';
+import {when} from 'lit/directives/when.js';
@customElement('gr-diff-section')
export class GrDiffSection extends LitElement {
@@ -170,7 +171,7 @@
`;
const moveCell = html`
<td class=${diffClasses('moveHeader')}>
- <gr-range-header class=${diffClasses()} icon="gr-icons:move-item">
+ <gr-range-header class=${diffClasses()} icon="move_item">
${this.renderMoveDescription(movedIn)}
</gr-range-header>
</td>
@@ -179,8 +180,13 @@
<tr
class=${diffClasses('moveControls', movedIn ? 'movedIn' : 'movedOut')}
>
- ${lineNumberCell} ${signCell} ${movedIn ? plainCell : moveCell}
- ${lineNumberCell} ${signCell} ${movedIn ? moveCell : plainCell}
+ ${when(
+ this.isUnifiedDiff(),
+ () => html`${lineNumberCell} ${lineNumberCell} ${moveCell}`,
+ () => html`${lineNumberCell} ${signCell}
+ ${movedIn ? plainCell : moveCell} ${lineNumberCell} ${signCell}
+ ${movedIn ? moveCell : plainCell}`
+ )}
</tr>
`;
}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section_test.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section_test.ts
index 33b3df0..381f9b2 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section_test.ts
@@ -9,7 +9,8 @@
import {fixture, html, assert} from '@open-wc/testing';
import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {GrDiffLineType} from '../../../api/diff';
+import {DiffViewMode, GrDiffLineType} from '../../../api/diff';
+import {waitQueryAndAssert} from '../../../test/test-utils';
suite('gr-diff-section test', () => {
let element: GrDiffSection;
@@ -22,6 +23,93 @@
await element.updateComplete;
});
+ suite('move controls', async () => {
+ setup(async () => {
+ const lines = [new GrDiffLine(GrDiffLineType.BOTH, 1, 1)];
+ lines[0].text = 'asdf';
+ const group = new GrDiffGroup({
+ type: GrDiffGroupType.BOTH,
+ lines,
+ moveDetails: {changed: false, range: {start: 1, end: 2}},
+ });
+ element.group = group;
+ await element.updateComplete;
+ });
+
+ test('side-by-side', async () => {
+ const row = await waitQueryAndAssert(element, 'tr.moveControls');
+ // Semantic dom diff has a problem with just comparing table rows or
+ // cells directly. So as a workaround put the row into an empty test
+ // table.
+ const testTable = document.createElement('table');
+ testTable.appendChild(row);
+ assert.dom.equal(
+ testTable,
+ /* HTML */ `
+ <table>
+ <tbody>
+ <tr class="gr-diff moveControls movedOut">
+ <td class="gr-diff moveControlsLineNumCol"></td>
+ <td class="gr-diff sign"></td>
+ <td class="gr-diff moveHeader">
+ <gr-range-header class="gr-diff" icon="move_item">
+ <div class="gr-diff">
+ <span class="gr-diff"> Moved to lines </span>
+ <a class="gr-diff" href="#1"> 1 </a>
+ <span class="gr-diff"> - </span>
+ <a class="gr-diff" href="#2"> 2 </a>
+ </div>
+ </gr-range-header>
+ </td>
+ <td class="gr-diff moveControlsLineNumCol"></td>
+ <td class="gr-diff sign"></td>
+ <td class="gr-diff"></td>
+ </tr>
+ </tbody>
+ </table>
+ `,
+ {}
+ );
+ });
+
+ test('unified', async () => {
+ element.renderPrefs = {
+ ...element.renderPrefs,
+ view_mode: DiffViewMode.UNIFIED,
+ };
+ const row = await waitQueryAndAssert(element, 'tr.moveControls');
+ // Semantic dom diff has a problem with just comparing table rows or
+ // cells directly. So as a workaround put the row into an empty test
+ // table.
+ const testTable = document.createElement('table');
+ testTable.appendChild(row);
+ assert.dom.equal(
+ testTable,
+ /* HTML */ `
+ <table>
+ <tbody>
+ <tr class="gr-diff moveControls movedOut">
+ <td class="gr-diff moveControlsLineNumCol"></td>
+ <td class="gr-diff moveControlsLineNumCol"></td>
+ <td class="gr-diff moveHeader">
+ <gr-range-header class="gr-diff" icon="move_item">
+ <div class="gr-diff">
+ <span class="gr-diff"> Moved to lines </span>
+ <a class="gr-diff" href="#1"> 1 </a>
+ <span class="gr-diff"> - </span>
+ <a class="gr-diff" href="#2"> 2 </a>
+ </div>
+ </gr-range-header>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ `,
+ {}
+ );
+ });
+ });
+
test('3 normal unchanged rows', async () => {
const lines = [
new GrDiffLine(GrDiffLineType.BOTH, 1, 1),
@@ -38,8 +126,14 @@
element,
/* HTML */ `
<gr-diff-row class="left-1 right-1"> </gr-diff-row>
+ <slot name="post-left-line-1"></slot>
+ <slot name="post-right-line-1"></slot>
<gr-diff-row class="left-1 right-1"> </gr-diff-row>
+ <slot name="post-left-line-1"></slot>
+ <slot name="post-right-line-1"></slot>
<gr-diff-row class="left-1 right-1"> </gr-diff-row>
+ <slot name="post-left-line-1"></slot>
+ <slot name="post-right-line-1"></slot>
<table>
<tbody class="both gr-diff section">
<tr
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts b/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts
new file mode 100644
index 0000000..8fbda14
--- /dev/null
+++ b/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts
@@ -0,0 +1,47 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import {Observable} from 'rxjs';
+import {filter} from 'rxjs/operators';
+import {
+ DiffInfo,
+ DiffPreferencesInfo,
+ RenderPreferences,
+} from '../../../api/diff';
+import {define} from '../../../models/dependency';
+import {Model} from '../../../models/model';
+import {isDefined} from '../../../types/types';
+import {select} from '../../../utils/observable-util';
+
+export interface DiffState {
+ diff: DiffInfo;
+ path?: string;
+ renderPrefs: RenderPreferences;
+ diffPrefs: DiffPreferencesInfo;
+}
+
+export const diffModelToken = define<DiffModel>('diff-model');
+
+export class DiffModel extends Model<DiffState | undefined> {
+ readonly diff$: Observable<DiffInfo> = select(
+ this.state$.pipe(filter(isDefined)),
+ diffState => diffState.diff
+ );
+
+ readonly path$: Observable<string | undefined> = select(
+ this.state$.pipe(filter(isDefined)),
+ diffState => diffState.path
+ );
+
+ readonly renderPrefs$: Observable<RenderPreferences> = select(
+ this.state$.pipe(filter(isDefined)),
+ diffState => diffState.renderPrefs
+ );
+
+ readonly diffPrefs$: Observable<DiffPreferencesInfo> = select(
+ this.state$.pipe(filter(isDefined)),
+ diffState => diffState.diffPrefs
+ );
+}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
index a0526ed..b9a01ce 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
@@ -69,6 +69,8 @@
import {classMap} from 'lit/directives/class-map.js';
import {iconStyles} from '../../../styles/gr-icon-styles';
import {expandFileMode} from '../../../utils/file-util';
+import {DiffModel, diffModelToken} from '../gr-diff-model/gr-diff-model';
+import {provide} from '../../../models/dependency';
const NO_NEWLINE_LEFT = 'No newline at end of left file.';
const NO_NEWLINE_RIGHT = 'No newline at end of right file.';
@@ -138,10 +140,7 @@
prefs?: DiffPreferencesInfo;
@property({type: Object})
- renderPrefs?: RenderPreferences;
-
- @property({type: Boolean})
- displayLine = false;
+ renderPrefs: RenderPreferences = {};
@property({type: Boolean})
isImageDiff?: boolean;
@@ -268,6 +267,8 @@
// Private but used in tests.
diffBuilder = new GrDiffBuilderElement();
+ private diffModel = new DiffModel(undefined);
+
static override get styles() {
return [
iconStyles,
@@ -987,6 +988,7 @@
constructor() {
super();
+ provide(this, diffModelToken, () => this.diffModel);
this.addEventListener('create-range-comment', (e: Event) =>
this.handleCreateRangeComment(e as CustomEvent)
);
@@ -1081,7 +1083,6 @@
unified: this.viewMode === DiffViewMode.UNIFIED,
sideBySide: this.viewMode === DiffViewMode.SIDE_BY_SIDE,
canComment: this.loggedIn,
- displayLine: this.displayLine,
};
return html`
<div class=${classMap(cssClasses)} @click=${this.handleTap}>
@@ -1444,6 +1445,7 @@
private prefsChanged() {
if (!this.prefs) return;
+ this.diffModel.updateState({diffPrefs: this.prefs});
this.blame = null;
this.updatePreferenceStyles();
@@ -1514,7 +1516,7 @@
}
private renderPrefsChanged() {
- if (!this.renderPrefs) return;
+ this.diffModel.updateState({renderPrefs: this.renderPrefs});
if (this.renderPrefs.hide_left_side) {
this.classList.add('no-left');
}
@@ -1581,7 +1583,7 @@
// Private but used in tests.
async renderDiffTable() {
this.unobserveNodes();
- if (!this.prefs) {
+ if (!this.diff || !this.prefs) {
fireEvent(this, 'render');
return;
}
@@ -1600,8 +1602,15 @@
const keyLocations = this.computeKeyLocations();
+ this.diffModel.setState({
+ diff: this.diff,
+ path: this.path,
+ renderPrefs: this.renderPrefs,
+ diffPrefs: this.prefs,
+ });
+
// TODO: Setting tons of public properties like this is obviously a code
- // smell. We are planning to introduce a diff model for managing all this
+ // smell. We are introducing a diff model for managing all this
// data. Then diff builder will only need access to that model.
this.diffBuilder.prefs = this.getBypassPrefs();
this.diffBuilder.renderPrefs = this.renderPrefs;
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
index ce1393d..4adb1cf 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
@@ -3141,18 +3141,6 @@
assert.isFalse(element.classList.contains('no-left'));
});
- test('view does not start with displayLine classList', () => {
- const container = queryAndAssert(element, '.diffContainer');
- assert.isFalse(container.classList.contains('displayLine'));
- });
-
- test('displayLine class added when displayLine is true', async () => {
- element.displayLine = true;
- await element.updateComplete;
- const container = queryAndAssert(element, '.diffContainer');
- assert.isTrue(container.classList.contains('displayLine'));
- });
-
suite('binary diffs', () => {
test('render binary diff', async () => {
element.prefs = {
@@ -3968,6 +3956,7 @@
setup(async () => {
element.prefs = {...MINIMAL_PREFS};
+ element.diff = createDiff();
renderStub = sinon.stub(element.diffBuilder, 'render');
await element.updateComplete;
});
diff --git a/polygerrit-ui/app/embed/diff/gr-range-header/gr-range-header_test.ts b/polygerrit-ui/app/embed/diff/gr-range-header/gr-range-header_test.ts
new file mode 100644
index 0000000..b31197d16
--- /dev/null
+++ b/polygerrit-ui/app/embed/diff/gr-range-header/gr-range-header_test.ts
@@ -0,0 +1,40 @@
+/**
+ * @license
+ * Copyright 2023 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import '../../../test/common-test-setup';
+import './gr-range-header';
+import {GrRangeHeader} from './gr-range-header';
+import {fixture, html, assert} from '@open-wc/testing';
+
+suite('gr-range-header test', () => {
+ let element: GrRangeHeader;
+
+ setup(async () => {
+ element = await fixture<GrRangeHeader>(
+ html`<gr-range-header></gr-range-header>`
+ );
+ await element.updateComplete;
+ });
+
+ test('renders', async () => {
+ element.filled = true;
+ element.icon = 'test-icon';
+ await element.updateComplete;
+ assert.shadowDom.equal(
+ element,
+ /* HTML */ `
+ <div class="row">
+ <gr-icon
+ aria-hidden="true"
+ class="icon"
+ filled
+ icon="test-icon"
+ ></gr-icon>
+ <slot></slot>
+ </div>
+ `
+ );
+ });
+});
diff --git a/polygerrit-ui/app/models/accounts-model/accounts-model.ts b/polygerrit-ui/app/models/accounts-model/accounts-model.ts
index 2bf6068..1c67857 100644
--- a/polygerrit-ui/app/models/accounts-model/accounts-model.ts
+++ b/polygerrit-ui/app/models/accounts-model/accounts-model.ts
@@ -8,11 +8,14 @@
import {RestApiService} from '../../services/gr-rest-api/gr-rest-api';
import {UserId} from '../../types/common';
import {getUserId, isDetailedAccount} from '../../utils/account-util';
+import {hasOwnProperty} from '../../utils/common-util';
import {define} from '../dependency';
import {Model} from '../model';
export interface AccountsState {
- accounts: {[id: UserId]: AccountDetailInfo};
+ accounts: {
+ [id: UserId]: AccountDetailInfo | AccountInfo;
+ };
}
export const accountsModelToken = define<AccountsModel>('accounts-model');
@@ -24,33 +27,36 @@
});
}
- private updateStateAccount(id: UserId, account?: AccountDetailInfo) {
+ private updateStateAccount(
+ id: UserId,
+ account: AccountDetailInfo | AccountInfo
+ ) {
if (!account) return;
const current = {...this.getState()};
current.accounts = {...current.accounts, [id]: account};
this.setState(current);
}
- async getAccount(partialAccount: AccountInfo) {
+ async getAccount(
+ partialAccount: AccountInfo
+ ): Promise<AccountDetailInfo | AccountInfo> {
const current = this.getState();
const id = getUserId(partialAccount);
- if (current.accounts[id]) return current.accounts[id];
+ if (hasOwnProperty(current.accounts, id)) return current.accounts[id];
// It is possible to add emails to CC when they don't have a Gerrit
- // account. In this case getAccountDetails will return a 404 error hence
- // pass an empty error function to handle that.
+ // account. In this case getAccountDetails will return a 404 error then
+ // we at least use what is in partialAccount.
const account = await this.restApiService.getAccountDetails(id, () => {
- this.updateStateAccount(id, partialAccount as AccountDetailInfo);
+ this.updateStateAccount(id, partialAccount);
return;
});
if (account) this.updateStateAccount(id, account);
- return account;
+ return account ?? partialAccount;
}
async fillDetails(account: AccountInfo) {
if (!isDetailedAccount(account)) {
- if (account.email) return await this.getAccount({email: account.email});
- else if (account._account_id)
- return await this.getAccount({_account_id: account._account_id});
+ return await this.getAccount(account);
}
return account;
}
diff --git a/polygerrit-ui/app/models/checks/checks-util.ts b/polygerrit-ui/app/models/checks/checks-util.ts
index ba43eb4..da05bbe 100644
--- a/polygerrit-ui/app/models/checks/checks-util.ts
+++ b/polygerrit-ui/app/models/checks/checks-util.ts
@@ -121,6 +121,7 @@
const eventDetail: OpenFixPreviewEventDetail = {
patchNum: result.patchset as PatchSetNumber,
fixSuggestions,
+ onCloseFixPreviewCallbacks: [],
};
return {
name: 'Show Fix',
diff --git a/polygerrit-ui/app/models/views/admin_test.ts b/polygerrit-ui/app/models/views/admin_test.ts
index b6089af..0881018 100644
--- a/polygerrit-ui/app/models/views/admin_test.ts
+++ b/polygerrit-ui/app/models/views/admin_test.ts
@@ -3,28 +3,34 @@
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
-import {assert} from '@open-wc/testing';
-import {PageContext} from '../../elements/core/gr-router/gr-page';
import {GerritView} from '../../services/router/router-model';
import '../../test/common-test-setup';
-import {AdminChildView, PLUGIN_LIST_ROUTE} from './admin';
+import {assertRouteFalse, assertRouteState} from '../../test/test-utils';
+import {
+ AdminChildView,
+ AdminViewState,
+ createAdminUrl,
+ PLUGIN_LIST_ROUTE,
+} from './admin';
suite('admin view model', () => {
suite('routes', () => {
test('PLUGIN_LIST', () => {
- const {urlPattern: pattern, createState} = PLUGIN_LIST_ROUTE;
+ assertRouteFalse(PLUGIN_LIST_ROUTE, 'admin/plugins');
+ assertRouteFalse(PLUGIN_LIST_ROUTE, '//admin/plugins');
+ assertRouteFalse(PLUGIN_LIST_ROUTE, '//admin/plugins?');
+ assertRouteFalse(PLUGIN_LIST_ROUTE, '/admin/plugins//');
- assert.isTrue(pattern.test('/admin/plugins'));
- assert.isTrue(pattern.test('/admin/plugins/'));
- assert.isFalse(pattern.test('admin/plugins'));
- assert.isFalse(pattern.test('//admin/plugins'));
- assert.isFalse(pattern.test('//admin/plugins?'));
- assert.isFalse(pattern.test('/admin/plugins//'));
-
- assert.deepEqual(createState(new PageContext('')), {
+ const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.PLUGINS,
- });
+ };
+ assertRouteState<AdminViewState>(
+ PLUGIN_LIST_ROUTE,
+ '/admin/plugins',
+ state,
+ createAdminUrl
+ );
});
});
});
diff --git a/polygerrit-ui/app/models/views/dashboard.ts b/polygerrit-ui/app/models/views/dashboard.ts
index 74523db..d2e7995 100644
--- a/polygerrit-ui/app/models/views/dashboard.ts
+++ b/polygerrit-ui/app/models/views/dashboard.ts
@@ -10,7 +10,21 @@
import {encodeURL, getBaseUrl} from '../../utils/url-util';
import {define} from '../dependency';
import {Model} from '../model';
-import {ViewState} from './base';
+import {Route, ViewState} from './base';
+
+export const PROJECT_DASHBOARD_ROUTE: Route<DashboardViewState> = {
+ urlPattern: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
+ createState: ctx => {
+ const project = (ctx.params[0] ?? '') as RepoName;
+ const dashboard = (ctx.params[1] ?? '') as DashboardId;
+ const state: DashboardViewState = {
+ view: GerritView.DASHBOARD,
+ project,
+ dashboard,
+ };
+ return state;
+ },
+};
export interface DashboardViewState extends ViewState {
view: GerritView.DASHBOARD;
diff --git a/polygerrit-ui/app/models/views/dashboard_test.ts b/polygerrit-ui/app/models/views/dashboard_test.ts
index a7620dd..9509977 100644
--- a/polygerrit-ui/app/models/views/dashboard_test.ts
+++ b/polygerrit-ui/app/models/views/dashboard_test.ts
@@ -5,11 +5,36 @@
*/
import {assert} from '@open-wc/testing';
import {RepoName} from '../../api/rest-api';
+import {GerritView} from '../../services/router/router-model';
import '../../test/common-test-setup';
+import {assertRouteFalse, assertRouteState} from '../../test/test-utils';
import {DashboardId} from '../../types/common';
-import {createDashboardUrl} from './dashboard';
+import {
+ createDashboardUrl,
+ DashboardViewState,
+ PROJECT_DASHBOARD_ROUTE,
+} from './dashboard';
suite('dashboard view state tests', () => {
+ suite('routes', () => {
+ test('PROJECT_DASHBOARD_ROUTE', () => {
+ assertRouteFalse(PROJECT_DASHBOARD_ROUTE, '/p//+/dashboard/qwer');
+ assertRouteFalse(PROJECT_DASHBOARD_ROUTE, '/p/asdf/+/dashboard/');
+
+ const state: DashboardViewState = {
+ view: GerritView.DASHBOARD,
+ project: 'asdf' as RepoName,
+ dashboard: 'qwer' as DashboardId,
+ };
+ assertRouteState(
+ PROJECT_DASHBOARD_ROUTE,
+ '/p/asdf/+/dashboard/qwer',
+ state,
+ createDashboardUrl
+ );
+ });
+ });
+
suite('createDashboardUrl()', () => {
test('self dashboard', () => {
assert.equal(createDashboardUrl({}), '/dashboard/self');
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
index 0d0c88f..610d8f3 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
@@ -769,7 +769,7 @@
userId: AccountId | EmailAddress,
errFn?: ErrorCallback
): Promise<AccountDetailInfo | undefined> {
- return this._restApiHelper.fetchJSON({
+ return this._fetchSharedCacheURL({
url: `/accounts/${encodeURIComponent(userId)}/detail`,
anonymizedUrl: '/accounts/*/detail',
errFn,
diff --git a/polygerrit-ui/app/test/common-test-setup.ts b/polygerrit-ui/app/test/common-test-setup.ts
index aed58d8..365bb16 100644
--- a/polygerrit-ui/app/test/common-test-setup.ts
+++ b/polygerrit-ui/app/test/common-test-setup.ts
@@ -6,7 +6,7 @@
// TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
// https://github.com/Polymer/polymer-resin/issues/9 is resolved.
import '../scripts/bundled-polymer';
-import {AppContext} from '../services/app-context';
+import {getAppContext} from '../services/app-context';
import {Finalizable} from '../services/registry';
import {
createTestAppContext,
@@ -60,7 +60,6 @@
});
let testSetupTimestampMs = 0;
-let appContext: AppContext & Finalizable;
const injectedDependencies: Map<
DependencyToken<unknown>,
@@ -101,11 +100,10 @@
// If the following asserts fails - then window.stub is
// overwritten by some other code.
assert.equal(getCleanupsCount(), 0);
- appContext = createTestAppContext();
- initGlobalVariables(appContext);
+ initGlobalVariables(createTestAppContext(), false);
- finalizers.push(appContext);
- const dependencies = createTestDependencies(appContext, testResolver);
+ finalizers.push(getAppContext());
+ const dependencies = createTestDependencies(getAppContext(), testResolver);
for (const [token, provider] of dependencies) {
injectDependency(token, provider);
}
@@ -124,7 +122,7 @@
// `awaitPluginsLoaded` will rely on that to kick off,
// in testing, we want to kick start this earlier.
testResolver(pluginLoaderToken).loadPlugins([]);
- testOnlyResetGrRestApiSharedObjects(appContext.authService);
+ testOnlyResetGrRestApiSharedObjects(getAppContext().authService);
});
export function removeRequestDependencyListener() {
diff --git a/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts b/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
index a5bc4bf..94e7f79 100644
--- a/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
+++ b/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
@@ -362,6 +362,7 @@
return Promise.resolve({});
},
getPreferences(): Promise<PreferencesInfo | undefined> {
+ // TODO: Use createDefaultPreferences() instead.
return Promise.resolve(createPreferences());
},
getProjectConfig(): Promise<ConfigInfo | undefined> {
diff --git a/polygerrit-ui/app/test/test-data-generators.ts b/polygerrit-ui/app/test/test-data-generators.ts
index 05e4df7..5abac09 100644
--- a/polygerrit-ui/app/test/test-data-generators.ts
+++ b/polygerrit-ui/app/test/test-data-generators.ts
@@ -73,21 +73,17 @@
import {
AccountsVisibility,
AccountTag,
- AppTheme,
AuthType,
ChangeStatus,
CommentSide,
- DateFormat,
- DefaultBase,
+ createDefaultPreferences,
DefaultDisplayNameConfig,
- DiffViewMode,
EmailStrategy,
InheritedBooleanInfoConfiguredValue,
MergeabilityComputationBehavior,
RequirementStatus,
RevisionKind,
SubmitType,
- TimeFormat,
} from '../constants/constants';
import {formatDate} from '../utils/date-util';
import {GetDiffCommentsOutput} from '../services/gr-rest-api/gr-rest-api';
@@ -687,18 +683,12 @@
};
}
-// TODO: Maybe reconcile with createDefaultPreferences() in constants.ts.
+// TODO: Do not change the values of createDefaultPreferences() here.
export function createPreferences(): PreferencesInfo {
return {
+ ...createDefaultPreferences(),
changes_per_page: 10,
- theme: AppTheme.AUTO,
- date_format: DateFormat.ISO,
- time_format: TimeFormat.HHMM_24,
- diff_view: DiffViewMode.SIDE_BY_SIDE,
- my: [],
- change_table: [],
email_strategy: EmailStrategy.ENABLED,
- default_base_for_merges: DefaultBase.AUTO_MERGE,
allow_browser_notifications: true,
};
}
diff --git a/polygerrit-ui/app/test/test-utils.ts b/polygerrit-ui/app/test/test-utils.ts
index c400d9c..19c3a7b 100644
--- a/polygerrit-ui/app/test/test-utils.ts
+++ b/polygerrit-ui/app/test/test-utils.ts
@@ -14,6 +14,8 @@
import {Observable} from 'rxjs';
import {filter, take, timeout} from 'rxjs/operators';
import {assert} from '@open-wc/testing';
+import {Route, ViewState} from '../models/views/base';
+import {PageContext} from '../elements/core/gr-router/gr-page';
export {query, queryAll, queryAndAssert} from '../utils/common-util';
export interface MockPromise<T> extends Promise<T> {
@@ -328,3 +330,26 @@
};
return new Proxy(obj, handler) as unknown as T;
}
+
+export function assertRouteState<T extends ViewState>(
+ route: Route<T>,
+ path: string,
+ state: T,
+ createUrl: (state: T) => string
+) {
+ const {urlPattern, createState} = route;
+ const ctx = new PageContext(path);
+ const matches = ctx.match(urlPattern);
+ assert.isTrue(matches);
+ assert.deepEqual(createState(ctx), state);
+ assert.equal(path, createUrl(state));
+}
+
+export function assertRouteFalse<T extends ViewState>(
+ route: Route<T>,
+ path: string
+) {
+ const ctx = new PageContext(path);
+ const matches = ctx.match(route.urlPattern);
+ assert.isFalse(matches);
+}
diff --git a/polygerrit-ui/app/types/events.ts b/polygerrit-ui/app/types/events.ts
index 2cc6742..e2612b0 100644
--- a/polygerrit-ui/app/types/events.ts
+++ b/polygerrit-ui/app/types/events.ts
@@ -26,7 +26,6 @@
MOVED_LINK_CLICKED = 'moved-link-clicked',
NETWORK_ERROR = 'network-error',
OPEN_FIX_PREVIEW = 'open-fix-preview',
- CLOSE_FIX_PREVIEW = 'close-fix-preview',
PAGE_ERROR = 'page-error',
RELOAD = 'reload',
REPLY = 'reply',
@@ -63,7 +62,6 @@
'line-cursor-moved-out': LineNumberEvent;
'moved-link-clicked': MovedLinkClickedEvent;
'open-fix-preview': OpenFixPreviewEvent;
- 'close-fix-preview': CloseFixPreviewEvent;
'reply-to-comment': ReplyToCommentEvent;
/* prettier-ignore */
'reload': ReloadEvent;
@@ -156,13 +154,10 @@
export interface OpenFixPreviewEventDetail {
patchNum: PatchSetNum;
fixSuggestions: FixSuggestionInfo[];
+ onCloseFixPreviewCallbacks: ((fixapplied: boolean) => void)[];
}
export type OpenFixPreviewEvent = CustomEvent<OpenFixPreviewEventDetail>;
-export interface CloseFixPreviewEventDetail {
- fixApplied: boolean;
-}
-export type CloseFixPreviewEvent = CustomEvent<CloseFixPreviewEventDetail>;
export interface ReplyToCommentEventDetail {
content: string;
userWantsToEdit: boolean;
diff --git a/polygerrit-ui/app/utils/async-util.ts b/polygerrit-ui/app/utils/async-util.ts
index cae6319..752de62 100644
--- a/polygerrit-ui/app/utils/async-util.ts
+++ b/polygerrit-ui/app/utils/async-util.ts
@@ -37,6 +37,11 @@
export const _testOnly_allTasks = new Map<number, DelayedTask>();
+export enum ResolvedDelayedTaskStatus {
+ CALLBACK_EXECUTED = 'CALLBACK_EXECUTED',
+ TASK_CANCELLED = 'TASK_CANCELLED',
+}
+
/**
* This is just a very simple and small wrapper around setTimeout(). Instead of
* the usual:
@@ -52,34 +57,54 @@
* It is just nicer to have an object for this instead of a number as a handle.
*/
export class DelayedTask {
- private timer?: number;
+ private timerId?: number;
+
+ /**
+ * Promise that is resolved after the callback is run or the task is
+ * cancelled.
+ */
+ public readonly promise: Promise<ResolvedDelayedTaskStatus>;
+
+ private resolvePromise?: (
+ value: ResolvedDelayedTaskStatus | PromiseLike<ResolvedDelayedTaskStatus>
+ ) => void;
constructor(private callback: () => void, waitMs = 0) {
- this.timer = window.setTimeout(() => {
- if (this.timer) _testOnly_allTasks.delete(this.timer);
- this.timer = undefined;
- if (this.callback) this.callback();
- }, waitMs);
- _testOnly_allTasks.set(this.timer, this);
+ this.promise = new Promise(resolve => {
+ this.resolvePromise = resolve;
+ this.timerId = window.setTimeout(() => {
+ if (this.timerId) _testOnly_allTasks.delete(this.timerId);
+ this.timerId = undefined;
+ if (this.callback) this.callback();
+ resolve(ResolvedDelayedTaskStatus.CALLBACK_EXECUTED);
+ }, waitMs);
+ _testOnly_allTasks.set(this.timerId, this);
+ });
+ }
+
+ private cancelTimer() {
+ window.clearTimeout(this.timerId);
+ if (this.timerId) _testOnly_allTasks.delete(this.timerId);
+ this.timerId = undefined;
}
cancel() {
if (this.isActive()) {
- window.clearTimeout(this.timer);
- if (this.timer) _testOnly_allTasks.delete(this.timer);
- this.timer = undefined;
+ this.cancelTimer();
+ this.resolvePromise?.(ResolvedDelayedTaskStatus.TASK_CANCELLED);
}
}
flush() {
if (this.isActive()) {
- this.cancel();
+ this.cancelTimer();
if (this.callback) this.callback();
+ this.resolvePromise?.(ResolvedDelayedTaskStatus.CALLBACK_EXECUTED);
}
}
isActive() {
- return this.timer !== undefined;
+ return this.timerId !== undefined;
}
}
diff --git a/polygerrit-ui/app/utils/date-util.ts b/polygerrit-ui/app/utils/date-util.ts
index 72e6cb7..d95d24a 100644
--- a/polygerrit-ui/app/utils/date-util.ts
+++ b/polygerrit-ui/app/utils/date-util.ts
@@ -83,21 +83,17 @@
return diff < 180 * Duration.DAY;
}
-// TODO(dmfilippov): TS-Fix review this type. All fields here must be optional,
-// but this require some changes in the code. During JS->TS migration
-// we want to avoid code changes where possible, so for simplicity we
-// define it with almost all fields mandatory
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/formatToParts
interface DateTimeFormatParts {
- year: string;
- month: string;
- day: string;
- hour: string;
- minute: string;
- second: string;
- dayPeriod: string;
- dayperiod?: string;
- // Object can have other properties, but our code doesn't use it
- [key: string]: string | undefined;
+ year?: string;
+ month?: string;
+ day?: string;
+ hour?: string;
+ minute?: string;
+ second?: string;
+ // AM or PM
+ dayPeriod?: string;
+ weekday?: string;
}
export function formatDate(date: Date, format: string) {
@@ -117,6 +113,14 @@
}
}
+ if (format.includes('ddd')) {
+ if (format.includes('dddd')) {
+ options.weekday = 'long';
+ } else {
+ options.weekday = 'short';
+ }
+ }
+
if (format.includes('DD')) {
options.day = '2-digit';
}
@@ -146,15 +150,38 @@
locale = 'en-GB';
}
- const dtf = new Intl.DateTimeFormat(locale, options);
- const parts = dtf
- .formatToParts(date)
- .filter(o => o.type !== 'literal')
- .reduce((acc, o: Intl.DateTimeFormatPart) => {
- acc[o.type] = o.value;
- return acc;
- }, {} as DateTimeFormatParts);
- if (format.includes('YY')) {
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/formatToParts
+ const dtfParts = new Intl.DateTimeFormat(locale, options).formatToParts(date);
+ const parts: DateTimeFormatParts = {};
+ for (const entry of dtfParts) {
+ switch (entry.type) {
+ case 'year':
+ parts.year = entry.value;
+ break;
+ case 'month':
+ parts.month = entry.value;
+ break;
+ case 'day':
+ parts.day = entry.value;
+ break;
+ case 'hour':
+ parts.hour = entry.value;
+ break;
+ case 'minute':
+ parts.minute = entry.value;
+ break;
+ case 'second':
+ parts.second = entry.value;
+ break;
+ case 'dayPeriod':
+ parts.dayPeriod = entry.value;
+ break;
+ case 'weekday':
+ parts.weekday = entry.value;
+ break;
+ }
+ }
+ if (parts.year && format.includes('YY')) {
if (format.includes('YYYY')) {
format = format.replace('YYYY', parts.year);
} else {
@@ -162,41 +189,50 @@
}
}
- if (format.includes('DD')) {
+ if (parts.day && format.includes('DD')) {
format = format.replace('DD', parts.day);
}
- if (format.includes('HH')) {
+ if (parts.hour && format.includes('HH')) {
format = format.replace('HH', parts.hour);
}
- if (format.includes('h')) {
+ if (parts.hour && format.includes('h')) {
format = format.replace('h', parts.hour);
}
- if (format.includes('mm')) {
+ if (parts.minute && format.includes('mm')) {
format = format.replace('mm', parts.minute);
}
- if (format.includes('ss')) {
+ if (parts.second && format.includes('ss')) {
format = format.replace('ss', parts.second);
}
- if (format.includes('A')) {
- if (parts.dayperiod) {
- // Workaround for chrome 70 and below
- format = format.replace('A', parts.dayperiod.toUpperCase());
- } else {
- format = format.replace('A', parts.dayPeriod.toUpperCase());
- }
+ if (parts.dayPeriod && format.includes('A')) {
+ format = format.replace('A', parts.dayPeriod.toUpperCase());
}
- if (format.includes('MM')) {
+
+ // Month and weekday must be last, because they will yield characters that
+ // could be interpreted as format strings, e.g. `h` in `Thursday` would
+ // otherwise be replaced by "hours".
+
+ if (parts.month && format.includes('MM')) {
if (format.includes('MMM')) {
format = format.replace('MMM', parts.month);
} else {
format = format.replace('MM', parts.month);
}
}
+
+ if (parts.weekday && format.includes('ddd')) {
+ if (format.includes('dddd')) {
+ format = format.replace('dddd', parts.weekday);
+ } else {
+ format = format.replace('ddd', parts.weekday);
+ }
+ }
+
return format;
}
diff --git a/polygerrit-ui/app/utils/date-util_test.ts b/polygerrit-ui/app/utils/date-util_test.ts
index 8e802b7..8d16655 100644
--- a/polygerrit-ui/app/utils/date-util_test.ts
+++ b/polygerrit-ui/app/utils/date-util_test.ts
@@ -194,6 +194,18 @@
)
);
});
+
+ test('weekday', () => {
+ assert.equal(
+ '2013-07-03 Wed',
+ formatDate(new Date('Jul 03 2013 12:14:00'), 'YYYY-MM-DD ddd')
+ );
+ assert.equal(
+ '2013-07-03 Wednesday',
+ formatDate(new Date('Jul 03 2013 00:15:00'), 'YYYY-MM-DD dddd')
+ );
+ });
+
test('h:mm:ss A shows correctly midnight and midday', () => {
const timeFormat = 'h:mm A';
assert.equal(
diff --git a/polygerrit-ui/app/utils/event-util.ts b/polygerrit-ui/app/utils/event-util.ts
index 714955b..49d5382 100644
--- a/polygerrit-ui/app/utils/event-util.ts
+++ b/polygerrit-ui/app/utils/event-util.ts
@@ -103,10 +103,6 @@
fire(target, EventType.SHOW_TAB, detail);
}
-export function fireCloseFixPreview(target: EventTarget, fixApplied: boolean) {
- fire(target, EventType.CLOSE_FIX_PREVIEW, {fixApplied});
-}
-
export function fireReload(target: EventTarget, clearPatchset?: boolean) {
fire(target, EventType.RELOAD, {clearPatchset: !!clearPatchset});
}
diff --git a/polygerrit-ui/app/utils/link-util.ts b/polygerrit-ui/app/utils/link-util.ts
index 48e9c07..ccafa5a 100644
--- a/polygerrit-ui/app/utils/link-util.ts
+++ b/polygerrit-ui/app/utils/link-util.ts
@@ -33,6 +33,11 @@
commentLinkInfo =>
commentLinkInfo.enabled !== false && commentLinkInfo.link !== undefined
);
+ // Always linkify URLs starting with https?://
+ enabledRewrites.push({
+ match: '(https?://\\S+[\\w/])',
+ link: '$1',
+ });
return enabledRewrites.flatMap(rewrite => {
const regexp = new RegExp(rewrite.match, 'g');
const partialResults: RewriteResult[] = [];
diff --git a/polygerrit-ui/app/utils/link-util_test.ts b/polygerrit-ui/app/utils/link-util_test.ts
index e4e719b..52b8288 100644
--- a/polygerrit-ui/app/utils/link-util_test.ts
+++ b/polygerrit-ui/app/utils/link-util_test.ts
@@ -12,6 +12,17 @@
}
suite('link rewrites', () => {
+ test('default linking', () => {
+ assert.equal(
+ linkifyUrlsAndApplyRewrite('http://www.google.com', {}),
+ link('http://www.google.com', 'http://www.google.com')
+ );
+ assert.equal(
+ linkifyUrlsAndApplyRewrite('https://www.google.com', {}),
+ link('https://www.google.com', 'https://www.google.com')
+ );
+ });
+
test('without text', () => {
assert.equal(
linkifyUrlsAndApplyRewrite('foo', {
diff --git a/tools/deps.bzl b/tools/deps.bzl
index 28904a9..4c21ae6 100644
--- a/tools/deps.bzl
+++ b/tools/deps.bzl
@@ -18,7 +18,7 @@
GITILES_REPO = GERRIT
# When updating Bouncy Castle, also update it in bazlets.
-BC_VERS = "1.64"
+BC_VERS = "1.72"
HTTPCOMP_VERS = "4.5.2"
JETTY_VERS = "9.4.49.v20220914"
BYTE_BUDDY_VERSION = "1.10.7"
@@ -558,20 +558,26 @@
maven_jar(
name = "bcprov",
- artifact = "org.bouncycastle:bcprov-jdk15on:" + BC_VERS,
- sha1 = "1467dac1b787b5ad2a18201c0c281df69882259e",
+ artifact = "org.bouncycastle:bcprov-jdk18on:" + BC_VERS,
+ sha1 = "d8dc62c28a3497d29c93fee3e71c00b27dff41b4",
)
maven_jar(
name = "bcpg",
- artifact = "org.bouncycastle:bcpg-jdk15on:" + BC_VERS,
- sha1 = "56956a8c63ccadf62e7c678571cf86f30bd84441",
+ artifact = "org.bouncycastle:bcpg-jdk18on:" + BC_VERS,
+ sha1 = "1a36a1740d07869161f6f0d01fae8d72dd1d8320",
)
maven_jar(
name = "bcpkix",
- artifact = "org.bouncycastle:bcpkix-jdk15on:" + BC_VERS,
- sha1 = "3dac163e20110817d850d17e0444852a6d7d0bd7",
+ artifact = "org.bouncycastle:bcpkix-jdk18on:" + BC_VERS,
+ sha1 = "bb3fdb5162ccd5085e8d7e57fada4d8eaa571f5a",
+ )
+
+ maven_jar(
+ name = "bcutil",
+ artifact = "org.bouncycastle:bcutil-jdk18on:" + BC_VERS,
+ sha1 = "41f19a69ada3b06fa48781120d8bebe1ba955c77",
)
maven_jar(