Merge "Implement inline edit REST API in PolyGerrit"
diff --git a/.bazelproject b/.bazelproject
index 41bb27f..e3a7a9c 100644
--- a/.bazelproject
+++ b/.bazelproject
@@ -18,3 +18,6 @@
java_language_level: 8
workspace_type: java
+
+build_flags:
+ --javacopt=-g
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index bb637f8..139284c 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2613,6 +2613,19 @@
+
Defaults to 1024.
+[[index.reindexAfterRefUpdate]]index.reindexAfterRefUpdate::
++
+Whether to reindex all affected open changes after a ref is updated. This
+includes reindexing all open changes to recompute the "mergeable" bit every time
+the destination branch moves, as well as reindexing changes to take into account
+new project configuration (e.g. label definitions).
++
+Leaving this enabled may result in fresher results, but may cause performance
+problems if there are lots of open changes on a project whose branches advance
+frequently.
++
+Defaults to true.
+
==== Lucene configuration
Open and closed changes are indexed in separate indexes named
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index b6d2c53..04adcbd 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -736,6 +736,68 @@
}
----
+[[query_attributes]]
+=== Query Attributes ===
+
+Plugins can provide additional attributes to be returned in Gerrit queries by
+implementing the ChangeAttributeFactory interface and registering it to the
+ChangeQueryProcessor.ChangeAttributeFactory class in the plugin module's
+'configure()' method. The new attribute(s) will be output under a "plugin"
+attribute in the change query output.
+
+The example below shows a plugin that adds two attributes ('exampleName' and
+'changeValue'), to the change query output.
+
+[source, java]
+----
+public class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(ChangeAttributeFactory.class)
+ .annotatedWith(Exports.named("example"))
+ .to(AttributeFactory.class);
+ }
+}
+
+public class AttributeFactory implements ChangeAttributeFactory {
+
+ public class PluginAttribute extends PluginDefinedInfo {
+ public String exampleName;
+ public String changeValue;
+
+ public PluginAttribute(ChangeData c) {
+ this.exampleName = "Attribute Example";
+ this.changeValue = Integer.toString(c.getId().get());
+ }
+ }
+
+ @Override
+ public PluginDefinedInfo create(ChangeData c, ChangeQueryProcessor qp, String plugin) {
+ return new PluginAttribute(c);
+ }
+}
+----
+
+Example
+----
+
+ssh -p 29418 localhost gerrit query "change:1" --format json
+
+Output:
+
+{
+ "url" : "http://localhost:8080/1",
+ "plugins" : [
+ {
+ "name" : "myplugin-name",
+ "exampleName" : "Attribute Example",
+ "changeValue" : "1"
+ }
+ ],
+ ...
+}
+----
+
[[simple-configuration]]
== Simple Configuration in `gerrit.config`
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index b7b6f6a..1e741a8 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -228,6 +228,7 @@
cfg.setInt("sshd", null, "commandStartThreads", 1);
cfg.setInt("receive", null, "threadPoolSize", 1);
cfg.setInt("index", null, "threads", 1);
+ cfg.setBoolean("index", null, "reindexAfterRefUpdate", false);
}
private static Injector createTestInjector(Daemon daemon) throws Exception {
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index ad8140e..89585c3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -437,6 +437,17 @@
}
@Test
+ public void cannotAddNonConfirmedEmailWithoutModifyAccountPermission() throws Exception {
+ TestAccount account = accounts.create(name("user"));
+ EmailInput input = new EmailInput();
+ input.email = "test@test.com";
+ input.noConfirmation = true;
+ setApiUser(user);
+ exception.expect(AuthException.class);
+ gApi.accounts().id(account.username).addEmail(input);
+ }
+
+ @Test
public void deleteEmail() throws Exception {
String email = "foo.bar@example.com";
EmailInput input = new EmailInput();
@@ -542,12 +553,7 @@
@Test
@Sandboxed
public void fetchUserBranch() throws Exception {
- // change something in the user preferences to ensure that the user branch
- // is created
- setApiUser(user);
- GeneralPreferencesInfo input = new GeneralPreferencesInfo();
- input.changesPerPage = GeneralPreferencesInfo.defaults().changesPerPage + 10;
- gApi.accounts().self().setPreferences(input);
+ ensureUserBranchCreated(user);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
String userRefName = RefNames.refsUsers(user.id);
@@ -597,11 +603,7 @@
@Test
public void pushToUserBranch() throws Exception {
- // change something in the user preferences to ensure that the user branch
- // is created
- GeneralPreferencesInfo input = new GeneralPreferencesInfo();
- input.changesPerPage = GeneralPreferencesInfo.defaults().changesPerPage + 10;
- gApi.accounts().self().setPreferences(input);
+ ensureUserBranchCreated(admin);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
@@ -615,11 +617,7 @@
@Test
public void pushToUserBranchForReview() throws Exception {
- // change something in the user preferences to ensure that the user branch
- // is created
- GeneralPreferencesInfo input = new GeneralPreferencesInfo();
- input.changesPerPage = GeneralPreferencesInfo.defaults().changesPerPage + 10;
- gApi.accounts().self().setPreferences(input);
+ ensureUserBranchCreated(admin);
String userRefName = RefNames.refsUsers(admin.id);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
@@ -642,11 +640,7 @@
@Test
public void pushWatchConfigToUserBranch() throws Exception {
- // change something in the user preferences to ensure that the user branch
- // is created
- GeneralPreferencesInfo input = new GeneralPreferencesInfo();
- input.changesPerPage = GeneralPreferencesInfo.defaults().changesPerPage + 10;
- gApi.accounts().self().setPreferences(input);
+ ensureUserBranchCreated(admin);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
@@ -689,6 +683,8 @@
@Test
@Sandboxed
public void cannotDeleteUserBranch() throws Exception {
+ ensureUserBranchCreated(admin);
+
grant(
Permission.DELETE,
allUsers,
@@ -711,6 +707,8 @@
@Test
@Sandboxed
public void deleteUserBranchWithAccessDatabaseCapability() throws Exception {
+ ensureUserBranchCreated(admin);
+
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
grant(
Permission.DELETE,
@@ -1021,4 +1019,12 @@
assertThat(accounts).hasSize(1);
assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.getId());
}
+
+ private void ensureUserBranchCreated(TestAccount account) throws Exception {
+ // Change something in the user preferences to ensure that the user branch is created.
+ setApiUser(account);
+ GeneralPreferencesInfo input = new GeneralPreferencesInfo();
+ input.changesPerPage = GeneralPreferencesInfo.defaults().changesPerPage + 10;
+ gApi.accounts().self().setPreferences(input);
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index 17c3488..727c8e2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -223,6 +223,50 @@
}
@Test
+ public void reviewerAndCCReceiveSameEmail() throws Exception {
+ assume().that(notesMigration.readChanges()).isTrue();
+
+ PushOneCommit.Result r = createChange();
+ for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
+ for (int i = 0; i < 10; i++) {
+ AddReviewerInput input = new AddReviewerInput();
+ input.reviewer = String.format("%s-%s@gerritcodereview.com", state, i);
+ input.state = state;
+ gApi.changes().id(r.getChangeId()).addReviewer(input);
+ }
+ }
+
+ // Also add user as a regular reviewer
+ AddReviewerInput input = new AddReviewerInput();
+ input.reviewer = user.email;
+ input.state = ReviewerState.REVIEWER;
+ gApi.changes().id(r.getChangeId()).addReviewer(input);
+
+ sender.clear();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
+ // Assert that only one email was sent out to everyone
+ assertThat(sender.getMessages()).hasSize(1);
+ }
+
+ @Test
+ public void addingMultipleReviewersAndCCsAtOnceSendsOnlyOneEmail() throws Exception {
+ assume().that(notesMigration.readChanges()).isTrue();
+
+ PushOneCommit.Result r = createChange();
+ ReviewInput reviewInput = new ReviewInput();
+ for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
+ for (int i = 0; i < 10; i++) {
+ reviewInput.reviewer(String.format("%s-%s@gerritcodereview.com", state, i), state, true);
+ }
+ }
+ assertThat(reviewInput.reviewers).hasSize(20);
+
+ sender.clear();
+ gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(reviewInput);
+ assertThat(sender.getMessages()).hasSize(1);
+ }
+
+ @Test
public void rejectMissingEmail() throws Exception {
assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index 63ea2df..f7fe4f1 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -67,7 +67,6 @@
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
import org.junit.Before;
import org.junit.Test;
@@ -919,7 +918,7 @@
new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws IOException {
- ctx.addRefUpdate(new ReceiveCommand(oldId, newId, dest));
+ ctx.addRefUpdate(oldId, newId, dest);
}
@Override
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index b4f68fa..fcbad4f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -517,7 +517,7 @@
}
@Test
- @GerritConfig(name = "index.testReindexAfterUpdate", value = "false")
+ @GerritConfig(name = "index.testAutoReindexIfStale", value = "false")
public void getRelatedForStaleChange() throws Exception {
RevCommit c1_1 = commitBuilder().add("a.txt", "1").message("subject: 1").create();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
index 9d15daf..15b74bd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
@@ -120,7 +120,7 @@
// unintentional auto-rebuilding of the change in NoteDb during the read
// path of the reindex-if-stale check. For the purposes of this test, we
// want precise control over when auto-rebuilding happens.
- cfg.setBoolean("index", null, "testReindexAfterUpdate", false);
+ cfg.setBoolean("index", null, "testAutoReindexIfStale", false);
return cfg;
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index 9850f2e..ada17b6 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -40,7 +40,6 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.ReceiveCommand;
import org.junit.Before;
import org.junit.Test;
@@ -71,7 +70,7 @@
public void updateRepo(RepoContext ctx) throws IOException {
ObjectId oldId = ctx.getRepoView().getRef(backup).orElse(ObjectId.zeroId());
newId = ctx.getRepoView().getRef(master).get();
- ctx.addRefUpdate(new ReceiveCommand(oldId, newId, backup));
+ ctx.addRefUpdate(oldId, newId, backup);
}
@Override
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java
index e13962d..2cb8384 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -63,4 +63,5 @@
public Boolean _moreChanges;
public List<ProblemInfo> problems;
+ public List<PluginDefinedInfo> plugins;
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginDefinedInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginDefinedInfo.java
new file mode 100644
index 0000000..e6fef0f
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginDefinedInfo.java
@@ -0,0 +1,19 @@
+// Copyright (C) 2017 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.extensions.common;
+
+public class PluginDefinedInfo {
+ public String name;
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/IndexServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/IndexServlet.java
index 3eb77ea..b55cf6c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/IndexServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/IndexServlet.java
@@ -37,7 +37,7 @@
private final byte[] indexSource;
IndexServlet(String canonicalURL, @Nullable String cdnPath) throws URISyntaxException {
- String resourcePath = "com/google/gerrit/httpd/raw/index.html.soy";
+ String resourcePath = "com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy";
SoyFileSet.Builder builder = SoyFileSet.builder();
builder.add(Resources.getResource(resourcePath));
SoyTofu.Renderer renderer =
@@ -47,7 +47,7 @@
.newRenderer("com.google.gerrit.httpd.raw.Index")
.setContentKind(SanitizedContent.ContentKind.HTML)
.setData(getTemplateData(canonicalURL, cdnPath));
- indexSource = renderer.render().getBytes();
+ indexSource = renderer.render().getBytes(UTF_8);
}
@Override
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
similarity index 92%
rename from gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy
rename to gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 4d8c43b..32f91f1 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/index.html.soy
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -27,7 +27,9 @@
<meta name="description" content="Gerrit Code Review">{\n}
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">{\n}
- <script>window.CANONICAL_PATH = '{$canonicalPath}';</script>{\n}
+ {if $canonicalPath != ''}
+ <script>window.CANONICAL_PATH = '{$canonicalPath}';</script>{\n}
+ {/if}
// SourceCodePro fonts are used in styles/fonts.css
// @see https://github.com/w3c/preload/issues/32 regarding crossorigin
@@ -40,5 +42,5 @@
<link rel="import" href="{$staticResourcePath}/elements/gr-app.html">{\n}
<body unresolved>{\n}
- <gr-app id="app" canonical-path="{$canonicalPath}"></gr-app>{\n}
+ <gr-app id="app"></gr-app>{\n}
{/template}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
index 21daa3e..17ce24a 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/RebuildNoteDb.java
@@ -48,7 +48,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.index.DummyIndexModule;
-import com.google.gerrit.server.index.change.ReindexAfterUpdate;
+import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
import com.google.gerrit.server.notedb.ChangeBundleReader;
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
import com.google.gerrit.server.notedb.NotesMigration;
@@ -212,7 +212,7 @@
public void configure() {
install(dbInjector.getInstance(BatchProgramModule.class));
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
- .to(ReindexAfterUpdate.class);
+ .to(ReindexAfterRefUpdate.class);
install(new DummyIndexModule());
factory(ChangeResource.Factory.class);
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index c86d5af..e625219 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -68,6 +68,7 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SectionSortCache;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -112,6 +113,8 @@
bind(new TypeLiteral<List<CommentLinkInfo>>() {})
.toProvider(CommentLinkProvider.class)
.in(SINGLETON);
+ bind(new TypeLiteral<DynamicMap<ChangeQueryProcessor.ChangeAttributeFactory>>() {})
+ .toInstance(DynamicMap.<ChangeQueryProcessor.ChangeAttributeFactory>emptyMap());
bind(String.class)
.annotatedWith(CanonicalWebUrl.class)
.toProvider(CanonicalWebUrlProvider.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalId.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalId.java
index a1d21c4..74e1fda 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -225,6 +225,13 @@
throw invalidConfig(noteId, String.format("Invalid external id: %s", externalIdKeyStr));
}
+ if (!externalIdKey.sha1().getName().equals(noteId)) {
+ throw invalidConfig(
+ noteId,
+ String.format(
+ "SHA1 of external ID %s does not match note ID %s", externalIdKeyStr, noteId));
+ }
+
String email = externalIdConfig.getString(EXTERNAL_ID_SECTION, externalIdKeyStr, EMAIL_KEY);
String password =
externalIdConfig.getString(EXTERNAL_ID_SECTION, externalIdKeyStr, PASSWORD_KEY);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
index 08473a0..8ac89ae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -341,7 +341,7 @@
return;
}
if (updateRefCommand == null) {
- ctx.addRefUpdate(new ReceiveCommand(ObjectId.zeroId(), commitId, psId.toRefName()));
+ ctx.addRefUpdate(ObjectId.zeroId(), commitId, psId.toRefName());
} else {
ctx.addRefUpdate(updateRefCommand);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index c86714a..4724ea1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -118,6 +118,7 @@
import com.google.gerrit.server.query.QueryResult;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
+import com.google.gerrit.server.query.change.PluginDefinedAttributesFactory;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -213,6 +214,7 @@
private boolean lazyLoad = true;
private AccountLoader accountLoader;
private FixInput fix;
+ private PluginDefinedAttributesFactory pluginDefinedAttributesFactory;
@Inject
ChangeJson(
@@ -276,6 +278,10 @@
return this;
}
+ public void setPluginDefinedAttributesFactory(PluginDefinedAttributesFactory pluginsFactory) {
+ this.pluginDefinedAttributesFactory = pluginsFactory;
+ }
+
public ChangeInfo format(ChangeResource rsrc) throws OrmException {
return format(changeDataFactory.create(db.get(), rsrc.getControl()));
}
@@ -520,6 +526,8 @@
out.labels = labelsFor(perm, ctl, cd, has(LABELS), has(DETAILED_LABELS));
out.submitted = getSubmittedOn(cd);
+ out.plugins =
+ pluginDefinedAttributesFactory != null ? pluginDefinedAttributesFactory.create(cd) : null;
if (out.labels != null && has(DETAILED_LABELS)) {
// If limited to specific patch sets but not the current patch set, don't
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
index f3df8e8..49d7bae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -76,7 +76,6 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -504,8 +503,7 @@
new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws IOException {
- ctx.addRefUpdate(
- new ReceiveCommand(commit, ObjectId.zeroId(), psIdToDelete.toRefName()));
+ ctx.addRefUpdate(commit, ObjectId.zeroId(), psIdToDelete.toRefName());
}
});
if (!reuseOldPsId) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java
index c7cfef9..992313d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteChangeOp.java
@@ -43,7 +43,6 @@
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
class DeleteChangeOp implements BatchUpdateOp {
static boolean allowDrafts(Config cfg) {
@@ -173,7 +172,7 @@
String prefix = new PatchSet.Id(id, 1).toRefName();
prefix = prefix.substring(0, prefix.length() - 1);
for (Map.Entry<String, ObjectId> e : ctx.getRepoView().getRefs(prefix).entrySet()) {
- ctx.addRefUpdate(new ReceiveCommand(e.getValue(), ObjectId.zeroId(), prefix + e.getKey()));
+ ctx.addRefUpdate(e.getValue(), ObjectId.zeroId(), prefix + e.getKey());
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index 222230b..a4db8c5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -47,7 +47,6 @@
import java.util.Collection;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.transport.ReceiveCommand;
@Singleton
public class DeleteDraftPatchSet
@@ -133,10 +132,9 @@
return;
}
ctx.addRefUpdate(
- new ReceiveCommand(
- ObjectId.fromString(patchSet.getRevision().get()),
- ObjectId.zeroId(),
- patchSet.getRefName()));
+ ObjectId.fromString(patchSet.getRevision().get()),
+ ObjectId.zeroId(),
+ patchSet.getRefName());
}
private void deleteDraftPatchSet(PatchSet patchSet, ChangeContext ctx) throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
index 1512976..32dbcda 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -207,9 +207,7 @@
public void updateRepo(RepoContext ctx)
throws AuthException, ResourceConflictException, IOException, OrmException {
validate(ctx);
- ctx.addRefUpdate(
- new ReceiveCommand(
- ObjectId.zeroId(), commitId, getPatchSetId().toRefName(), ReceiveCommand.Type.CREATE));
+ ctx.addRefUpdate(ObjectId.zeroId(), commitId, getPatchSetId().toRefName());
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java
index 7dec0fe..42dba3f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java
@@ -46,6 +46,7 @@
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.BundleWriter;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.kohsuke.args4j.Option;
@@ -145,9 +146,9 @@
MergeOpRepoManager orm = mergeOp.getMergeOpRepoManager();
for (Project.NameKey p : mergeOp.getAllProjects()) {
OpenRepo or = orm.getRepo(p);
- BundleWriter bw = new BundleWriter(or.getRepo());
+ BundleWriter bw = new BundleWriter(or.getCodeReviewRevWalk().getObjectReader());
bw.setObjectCountCallback(null);
- bw.setPackConfig(null);
+ bw.setPackConfig(new PackConfig(or.getRepo()));
Collection<ReceiveCommand> refs = or.getUpdate().getRefUpdates().values();
for (ReceiveCommand r : refs) {
bw.include(r.getRefName(), r.getNewId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 5f70786..b7f872b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -132,7 +132,7 @@
import com.google.gerrit.server.git.validators.UploadValidationListener;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.group.GroupModule;
-import com.google.gerrit.server.index.change.ReindexAfterUpdate;
+import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
import com.google.gerrit.server.mail.EmailModule;
import com.google.gerrit.server.mail.ListMailFilter;
import com.google.gerrit.server.mail.MailFilter;
@@ -166,6 +166,7 @@
import com.google.gerrit.server.project.SectionSortCache;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.query.change.ConflictsCacheImpl;
import com.google.gerrit.server.ssh.SshAddressesModule;
import com.google.gerrit.server.tools.ToolsCatalog;
@@ -330,7 +331,7 @@
DynamicSet.setOf(binder(), GarbageCollectorListener.class);
DynamicSet.setOf(binder(), HeadUpdatedListener.class);
DynamicSet.setOf(binder(), UsageDataPublishedListener.class);
- DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterUpdate.class);
+ DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterRefUpdate.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
.to(ProjectConfigEntry.UpdateChecker.class);
DynamicSet.setOf(binder(), EventListener.class);
@@ -378,6 +379,8 @@
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
+ DynamicMap.mapOf(binder(), ChangeQueryProcessor.ChangeAttributeFactory.class);
+
install(new GitwebConfig.LegacyModule(cfg));
bind(AnonymousUser.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
index 1a8a788..0467c92 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/data/ChangeAttribute.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.data;
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
import com.google.gerrit.reviewdb.client.Change;
import java.util.List;
@@ -43,4 +44,5 @@
public List<DependencyAttribute> neededBy;
public List<SubmitRecordAttribute> submitRecords;
public List<AccountAttribute> allReviewers;
+ public List<PluginDefinedInfo> plugins;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index 92333c6..1e11968 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -59,7 +59,6 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
/**
* Utility functions to manipulate change edits.
@@ -223,9 +222,7 @@
new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws Exception {
- ctx.addRefUpdate(
- new ReceiveCommand(
- edit.getEditCommit().copy(), ObjectId.zeroId(), edit.getRefName()));
+ ctx.addRefUpdate(edit.getEditCommit().copy(), ObjectId.zeroId(), edit.getRefName());
}
});
bu.execute();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
index 7bb8271..f504c45 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
@@ -71,7 +71,6 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushCertificate;
-import org.eclipse.jgit.transport.ReceiveCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -211,7 +210,7 @@
}
if (updateRef) {
- ctx.addRefUpdate(new ReceiveCommand(ObjectId.zeroId(), commitId, patchSetId.toRefName()));
+ ctx.addRefUpdate(ObjectId.zeroId(), commitId, patchSetId.toRefName());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
index 7c236e1..93aea89 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
@@ -63,7 +63,6 @@
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.RefSpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,7 +81,7 @@
public void updateRepo(RepoContext ctx) throws Exception {
CodeReviewCommit c = composeGitlinksCommit(branch);
if (c != null) {
- ctx.addRefUpdate(new ReceiveCommand(c.getParent(0), c, branch.get()));
+ ctx.addRefUpdate(c.getParent(0), c, branch.get());
addBranchTip(branch, c);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
index 879ca0e..49399ef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
@@ -37,7 +37,6 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
public class CherryPick extends SubmitStrategy {
@@ -135,7 +134,7 @@
args.mergeTip.moveTipTo(newCommit, newCommit);
args.commitStatus.put(newCommit);
- ctx.addRefUpdate(new ReceiveCommand(ObjectId.zeroId(), newCommit, psId.toRefName()));
+ ctx.addRefUpdate(ObjectId.zeroId(), newCommit, psId.toRefName());
patchSetInfo = args.patchSetInfoFactory.get(ctx.getRevWalk(), newCommit, psId);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java
index 18b4173..a156a90 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseSubmitStrategy.java
@@ -43,7 +43,6 @@
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
/** This strategy covers RebaseAlways and RebaseIfNecessary ones. */
public class RebaseSubmitStrategy extends SubmitStrategy {
@@ -156,8 +155,7 @@
toMerge.setStatusCode(SKIPPED_IDENTICAL_TREE);
return;
}
- ctx.addRefUpdate(
- new ReceiveCommand(ObjectId.zeroId(), newCommit, newPatchSetId.toRefName()));
+ ctx.addRefUpdate(ObjectId.zeroId(), newCommit, newPatchSetId.toRefName());
} else {
// Stale read of patch set is ok; see comments in RebaseChangeOp.
PatchSet origPs =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index a788f8c..4edfab2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -107,7 +107,7 @@
private final ListeningExecutorService executor;
private final DynamicSet<ChangeIndexedListener> indexedListeners;
private final StalenessChecker stalenessChecker;
- private final boolean reindexAfterIndexUpdate;
+ private final boolean autoReindexIfStale;
@AssistedInject
ChangeIndexer(
@@ -131,7 +131,7 @@
this.indexedListeners = indexedListeners;
this.stalenessChecker = stalenessChecker;
this.batchExecutor = batchExecutor;
- this.reindexAfterIndexUpdate = reindexAfterIndexUpdate(cfg);
+ this.autoReindexIfStale = autoReindexIfStale(cfg);
this.index = index;
this.indexes = null;
}
@@ -158,13 +158,13 @@
this.indexedListeners = indexedListeners;
this.stalenessChecker = stalenessChecker;
this.batchExecutor = batchExecutor;
- this.reindexAfterIndexUpdate = reindexAfterIndexUpdate(cfg);
+ this.autoReindexIfStale = autoReindexIfStale(cfg);
this.index = null;
this.indexes = indexes;
}
- private static boolean reindexAfterIndexUpdate(Config cfg) {
- return cfg.getBoolean("index", null, "testReindexAfterUpdate", true);
+ private static boolean autoReindexIfStale(Config cfg) {
+ return cfg.getBoolean("index", null, "testAutoReindexIfStale", true);
}
/**
@@ -221,7 +221,7 @@
// and fix the staleness. It doesn't matter which order the two
// reindexIfStale calls actually execute in; we are guaranteed that at least
// one of them will execute after the second index write, (4).
- reindexAfterIndexUpdate(cd);
+ autoReindexIfStale(cd);
}
private void fireChangeIndexedEvent(int id) {
@@ -253,7 +253,7 @@
public void index(ReviewDb db, Change change) throws IOException, OrmException {
index(newChangeData(db, change));
// See comment in #index(ChangeData).
- reindexAfterIndexUpdate(change.getProject(), change.getId());
+ autoReindexIfStale(change.getProject(), change.getId());
}
/**
@@ -268,7 +268,7 @@
ChangeData cd = newChangeData(db, project, changeId);
index(cd);
// See comment in #index(ChangeData).
- reindexAfterIndexUpdate(cd);
+ autoReindexIfStale(cd);
}
/**
@@ -304,16 +304,16 @@
return submit(new ReindexIfStaleTask(project, id), batchExecutor);
}
- private void reindexAfterIndexUpdate(ChangeData cd) throws IOException {
+ private void autoReindexIfStale(ChangeData cd) throws IOException {
try {
- reindexAfterIndexUpdate(cd.project(), cd.getId());
+ autoReindexIfStale(cd.project(), cd.getId());
} catch (OrmException e) {
throw new IOException(e);
}
}
- private void reindexAfterIndexUpdate(Project.NameKey project, Change.Id id) {
- if (reindexAfterIndexUpdate) {
+ private void autoReindexIfStale(Project.NameKey project, Change.Id id) {
+ if (autoReindexIfStale) {
// Don't retry indefinitely; if this fails the change will be stale.
@SuppressWarnings("unused")
Future<?> possiblyIgnoredError = reindexIfStale(project, id);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
similarity index 92%
rename from gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
index 2f6f898..e863186 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
@@ -26,6 +26,7 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.QueueProvider.QueueType;
import com.google.gerrit.server.index.IndexExecutor;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -41,11 +42,12 @@
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
+import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
- private static final Logger log = LoggerFactory.getLogger(ReindexAfterUpdate.class);
+public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
+ private static final Logger log = LoggerFactory.getLogger(ReindexAfterRefUpdate.class);
private final OneOffRequestContext requestContext;
private final Provider<InternalChangeQuery> queryProvider;
@@ -53,9 +55,11 @@
private final ChangeIndexCollection indexes;
private final ChangeNotes.Factory notesFactory;
private final ListeningExecutorService executor;
+ private final boolean enabled;
@Inject
- ReindexAfterUpdate(
+ ReindexAfterRefUpdate(
+ @GerritServerConfig Config cfg,
OneOffRequestContext requestContext,
Provider<InternalChangeQuery> queryProvider,
ChangeIndexer.Factory indexerFactory,
@@ -68,11 +72,13 @@
this.indexes = indexes;
this.notesFactory = notesFactory;
this.executor = executor;
+ this.enabled = cfg.getBoolean("index", null, "reindexAfterRefUpdate", true);
}
@Override
public void onGitReferenceUpdated(final Event event) {
- if (event.getRefName().startsWith(RefNames.REFS_CHANGES)
+ if (!enabled
+ || event.getRefName().startsWith(RefNames.REFS_CHANGES)
|| event.getRefName().startsWith(RefNames.REFS_DRAFT_COMMENTS)
|| event.getRefName().startsWith(RefNames.REFS_USERS)) {
return;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
index d23e856..4ecd684 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
@@ -170,7 +170,6 @@
void flush() throws IOException {
flushToFinalInserter();
finalIns.flush();
- tempIns.clear();
}
void flushToFinalInserter() throws IOException {
@@ -178,6 +177,7 @@
for (InsertedObject obj : tempIns.getInsertedObjects()) {
finalIns.insert(obj.type(), obj.data().toByteArray());
}
+ tempIns.clear();
}
@Override
@@ -505,11 +505,7 @@
} else {
// OpenRepo buffers objects separately; caller may assume that objects are available in the
// inserter it previously passed via setChangeRepo.
- // TODO(dborowitz): This should be flushToFinalInserter to avoid flushing objects to the
- // underlying repo during a dry run. However, the only user of this is PreviewSubmit, which
- // uses BundleWriter, which only takes a Repository so it can't read unflushed objects. Fix
- // BundleWriter, then fix this call.
- or.flush();
+ or.flushToFinalInserter();
}
BatchRefUpdate bru = or.repo.getRefDatabase().newBatchUpdate();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 64ef091..863cffb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -758,8 +758,8 @@
return change;
}
- public LabelTypes getLabelTypes() {
- return changeControl.getLabelTypes();
+ public LabelTypes getLabelTypes() throws OrmException {
+ return changeControl().getLabelTypes();
}
public ChangeNotes notes() throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
index 91a37d5..efe44fa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
@@ -17,6 +17,8 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_LIMIT;
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
+import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.index.IndexConfig;
@@ -32,12 +34,26 @@
import com.google.gerrit.server.query.QueryProcessor;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
-public class ChangeQueryProcessor extends QueryProcessor<ChangeData> {
+public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
+ implements PluginDefinedAttributesFactory {
+ /**
+ * Register a ChangeAttributeFactory in a config Module like this:
+ *
+ * <p>bind(ChangeAttributeFactory.class) .annotatedWith(Exports.named("export-name"))
+ * .to(YourClass.class);
+ */
+ public interface ChangeAttributeFactory {
+ PluginDefinedInfo create(ChangeData a, ChangeQueryProcessor qp, String plugin);
+ }
+
private final Provider<ReviewDb> db;
private final ChangeControl.GenericFactory changeControlFactory;
private final ChangeNotes.Factory notesFactory;
+ private final DynamicMap<ChangeAttributeFactory> attributeFactories;
static {
// It is assumed that basic rewrites do not touch visibleto predicates.
@@ -55,7 +71,8 @@
ChangeIndexRewriter rewriter,
Provider<ReviewDb> db,
ChangeControl.GenericFactory changeControlFactory,
- ChangeNotes.Factory notesFactory) {
+ ChangeNotes.Factory notesFactory,
+ DynamicMap<ChangeAttributeFactory> attributeFactories) {
super(
userProvider,
metrics,
@@ -67,6 +84,7 @@
this.db = db;
this.changeControlFactory = changeControlFactory;
this.notesFactory = notesFactory;
+ this.attributeFactories = attributeFactories;
}
@Override
@@ -82,6 +100,30 @@
}
@Override
+ public List<PluginDefinedInfo> create(ChangeData cd) {
+ List<PluginDefinedInfo> plugins = new ArrayList<>(attributeFactories.plugins().size());
+ for (String plugin : attributeFactories.plugins()) {
+ for (Provider<ChangeAttributeFactory> provider :
+ attributeFactories.byPlugin(plugin).values()) {
+ PluginDefinedInfo pda = null;
+ try {
+ pda = provider.get().create(cd, this, plugin);
+ } catch (RuntimeException e) {
+ /* Eat runtime exceptions so that queries don't fail. */
+ }
+ if (pda != null) {
+ pda.name = plugin;
+ plugins.add(pda);
+ }
+ }
+ }
+ if (plugins.isEmpty()) {
+ plugins = null;
+ }
+ return plugins;
+ }
+
+ @Override
protected Predicate<ChangeData> enforceVisibility(Predicate<ChangeData> pred) {
return new AndChangeSource(
pred,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index cd98087..0d12132 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -313,6 +313,7 @@
eventFactory.addDependencies(rw, c, d.change(), d.currentPatchSet());
}
+ c.plugins = queryProcessor.create(d);
return c;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java
new file mode 100644
index 0000000..a795025
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2016 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.query.change;
+
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
+import java.util.List;
+
+public interface PluginDefinedAttributesFactory {
+ List<PluginDefinedInfo> create(ChangeData cd);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 7eccf45..f0ef40d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -137,13 +137,18 @@
int cnt = queries.size();
List<QueryResult<ChangeData>> results = imp.query(qb.parse(queries));
+
boolean requireLazyLoad =
containsAnyOf(options, ImmutableSet.of(DETAILED_LABELS, LABELS))
&& !qb.getArgs().getSchema().hasField(ChangeField.STORED_SUBMIT_RECORD_LENIENT);
+
+ ChangeJson cjson = json.create(options);
+ cjson.setPluginDefinedAttributesFactory(this.imp);
List<List<ChangeInfo>> res =
- json.create(options)
+ cjson
.lazyLoad(requireLazyLoad || containsAnyOf(options, ChangeJson.REQUIRE_LAZY_LOAD))
.formatQueryResults(results);
+
for (int n = 0; n < cnt; n++) {
List<ChangeInfo> info = res.get(n);
if (results.get(n).more()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java
index e457d9c..03a7c37 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -17,12 +17,14 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.ImmutableMultiset.toImmutableMultiset;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Multiset;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -129,6 +131,7 @@
boolean dryRun)
throws UpdateException, RestApiException {
checkNotNull(listener);
+ checkDifferentProject(updates);
// It's safe to downcast all members of the input collection in this case, because the only
// way a caller could have gotten any BatchUpdates in the first place is to call the create
// method above, which always returns instances of the type we expect. Just to be safe,
@@ -144,6 +147,15 @@
ReviewDbBatchUpdate.execute(reviewDbUpdates, listener, requestId, dryRun);
}
}
+
+ private static void checkDifferentProject(Collection<BatchUpdate> updates) {
+ Multiset<Project.NameKey> projectCounts =
+ updates.stream().map(u -> u.project).collect(toImmutableMultiset());
+ checkArgument(
+ projectCounts.entrySet().size() == updates.size(),
+ "updates must all be for different projects, got: %s",
+ projectCounts);
+ }
}
static void setRequestIds(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/RepoContext.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/RepoContext.java
index 3c1e896..9faf628 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/RepoContext.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/RepoContext.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.update;
import java.io.IOException;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.transport.ReceiveCommand;
@@ -31,10 +32,28 @@
/**
* Add a command to the pending list of commands.
*
- * <p>This method is the only way of updating refs in the repository from a {@link BatchUpdateOp}.
+ * <p>Adding commands to the {@code RepoContext} is the only way of updating refs in the
+ * repository from a {@link BatchUpdateOp}.
*
* @param cmd ref update command.
* @throws IOException if an error occurred opening the repo.
*/
void addRefUpdate(ReceiveCommand cmd) throws IOException;
+
+ /**
+ * Add a command to the pending list of commands.
+ *
+ * <p>Adding commands to the {@code RepoContext} is the only way of updating refs in the
+ * repository from a {@link BatchUpdateOp}.
+ *
+ * @param oldId the old object ID; must not be null. Use {@link ObjectId#zeroId()} for ref
+ * creation.
+ * @param newId the new object ID; must not be null. Use {@link ObjectId#zeroId()} for ref
+ * deletion.
+ * @param refName the ref name.
+ * @throws IOException if an error occurred opening the repo.
+ */
+ default void addRefUpdate(ObjectId oldId, ObjectId newId, String refName) throws IOException {
+ addRefUpdate(new ReceiveCommand(oldId, newId, refName));
+ }
}
diff --git a/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java b/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java
index b2b9890..e84b3ac 100644
--- a/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java
+++ b/gerrit-server/src/main/java/gerrit/PRED__check_user_label_3.java
@@ -21,7 +21,9 @@
import com.google.gerrit.server.permissions.LabelPermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gwtorm.server.OrmException;
import com.googlecode.prolog_cafe.exceptions.IllegalTypeException;
+import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PInstantiationException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.exceptions.SystemException;
@@ -80,19 +82,20 @@
}
short val = (short) ((IntegerTerm) a3).intValue();
- ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
- LabelType type = cd.getLabelTypes().byLabel(label);
- if (type == null) {
- return engine.fail();
- }
-
try {
+ ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
+ LabelType type = cd.getLabelTypes().byLabel(label);
+ if (type == null) {
+ return engine.fail();
+ }
StoredValues.PERMISSION_BACKEND
.get(engine)
.user(user)
.change(cd)
.check(new LabelPermission.WithValue(type, val));
return cont;
+ } catch (OrmException err) {
+ throw new JavaException(this, 1, err);
} catch (AuthException err) {
return engine.fail();
} catch (PermissionBackendException err) {
diff --git a/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java b/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
index 5c61007..f7f39da 100644
--- a/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
+++ b/gerrit-server/src/main/java/gerrit/PRED__user_label_range_4.java
@@ -20,7 +20,9 @@
import com.google.gerrit.server.permissions.LabelPermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gwtorm.server.OrmException;
import com.googlecode.prolog_cafe.exceptions.IllegalTypeException;
+import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PInstantiationException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.exceptions.SystemException;
@@ -74,15 +76,16 @@
}
CurrentUser user = (CurrentUser) ((JavaObjectTerm) a2).object();
- ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
- LabelType type = cd.getLabelTypes().byLabel(label);
- if (type == null) {
- return engine.fail();
- }
-
Set<LabelPermission.WithValue> can;
try {
+ ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
+ LabelType type = cd.getLabelTypes().byLabel(label);
+ if (type == null) {
+ return engine.fail();
+ }
can = StoredValues.PERMISSION_BACKEND.get(engine).user(user).change(cd).test(type);
+ } catch (OrmException err) {
+ throw new JavaException(this, 1, err);
} catch (PermissionBackendException err) {
SystemException se = new SystemException(err.getMessage());
se.initCause(err);
diff --git a/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java b/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
index 33d63c4..9bfcc61 100644
--- a/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
+++ b/gerrit-server/src/main/java/gerrit/PRED_get_legacy_label_types_1.java
@@ -17,6 +17,8 @@
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.rules.StoredValues;
+import com.google.gwtorm.server.OrmException;
+import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -51,7 +53,12 @@
public Operation exec(Prolog engine) throws PrologException {
engine.setB0();
Term a1 = arg1.dereference();
- List<LabelType> list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
+ List<LabelType> list;
+ try {
+ list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
+ } catch (OrmException err) {
+ throw new JavaException(this, 1, err);
+ }
Term head = Prolog.Nil;
for (int idx = list.size() - 1; 0 <= idx; idx--) {
head = new ListTerm(export(list.get(idx)), head);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
index 3dfcaec..40596e8 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/GerritCommonTest.java
@@ -54,7 +54,7 @@
}
@Override
- protected void setUpEnvironment(PrologEnvironment env) {
+ protected void setUpEnvironment(PrologEnvironment env) throws Exception {
LabelTypes labelTypes = new LabelTypes(Arrays.asList(Util.codeReview(), Util.verified()));
ChangeData cd = EasyMock.createMock(ChangeData.class);
expect(cd.getLabelTypes()).andStubReturn(labelTypes);
@@ -63,12 +63,12 @@
}
@Test
- public void gerritCommon() {
+ public void gerritCommon() throws Exception {
runPrologBasedTests();
}
@Test
- public void reductionLimit() throws CompileException {
+ public void reductionLimit() throws Exception {
PrologEnvironment env = envFactory.create(machine);
setUpEnvironment(env);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
index 6f6d189..7b2b388 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/rules/PrologTestCase.java
@@ -84,7 +84,7 @@
*
* @param env Prolog environment.
*/
- protected void setUpEnvironment(PrologEnvironment env) {}
+ protected void setUpEnvironment(PrologEnvironment env) throws Exception {}
private PrologMachineCopy newMachine() {
BufferingPrologControl ctl = new BufferingPrologControl();
@@ -115,7 +115,7 @@
return env.execute(Prolog.BUILTIN, "clause", head, new VariableTerm());
}
- public void runPrologBasedTests() {
+ public void runPrologBasedTests() throws Exception {
int errors = 0;
long start = TimeUtil.nowMs();
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/update/BatchUpdateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/update/BatchUpdateTest.java
index 5e82836..892a9ff 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -39,7 +39,6 @@
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -122,9 +121,7 @@
new RepoOnlyOp() {
@Override
public void updateRepo(RepoContext ctx) throws Exception {
- ctx.addRefUpdate(
- new ReceiveCommand(
- masterCommit.getId(), branchCommit.getId(), "refs/heads/master"));
+ ctx.addRefUpdate(masterCommit.getId(), branchCommit.getId(), "refs/heads/master");
}
});
bu.execute();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
index 1192eb5..2e5bf71 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Query.java
@@ -24,7 +24,7 @@
import org.kohsuke.args4j.Option;
@CommandMetaData(name = "query", description = "Query the change database")
-class Query extends SshCommand {
+public class Query extends SshCommand {
@Inject private OutputStreamQuery processor;
@Option(name = "--format", metaVar = "FMT", usage = "Output display format")
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index 7b5acd8..38eb791 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,6 +1,6 @@
load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "MAVEN_CENTRAL", "maven_jar")
-_JGIT_VERS = "4.7.0.201704051617-r.15-gc4e952109"
+_JGIT_VERS = "4.7.0.201704051617-r.37-gc80d8c590"
_DOC_VERS = "4.7.0.201704051617-r" # Set to _JGIT_VERS unless using a snapshot
@@ -26,28 +26,28 @@
name = "jgit_lib",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "875277521153030e2bdab12bf602b740232b2b28",
- src_sha1 = "f9adcd3ef0f77c5db16569771f95bc0142c36f46",
+ sha1 = "edb739cd1e7c72dab361a8f6011807ae7fae35e2",
+ src_sha1 = "ddf922143dd88ec8fbd2c44f48f203340e6b4d54",
unsign = True,
)
maven_jar(
name = "jgit_servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "e1037f50696a6e19fb5d30f9d44cb31e3c5fe8b0",
+ sha1 = "e864cb9f7e16d77ff75805708cd82e6f82a73246",
unsign = True,
)
maven_jar(
name = "jgit_archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "660bc82c9ff3c33249d269860d9793e830d6c374",
+ sha1 = "cc944356eb8ca74446341729d539f5b9faccb698",
)
maven_jar(
name = "jgit_junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "c2b28646cc2531df947a9e0f73fa9c415567b05e",
+ sha1 = "eae23cc952d8b9d332287f7a4d4200c17ae78411",
unsign = True,
)
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
index 1e277bc..0f38f36 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -23,6 +23,7 @@
<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
<script>
+ /** @type {String} */
window.CANONICAL_PATH = '/r';
</script>
<link rel="import" href="base-url-behavior.html">
@@ -72,4 +73,4 @@
);
});
});
-</script>
\ No newline at end of file
+</script>
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
index e4c4d11..8eb63a4 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior.js
@@ -20,7 +20,10 @@
var TooltipBehavior = {
properties: {
- hasTooltip: Boolean,
+ hasTooltip: {
+ type: Boolean,
+ observer: '_setupTooltipListeners',
+ },
_isTouchDevice: {
type: Boolean,
@@ -30,16 +33,10 @@
},
_tooltip: Element,
_titleText: String,
- },
-
- attached: function() {
- if (!this.hasTooltip) { return; }
-
- this.addEventListener('mouseenter', this._handleShowTooltip.bind(this));
- this.addEventListener('mouseleave', this._handleHideTooltip.bind(this));
- this.addEventListener('tap', this._handleHideTooltip.bind(this));
-
- this.listen(window, 'scroll', '_handleWindowScroll');
+ _hasSetupTooltipListeners: {
+ type: Boolean,
+ value: false,
+ },
},
detached: function() {
@@ -47,6 +44,16 @@
this.unlisten(window, 'scroll', '_handleWindowScroll');
},
+ _setupTooltipListeners: function() {
+ if (this._hasSetupTooltipListeners || !this.hasTooltip) { return; }
+ this._hasSetupTooltipListeners = true;
+
+ this.addEventListener('mouseenter', this._handleShowTooltip.bind(this));
+ this.addEventListener('mouseleave', this._handleHideTooltip.bind(this));
+ this.addEventListener('tap', this._handleHideTooltip.bind(this));
+ this.listen(window, 'scroll', '_handleWindowScroll');
+ },
+
_handleShowTooltip: function(e) {
if (this._isTouchDevice) { return; }
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
index 2d7b6a2..580bb4b 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
@@ -23,6 +23,8 @@
<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-tooltip-behavior.html">
+<script>void(0);</script>
+
<test-fixture id="basic">
<template>
<tooltip-behavior-element></tooltip-behavior-element>
@@ -116,5 +118,11 @@
flushAsynchronousOperations();
assert.isTrue(element._handleHideTooltip.called);
});
+
+ test('sets up listeners when has-tooltip is changed', function() {
+ var addListenerStub = sandbox.stub(element, 'addEventListener');
+ element.hasTooltip = true;
+ assert.isTrue(addListenerStub.called);
+ });
});
</script>
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
index 3d99cec..6003bc4 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
@@ -32,6 +32,13 @@
return e.altKey || e.ctrlKey || e.metaKey || e.shiftKey;
},
+ isModifierPressed: function(e, modifier) {
+ e = getKeyboardEvent(e);
+ // When e is a keyboardEvent, e.event is not null.
+ if (e.event) { e = e.event; }
+ return e[modifier];
+ },
+
shouldSuppressKeyboardShortcut: function(e) {
e = getKeyboardEvent(e);
if (e.path[0].tagName === 'INPUT' || e.path[0].tagName === 'TEXTAREA') {
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
index a72eb75..840998c 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
@@ -127,5 +127,26 @@
MockInteractions.keyDownOn(element, 75, 'alt', 'k');
assert.isTrue(spy.lastCall.returnValue);
});
+
+ test('isModifierPressed returns accurate value', function() {
+ var spy = sandbox.spy(element, 'isModifierPressed');
+ element._handleKey = function(e) {
+ element.isModifierPressed(e, 'shiftKey');
+ };
+ MockInteractions.keyDownOn(element, 75, 'shift', 'k');
+ assert.isTrue(spy.lastCall.returnValue);
+ MockInteractions.keyDownOn(element, 75, null, 'k');
+ assert.isFalse(spy.lastCall.returnValue);
+ MockInteractions.keyDownOn(element, 75, 'ctrl', 'k');
+ assert.isFalse(spy.lastCall.returnValue);
+ MockInteractions.keyDownOn(element, 75, null, 'k');
+ assert.isFalse(spy.lastCall.returnValue);
+ MockInteractions.keyDownOn(element, 75, 'meta', 'k');
+ assert.isFalse(spy.lastCall.returnValue);
+ MockInteractions.keyDownOn(element, 75, null, 'k');
+ assert.isFalse(spy.lastCall.returnValue);
+ MockInteractions.keyDownOn(element, 75, 'alt', 'k');
+ assert.isFalse(spy.lastCall.returnValue);
+ });
});
</script>
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
index f71fe8f..e862cba 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior.html
@@ -100,7 +100,7 @@
},
changeBaseURL: function(changeNum, patchNum) {
- var v = this.getBaseUrl() + '/changes/' + changeNum;
+ var v = this.getBaseUrl() + '/changes/' + changeNum;
if (patchNum) {
v += '/revisions/' + patchNum;
}
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
index 2b3e858..f3c44eb 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
@@ -21,6 +21,7 @@
<script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../bower_components/web-component-tester/browser.js"></script>
<script>
+ /** @type {String} */
window.CANONICAL_PATH = '/r';
</script>
@@ -74,4 +75,4 @@
assert.deepEqual(element.changePath('1'), '/r/c/1');
});
});
-</script>
\ No newline at end of file
+</script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
index 057dd1d..afe0e38 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
index b2e8051..82d85ac 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
@@ -28,7 +28,11 @@
* @event title-change
*/
- behaviors: [Gerrit.URLEncodingBehavior],
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.URLEncodingBehavior,
+ ],
+
properties: {
/**
* URL params passed from the router.
@@ -140,7 +144,7 @@
offset = +(offset || 0);
var newOffset = Math.max(0, offset + (changesPerPage * direction));
// Double encode URI component.
- var href = '/q/' + this.encodeURL(query, false);
+ var href = this.getBaseUrl() + '/q/' + this.encodeURL(query, false);
if (newOffset > 0) {
href += ',' + newOffset;
}
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
index 0097a50..661dd2c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
@@ -89,6 +89,26 @@
'/q/status:open,10');
});
+ test('_computeNavLink with path', function() {
+ window.CANONICAL_PATH = '/r';
+ var query = 'status:open';
+ var offset = 0;
+ var direction = 1;
+ var changesPerPage = 5;
+ assert.equal(
+ element._computeNavLink(query, offset, direction, changesPerPage),
+ '/r/q/status:open,5');
+ direction = -1;
+ assert.equal(
+ element._computeNavLink(query, offset, direction, changesPerPage),
+ '/r/q/status:open');
+ offset = 5;
+ direction = 1;
+ assert.equal(
+ element._computeNavLink(query, offset, direction, changesPerPage),
+ '/r/q/status:open,10');
+ });
+
test('_hidePrevArrow', function() {
var offset = 0;
assert.isTrue(element._hidePrevArrow(offset));
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index cec9487..cbe5dad 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -465,7 +465,8 @@
project-config="[[_projectConfig]]"
selected-index="{{viewState.selectedFileIndex}}"
diff-view-mode="{{viewState.diffMode}}"
- num-files-shown="{{_numFilesShown}}"></gr-file-list>
+ num-files-shown="{{_numFilesShown}}"
+ file-list-increment="{{_numFilesShown}}"></gr-file-list>
</section>
<gr-messages-list id="messageList"
change-num="[[_changeNum]]"
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 8ce7a83..e576a71 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -22,7 +22,7 @@
var COMMENT_SAVE = 'Saving... Try again after all comments are saved.';
var MIN_LINES_FOR_COMMIT_COLLAPSE = 30;
- var DEFAULT_NUM_FILES_SHOWN = 75;
+ var DEFAULT_NUM_FILES_SHOWN = 200;
// Maximum length for patch set descriptions.
var PATCH_DESC_MAX_LENGTH = 500;
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index 3534808..5b27b72 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -374,8 +374,8 @@
};
element.viewState.changeNum = null;
element.viewState.diffMode = 'UNIFIED';
- assert.equal(element.viewState.numFilesShown, 75);
- assert.equal(element._numFilesShown, 75);
+ assert.equal(element.viewState.numFilesShown, 200);
+ assert.equal(element._numFilesShown, 200);
element._numFilesShown = 150;
flushAsynchronousOperations();
assert.equal(element.viewState.diffMode, 'UNIFIED');
@@ -394,8 +394,8 @@
flushAsynchronousOperations();
assert.isNull(element.viewState.diffMode);
assert.equal(element.viewState.changeNum, '2');
- assert.equal(element.viewState.numFilesShown, 75);
- assert.equal(element._numFilesShown, 75);
+ assert.equal(element.viewState.numFilesShown, 200);
+ assert.equal(element._numFilesShown, 200);
});
test('patch num change', function(done) {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index b20c82c..f844cfd 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -27,6 +27,7 @@
<link rel="import" href="../../shared/gr-linked-text/gr-linked-text.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-select/gr-select.html">
+<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
<dom-module id="gr-file-list">
<template>
@@ -326,6 +327,7 @@
path="[[file.__path]]"
prefs="[[_diffPrefs]]"
project-config="[[projectConfig]]"
+ on-line-selected="_onLineSelected"
view-mode="[[_getDiffViewMode(diffViewMode, _userPrefs)]]"></gr-diff>
</template>
</template>
@@ -367,13 +369,18 @@
link on-tap="_incrementNumFilesShown">
[[_computeIncrementText(numFilesShown, _files)]]
</gr-button>
- <gr-button
- class="fileListButton"
- id="showAllButton"
- hidden$="[[_computeFileListButtonHidden(numFilesShown, _files)]]"
- link on-tap="_showAllFiles">
- [[_computeShowAllText(_files)]]
- </gr-button>
+ <gr-tooltip-content
+ has-tooltip="[[_computeWarnShowAll(_files)]]"
+ show-icon="[[_computeWarnShowAll(_files)]]"
+ title$="[[_computeShowAllWarning(_files)]]">
+ <gr-button
+ class="fileListButton"
+ id="showAllButton"
+ hidden$="[[_computeFileListButtonHidden(numFilesShown, _files)]]"
+ link on-tap="_showAllFiles">
+ [[_computeShowAllText(_files)]]
+ </gr-button><!--
+ --></gr-tooltip-content>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
<gr-storage id="storage"></gr-storage>
<gr-diff-cursor id="diffCursor"></gr-diff-cursor>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 73dd969..d6eb7be 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -16,7 +16,7 @@
// Maximum length for patch set descriptions.
var PATCH_DESC_MAX_LENGTH = 500;
-
+ var WARN_SHOW_ALL_THRESHOLD = 1000;
var COMMIT_MESSAGE_PATH = '/COMMIT_MSG';
var FileStatus = {
@@ -80,11 +80,7 @@
type: Object,
computed: '_calculatePatchChange(_files)',
},
- _fileListIncrement: {
- type: Number,
- readOnly: true,
- value: 75,
- },
+ fileListIncrement: Number,
_hideChangeTotals: {
type: Boolean,
computed: '_shouldHideChangeTotals(_patchChange)',
@@ -528,11 +524,13 @@
_handleNKey: function(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ this.modifierPressed(e) && !this.isModifierPressed(e, 'shiftKey')) {
+ return;
+ }
if (!this._showInlineDiffs) { return; }
e.preventDefault();
- if (e.shiftKey) {
+ if (this.isModifierPressed(e, 'shiftKey')) {
this.$.diffCursor.moveToNextCommentThread();
} else {
this.$.diffCursor.moveToNextChunk();
@@ -541,11 +539,13 @@
_handlePKey: function(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e)) { return; }
+ this.modifierPressed(e) && !this.isModifierPressed(e, 'shiftKey')) {
+ return;
+ }
if (!this._showInlineDiffs) { return; }
e.preventDefault();
- if (e.shiftKey) {
+ if (this.isModifierPressed(e, 'shiftKey')) {
this.$.diffCursor.moveToPreviousCommentThread();
} else {
this.$.diffCursor.moveToPreviousChunk();
@@ -687,22 +687,23 @@
}
},
- _filesChanged: function() {
- this.async(function() {
- var diffElements = Polymer.dom(this.root).querySelectorAll('gr-diff');
+ _updateDiffCursor: function() {
+ var diffElements = Polymer.dom(this.root).querySelectorAll('gr-diff');
- // Overwrite the cursor's list of diffs:
- this.$.diffCursor.splice.apply(this.$.diffCursor,
- ['diffs', 0, this.$.diffCursor.diffs.length].concat(diffElements));
+ // Overwrite the cursor's list of diffs:
+ this.$.diffCursor.splice.apply(this.$.diffCursor,
+ ['diffs', 0, this.$.diffCursor.diffs.length].concat(diffElements));
+ },
- var files = Polymer.dom(this.root).querySelectorAll('.file-row');
- this.$.fileCursor.stops = files;
- this.$.fileCursor.setCursorAtIndex(this.selectedIndex, true);
- }.bind(this), 1);
+ _filesChanged: function(files) {
+ Polymer.dom.flush();
+ var files = Polymer.dom(this.root).querySelectorAll('.file-row');
+ this.$.fileCursor.stops = files;
+ this.$.fileCursor.setCursorAtIndex(this.selectedIndex, true);
},
_incrementNumFilesShown: function() {
- this.numFilesShown += this._fileListIncrement;
+ this.numFilesShown += this.fileListIncrement;
},
_computeFileListButtonHidden: function(numFilesShown, files) {
@@ -712,7 +713,7 @@
_computeIncrementText: function(numFilesShown, files) {
if (!files) { return ''; }
var text =
- Math.min(this._fileListIncrement, files.length - numFilesShown);
+ Math.min(this.fileListIncrement, files.length - numFilesShown);
return 'Show ' + text + ' more';
},
@@ -721,6 +722,16 @@
return 'Show all ' + files.length + ' files';
},
+ _computeWarnShowAll: function(files) {
+ return files.length > WARN_SHOW_ALL_THRESHOLD;
+ },
+
+ _computeShowAllWarning: function(files) {
+ if (!this._computeWarnShowAll(files)) { return ''; }
+ return 'Warning: showing all ' + files.length +
+ ' files may take several seconds.';
+ },
+
_showAllFiles: function() {
this.numFilesShown = this._files.length;
},
@@ -770,6 +781,11 @@
return expandedFilesRecord.base.indexOf(path) !== -1;
},
+ _onLineSelected: function(e, detail) {
+ this.$.diffCursor.moveToLineNumber(detail.number, detail.side,
+ detail.path);
+ },
+
/**
* Handle splices to the list of expanded file paths. If there are any new
* entries in the expanded list, then render each diff corresponding in
@@ -799,6 +815,8 @@
this.$.reporting.timeEnd(timerName);
this.$.diffCursor.handleDiffUpdate();
}.bind(this));
+ this._updateDiffCursor();
+ this.$.diffCursor.handleDiffUpdate();
},
/**
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index c006f68..1b5a4e4 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -23,6 +23,7 @@
<script src="../../../bower_components/page/page.js"></script>
<script src="../../../scripts/util.js"></script>
+<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-file-list.html">
@@ -60,7 +61,7 @@
reload: function() { return Promise.resolve(); },
});
element = fixture('basic');
- element.numFilesShown = 75;
+ element.numFilesShown = 200;
saveStub = sandbox.stub(element, '_saveReviewedState',
function() { return Promise.resolve(); });
});
@@ -69,6 +70,15 @@
sandbox.restore();
});
+ test('correct number of files are shown', function() {
+ element._files = _.times(500, function(i) {
+ return {__path: '/file' + i, lines_inserted: 9}; });
+ flushAsynchronousOperations();
+ assert.equal(
+ Polymer.dom(element.root).querySelectorAll('.file-row').length,
+ element.numFilesShown);
+ });
+
test('get file list', function(done) {
var getChangeFilesStub = sandbox.stub(element.$.restAPI, 'getChangeFiles',
function() {
@@ -927,4 +937,236 @@
});
});
});
+
+ suite('gr-file-list inline diff tests', function() {
+ var element;
+ var sandbox;
+
+ var setupDiff = function(diff) {
+ var mock = document.createElement('mock-diff-response');
+ diff._diff = mock.diffResponse;
+ diff._comments = {
+ left: [],
+ right: [],
+ };
+ diff.prefs = {
+ context: 10,
+ tab_size: 8,
+ font_size: 12,
+ line_length: 100,
+ cursor_blink_rate: 0,
+ line_wrapping: false,
+ intraline_difference: true,
+ show_line_endings: true,
+ show_tabs: true,
+ show_whitespace_errors: true,
+ syntax_highlighting: true,
+ auto_hide_diff_table_header: true,
+ theme: 'DEFAULT',
+ ignore_whitespace: 'IGNORE_NONE',
+ };
+ diff._renderDiffTable();
+ };
+
+ var renderAndGetNewDiffs = function(index) {
+ var diffs =
+ Polymer.dom(element.root).querySelectorAll('gr-diff');
+
+ for (var i = index; i < diffs.length; i++) {
+ setupDiff(diffs[i]);
+ }
+
+ element._updateDiffCursor();
+ element.$.diffCursor.handleDiffUpdate();
+ return diffs;
+ };
+
+ setup(function() {
+ sandbox = sinon.sandbox.create();
+ stub('gr-rest-api-interface', {
+ getLoggedIn: function() { return Promise.resolve(true); },
+ getPreferences: function() { return Promise.resolve({}); },
+ });
+ stub('gr-date-formatter', {
+ _loadTimeFormat: function() { return Promise.resolve(''); },
+ });
+ stub('gr-diff', {
+ reload: function() { return Promise.resolve(); },
+ });
+ element = fixture('basic');
+ element.numFilesShown = 75;
+ element.selectedIndex = 0;
+ element._files = [
+ {__path: '/COMMIT_MSG', lines_inserted: 9},
+ {
+ __path: 'file_added_in_rev2.txt',
+ lines_inserted: 1,
+ lines_deleted: 1,
+ size_delta: 10,
+ size: 100,
+ },
+ {
+ __path: 'myfile.txt',
+ lines_inserted: 1,
+ lines_deleted: 1,
+ size_delta: 10,
+ size: 100,
+ },
+ ];
+ element._reviewed = ['/COMMIT_MSG', 'myfile.txt'];
+ element._loggedIn = true;
+ element.changeNum = '42';
+ element.patchRange = {
+ basePatchNum: 'PARENT',
+ patchNum: '2',
+ };
+ sandbox.stub(window, 'fetch', function() {
+ return Promise.resolve();
+ });
+ flushAsynchronousOperations();
+ });
+
+ teardown(function() {
+ sandbox.restore();
+ });
+
+ test('cursor with individually opened files', function() {
+ MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
+ flushAsynchronousOperations();
+ var diffs = renderAndGetNewDiffs(0);
+ var diffStops = diffs[0].getCursorStops();
+
+ // 1 diff should be rendered.
+ assert.equal(diffs.length, 1);
+
+ // No line number is selected.
+ assert.isFalse(diffStops[10].classList.contains('target-row'));
+
+ // Tapping content on a line selects the line number.
+ MockInteractions.tap(Polymer.dom(
+ diffStops[10]).querySelectorAll('.contentText')[0]);
+ flushAsynchronousOperations();
+ assert.isTrue(diffStops[10].classList.contains('target-row'));
+
+ // Keyboard shortcuts are still moving the file cursor, not the diff
+ // cursor.
+ MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
+ flushAsynchronousOperations();
+ assert.isTrue(diffStops[10].classList.contains('target-row'));
+ assert.isFalse(diffStops[11].classList.contains('target-row'));
+
+ // The file cusor is now at 1.
+ assert.equal(element.$.fileCursor.index, 1);
+ MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
+ flushAsynchronousOperations();
+
+ diffs = renderAndGetNewDiffs(1);
+ // Two diffs should be rendered.
+ assert.equal(diffs.length, 2);
+ var diffStopsFirst = diffs[0].getCursorStops();
+ var diffStopsSecond = diffs[1].getCursorStops();
+
+ // The line on the first diff is stil selected
+ assert.isTrue(diffStopsFirst[10].classList.contains('target-row'));
+ assert.isFalse(diffStopsSecond[10].classList.contains('target-row'));
+ });
+
+ test('cursor with toggle all files', function() {
+ MockInteractions.pressAndReleaseKeyOn(element, 73, 'shift', 'i');
+ flushAsynchronousOperations();
+
+ var diffs = renderAndGetNewDiffs(0);
+ var diffStops = diffs[0].getCursorStops();
+
+ // 1 diff should be rendered.
+ assert.equal(diffs.length, 3);
+
+ // No line number is selected.
+ assert.isFalse(diffStops[10].classList.contains('target-row'));
+
+ // Tapping content on a line selects the line number.
+ MockInteractions.tap(Polymer.dom(
+ diffStops[10]).querySelectorAll('.contentText')[0]);
+ flushAsynchronousOperations();
+ assert.isTrue(diffStops[10].classList.contains('target-row'));
+
+ // Keyboard shortcuts are still moving the file cursor, not the diff
+ // cursor.
+ MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
+ flushAsynchronousOperations();
+ assert.isFalse(diffStops[10].classList.contains('target-row'));
+ assert.isTrue(diffStops[11].classList.contains('target-row'));
+
+ // The file cusor is still at 0.
+ assert.equal(element.$.fileCursor.index, 0);
+ });
+
+ suite('n key presses', function() {
+ var nKeySpy;
+ var nextCommentStub;
+ var nextChunkStub;
+ var fileRows;
+ setup(function() {
+ nKeySpy = sandbox.spy(element, '_handleNKey');
+ nextCommentStub = sandbox.stub(element.$.diffCursor,
+ 'moveToNextCommentThread');
+ nextChunkStub = sandbox.stub(element.$.diffCursor,
+ 'moveToNextChunk');
+ fileRows =
+ Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
+ });
+ test('n key with all files expanded and no shift key', function() {
+ MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, null, 'i');
+ flushAsynchronousOperations();
+
+ // Handle N key should return before calling diff cursor functions.
+ MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
+ assert.isTrue(nKeySpy.called);
+ assert.isFalse(nextCommentStub.called);
+
+ // This is also called in diffCursor.moveToFirstChunk.
+ assert.equal(nextChunkStub.callCount, 1);
+ assert.isFalse(!!element._showInlineDiffs);
+ });
+
+ test('n key with all files expanded and shift key', function() {
+ MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, null, 'i');
+ flushAsynchronousOperations();
+
+ MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
+ assert.isTrue(nKeySpy.called);
+ assert.isFalse(nextCommentStub.called);
+
+ // This is also called in diffCursor.moveToFirstChunk.
+ assert.equal(nextChunkStub.callCount, 1);
+ assert.isFalse(!!element._showInlineDiffs);
+ });
+
+ test('n key without all files expanded and shift key', function() {
+ MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, 'shift', 'i');
+ flushAsynchronousOperations();
+
+ MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
+ assert.isTrue(nKeySpy.called);
+ assert.isFalse(nextCommentStub.called);
+
+ // This is also called in diffCursor.moveToFirstChunk.
+ assert.equal(nextChunkStub.callCount, 2);
+ assert.isTrue(element._showInlineDiffs);
+ });
+
+ test('n key without all files expanded and no shift key', function() {
+ MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, 'shift', 'i');
+ flushAsynchronousOperations();
+
+ MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
+ assert.isTrue(nKeySpy.called);
+ assert.isTrue(nextCommentStub.called);
+
+ // This is also called in diffCursor.moveToFirstChunk.
+ assert.equal(nextChunkStub.callCount, 1);
+ assert.isTrue(element._showInlineDiffs);
+ });
+ });
+ });
</script>
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
index 2d7d2a9..5765411 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
@@ -14,6 +14,7 @@
limitations under the License.
-->
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-alert/gr-alert.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 1e609c9..22349de 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -25,6 +25,10 @@
Polymer({
is: 'gr-error-manager',
+ behaviors: [
+ Gerrit.BaseUrlBehavior,
+ ],
+
properties: {
/**
* The ID of the account that was logged in when the app was launched. If
@@ -207,7 +211,8 @@
'left=' + left,
'top=' + top,
];
- window.open('/login/%3FcloseAfterLogin', '_blank', options.join(','));
+ window.open(this.getBaseUrl() +
+ '/login/%3FcloseAfterLogin', '_blank', options.join(','));
this.listen(window, 'focus', '_handleWindowFocus');
},
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
index 368c613..952dc67 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
@@ -17,6 +17,8 @@
// Prevent redefinition.
if (window.GrDiffBuilderImage) { return; }
+ var IMAGE_MIME_PATTERN = /^image\/(bmp|gif|jpeg|jpg|png|tiff|webp)$/;
+
function GrDiffBuilderImage(diff, comments, prefs, outputEl, baseImage,
revisionImage) {
GrDiffBuilderSideBySide.call(this, diff, comments, prefs, outputEl, []);
@@ -53,7 +55,7 @@
GrDiffBuilderImage.prototype._createImageCell =
function(image, className, section) {
var td = this._createElement('td', className);
- if (image) {
+ if (image && IMAGE_MIME_PATTERN.test(image.type)) {
var imageEl = this._createElement('img');
imageEl.onload = function() {
image._height = imageEl.naturalHeight;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
index e40ccf3..abc7db3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
@@ -149,8 +149,8 @@
this._fixSide();
},
- moveToLineNumber: function(number, side) {
- var row = this._findRowByNumber(number, side);
+ moveToLineNumber: function(number, side, opt_path) {
+ var row = this._findRowByNumberAndFile(number, side, opt_path);
if (row) {
this.side = side;
this.$.cursorManager.setCursor(row);
@@ -376,8 +376,16 @@
}
},
- _findRowByNumber: function(targetNumber, side) {
- var stops = this.$.cursorManager.stops;
+ _findRowByNumberAndFile: function(targetNumber, side, opt_path) {
+ var stops;
+ if (opt_path) {
+ var diff = this.diffs.filter(function(diff) {
+ return diff.path === opt_path;
+ })[0];
+ stops = diff.getCursorStops();
+ } else {
+ stops = this.$.cursorManager.stops;
+ }
var selector;
for (var i = 0; i < stops.length; i++) {
selector = '.lineNum.' + side + '[data-value="' + targetNumber + '"]';
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
index a77c617..16acc03 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
@@ -266,13 +266,13 @@
assert.equal(cursorElement.getAddress(), '');
});
- test('_findRowByNumber', function() {
+ test('_findRowByNumberAndFile', function() {
// Get the first ab row after the first chunk.
var row = Polymer.dom(diffElement.root).querySelectorAll('tr')[8];
// It should be line 8 on the right, but line 5 on the left.
- assert.equal(cursorElement._findRowByNumber(8, 'right'), row);
- assert.equal(cursorElement._findRowByNumber(5, 'left'), row);
+ assert.equal(cursorElement._findRowByNumberAndFile(8, 'right'), row);
+ assert.equal(cursorElement._findRowByNumberAndFile(5, 'left'), row);
});
});
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 1bf6268..159f27c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -200,6 +200,7 @@
id="diffBuilder"
comments="[[_comments]]"
diff="[[_diff]]"
+ diff-path="[[path]]"
view-mode="[[viewMode]]"
line-wrapping="[[lineWrapping]]"
is-image-diff="[[isImageDiff]]"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 155ed5a..cd1931a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -232,6 +232,7 @@
this.fire('line-selected', {
side: el.classList.contains('left') ? DiffSide.LEFT : DiffSide.RIGHT,
number: el.getAttribute('data-value'),
+ path: this.path,
});
},
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
index f010add..c267eb0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -577,6 +577,43 @@
element.reload();
});
});
+
+ test('does not render disallowed image type', function(done) {
+ var mockDiff = {
+ meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg-evil',
+ lines: 560},
+ intraline_status: 'OK',
+ change_type: 'DELETED',
+ diff_header: [
+ 'diff --git a/carrot.jpg b/carrot.jpg',
+ 'index f9c2f2c..0000000 100644',
+ '--- a/carrot.jpg',
+ '+++ /dev/null',
+ 'Binary files differ',
+ ],
+ content: [{skip: 66}],
+ binary: true,
+ };
+ mockFile1.type = 'image/jpeg-evil';
+
+ stubs.push(sandbox.stub(element, '_getDiff',
+ function() { return Promise.resolve(mockDiff); }));
+
+ element.addEventListener('render', function() {
+ // Recognizes that it should be an image diff.
+ assert.isTrue(element.isImageDiff);
+ assert.instanceOf(
+ element.$.diffBuilder._builder, GrDiffBuilderImage);
+ var leftImage = element.$.diffTable.querySelector('td.left img');
+ assert.isNotOk(leftImage);
+ done();
+ });
+
+ element.$.restAPI.getDiffPreferences().then(function(prefs) {
+ element.prefs = prefs;
+ element.reload();
+ });
+ });
});
test('_handleTap lineNum', function(done) {
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index acf38c7..90f641d 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -37,12 +37,6 @@
value: function() { return document.body; },
},
- /**
- * The path component of the canonicalWebURL. If Gerrit is running from
- * the root of the domain, this should be empty.
- */
- canonicalPath: String,
-
_account: {
type: Object,
observer: '_accountChanged',
@@ -80,8 +74,6 @@
},
ready: function() {
- Gerrit.CANONICAL_PATH = this.canonicalPath;
-
this.$.router.start();
this.$.restAPI.getAccount().then(function(account) {
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index 2eb835d..35b5ab1 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -27,7 +27,7 @@
<test-fixture id="basic">
<template>
- <gr-app id="app" canonical-path="/abc/def/ghi"></gr-app>
+ <gr-app id="app"></gr-app>
</template>
</test-fixture>
@@ -72,10 +72,8 @@
element._path = '/test/path';
flush(function() {
var gwtLink = element.$$('#gwtLink');
- assert.equal(
- gwtLink.href,
- 'http://' + location.host + element.getBaseUrl() + '/?polygerrit=0#/test/path'
- );
+ assert.equal(gwtLink.href, 'http://' + location.host +
+ element.getBaseUrl() + '/?polygerrit=0#/test/path');
done();
});
});
@@ -107,9 +105,5 @@
done();
});
});
-
- test('canonical-path', function() {
- assert.equal(Gerrit.CANONICAL_PATH, '/abc/def/ghi');
- });
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index 2adfb91..d7406db 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -184,7 +184,7 @@
}
this._suggestions = suggestions;
Polymer.dom.flush();
- this._suggestionEls = this.$.suggestions.querySelectorAll('li')
+ this._suggestionEls = this.$.suggestions.querySelectorAll('li');
this.$.cursor.moveToStart();
if (this._index === -1) {
this.value = null;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 1537105..95fa2a7 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -963,7 +963,8 @@
},
_fetchB64File: function(url) {
- return fetch(this.getBaseUrl() + url, {credentials: 'same-origin'}).then(function(response) {
+ return fetch(this.getBaseUrl() + url, {credentials: 'same-origin'}).then(
+ function(response) {
var type = response.headers.get('X-FYI-Content-Type');
return response.text()
.then(function(text) {
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
index 81e65e3..58f8e39 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
@@ -19,8 +19,8 @@
<dom-module id="gr-tooltip-content">
<template>
- <content></content>
- <span class="arrow" hidden$="[[!showIcon]]">ⓘ</span>
+ <content></content><!--
+ --><span class="arrow" hidden$="[[!showIcon]]">ⓘ</span>
</template>
<script src="gr-tooltip-content.js"></script>
</dom-module>