diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 38bc87d8a1..f29383e8e1 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -2865,15 +2865,15 @@ manually. [[elasticsearch.numberOfShards]]elasticsearch.numberOfShards:: + Sets the number of shards to use per index. Refer to the -link:https://www.elastic.co/guide/en/elasticsearch/reference/current/_basic_concepts.html#getting-started-shards-and-replicas[ +link:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-concepts.html#getting-started-shards-and-replicas[ Elasticsearch documentation] for details. + -Defaults to 5. +Defaults to 5 for Elasticsearch versions 5 and 6, and to 1 starting with Elasticsearch 7. [[elasticsearch.numberOfReplicas]]elasticsearch.numberOfReplicas:: + Sets the number of replicas to use per index. Refer to the -link:https://www.elastic.co/guide/en/elasticsearch/reference/current/_basic_concepts.html#getting-started-shards-and-replicas[ +link:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-concepts.html#getting-started-shards-and-replicas[ Elasticsearch documentation] for details. + Defaults to 1. diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java index e21575972d..996bbfd5a4 100644 --- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java +++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java @@ -198,7 +198,7 @@ abstract class AbstractElasticIndex implements Index { } // Recreate the index. - String indexCreationFields = concatJsonString(getSettings(), getMappings()); + String indexCreationFields = concatJsonString(getSettings(client.adapter()), getMappings()); response = performRequest( "PUT", indexName + client.adapter().includeTypeNameParam(), indexCreationFields); @@ -213,8 +213,8 @@ abstract class AbstractElasticIndex implements Index { protected abstract String getMappings(); - private String getSettings() { - return gson.toJson(ImmutableMap.of(SETTINGS, ElasticSetting.createSetting(config))); + private String getSettings(ElasticQueryAdapter adapter) { + return gson.toJson(ImmutableMap.of(SETTINGS, ElasticSetting.createSetting(config, adapter))); } protected abstract String getId(V v); diff --git a/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java b/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java index 5f48499168..cbe9bc73d0 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java +++ b/java/com/google/gerrit/elasticsearch/ElasticConfiguration.java @@ -42,7 +42,7 @@ class ElasticConfiguration { static final String KEY_NUMBER_OF_REPLICAS = "numberOfReplicas"; static final String DEFAULT_PORT = "9200"; static final String DEFAULT_USERNAME = "elastic"; - static final int DEFAULT_NUMBER_OF_SHARDS = 5; + static final int DEFAULT_NUMBER_OF_SHARDS = 0; static final int DEFAULT_NUMBER_OF_REPLICAS = 1; private final Config cfg; @@ -100,4 +100,11 @@ class ElasticConfiguration { String getIndexName(String name, int schemaVersion) { return String.format("%s%s_%04d", prefix, name, schemaVersion); } + + int getNumberOfShards(ElasticQueryAdapter adapter) { + if (numberOfShards == DEFAULT_NUMBER_OF_SHARDS) { + return adapter.getDefaultNumberOfShards(); + } + return numberOfShards; + } } diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java index b0156781fc..72c52b0c86 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java +++ b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java @@ -27,6 +27,7 @@ public class ElasticQueryAdapter { private final boolean useV5Type; private final boolean useV6Type; private final boolean omitType; + private final int defaultNumberOfShards; private final String searchFilteringName; private final String indicesExistParams; @@ -41,6 +42,7 @@ public class ElasticQueryAdapter { this.useV5Type = !version.isV6OrLater(); this.useV6Type = version.isV6(); this.omitType = version.isV7OrLater(); + this.defaultNumberOfShards = version.isV7OrLater() ? 1 : 5; this.versionDiscoveryUrl = version.isV6OrLater() ? "/%s*" : "/%s*/_aliases"; this.searchFilteringName = "_source"; this.indicesExistParams = @@ -98,6 +100,10 @@ public class ElasticQueryAdapter { return omitType; } + int getDefaultNumberOfShards() { + return defaultNumberOfShards; + } + String getType() { return getType(""); } diff --git a/java/com/google/gerrit/elasticsearch/ElasticSetting.java b/java/com/google/gerrit/elasticsearch/ElasticSetting.java index 98c313c78a..14e462342d 100644 --- a/java/com/google/gerrit/elasticsearch/ElasticSetting.java +++ b/java/com/google/gerrit/elasticsearch/ElasticSetting.java @@ -22,18 +22,18 @@ class ElasticSetting { private static final ImmutableMap CUSTOM_CHAR_MAPPING = ImmutableMap.of("\\u002E", "\\u0020", "\\u005F", "\\u0020"); - static SettingProperties createSetting(ElasticConfiguration config) { - return new ElasticSetting.Builder().addCharFilter().addAnalyzer().build(config); + static SettingProperties createSetting(ElasticConfiguration config, ElasticQueryAdapter adapter) { + return new ElasticSetting.Builder().addCharFilter().addAnalyzer().build(config, adapter); } static class Builder { private final ImmutableMap.Builder fields = new ImmutableMap.Builder<>(); - SettingProperties build(ElasticConfiguration config) { + SettingProperties build(ElasticConfiguration config, ElasticQueryAdapter adapter) { SettingProperties properties = new SettingProperties(); properties.analysis = fields.build(); - properties.numberOfShards = config.numberOfShards; + properties.numberOfShards = config.getNumberOfShards(adapter); properties.numberOfReplicas = config.numberOfReplicas; return properties; } diff --git a/java/com/google/gerrit/server/config/UrlFormatter.java b/java/com/google/gerrit/server/config/UrlFormatter.java index 066a3cabce..740daf02dc 100644 --- a/java/com/google/gerrit/server/config/UrlFormatter.java +++ b/java/com/google/gerrit/server/config/UrlFormatter.java @@ -47,10 +47,32 @@ public interface UrlFormatter { return getWebUrl().map(url -> url + "c/" + project.get() + "/+/" + id.get()); } + /** Returns the URL for viewing a file in a given patch set of a change. */ + default Optional getPatchFileView(Change change, int patchsetId, String filename) { + return getChangeViewUrl(change.getProject(), change.getId()) + .map(url -> url + "/" + patchsetId + "/" + filename); + } + + /** Returns the URL for viewing a comment in a file in a given patch set of a change. */ + default Optional getInlineCommentView( + Change change, int patchsetId, String filename, short side, int startLine) { + return getPatchFileView(change, patchsetId, filename) + .map(url -> url + String.format("@%s%d", side == 0 ? "a" : "", startLine)); + } + /** Returns a URL pointing to a section of the settings page. */ + default Optional getSettingsUrl() { + return getWebUrl().map(url -> url + "settings"); + } + + /** + * Returns a URL pointing to a section of the settings page, or the settings page if {@code + * section} is null. + */ default Optional getSettingsUrl(@Nullable String section) { - return getWebUrl() - .map(url -> url + "settings" + (Strings.isNullOrEmpty(section) ? "" : "#" + section)); + return Strings.isNullOrEmpty(section) + ? getSettingsUrl() + : getSettingsUrl().map(url -> url + "#" + section); } /** Returns a URL pointing to a documentation page, at a given named anchor. */ diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java index 348e0cec75..a64980f151 100644 --- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java +++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java @@ -16,6 +16,7 @@ package com.google.gerrit.server.index.change; import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH; +import com.google.common.base.Objects; import com.google.common.flogger.FluentLogger; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -40,7 +41,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Config; @@ -70,6 +73,11 @@ public class ChangeIndexer { private final StalenessChecker stalenessChecker; private final boolean autoReindexIfStale; + private final Set queuedIndexTasks = + Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set queuedReindexIfStaleTasks = + Collections.newSetFromMap(new ConcurrentHashMap<>()); + @AssistedInject ChangeIndexer( @GerritServerConfig Config cfg, @@ -123,7 +131,11 @@ public class ChangeIndexer { * @return future for the indexing task. */ public ListenableFuture indexAsync(Project.NameKey project, Change.Id id) { - return submit(new IndexTask(project, id)); + IndexTask task = new IndexTask(project, id); + if (queuedIndexTasks.add(task)) { + return submit(task); + } + return Futures.immediateFuture(null); } /** @@ -239,7 +251,11 @@ public class ChangeIndexer { * @return future for reindexing the change; returns true if the change was stale. */ public ListenableFuture reindexIfStale(Project.NameKey project, Change.Id id) { - return submit(new ReindexIfStaleTask(project, id), batchExecutor); + ReindexIfStaleTask task = new ReindexIfStaleTask(project, id); + if (queuedReindexIfStaleTasks.add(task)) { + return submit(task, batchExecutor); + } + return Futures.immediateCheckedFuture(false); } private void autoReindexIfStale(ChangeData cd) { @@ -278,6 +294,8 @@ public class ChangeIndexer { protected abstract T callImpl() throws Exception; + protected abstract void remove(); + @Override public abstract String toString(); @@ -308,15 +326,35 @@ public class ChangeIndexer { @Override public Void callImpl() throws Exception { + remove(); ChangeData cd = changeDataFactory.create(project, id); index(cd); return null; } + @Override + public int hashCode() { + return Objects.hashCode(IndexTask.class, id.get()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IndexTask)) { + return false; + } + IndexTask other = (IndexTask) obj; + return id.get() == other.id.get(); + } + @Override public String toString() { return "index-change-" + id; } + + @Override + protected void remove() { + queuedIndexTasks.remove(this); + } } // Not AbstractIndexTask as it doesn't need a request context. @@ -352,6 +390,7 @@ public class ChangeIndexer { @Override public Boolean callImpl() throws Exception { + remove(); try { if (stalenessChecker.isStale(id)) { indexImpl(changeDataFactory.create(project, id)); @@ -368,10 +407,29 @@ public class ChangeIndexer { return false; } + @Override + public int hashCode() { + return Objects.hashCode(ReindexIfStaleTask.class, id.get()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ReindexIfStaleTask)) { + return false; + } + ReindexIfStaleTask other = (ReindexIfStaleTask) obj; + return id.get() == other.id.get(); + } + @Override public String toString() { return "reindex-if-stale-change-" + id; } + + @Override + protected void remove() { + queuedReindexIfStaleTasks.remove(this); + } } private boolean isCausedByRepositoryNotFoundException(Throwable throwable) { diff --git a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java index 4062988731..fd12345e16 100644 --- a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java +++ b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java @@ -17,6 +17,7 @@ package com.google.gerrit.server.index.change; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.gerrit.server.query.change.ChangeData.asChanges; +import com.google.common.base.Objects; import com.google.common.flogger.FluentLogger; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -42,8 +43,11 @@ import com.google.gerrit.server.util.RequestContext; import com.google.inject.Inject; import com.google.inject.Provider; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import org.eclipse.jgit.lib.Config; @@ -61,6 +65,8 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener { private final ListeningExecutorService executor; private final boolean enabled; + private final Set queuedIndexTasks = Collections.newSetFromMap(new ConcurrentHashMap<>()); + @Inject ReindexAfterRefUpdate( @GerritServerConfig Config cfg, @@ -107,9 +113,12 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener { @Override public void onSuccess(List changes) { for (Change c : changes) { - // Don't retry indefinitely; if this fails changes may be stale. - @SuppressWarnings("unused") - Future possiblyIgnoredError = executor.submit(new Index(event, c.getId())); + Index task = new Index(event, c.getId()); + if (queuedIndexTasks.add(task)) { + // Don't retry indefinitely; if this fails changes may be stale. + @SuppressWarnings("unused") + Future possiblyIgnoredError = executor.submit(task); + } } } @@ -139,6 +148,8 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener { } protected abstract V impl(RequestContext ctx) throws Exception; + + protected abstract void remove(); } private class GetChanges extends Task> { @@ -163,6 +174,9 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener { + " update of project " + event.getProjectName(); } + + @Override + protected void remove() {} } private class Index extends Task { @@ -176,6 +190,7 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener { @Override protected Void impl(RequestContext ctx) throws IOException { // Reload change, as some time may have passed since GetChanges. + remove(); try { Change c = notesFactory.createChecked(new Project.NameKey(event.getProjectName()), id).getChange(); @@ -186,9 +201,28 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener { return null; } + @Override + public int hashCode() { + return Objects.hashCode(Index.class, id.get()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Index)) { + return false; + } + Index other = (Index) obj; + return id.get() == other.id.get(); + } + @Override public String toString() { return "Index change " + id.get() + " of project " + event.getProjectName(); } + + @Override + protected void remove() { + queuedIndexTasks.remove(this); + } } } diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java index 01d8a17711..6468038d98 100644 --- a/java/com/google/gerrit/server/mail/send/CommentSender.java +++ b/java/com/google/gerrit/server/mail/send/CommentSender.java @@ -75,21 +75,19 @@ public class CommentSender extends ReplyToChangeSender { public List comments = new ArrayList<>(); /** @return a web link to the given patch set and file. */ - public String getLink() { - String url = getGerritUrl(); - if (url == null) { - return null; - } + public String getFileLink() { + return args.urlFormatter + .get() + .getPatchFileView(change, patchSetId, KeyUtil.encode(filename)) + .orElse(null); + } - return new StringBuilder() - .append(url) - .append("#/c/") - .append(change.getId()) - .append('/') - .append(patchSetId) - .append('/') - .append(KeyUtil.encode(filename)) - .toString(); + /** @return a web link to a comment within a given patch set and file. */ + public String getCommentLink(short side, int startLine) { + return args.urlFormatter + .get() + .getInlineCommentView(change, patchSetId, KeyUtil.encode(filename), side, startLine) + .orElse(null); } /** @@ -391,7 +389,7 @@ public class CommentSender extends ReplyToChangeSender { for (CommentSender.FileCommentGroup group : getGroupedInlineComments(repo)) { Map groupData = new HashMap<>(); - groupData.put("link", group.getLink()); + groupData.put("link", group.getFileLink()); groupData.put("title", group.getTitle()); groupData.put("patchSetId", group.patchSetId); @@ -420,11 +418,9 @@ public class CommentSender extends ReplyToChangeSender { // Set the comment link. if (comment.lineNbr == 0) { - commentData.put("link", group.getLink()); - } else if (comment.side == 0) { - commentData.put("link", group.getLink() + "@a" + startLine); + commentData.put("link", group.getFileLink()); } else { - commentData.put("link", group.getLink() + '@' + startLine); + commentData.put("link", group.getCommentLink(comment.side, startLine)); } // Set robot comment data. diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java index db97f06511..2efcb381c5 100644 --- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java +++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java @@ -282,16 +282,10 @@ public abstract class OutgoingEmail { } public String getSettingsUrl() { - if (getGerritUrl() != null) { - final StringBuilder r = new StringBuilder(); - r.append(getGerritUrl()); - r.append("settings"); - return r.toString(); - } - return null; + return args.urlFormatter.get().getSettingsUrl().orElse(null); } - public String getGerritUrl() { + private String getGerritUrl() { return args.urlFormatter.get().getWebUrl().orElse(null); } diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java index e37015105f..dfc33395f2 100644 --- a/java/com/google/gerrit/server/permissions/ProjectControl.java +++ b/java/com/google/gerrit/server/permissions/ProjectControl.java @@ -15,7 +15,10 @@ package com.google.gerrit.server.permissions; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.gerrit.common.data.AccessSection.ALL; +import static com.google.gerrit.common.data.AccessSection.REGEX_PREFIX; import static com.google.gerrit.reviewdb.client.RefNames.REFS_TAGS; +import static com.google.gerrit.server.util.MagicBranch.NEW_CHANGE; import com.google.common.collect.Sets; import com.google.gerrit.common.data.AccessSection; @@ -139,7 +142,7 @@ class ProjectControl { /** Is this user a project owner? */ boolean isOwner() { - return (isDeclaredOwner() && controlForRef("refs/*").canPerform(Permission.OWNER)) || isAdmin(); + return (isDeclaredOwner() && controlForRef(ALL).canPerform(Permission.OWNER)) || isAdmin(); } /** @@ -200,7 +203,8 @@ class ProjectControl { private boolean canCreateChanges() { for (SectionMatcher matcher : access()) { AccessSection section = matcher.getSection(); - if (section.getName().startsWith("refs/for/")) { + if (section.getName().startsWith(NEW_CHANGE) + || section.getName().startsWith(REGEX_PREFIX + NEW_CHANGE)) { Permission permission = section.getPermission(Permission.PUSH); if (permission != null && controlForRef(section.getName()).canPerform(Permission.PUSH)) { return true; @@ -222,7 +226,8 @@ class ProjectControl { for (SectionMatcher matcher : access()) { AccessSection section = matcher.getSection(); - if (section.getName().startsWith(REFS_TAGS)) { + if (section.getName().startsWith(REFS_TAGS) + || section.getName().startsWith(REGEX_PREFIX + REFS_TAGS)) { Permission permission = section.getPermission(permissionName); if (permission == null) { continue; @@ -276,7 +281,7 @@ class ProjectControl { private boolean canPerformOnAllRefs(String permission, Set ignore) { boolean canPerform = false; Set patterns = allRefPatterns(permission); - if (patterns.contains(AccessSection.ALL)) { + if (patterns.contains(ALL)) { // Only possible if granted on the pattern that // matches every possible reference. Check all // patterns also have the permission. diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java index 75c05223bc..937cb1bc1b 100644 --- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java +++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java @@ -78,6 +78,8 @@ import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.common.LabelInfo; import com.google.gerrit.extensions.common.RevisionInfo; import com.google.gerrit.extensions.common.testing.EditInfoSubject; +import com.google.gerrit.extensions.registration.DynamicSet; +import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.mail.Address; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.BooleanProjectConfig; @@ -88,8 +90,12 @@ import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.server.ChangeMessagesUtil; +import com.google.gerrit.server.events.CommitReceivedEvent; import com.google.gerrit.server.git.receive.NoteDbPushOption; import com.google.gerrit.server.git.receive.ReceiveConstants; +import com.google.gerrit.server.git.validators.CommitValidationException; +import com.google.gerrit.server.git.validators.CommitValidationListener; +import com.google.gerrit.server.git.validators.CommitValidationMessage; import com.google.gerrit.server.git.validators.CommitValidators.ChangeIdValidator; import com.google.gerrit.server.group.SystemGroupBackend; import com.google.gerrit.server.project.testing.Util; @@ -99,6 +105,7 @@ import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; @@ -106,6 +113,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import java.util.stream.Stream; import org.eclipse.jgit.api.errors.GitAPIException; @@ -139,6 +147,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest { private static String NEW_CHANGE_INDICATOR = " [NEW]"; private LabelType patchSetLock; + @Inject private DynamicSet commitValidators; + @BeforeClass public static void setTimeForTesting() { TestTimeUtil.resetWithClockStep(1, SECONDS); @@ -2281,6 +2291,57 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest { .isEqualTo(Iterables.getLast(commits).name()); } + private static class TestValidator implements CommitValidationListener { + private final AtomicInteger count = new AtomicInteger(); + + @Override + public List onCommitReceived(CommitReceivedEvent receiveEvent) + throws CommitValidationException { + count.incrementAndGet(); + return Collections.emptyList(); + } + + public int count() { + return count.get(); + } + } + + @Test + public void skipValidation() throws Exception { + String master = "refs/heads/master"; + TestValidator validator = new TestValidator(); + RegistrationHandle handle = commitValidators.add("test-validator", validator); + + try { + // Validation listener is called on normal push + PushOneCommit push = + pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content"); + PushOneCommit.Result r = push.to(master); + r.assertOkStatus(); + assertThat(validator.count()).isEqualTo(1); + + // Push is rejected and validation listener is not called when not allowed + // to use skip option + PushOneCommit push2 = + pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content"); + push2.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION)); + r = push2.to(master); + r.assertErrorStatus("not permitted: skip validation"); + assertThat(validator.count()).isEqualTo(1); + + // Validation listener is not called when skip option is used + grantSkipValidation(project, master, SystemGroupBackend.REGISTERED_USERS); + PushOneCommit push3 = + pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content"); + push3.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION)); + r = push3.to(master); + r.assertOkStatus(); + assertThat(validator.count()).isEqualTo(1); + } finally { + handle.remove(); + } + } + @Test public void pushNoteDbRef() throws Exception { String ref = "refs/changes/34/1234/meta"; diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java index 15dd3fb087..60aacf73b3 100644 --- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java +++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java @@ -622,13 +622,17 @@ public class CommentsIT extends AbstractDaemonTest { + "comments\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/1/a.txt \n" + "File a.txt:\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/1/a.txt@a2 \n" + "PS1, Line 2: \n" @@ -636,7 +640,9 @@ public class CommentsIT extends AbstractDaemonTest { + "\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/1/a.txt@1 \n" + "PS1, Line 1: boring\n" @@ -644,13 +650,17 @@ public class CommentsIT extends AbstractDaemonTest { + "\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/2/a.txt \n" + "File a.txt:\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/2/a.txt@a1 \n" + "PS2, Line 1: \n" @@ -658,7 +668,9 @@ public class CommentsIT extends AbstractDaemonTest { + "\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/2/a.txt@a2 \n" + "PS2, Line 2: \n" @@ -666,7 +678,9 @@ public class CommentsIT extends AbstractDaemonTest { + "\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/2/a.txt@1 \n" + "PS2, Line 1: interesting\n" @@ -674,7 +688,9 @@ public class CommentsIT extends AbstractDaemonTest { + "\n" + "\n" + url - + "#/c/" + + "c/" + + project.get() + + "/+/" + c + "/2/a.txt@2 \n" + "PS2, Line 2: nten\n" diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html index 89e2b7dd2e..6accdfc8ad 100644 --- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html +++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.html @@ -16,12 +16,14 @@ limitations under the License. --> + + + -