Merge "Rename gr-confirm-dialog to gr-dialog"
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 3db621d..1aa6cd7c 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -238,7 +238,9 @@
The `PatchSetLock` function provides a locking mechanism for patch
sets. This function's values are not considered when determining
whether a change is submittable. When set, no new patchsets can be
-created and rebase and abandon are blocked.
+created and rebase and abandon are blocked. This is useful to prevent
+updates to a change while (potentially expensive) CI
+validation is running.
+
This function is designed to allow overlapping locks, so several lock
accounts could lock the same change.
diff --git a/java/com/google/gerrit/index/query/QueryProcessor.java b/java/com/google/gerrit/index/query/QueryProcessor.java
index b318199..27ed72f 100644
--- a/java/com/google/gerrit/index/query/QueryProcessor.java
+++ b/java/com/google/gerrit/index/query/QueryProcessor.java
@@ -38,6 +38,7 @@
import com.google.gwtorm.server.ResultSet;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -199,58 +200,67 @@
return disabledResults(queryStrings, queries);
}
- // Parse and rewrite all queries.
- List<Integer> limits = new ArrayList<>(cnt);
- List<Predicate<T>> predicates = new ArrayList<>(cnt);
- List<DataSource<T>> sources = new ArrayList<>(cnt);
- for (Predicate<T> q : queries) {
- int limit = getEffectiveLimit(q);
- limits.add(limit);
+ List<QueryResult<T>> out;
+ try {
+ // Parse and rewrite all queries.
+ List<Integer> limits = new ArrayList<>(cnt);
+ List<Predicate<T>> predicates = new ArrayList<>(cnt);
+ List<DataSource<T>> sources = new ArrayList<>(cnt);
+ for (Predicate<T> q : queries) {
+ int limit = getEffectiveLimit(q);
+ limits.add(limit);
- if (limit == getBackendSupportedLimit()) {
- limit--;
+ if (limit == getBackendSupportedLimit()) {
+ limit--;
+ }
+
+ int page = (start / limit) + 1;
+ if (page > indexConfig.maxPages()) {
+ throw new QueryParseException(
+ "Cannot go beyond page " + indexConfig.maxPages() + " of results");
+ }
+
+ // Always bump limit by 1, even if this results in exceeding the permitted
+ // max for this user. The only way to see if there are more entities is to
+ // ask for one more result from the query.
+ QueryOptions opts = createOptions(indexConfig, start, limit + 1, getRequestedFields());
+ Predicate<T> pred = rewriter.rewrite(q, opts);
+ if (enforceVisibility) {
+ pred = enforceVisibility(pred);
+ }
+ predicates.add(pred);
+
+ @SuppressWarnings("unchecked")
+ DataSource<T> s = (DataSource<T>) pred;
+ sources.add(s);
}
- int page = (start / limit) + 1;
- if (page > indexConfig.maxPages()) {
- throw new QueryParseException(
- "Cannot go beyond page " + indexConfig.maxPages() + " of results");
+ // Run each query asynchronously, if supported.
+ List<ResultSet<T>> matches = new ArrayList<>(cnt);
+ for (DataSource<T> s : sources) {
+ matches.add(s.read());
}
- // Always bump limit by 1, even if this results in exceeding the permitted
- // max for this user. The only way to see if there are more entities is to
- // ask for one more result from the query.
- QueryOptions opts = createOptions(indexConfig, start, limit + 1, getRequestedFields());
- Predicate<T> pred = rewriter.rewrite(q, opts);
- if (enforceVisibility) {
- pred = enforceVisibility(pred);
+ out = new ArrayList<>(cnt);
+ for (int i = 0; i < cnt; i++) {
+ out.add(
+ QueryResult.create(
+ queryStrings != null ? queryStrings.get(i) : null,
+ predicates.get(i),
+ limits.get(i),
+ matches.get(i).toList()));
}
- predicates.add(pred);
- @SuppressWarnings("unchecked")
- DataSource<T> s = (DataSource<T>) pred;
- sources.add(s);
+ // Only measure successful queries that actually touched the index.
+ metrics.executionTime.record(
+ schemaDef.getName(), System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
+ } catch (OrmException | OrmRuntimeException e) {
+ Optional<QueryParseException> qpe = findQueryParseException(e);
+ if (qpe.isPresent()) {
+ throw new QueryParseException(qpe.get().getMessage(), e);
+ }
+ throw e;
}
-
- // Run each query asynchronously, if supported.
- List<ResultSet<T>> matches = new ArrayList<>(cnt);
- for (DataSource<T> s : sources) {
- matches.add(s.read());
- }
-
- List<QueryResult<T>> out = new ArrayList<>(cnt);
- for (int i = 0; i < cnt; i++) {
- out.add(
- QueryResult.create(
- queryStrings != null ? queryStrings.get(i) : null,
- predicates.get(i),
- limits.get(i),
- matches.get(i).toList()));
- }
-
- // Only measure successful queries that actually touched the index.
- metrics.executionTime.record(
- schemaDef.getName(), System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
return out;
}
@@ -332,4 +342,12 @@
checkState(result > 0, "effective limit should be positive");
return result;
}
+
+ private static Optional<QueryParseException> findQueryParseException(Throwable t) {
+ return Throwables.getCausalChain(t)
+ .stream()
+ .filter(c -> c instanceof QueryParseException)
+ .map(QueryParseException.class::cast)
+ .findFirst();
+ }
}
diff --git a/java/com/google/gerrit/proto/BUILD b/java/com/google/gerrit/proto/BUILD
new file mode 100644
index 0000000..48185d6
--- /dev/null
+++ b/java/com/google/gerrit/proto/BUILD
@@ -0,0 +1,14 @@
+java_binary(
+ name = "ProtoGen",
+ srcs = ["ProtoGen.java"],
+ resource_strip_prefix = "resources",
+ resources = ["//resources/com/google/gerrit/proto"],
+ visibility = ["//proto:__pkg__"],
+ deps = [
+ "//java/com/google/gerrit/reviewdb:server",
+ "//lib:args4j",
+ "//lib:guava",
+ "//lib:gwtorm",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/java/com/google/gerrit/pgm/ProtoGen.java b/java/com/google/gerrit/proto/ProtoGen.java
similarity index 67%
rename from java/com/google/gerrit/pgm/ProtoGen.java
rename to java/com/google/gerrit/proto/ProtoGen.java
index a882412..1c55a05 100644
--- a/java/com/google/gerrit/pgm/ProtoGen.java
+++ b/java/com/google/gerrit/proto/ProtoGen.java
@@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.pgm;
+package com.google.gerrit.proto;
+import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.pgm.util.AbstractProgram;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gwtorm.schema.java.JavaSchemaModel;
import java.io.BufferedWriter;
@@ -28,23 +28,37 @@
import java.nio.ByteBuffer;
import org.eclipse.jgit.internal.storage.file.LockFile;
import org.eclipse.jgit.util.IO;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
-public class ProtoGen extends AbstractProgram {
+public class ProtoGen {
@Option(
- name = "--output",
- aliases = {"-o"},
- required = true,
- metaVar = "FILE",
- usage = "File to write .proto into")
+ name = "--output",
+ aliases = {"-o"},
+ required = true,
+ metaVar = "FILE",
+ usage = "File to write .proto into"
+ )
private File file;
- @Override
- public int run() throws Exception {
- LockFile lock = new LockFile(file.getAbsoluteFile());
- if (!lock.lock()) {
- throw die("Cannot lock " + file);
+ public static void main(String[] argv) throws Exception {
+ System.exit(new ProtoGen().run(argv));
+ }
+
+ private int run(String[] argv) throws Exception {
+ CmdLineParser parser = new CmdLineParser(this);
+ try {
+ parser.parseArgument(argv);
+ } catch (CmdLineException e) {
+ System.err.println(e.getMessage());
+ System.err.println(getClass().getSimpleName() + " -o output.proto");
+ parser.printUsage(System.err);
+ return 1;
}
+
+ LockFile lock = new LockFile(file.getAbsoluteFile());
+ checkState(lock.lock(), "cannot lock %s", file);
try {
JavaSchemaModel jsm = new JavaSchemaModel(ReviewDb.class);
try (OutputStream o = lock.getOutputStream();
@@ -61,9 +75,7 @@
jsm.generateProto(out);
out.flush();
}
- if (!lock.commit()) {
- throw die("Could not write to " + file);
- }
+ checkState(lock.commit(), "Could not write to %s", file);
} finally {
lock.unlock();
}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 6879ec6..3c5585c 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -38,6 +38,7 @@
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
@@ -370,7 +371,15 @@
private final ListMultimap<ReceiveError, String> errors;
private final ListMultimap<String, String> pushOptions;
private final Map<Change.Id, ReplaceRequest> replaceByChange;
- private final Set<ObjectId> validCommits;
+
+ @AutoValue
+ protected abstract static class ValidCommitKey {
+ abstract ObjectId getObjectId();
+
+ abstract Branch.NameKey getBranch();
+ }
+
+ private final Set<ValidCommitKey> validCommits;
/**
* Actual commands to be executed, as opposed to the mix of actual and magic commands that were
@@ -1893,13 +1902,7 @@
logDebug("Creating new change for %s even though it is already tracked", name);
}
- if (!validCommit(
- receivePack.getRevWalk(),
- magicBranch.perm,
- magicBranch.dest,
- magicBranch.cmd,
- c,
- null)) {
+ if (!validCommit(receivePack.getRevWalk(), magicBranch.dest, magicBranch.cmd, c, null)) {
// Not a change the user can propose? Abort as early as possible.
logDebug("Aborting early due to invalid commit");
return Collections.emptyList();
@@ -2486,9 +2489,8 @@
}
}
- PermissionBackend.ForRef perm = permissions.ref(change.getDest().get());
if (!validCommit(
- receivePack.getRevWalk(), perm, change.getDest(), inputCommand, newCommit, change)) {
+ receivePack.getRevWalk(), change.getDest(), inputCommand, newCommit, change)) {
return false;
}
receivePack.getRevWalk().parseBody(priorCommit);
@@ -2860,7 +2862,7 @@
}
if (existing.keySet().contains(c)) {
continue;
- } else if (!validCommit(walk, perm, branch, cmd, c, null)) {
+ } else if (!validCommit(walk, branch, cmd, c, null)) {
break;
}
@@ -2878,15 +2880,12 @@
}
private boolean validCommit(
- RevWalk rw,
- PermissionBackend.ForRef perm,
- Branch.NameKey branch,
- ReceiveCommand cmd,
- ObjectId id,
- @Nullable Change change)
+ RevWalk rw, Branch.NameKey branch, ReceiveCommand cmd, ObjectId id, @Nullable Change change)
throws IOException {
+ PermissionBackend.ForRef perm = permissions.ref(branch.get());
- if (validCommits.contains(id)) {
+ ValidCommitKey key = new AutoValue_ReceiveCommits_ValidCommitKey(id.copy(), branch);
+ if (validCommits.contains(key)) {
return true;
}
@@ -2912,7 +2911,7 @@
reject(cmd, e.getMessage());
return false;
}
- validCommits.add(c.copy());
+ validCommits.add(key);
return true;
}
diff --git a/java/com/google/gerrit/server/submit/SubmoduleOp.java b/java/com/google/gerrit/server/submit/SubmoduleOp.java
index 0ed189e..319e2e1 100644
--- a/java/com/google/gerrit/server/submit/SubmoduleOp.java
+++ b/java/com/google/gerrit/server/submit/SubmoduleOp.java
@@ -516,13 +516,26 @@
+ "doesn't have gitlink file mode.";
throw new SubmoduleException(errMsg);
}
+ // Parse the current gitlink entry commit in the subproject repo. This is used to add a
+ // shortlog for this submodule to the commit message in the superproject.
+ //
+ // Even if we don't strictly speaking need that commit message, parsing the commit is a sanity
+ // check that the old gitlink is a commit that actually exists. If not, then there is an
+ // inconsistency between the superproject and subproject state, and we don't want to risk
+ // making things worse by updating the gitlink to something else.
oldCommit = subOr.rw.parseCommit(dce.getObjectId());
}
final CodeReviewCommit newCommit;
if (branchTips.containsKey(s.getSubmodule())) {
+ // This submodule's branch was updated as part of this specific submit batch: update the
+ // gitlink to point to the new commit from the batch.
newCommit = branchTips.get(s.getSubmodule());
} else {
+ // For whatever reason, this submodule was not updated as part of this submit batch, but the
+ // superproject is still subscribed to this branch. Re-read the ref to see if anything has
+ // changed since the last time the gitlink was updated, and roll that update into the same
+ // commit as all other submodule updates.
Ref ref = subOr.repo.getRefDatabase().exactRef(s.getSubmodule().get());
if (ref == null) {
ed.add(new DeletePath(s.getPath()));
diff --git a/javatests/com/google/gerrit/proto/BUILD b/javatests/com/google/gerrit/proto/BUILD
new file mode 100644
index 0000000..0940f6b
--- /dev/null
+++ b/javatests/com/google/gerrit/proto/BUILD
@@ -0,0 +1,17 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "proto_tests",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//lib/truth:truth-proto-extension",
+ "//proto:reviewdb_java_proto",
+
+ # TODO(dborowitz): These are already runtime_deps of
+ # truth-proto-extension, but either omitting them or adding them as
+ # runtime_deps to this target fails with:
+ # class file for com.google.common.collect.Multimap not found
+ "//lib:guava",
+ "//lib/truth",
+ ],
+)
diff --git a/javatests/com/google/gerrit/proto/ReviewDbProtoTest.java b/javatests/com/google/gerrit/proto/ReviewDbProtoTest.java
new file mode 100644
index 0000000..49cd2921
--- /dev/null
+++ b/javatests/com/google/gerrit/proto/ReviewDbProtoTest.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2018 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.proto;
+
+import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+
+import com.google.gerrit.proto.reviewdb.Reviewdb.Change;
+import com.google.gerrit.proto.reviewdb.Reviewdb.Change_Id;
+import org.junit.Test;
+
+public class ReviewDbProtoTest {
+ @Test
+ public void generatedProtoApi() {
+ Change c1 = Change.newBuilder().setChangeId(Change_Id.newBuilder().setId(1234).build()).build();
+ Change c2 = Change.newBuilder().setChangeId(Change_Id.newBuilder().setId(5678).build()).build();
+ assertThat(c1).isEqualTo(c1);
+ assertThat(c1).isNotEqualTo(c2);
+ }
+}
diff --git a/lib/BUILD b/lib/BUILD
index a9d82e9..38b0b80 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -72,7 +72,11 @@
name = "gwtorm",
visibility = ["//visibility:public"],
exports = [":gwtorm-client"],
- runtime_deps = [":protobuf"],
+ runtime_deps = [
+ ":protobuf",
+ "//lib/antlr:java-runtime",
+ "//lib/ow2:ow2-asm",
+ ],
)
java_library(
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 89f3038..60eaf1f 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
@@ -48,7 +48,9 @@
shouldSuppressKeyboardShortcut(e) {
e = getKeyboardEvent(e);
const tagName = Polymer.dom(e).rootTarget.tagName;
- if (tagName === 'INPUT' || tagName === 'TEXTAREA') {
+ if (tagName === 'INPUT' || tagName === 'TEXTAREA' ||
+ (e.keyCode === 13 && tagName === 'A')) {
+ // Suppress shortcuts if the key is 'enter' and target is an anchor.
return true;
}
for (let i = 0; e.path && i < e.path.length; i++) {
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 8e10302..04193dd 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
@@ -51,6 +51,7 @@
behaviors: [Gerrit.KeyboardShortcutBehavior],
keyBindings: {
k: '_handleKey',
+ enter: '_handleKey',
},
_handleKey() {},
});
@@ -107,6 +108,17 @@
MockInteractions.keyDownOn(divEl, 75, null, 'k');
});
+ test('blocks enter shortcut on an anchor', done => {
+ const anchorEl = document.createElement('a');
+ const element = overlay.querySelector('test-element');
+ element.appendChild(anchorEl);
+ element._handleKey = e => {
+ assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
+ done();
+ };
+ MockInteractions.keyDownOn(anchorEl, 13, null, 'enter');
+ });
+
test('modifierPressed returns accurate values', () => {
const spy = sandbox.spy(element, 'modifierPressed');
element._handleKey = e => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
index edf9f5a..90d465f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
@@ -449,6 +449,13 @@
this.fire('comment-discard', this._getEventPayload());
},
+ _handleFix() {
+ this.dispatchEvent(new CustomEvent('create-fix-comment', {
+ bubbles: true,
+ detail: this._getEventPayload(),
+ }));
+ },
+
_handleDiscard(e) {
e.preventDefault();
this.$.reporting.recordDraftInteraction();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
index f1ac649..ca85892 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
@@ -841,5 +841,16 @@
element.save();
assert.isTrue(discardStub.called);
});
+
+ test('_handleFix fires create-fix event', done => {
+ element.addEventListener('create-fix-comment', e => {
+ assert.deepEqual(e.detail, element._getEventPayload());
+ done();
+ });
+ element.isRobotComment = true;
+ flushAsynchronousOperations();
+
+ MockInteractions.tap(element.$$('.fix'));
+ });
});
</script>
diff --git a/proto/BUILD b/proto/BUILD
index 4528dcb..0578ca5 100644
--- a/proto/BUILD
+++ b/proto/BUILD
@@ -8,3 +8,21 @@
visibility = ["//visibility:public"],
deps = [":cache_proto"],
)
+
+genrule(
+ name = "gen_reviewdb_proto",
+ outs = ["reviewdb.proto"],
+ cmd = "$(location //java/com/google/gerrit/proto:ProtoGen) -o $@",
+ tools = ["//java/com/google/gerrit/proto:ProtoGen"],
+)
+
+proto_library(
+ name = "reviewdb_proto",
+ srcs = [":reviewdb.proto"],
+)
+
+java_proto_library(
+ name = "reviewdb_java_proto",
+ visibility = ["//javatests/com/google/gerrit/proto:__pkg__"],
+ deps = [":reviewdb_proto"],
+)
diff --git a/resources/com/google/gerrit/proto/BUILD b/resources/com/google/gerrit/proto/BUILD
new file mode 100644
index 0000000..ec2e05c
--- /dev/null
+++ b/resources/com/google/gerrit/proto/BUILD
@@ -0,0 +1,8 @@
+filegroup(
+ name = "proto",
+ srcs = glob(
+ ["**/*"],
+ exclude = ["BUILD"],
+ ),
+ visibility = ["//visibility:public"],
+)
diff --git a/resources/com/google/gerrit/pgm/ProtoGenHeader.txt b/resources/com/google/gerrit/proto/ProtoGenHeader.txt
similarity index 95%
rename from resources/com/google/gerrit/pgm/ProtoGenHeader.txt
rename to resources/com/google/gerrit/proto/ProtoGenHeader.txt
index a380955..8797790 100644
--- a/resources/com/google/gerrit/pgm/ProtoGenHeader.txt
+++ b/resources/com/google/gerrit/proto/ProtoGenHeader.txt
@@ -14,7 +14,6 @@
syntax = "proto2";
-option java_api_version = 2;
option java_package = "com.google.gerrit.proto.reviewdb";
package devtools.gerritcodereview;