Merge "Rebase: Use committer identity in new commit"
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index e5614cd..6e248c8 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -413,6 +413,14 @@
https://github.com/davido/gerrit-oauth-provider[Project] |
https://github.com/davido/gerrit-oauth-provider/wiki/Getting-Started[Configuration]
+[[owners]]
+=== owners
+This plugin provides a Prolog predicate `add_owner_approval/3` that
+appends `label('Owner-Approval', need(_))` to a provided list.
+
+link:https://gerrit-review.googlesource.com/#/admin/projects/plugins/owners[Project] |
+link:https://gerrit.googlesource.com/plugins/owners/+/refs/heads/master/README.md[Documentation]
+
[[project-download-commands]]
=== project-download-commands
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index 0d3ff58..7d03681 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -164,6 +164,17 @@
Default is `INHERIT`, which means that this property is inherited from
the parent project.
+[[receive.requireSignedPush]]receive.requireSignedPush::
++
+Controls whether server-side signed push validation is required on the
+project. Only has an effect if signed push validation is enabled on the
+server, and link:#receive.enableSignedPush is set on the project. See
+the link:config-gerrit.html#receive.enableSignedPush[global
+configuration] for details.
++
+Default is `INHERIT`, which means that this property is inherited from
+the parent project.
+
[[submit-section]]
=== Submit section
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 26072e3..e9ba824 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -766,6 +766,17 @@
can be notified when this configuration parameter is updated on a
project.
+[[configuring-groups]]
+=== Referencing groups in `project.config`
+
+Plugins can refer to groups so that when they are renamed, the project
+config will also be updated in this section. The proper format to use is
+the string representation of a GroupReference, as shown below.
+
+----
+Group[group_name / group_uuid]
+----
+
[[project-specific-configuration]]
== Project Specific Configuration in own config file
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index a320a54..6920c4c 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1256,7 +1256,7 @@
{
"context": 10,
"theme": "DEFAULT",
- "ignore_whitespace": "IGNORE_ALL_SPACE",
+ "ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
"show_tabs": true,
@@ -1285,7 +1285,7 @@
{
"context": 10,
"theme": "ECLIPSE",
- "ignore_whitespace": "IGNORE_ALL_SPACE",
+ "ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
"show_line_endings": true,
@@ -1309,7 +1309,7 @@
{
"context": 10,
"theme": "ECLIPSE",
- "ignore_whitespace": "IGNORE_ALL_SPACE",
+ "ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
"show_line_endings": true,
@@ -1666,8 +1666,8 @@
|`ignore_whitespace` ||
Whether whitespace changes should be ignored and if yes, which
whitespace changes should be ignored. +
-Allowed values are `IGNORE_NONE`, `IGNORE_SPACE_AT_EOL`,
-`IGNORE_SPACE_CHANGE`, `IGNORE_ALL_SPACE`.
+Allowed values are `IGNORE_NONE`, `IGNORE_TRAILING`,
+`IGNORE_LEADING_AND_TRAILING`, `IGNORE_ALL`.
|`intraline_difference` |not set if `false`|
Whether intraline differences should be highlighted.
|`line_length` ||
@@ -1722,8 +1722,8 @@
|`ignore_whitespace` |optional|
Whether whitespace changes should be ignored and if yes, which
whitespace changes should be ignored. +
-Allowed values are `IGNORE_NONE`, `IGNORE_SPACE_AT_EOL`,
-`IGNORE_SPACE_CHANGE`, `IGNORE_ALL_SPACE`.
+Allowed values are `IGNORE_NONE`, `IGNORE_TRAILING`,
+`IGNORE_LEADING_AND_TRAILING`, `IGNORE_ALL`.
|`intraline_difference` |optional|
Whether intraline differences should be highlighted.
|`line_length` |optional|
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 7e96556..55764b1 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -4482,23 +4482,23 @@
[options="header",cols="1,^1,5"]
|============================
-|Field Name ||Description
-|`message` |optional|
+|Field Name ||Description
+|`message` |optional|
The message to be added as review comment.
-|`labels` |optional|
+|`labels` |optional|
The votes that should be added to the revision as a map that maps the
label names to the voting values.
-|`comments` |optional|
+|`comments` |optional|
The comments that should be added as a map that maps a file path to a
list of link:#comment-input[CommentInput] entities.
-|`strict_labels`|`true` if not set|
+|`strict_labels` |`true` if not set|
Whether all labels are required to be within the user's permitted ranges
based on access controls. +
If `true`, attempting to use a label not granted to the user will fail
the entire modify operation early. +
If `false`, the operation will execute anyway, but the proposed labels
will be modified to be the "best" value allowed by the access controls.
-|`drafts` |optional|
+|`drafts` |optional|
Draft handling that defines how draft comments are handled that are
already in the database but that were not also described in this
input. +
@@ -4506,12 +4506,14 @@
`KEEP`. All values except `PUBLISH_ALL_REVISIONS` operate only on drafts
for a single revision. +
If not set, the default is `DELETE`.
-|`notify` |optional|
+|`notify` |optional|
Notify handling that defines to whom email notifications should be sent
after the review is stored. +
Allowed values are `NONE`, `OWNER`, `OWNER_REVIEWERS` and `ALL`. +
If not set, the default is `ALL`.
-|`on_behalf_of`|optional|
+|`omit_duplicate_comments`|optional|
+If `true`, comments with the same content at the same place will be omitted.
+|`on_behalf_of` |optional|
link:rest-api-accounts.html#account-id[\{account-id\}] the review
should be posted on behalf of. To use this option the caller must
have been granted `labelAs-NAME` permission for all keys of labels.
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 7ae320d..59184a8 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -732,6 +732,7 @@
"use_signed_off_by": "INHERIT",
"create_new_change_for_all_not_in_target": "INHERIT",
"enable_signed_push": "INHERIT",
+ "require_signed_push": "INHERIT",
"require_change_id": "TRUE",
"max_object_size_limit": "10m",
"submit_type": "REBASE_IF_NECESSARY",
@@ -780,6 +781,11 @@
"configured_value": "INHERIT",
"inherited_value": false
},
+ "require_signed_push": {
+ "value": false,
+ "configured_value": "INHERIT",
+ "inherited_value": false
+ },
"max_object_size_limit": {
"value": "10m",
"configured_value": "10m",
@@ -1982,9 +1988,14 @@
valid link:user-changeid.html[Change-Id] footer in any commit uploaded
for review is required. This does not apply to commits pushed directly
to a branch or tag.
-|`enable_signed_push` |optional|
+|`enable_signed_push`|
+optional, not set if signed push is disabled|
link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
signed push validation is enabled on the project.
+|`require_signed_push`|
+optional, not set if signed push is disabled
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+signed push validation is required on the project.
|`max_object_size_limit` ||
The link:config-gerrit.html#receive.maxObjectSizeLimit[max object size
limit] of this project as a link:#max-object-size-limit-info[
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt
index fff2501..05932df 100644
--- a/Documentation/user-inline-edit.txt
+++ b/Documentation/user-inline-edit.txt
@@ -59,6 +59,8 @@
To save edits, click the 'Save' button or press `CTRL-S`. To return to the
change screen, click the 'Close' button.
+Note that when editing the commit message, trailing blank lines will be stripped.
+
image::images/inline-edit-full-screen-editor.png[width=800, link="images/inline-edit-full-screen-editor.png"]
If there are unsaved edits when the 'Close' button is pressed, a dialog will
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 45e5c22..39296d0 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
@@ -19,6 +19,10 @@
import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.testutil.TestKeys.allValidKeys;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithSecondUserId;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithoutExpiration;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Function;
@@ -38,7 +42,6 @@
import com.google.gerrit.gpg.PublicKeyStore;
import com.google.gerrit.gpg.server.GpgKeys;
import com.google.gerrit.gpg.testutil.TestKey;
-import com.google.gerrit.gpg.testutil.TestKeys;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.server.config.AllUsersName;
@@ -204,7 +207,7 @@
@Test
public void addGpgKey() throws Exception {
- TestKey key = TestKeys.key1();
+ TestKey key = validKeyWithoutExpiration();
String id = key.getKeyIdString();
addExternalIdEmail(admin, "test1@example.com");
@@ -220,7 +223,7 @@
@Test
public void reAddExistingGpgKey() throws Exception {
addExternalIdEmail(admin, "test5@example.com");
- TestKey key = TestKeys.key5();
+ TestKey key = validKeyWithSecondUserId();
String id = key.getKeyIdString();
PGPPublicKey pk = key.getPublicKey();
@@ -243,7 +246,7 @@
db.accountExternalIds().insert(Collections.singleton(extId));
- TestKey key = TestKeys.key5();
+ TestKey key = validKeyWithSecondUserId();
addGpgKey(key.getPublicKeyArmored());
setApiUser(user);
@@ -254,7 +257,7 @@
@Test
public void listGpgKeys() throws Exception {
- List<TestKey> keys = TestKeys.allValidKeys();
+ List<TestKey> keys = allValidKeys();
List<String> toAdd = new ArrayList<>(keys.size());
for (TestKey key : keys) {
addExternalIdEmail(admin,
@@ -267,7 +270,7 @@
@Test
public void deleteGpgKey() throws Exception {
- TestKey key = TestKeys.key1();
+ TestKey key = validKeyWithoutExpiration();
String id = key.getKeyIdString();
addExternalIdEmail(admin, "test1@example.com");
addGpgKey(key.getPublicKeyArmored());
@@ -283,13 +286,13 @@
@Test
public void addAndRemoveGpgKeys() throws Exception {
- for (TestKey key : TestKeys.allValidKeys()) {
+ for (TestKey key : allValidKeys()) {
addExternalIdEmail(admin,
PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
}
- TestKey key1 = TestKeys.key1();
- TestKey key2 = TestKeys.key2();
- TestKey key5 = TestKeys.key5();
+ TestKey key1 = validKeyWithoutExpiration();
+ TestKey key2 = validKeyWithExpiration();
+ TestKey key5 = validKeyWithSecondUserId();
Map<String, GpgKeyInfo> infos = gApi.accounts().self().putGpgKeys(
ImmutableList.of(
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 3d24c71..a9293f6 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -29,6 +29,7 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
@@ -38,6 +39,7 @@
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -53,6 +55,7 @@
import org.junit.Test;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
@@ -124,6 +127,45 @@
.id(r.getChangeId())
.revision(r.getCommit().name())
.submit();
+ ChangeInfo revertChange =
+ gApi.changes()
+ .id(r.getChangeId())
+ .revert().get();
+
+ // expected messages on source change:
+ // 1. Uploaded patch set 1.
+ // 2. Patch Set 1: Code-Review+2
+ // 3. Change has been successfully merged by Administrator
+ // 4. Patch Set 1: Reverted
+ List<ChangeMessageInfo> sourceMessages = new ArrayList<>(
+ gApi.changes().id(r.getChangeId()).get().messages);
+ assertThat(sourceMessages).hasSize(4);
+ String expectedMessage = String.format(
+ "Patch Set 1: Reverted\n\n" +
+ "This patchset was reverted in change: %s",
+ revertChange.changeId);
+ assertThat(sourceMessages.get(3).message).isEqualTo(expectedMessage);
+
+ assertThat(revertChange.messages).hasSize(1);
+ assertThat(revertChange.messages.iterator().next().message)
+ .isEqualTo("Uploaded patch set 1.");
+ }
+
+ @Test
+ @TestProjectInput(createEmptyCommit = false)
+ public void revertInitialCommit() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().name())
+ .review(ReviewInput.approve());
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().name())
+ .submit();
+
+ exception.expect(ResourceConflictException.class);
+ exception.expectMessage("Cannot revert initial commit");
gApi.changes()
.id(r.getChangeId())
.revert();
@@ -140,6 +182,31 @@
}
@Test
+ public void rebaseConflict() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().name())
+ .review(ReviewInput.approve());
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().name())
+ .submit();
+
+ PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo,
+ PushOneCommit.SUBJECT, PushOneCommit.FILE_NAME, "other content",
+ "If09d8782c1e59dd0b33de2b1ec3595d69cc10ad5");
+ r = push.to("refs/for/master");
+ r.assertOkStatus();
+
+ exception.expect(ResourceConflictException.class);
+ gApi.changes()
+ .id(r.getChangeId())
+ .revision(r.getCommit().name())
+ .rebase();
+ }
+
+ @Test
public void rebaseChangeBase() throws Exception {
PushOneCommit.Result r1 = createChange();
PushOneCommit.Result r2 = createChange();
@@ -448,16 +515,23 @@
assertThat(actual.revisions.get(r1.getCommit().getName()).commitWithFooters)
.isNull();
- String expected = SUBJECT + "\n"
- + "\n"
- + "Change-Id: " + r2.getChangeId() + "\n"
- + "Reviewed-on: "
- + canonicalWebUrl.get() + r2.getChange().getId() + "\n"
- + "Reviewed-by: Administrator <admin@example.com>\n"
- + "Custom2: Administrator <admin@example.com>\n"
- + "Tested-by: Administrator <admin@example.com>\n";
- assertThat(actual.revisions.get(r2.getCommit().getName()).commitWithFooters)
- .isEqualTo(expected);
+ List<String> footers =
+ new ArrayList<>(Arrays.asList(
+ actual.revisions.get(r2.getCommit().getName())
+ .commitWithFooters.split("\\n")));
+ // remove subject + blank line
+ footers.remove(0);
+ footers.remove(0);
+
+ List<String> expectedFooters = Arrays.asList(
+ "Change-Id: " + r2.getChangeId(),
+ "Reviewed-on: "
+ + canonicalWebUrl.get() + r2.getChange().getId(),
+ "Reviewed-by: Administrator <admin@example.com>",
+ "Custom2: Administrator <admin@example.com>",
+ "Tested-by: Administrator <admin@example.com>");
+
+ assertThat(footers).containsExactlyElementsIn(expectedFooters);
}
@Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 7ecf1f3..3e970e4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -18,8 +18,8 @@
import static com.google.gerrit.acceptance.PushOneCommit.FILE_CONTENT;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
import static com.google.gerrit.acceptance.PushOneCommit.PATCH;
-import static org.eclipse.jgit.lib.Constants.HEAD;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.HEAD;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 625b33a..ed20e24 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -318,7 +318,7 @@
Optional<ChangeEdit> edit = editUtil.byChange(change);
assertThat(edit.get().getEditCommit().getParentCount()).isEqualTo(0);
- String msg = String.format("New commit message\n\nChange-Id: %s",
+ String msg = String.format("New commit message\n\nChange-Id: %s\n",
change.getKey());
assertThat(modifier.modifyMessage(edit.get(), msg))
.isEqualTo(RefUpdate.Result.FORCED);
@@ -345,8 +345,9 @@
assertThat(modifier.createEdit(change, getCurrentPatchSet(changeId)))
.isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
-
- String msg = String.format("New commit message\n\nChange-Id: %s",
+ assertUnchangedMessage(edit, edit.get().getEditCommit().getFullMessage());
+ assertUnchangedMessage(edit, edit.get().getEditCommit().getFullMessage() + "\n\n");
+ String msg = String.format("New commit message\n\nChange-Id: %s\n",
change.getKey());
assertThat(modifier.modifyMessage(edit.get(), msg)).isEqualTo(
RefUpdate.Result.FORCED);
@@ -373,7 +374,7 @@
.isEqualTo(SC_NOT_FOUND);
EditMessage.Input in = new EditMessage.Input();
in.message = String.format("New commit message\n\n" +
- CONTENT_NEW2_STR + "\n\nChange-Id: %s",
+ CONTENT_NEW2_STR + "\n\nChange-Id: %s\n",
change.getKey());
assertThat(adminSession.put(urlEditMessage(), in).getStatusCode())
.isEqualTo(SC_NO_CONTENT);
@@ -383,7 +384,7 @@
Optional<ChangeEdit> edit = editUtil.byChange(change);
assertThat(edit.get().getEditCommit().getFullMessage())
.isEqualTo(in.message);
- in.message = String.format("New commit message2\n\nChange-Id: %s",
+ in.message = String.format("New commit message2\n\nChange-Id: %s\n",
change.getKey());
assertThat(adminSession.put(urlEditMessage(), in).getStatusCode())
.isEqualTo(SC_NO_CONTENT);
@@ -712,6 +713,14 @@
assertThat(approvals.get(0).value).isEqualTo(1);
}
+ private void assertUnchangedMessage(Optional<ChangeEdit> edit, String message)
+ throws Exception {
+ exception.expect(UnchangedCommitMessageException.class);
+ exception.expectMessage(
+ "New commit message cannot be same as existing commit message");
+ modifier.modifyMessage(edit.get(), message);
+ }
+
@Test
public void testHasEditPredicate() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java
new file mode 100644
index 0000000..5550cb6
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java
@@ -0,0 +1,160 @@
+// Copyright (C) 2015 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.acceptance.rest.account;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
+import com.google.gerrit.extensions.client.Theme;
+import com.google.gerrit.testutil.ConfigSuite;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class DiffPreferencesIT extends AbstractDaemonTest {
+ @ConfigSuite.Config
+ public static Config readFromGitConfig() {
+ Config cfg = new Config();
+ cfg.setBoolean("user", null, "readPrefsFromGit", true);
+ return cfg;
+ }
+
+ @Test
+ public void getDiffPreferencesOfNonExistingAccount_NotFound()
+ throws Exception {
+ assertEquals(HttpStatus.SC_NOT_FOUND,
+ adminSession.get("/accounts/non-existing/preferences.diff")
+ .getStatusCode());
+ }
+
+ @Test
+ public void getDiffPreferences() throws Exception {
+ RestResponse r = adminSession.get("/accounts/" + admin.email
+ + "/preferences.diff");
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ DiffPreferencesInfo d = DiffPreferencesInfo.defaults();
+ DiffPreferencesInfo o =
+ newGson().fromJson(r.getReader(), DiffPreferencesInfo.class);
+
+ assertThat(o.context).isEqualTo(d.context);
+ assertThat(o.tabSize).isEqualTo(d.tabSize);
+ assertThat(o.lineLength).isEqualTo(d.lineLength);
+ assertThat(o.expandAllComments).isNull();
+ assertThat(o.intralineDifference).isEqualTo(d.intralineDifference);
+ assertThat(o.manualReview).isNull();
+ assertThat(o.retainHeader).isNull();
+ assertThat(o.showLineEndings).isEqualTo(d.showLineEndings);
+ assertThat(o.showTabs).isEqualTo(d.showTabs);
+ assertThat(o.showWhitespaceErrors).isEqualTo(d.showWhitespaceErrors);
+ assertThat(o.skipDeleted).isNull();
+ assertThat(o.skipUncommented).isNull();
+ assertThat(o.syntaxHighlighting).isEqualTo(d.syntaxHighlighting);
+ assertThat(o.hideTopMenu).isNull();
+ assertThat(o.autoHideDiffTableHeader).isEqualTo(d.autoHideDiffTableHeader);
+ assertThat(o.hideLineNumbers).isNull();
+ assertThat(o.renderEntireFile).isNull();
+ assertThat(o.hideEmptyPane).isNull();
+ assertThat(o.ignoreWhitespace).isEqualTo(d.ignoreWhitespace);
+ assertThat(o.theme).isEqualTo(d.theme);
+ }
+
+ @Test
+ public void setDiffPreferences() throws Exception {
+ DiffPreferencesInfo i = DiffPreferencesInfo.defaults();
+
+ // change all default values
+ i.context *= -1;
+ i.tabSize *= -1;
+ i.lineLength *= -1;
+ i.theme = Theme.MIDNIGHT;
+ i.ignoreWhitespace = Whitespace.IGNORE_ALL;
+ i.expandAllComments ^= true;
+ i.intralineDifference ^= true;
+ i.manualReview ^= true;
+ i.retainHeader ^= true;
+ i.showLineEndings ^= true;
+ i.showTabs ^= true;
+ i.showWhitespaceErrors ^= true;
+ i.skipDeleted ^= true;
+ i.skipUncommented ^= true;
+ i.syntaxHighlighting ^= true;
+ i.hideTopMenu ^= true;
+ i.autoHideDiffTableHeader ^= true;
+ i.hideLineNumbers ^= true;
+ i.renderEntireFile ^= true;
+ i.hideEmptyPane ^= true;
+
+ RestResponse r = adminSession.put("/accounts/" + admin.email
+ + "/preferences.diff", i);
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ DiffPreferencesInfo o = newGson().fromJson(r.getReader(),
+ DiffPreferencesInfo.class);
+
+ assertThat(o.context).isEqualTo(i.context);
+ assertThat(o.tabSize).isEqualTo(i.tabSize);
+ assertThat(o.lineLength).isEqualTo(i.lineLength);
+ assertThat(o.expandAllComments).isEqualTo(i.expandAllComments);
+ assertThat(o.intralineDifference).isNull();
+ assertThat(o.manualReview).isEqualTo(i.manualReview);
+ assertThat(o.retainHeader).isEqualTo(i.retainHeader);
+ assertThat(o.showLineEndings).isNull();
+ assertThat(o.showTabs).isNull();
+ assertThat(o.showWhitespaceErrors).isNull();
+ assertThat(o.skipDeleted).isEqualTo(i.skipDeleted);
+ assertThat(o.skipUncommented).isEqualTo(i.skipUncommented);
+ assertThat(o.syntaxHighlighting).isNull();
+ assertThat(o.hideTopMenu).isEqualTo(i.hideTopMenu);
+ assertThat(o.autoHideDiffTableHeader).isNull();
+ assertThat(o.hideLineNumbers).isEqualTo(i.hideLineNumbers);
+ assertThat(o.renderEntireFile).isEqualTo(i.renderEntireFile);
+ assertThat(o.hideEmptyPane).isEqualTo(i.hideEmptyPane);
+ assertThat(o.ignoreWhitespace).isEqualTo(i.ignoreWhitespace);
+ assertThat(o.theme).isEqualTo(i.theme);
+
+ // Partially fill input record
+ i = new DiffPreferencesInfo();
+ i.tabSize = 42;
+ r = adminSession.put("/accounts/" + admin.email
+ + "/preferences.diff", i);
+ DiffPreferencesInfo a = newGson().fromJson(r.getReader(),
+ DiffPreferencesInfo.class);
+
+ assertThat(a.context).isEqualTo(o.context);
+ assertThat(a.tabSize).isEqualTo(42);
+ assertThat(a.lineLength).isEqualTo(o.lineLength);
+ assertThat(a.expandAllComments).isEqualTo(o.expandAllComments);
+ assertThat(a.intralineDifference).isNull();
+ assertThat(a.manualReview).isEqualTo(o.manualReview);
+ assertThat(a.retainHeader).isEqualTo(o.retainHeader);
+ assertThat(a.showLineEndings).isNull();
+ assertThat(a.showTabs).isNull();
+ assertThat(a.showWhitespaceErrors).isNull();
+ assertThat(a.skipDeleted).isEqualTo(o.skipDeleted);
+ assertThat(a.skipUncommented).isEqualTo(o.skipUncommented);
+ assertThat(a.syntaxHighlighting).isNull();
+ assertThat(a.hideTopMenu).isEqualTo(o.hideTopMenu);
+ assertThat(a.autoHideDiffTableHeader).isNull();
+ assertThat(a.hideLineNumbers).isEqualTo(o.hideLineNumbers);
+ assertThat(a.renderEntireFile).isEqualTo(o.renderEntireFile);
+ assertThat(a.hideEmptyPane).isEqualTo(o.hideEmptyPane);
+ assertThat(a.ignoreWhitespace).isEqualTo(o.ignoreWhitespace);
+ assertThat(a.theme).isEqualTo(o.theme);
+ }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java
index 8770c3c..cf89c5a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java
@@ -69,6 +69,18 @@
assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
EditPreferencesInfo info = getEditPrefInfo(r);
assertEditPreferences(info, out);
+
+ // Partially filled input record
+ EditPreferencesInfo in = new EditPreferencesInfo();
+ in.tabSize = 42;
+ r = adminSession.put(endPoint, in);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
+
+ r = adminSession.get(endPoint);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+ info = getEditPrefInfo(r);
+ out.tabSize = in.tabSize;
+ assertEditPreferences(info, out);
}
private EditPreferencesInfo getEditPrefInfo(RestResponse r)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetDiffPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetDiffPreferencesIT.java
deleted file mode 100644
index 02a94f8..0000000
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetDiffPreferencesIT.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2013 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.acceptance.rest.account;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.RestResponse;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.server.account.GetDiffPreferences.DiffPreferencesInfo;
-
-import org.apache.http.HttpStatus;
-import org.junit.Test;
-
-public class GetDiffPreferencesIT extends AbstractDaemonTest {
- @Test
- public void getDiffPreferencesOfNonExistingAccount_NotFound()
- throws Exception {
- assertThat(adminSession.get("/accounts/non-existing/preferences.diff").getStatusCode())
- .isEqualTo(HttpStatus.SC_NOT_FOUND);
- }
-
- @Test
- public void getDiffPreferences() throws Exception {
- RestResponse r = adminSession.get("/accounts/" + admin.email + "/preferences.diff");
- assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
- DiffPreferencesInfo diffPreferences =
- newGson().fromJson(r.getReader(), DiffPreferencesInfo.class);
- assertDiffPreferences(new AccountDiffPreference(admin.id), diffPreferences);
- }
-
- private static void assertDiffPreferences(AccountDiffPreference expected, DiffPreferencesInfo actual) {
- assertThat(actual.context).isEqualTo(expected.getContext());
- assertThat(toBoolean(actual.expandAllComments)).isEqualTo(expected.isExpandAllComments());
- assertThat(actual.ignoreWhitespace).isEqualTo(expected.getIgnoreWhitespace());
- assertThat(toBoolean(actual.intralineDifference)).isEqualTo(expected.isIntralineDifference());
- assertThat(actual.lineLength).isEqualTo(expected.getLineLength());
- assertThat(toBoolean(actual.manualReview)).isEqualTo(expected.isManualReview());
- assertThat(toBoolean(actual.retainHeader)).isEqualTo(expected.isRetainHeader());
- assertThat(toBoolean(actual.showLineEndings)).isEqualTo(expected.isShowLineEndings());
- assertThat(toBoolean(actual.showTabs)).isEqualTo(expected.isShowTabs());
- assertThat(toBoolean(actual.showWhitespaceErrors)).isEqualTo(expected.isShowWhitespaceErrors());
- assertThat(toBoolean(actual.skipDeleted)).isEqualTo(expected.isSkipDeleted());
- assertThat(toBoolean(actual.skipUncommented)).isEqualTo(expected.isSkipUncommented());
- assertThat(toBoolean(actual.syntaxHighlighting)).isEqualTo(expected.isSyntaxHighlighting());
- assertThat(actual.tabSize).isEqualTo(expected.getTabSize());
- }
-
- private static boolean toBoolean(Boolean b) {
- if (b == null) {
- return false;
- }
- return b.booleanValue();
- }
-}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
index 621d94a..2707507 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
@@ -82,10 +82,12 @@
ChangeInfo c = get(triplet);
assertThat(c.id).isEqualTo(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.DRAFT);
+ assertThat(c.revisions.get(c.currentRevision).draft).isTrue();
RestResponse response = publishChange(changeId);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
c = get(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.NEW);
+ assertThat(c.revisions.get(c.currentRevision).draft).isNull();
}
@Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java
index 5592755..b784f05 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -189,11 +189,34 @@
}
@Test
+ public void addDuplicateComments() throws Exception {
+ PushOneCommit.Result r1 = createChange();
+ String changeId = r1.getChangeId();
+ String revId = r1.getCommit().getName();
+ addComment(r1, "nit: trailing whitespace");
+ addComment(r1, "nit: trailing whitespace");
+ Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
+ assertThat(result.get(FILE_NAME)).hasSize(2);
+ addComment(r1, "nit: trailing whitespace", true);
+ result = getPublishedComments(changeId, revId);
+ assertThat(result.get(FILE_NAME)).hasSize(2);
+
+ PushOneCommit.Result r2 = pushFactory.create(
+ db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "content")
+ .to("refs/for/master");
+ changeId = r2.getChangeId();
+ revId = r2.getCommit().getName();
+ addComment(r2, "nit: trailing whitespace", true);
+ result = getPublishedComments(changeId, revId);
+ assertThat(result.get(FILE_NAME)).hasSize(1);
+ }
+
+ @Test
public void listChangeDrafts() throws Exception {
PushOneCommit.Result r1 = createChange();
PushOneCommit.Result r2 = pushFactory.create(
- db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent",
+ db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content",
r1.getChangeId())
.to("refs/for/master");
@@ -363,9 +386,13 @@
+ "-- \n");
}
-
private void addComment(PushOneCommit.Result r, String message)
throws Exception {
+ addComment(r, message, false);
+ }
+
+ private void addComment(PushOneCommit.Result r, String message,
+ boolean omitDuplicateComments) throws Exception {
CommentInput c = new CommentInput();
c.line = 1;
c.message = message;
@@ -373,6 +400,7 @@
ReviewInput in = new ReviewInput();
in.comments = ImmutableMap.<String, List<CommentInput>> of(
FILE_NAME, ImmutableList.of(c));
+ in.omitDuplicateComments = omitDuplicateComments;
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
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 42f3fe7..2f110de 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
@@ -30,11 +30,9 @@
import com.google.gerrit.server.edit.ChangeEditModifier;
import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.testutil.ConfigSuite;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
@@ -43,20 +41,6 @@
import java.util.List;
public class GetRelatedIT extends AbstractDaemonTest {
- @ConfigSuite.Default
- public static Config byGroup() {
- Config cfg = new Config();
- cfg.setBoolean("change", null, "getRelatedByAncestors", false);
- return cfg;
- }
-
- @ConfigSuite.Config
- public static Config byAncestors() {
- Config cfg = new Config();
- cfg.setBoolean("change", null, "getRelatedByAncestors", true);
- return cfg;
- }
-
@Inject
private ChangeEditUtil editUtil;
@@ -347,8 +331,8 @@
// 1,2 is related directly to 4,1, and the 2-3 parallel branch stays intact.
assertRelated(ps1_2,
- changeAndCommit(ps3_2, c3_2, 2),
changeAndCommit(ps4_1, c4_1, 1),
+ changeAndCommit(ps3_2, c3_2, 2),
changeAndCommit(ps2_2, c2_2, 2),
changeAndCommit(ps1_2, c1_2, 2));
@@ -433,6 +417,91 @@
}
@Test
+ public void getRelatedParallelDescendentBranches() throws Exception {
+ // 1,1---2,1---3,1
+ // \---4,1---5,1
+ // \--6,1---7,1
+
+ RevCommit c1_1 = commitBuilder()
+ .add("a.txt", "1")
+ .message("subject: 1")
+ .create();
+ RevCommit c2_1 = commitBuilder()
+ .add("b.txt", "2")
+ .message("subject: 2")
+ .create();
+ RevCommit c3_1 = commitBuilder()
+ .add("c.txt", "3")
+ .message("subject: 3")
+ .create();
+ pushHead(testRepo, "refs/for/master", false);
+ PatchSet.Id ps1_1 = getPatchSetId(c1_1);
+ PatchSet.Id ps2_1 = getPatchSetId(c2_1);
+ PatchSet.Id ps3_1 = getPatchSetId(c3_1);
+
+ testRepo.reset(c1_1);
+ RevCommit c4_1 = commitBuilder()
+ .add("d.txt", "4")
+ .message("subject: 4")
+ .create();
+ RevCommit c5_1 = commitBuilder()
+ .add("e.txt", "5")
+ .message("subject: 5")
+ .create();
+ pushHead(testRepo, "refs/for/master", false);
+ PatchSet.Id ps4_1 = getPatchSetId(c4_1);
+ PatchSet.Id ps5_1 = getPatchSetId(c5_1);
+
+ testRepo.reset(c1_1);
+ RevCommit c6_1 = commitBuilder()
+ .add("f.txt", "6")
+ .message("subject: 6")
+ .create();
+ RevCommit c7_1 = commitBuilder()
+ .add("g.txt", "7")
+ .message("subject: 7")
+ .create();
+ pushHead(testRepo, "refs/for/master", false);
+ PatchSet.Id ps6_1 = getPatchSetId(c6_1);
+ PatchSet.Id ps7_1 = getPatchSetId(c7_1);
+
+ // All changes are related to 1,1, keeping each of the parallel branches
+ // intact.
+ assertRelated(ps1_1,
+ changeAndCommit(ps7_1, c7_1, 1),
+ changeAndCommit(ps6_1, c6_1, 1),
+ changeAndCommit(ps5_1, c5_1, 1),
+ changeAndCommit(ps4_1, c4_1, 1),
+ changeAndCommit(ps3_1, c3_1, 1),
+ changeAndCommit(ps2_1, c2_1, 1),
+ changeAndCommit(ps1_1, c1_1, 1));
+
+ // The 2-3 branch is only related back to 1, not the other branches.
+ for (PatchSet.Id ps : ImmutableList.of(ps2_1, ps3_1)) {
+ assertRelated(ps,
+ changeAndCommit(ps3_1, c3_1, 1),
+ changeAndCommit(ps2_1, c2_1, 1),
+ changeAndCommit(ps1_1, c1_1, 1));
+ }
+
+ // The 4-5 branch is only related back to 1, not the other branches.
+ for (PatchSet.Id ps : ImmutableList.of(ps4_1, ps5_1)) {
+ assertRelated(ps,
+ changeAndCommit(ps5_1, c5_1, 1),
+ changeAndCommit(ps4_1, c4_1, 1),
+ changeAndCommit(ps1_1, c1_1, 1));
+ }
+
+ // The 6-7 branch is only related back to 1, not the other branches.
+ for (PatchSet.Id ps : ImmutableList.of(ps6_1, ps7_1)) {
+ assertRelated(ps,
+ changeAndCommit(ps7_1, c7_1, 1),
+ changeAndCommit(ps6_1, c6_1, 1),
+ changeAndCommit(ps1_1, c1_1, 1));
+ }
+ }
+
+ @Test
public void getRelatedEdit() throws Exception {
// 1,1---2,1---3,1
// \---2,E---/
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
index b80d6d9..c4a07f2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
@@ -20,8 +20,8 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Patch.ChangeType;
import com.google.gerrit.server.patch.PatchListCache;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
index 9a30696..9bc2ea5 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
@@ -26,6 +26,7 @@
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Set;
public final class IoUtil {
@@ -86,8 +87,8 @@
}
}
- public static void loadJARs(Path... jars) {
- loadJARs(Arrays.asList(jars));
+ public static void loadJARs(Path jar) {
+ loadJARs(Collections.singleton(jar));
}
private static UnsupportedOperationException noAddURL(String m, Throwable why) {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
index 81bca20..533dfa2 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
@@ -16,8 +16,8 @@
import com.google.gerrit.common.audit.Audit;
import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountProjectWatch;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.RemoteJsonService;
@@ -35,7 +35,7 @@
@Audit
@SignInRequired
- void changeDiffPreferences(AccountDiffPreference diffPref,
+ void changeDiffPreferences(DiffPreferencesInfo diffPref,
AsyncCallback<VoidResult> callback);
@SignInRequired
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
index 5824415..6d9c2cd 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
@@ -15,7 +15,7 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.audit.Audit;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.RemoteJsonService;
@@ -29,5 +29,5 @@
@Audit
void patchSetDetail2(PatchSet.Id baseId, PatchSet.Id key,
- AccountDiffPreference diffPrefs, AsyncCallback<PatchSetDetail> callback);
+ DiffPreferencesInfo diffPrefs, AsyncCallback<PatchSetDetail> callback);
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
index c261fdd..3362ba2 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
@@ -27,6 +27,14 @@
return new GroupReference(group.getGroupUUID(), group.getName());
}
+ public static GroupReference fromString(String ref) {
+ String name =
+ ref.substring(ref.indexOf("[") + 1, ref.lastIndexOf("/")).trim();
+ String uuid =
+ ref.substring(ref.lastIndexOf("/") + 1, ref.lastIndexOf("]")).trim();
+ return new GroupReference(new AccountGroup.UUID(uuid), name);
+ }
+
protected String uuid;
protected String name;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
index aa86c47..2e991d9 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
@@ -14,7 +14,7 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import java.util.Date;
import java.util.List;
@@ -22,7 +22,7 @@
/** Data sent as part of the host page, to bootstrap the UI. */
public class HostPageData {
public String version;
- public AccountDiffPreference accountDiffPref;
+ public DiffPreferencesInfo accountDiffPref;
public String xGerritAuth;
public Theme theme;
public List<String> plugins;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
index 19fcbeb..d9601f0 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
@@ -15,7 +15,7 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.audit.Audit;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -27,5 +27,5 @@
public interface PatchDetailService extends RemoteJsonService {
@Audit
void patchScript(Patch.Key key, PatchSet.Id a, PatchSet.Id b,
- AccountDiffPreference diffPrefs, AsyncCallback<PatchScript> callback);
+ DiffPreferencesInfo diffPrefs, AsyncCallback<PatchScript> callback);
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java
index 7bab43a..f23afb1 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java
@@ -14,10 +14,10 @@
package com.google.gerrit.common.data;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.prettify.common.EditList;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Patch.ChangeType;
@@ -42,7 +42,7 @@
protected FileMode oldMode;
protected FileMode newMode;
protected List<String> header;
- protected AccountDiffPreference diffPrefs;
+ protected DiffPreferencesInfo diffPrefs;
protected SparseFileContent a;
protected SparseFileContent b;
protected List<Edit> edits;
@@ -62,7 +62,7 @@
public PatchScript(final Change.Key ck, final ChangeType ct, final String on,
final String nn, final FileMode om, final FileMode nm,
- final List<String> h, final AccountDiffPreference dp,
+ final List<String> h, final DiffPreferencesInfo dp,
final SparseFileContent ca, final SparseFileContent cb,
final List<Edit> e, final DisplayMethod ma, final DisplayMethod mb,
final String mta, final String mtb, final CommentDetail cd,
@@ -142,11 +142,11 @@
return history;
}
- public AccountDiffPreference getDiffPrefs() {
+ public DiffPreferencesInfo getDiffPrefs() {
return diffPrefs;
}
- public void setDiffPrefs(AccountDiffPreference dp) {
+ public void setDiffPrefs(DiffPreferencesInfo dp) {
diffPrefs = dp;
}
@@ -155,7 +155,7 @@
}
public boolean isIgnoreWhitespace() {
- return diffPrefs.getIgnoreWhitespace() != Whitespace.IGNORE_NONE;
+ return diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE;
}
public boolean hasIntralineDifference() {
@@ -171,7 +171,7 @@
}
public boolean isExpandAllComments() {
- return diffPrefs.isExpandAllComments();
+ return diffPrefs.expandAllComments;
}
public SparseFileContent getA() {
@@ -195,8 +195,8 @@
}
public Iterable<EditList.Hunk> getHunks() {
- int ctx = diffPrefs.getContext();
- if (ctx == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
+ int ctx = diffPrefs.context;
+ if (ctx == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
ctx = Math.max(a.size(), b.size());
}
return new EditList(edits, ctx, a.size(), b.size()).getHunks();
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
index 873c560..e6ce6b2 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ReviewInput.java
@@ -49,6 +49,12 @@
public NotifyHandling notify = NotifyHandling.ALL;
/**
+ * If true check to make sure that the comments being posted aren't already
+ * present.
+ */
+ public boolean omitDuplicateComments;
+
+ /**
* Account ID, name, email address or username of another user. The review
* will be posted/updated on behalf of this named user instead of the
* caller. Caller must have the labelAs-$NAME permission granted for each
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
new file mode 100644
index 0000000..c488bd7
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2014 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.client;
+
+public class DiffPreferencesInfo {
+
+ /** Default number of lines of context. */
+ public static final int DEFAULT_CONTEXT = 10;
+
+ /** Default tab size. */
+ public static final int DEFAULT_TAB_SIZE = 8;
+
+ /** Default line length. */
+ public static final int DEFAULT_LINE_LENGTH = 100;
+
+ /** Context setting to display the entire file. */
+ public static final short WHOLE_FILE_CONTEXT = -1;
+
+ /** Typical valid choices for the default context setting. */
+ public static final short[] CONTEXT_CHOICES =
+ {3, 10, 25, 50, 75, 100, WHOLE_FILE_CONTEXT};
+
+ public static enum Whitespace {
+ IGNORE_NONE,
+ IGNORE_TRAILING,
+ IGNORE_LEADING_AND_TRAILING,
+ IGNORE_ALL;
+ }
+
+ public Integer context;
+ public Integer tabSize;
+ public Integer lineLength;
+ public Boolean expandAllComments;
+ public Boolean intralineDifference;
+ public Boolean manualReview;
+ public Boolean showLineEndings;
+ public Boolean showTabs;
+ public Boolean showWhitespaceErrors;
+ public Boolean syntaxHighlighting;
+ public Boolean hideTopMenu;
+ public Boolean autoHideDiffTableHeader;
+ public Boolean hideLineNumbers;
+ public Boolean renderEntireFile;
+ public Boolean hideEmptyPane;
+ public Theme theme;
+ public Whitespace ignoreWhitespace;
+ public Boolean retainHeader;
+ public Boolean skipDeleted;
+ public Boolean skipUncommented;
+
+ public static DiffPreferencesInfo defaults() {
+ DiffPreferencesInfo i = new DiffPreferencesInfo();
+ i.context = DEFAULT_CONTEXT;
+ i.tabSize = DEFAULT_TAB_SIZE;
+ i.lineLength = DEFAULT_LINE_LENGTH;
+ i.ignoreWhitespace = Whitespace.IGNORE_NONE;
+ i.theme = Theme.DEFAULT;
+ i.expandAllComments = false;
+ i.intralineDifference = true;
+ i.manualReview = false;
+ i.retainHeader = false;
+ i.showLineEndings = true;
+ i.showTabs = true;
+ i.showWhitespaceErrors = true;
+ i.skipDeleted = false;
+ i.skipUncommented = false;
+ i.syntaxHighlighting = true;
+ i.hideTopMenu = false;
+ i.autoHideDiffTableHeader = true;
+ i.hideLineNumbers = false;
+ i.renderEntireFile = false;
+ i.hideEmptyPane = false;
+ return i;
+ }
+}
\ No newline at end of file
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Side.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Side.java
index 5d5af75..3485b8b 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Side.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Side.java
@@ -15,5 +15,16 @@
package com.google.gerrit.extensions.client;
public enum Side {
- PARENT, REVISION
-}
\ No newline at end of file
+ PARENT,
+ REVISION;
+
+ public static Side fromShort(short s) {
+ switch (s) {
+ case 0:
+ return PARENT;
+ case 1:
+ return REVISION;
+ }
+ return null;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeConflictException.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/MergeConflictException.java
similarity index 74%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/MergeConflictException.java
rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/MergeConflictException.java
index 02bc8dc..a67db0f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeConflictException.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/MergeConflictException.java
@@ -12,11 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.extensions.restapi;
-/** Indicates that the commit cannot be merged without conflicts. */
-public class MergeConflictException extends Exception {
+/**
+ * Indicates that a commit cannot be merged without conflicts.
+ * <p>
+ * Messages should be viewable by end users.
+ */
+public class MergeConflictException extends ResourceConflictException {
private static final long serialVersionUID = 1L;
+
public MergeConflictException(String msg) {
super(msg, null);
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java
index 6fd8bac..fa78f01 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java
@@ -19,6 +19,9 @@
import org.eclipse.jgit.util.NB;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
public class Fingerprint {
private final byte[] fp;
@@ -33,6 +36,18 @@
NB.decodeUInt16(fp, 16), NB.decodeUInt16(fp, 18));
}
+ public static long getId(byte[] fp) {
+ return NB.decodeInt64(fp, 12);
+ }
+
+ public static Map<Long, Fingerprint> byId(Iterable<Fingerprint> fps) {
+ Map<Long, Fingerprint> result = new HashMap<>();
+ for (Fingerprint fp : fps) {
+ result.put(fp.getId(), fp);
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
private static byte[] checkLength(byte[] fp) {
checkArgument(fp.length == 20,
"fingerprint must be 20 bytes, got %s", fp.length);
@@ -54,6 +69,23 @@
this.fp = checkLength(fp);
}
+ /**
+ * Wrap a portion of a fingerprint byte array.
+ * <p>
+ * Unlike {@link #Fingerprint(byte[])}, creates a new copy of the byte array.
+ *
+ * @param buf byte array to wrap; must have at least {@code off + 20} bytes.
+ * @param off offset in buf.
+ */
+ public Fingerprint(byte[] buf, int off) {
+ int expected = 20 + off;
+ checkArgument(buf.length >= expected,
+ "fingerprint buffer must have at least %s bytes, got %s",
+ expected, buf.length);
+ this.fp = new byte[20];
+ System.arraycopy(buf, off, fp, 0, 20);
+ }
+
public byte[] get() {
return fp;
}
@@ -79,6 +111,6 @@
}
public long getId() {
- return NB.decodeInt64(fp, 12);
+ return getId(fp);
}
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index d942c75..c3c886f 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -14,14 +14,14 @@
package com.google.gerrit.gpg;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_GPGKEY;
import com.google.common.base.CharMatcher;
import com.google.common.base.MoreObjects;
import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.io.BaseEncoding;
import com.google.gerrit.common.PageLinks;
@@ -44,11 +44,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -61,18 +60,13 @@
private static final Logger log =
LoggerFactory.getLogger(GerritPublicKeyChecker.class);
- private final Provider<ReviewDb> db;
- private final String webUrl;
- private final IdentifiedUser.GenericFactory userFactory;
- private final IdentifiedUser expectedUser;
-
@Singleton
public static class Factory {
private final Provider<ReviewDb> db;
private final String webUrl;
private final IdentifiedUser.GenericFactory userFactory;
private final int maxTrustDepth;
- private final ImmutableList<Fingerprint> trusted;
+ private final ImmutableMap<Long, Fingerprint> trusted;
@Inject
Factory(@GerritServerConfig Config cfg,
@@ -86,52 +80,58 @@
String[] strs = cfg.getStringList("receive", null, "trustedKey");
if (strs.length != 0) {
- List<Fingerprint> fps = new ArrayList<>(strs.length);
+ Map<Long, Fingerprint> fps =
+ Maps.newHashMapWithExpectedSize(strs.length);
for (String str : strs) {
str = CharMatcher.WHITESPACE.removeFrom(str).toUpperCase();
- fps.add(new Fingerprint(BaseEncoding.base16().decode(str)));
+ Fingerprint fp = new Fingerprint(BaseEncoding.base16().decode(str));
+ fps.put(fp.getId(), fp);
}
- trusted = ImmutableList.copyOf(fps);
+ trusted = ImmutableMap.copyOf(fps);
} else {
trusted = null;
}
}
- /**
- * Create a checker that can check arbitrary public keys.
- * <p>
- * Each key is checked against the set of identities in the database
- * belonging to the same user as the key.
- *
- * @return a new checker.
- */
public GerritPublicKeyChecker create() {
- return new GerritPublicKeyChecker(this, null);
+ return new GerritPublicKeyChecker(this);
}
- /**
- * Create a checker for checking a single public key against a known user.
- * <p>
- * The top-level key passed to {@link #check(PGPPublicKey, PublicKeyStore)}
- * must belong to the given user. (Other keys checked in the course of
- * verifying the web of trust are checked against the set of identities in
- * the database belonging to the same user as the key.)
- *
- * @param expectedUser the user
- * @return a new checker.
- */
- public GerritPublicKeyChecker create(IdentifiedUser expectedUser) {
- checkNotNull(expectedUser);
- return new GerritPublicKeyChecker(this, expectedUser);
+ public GerritPublicKeyChecker create(IdentifiedUser expectedUser,
+ PublicKeyStore store) {
+ GerritPublicKeyChecker checker = new GerritPublicKeyChecker(this);
+ checker.setExpectedUser(expectedUser);
+ checker.setStore(store);
+ return checker;
}
}
- private GerritPublicKeyChecker(Factory factory, IdentifiedUser expectedUser) {
- super(factory.maxTrustDepth, factory.trusted);
+ private final Provider<ReviewDb> db;
+ private final String webUrl;
+ private final IdentifiedUser.GenericFactory userFactory;
+
+ private IdentifiedUser expectedUser;
+
+ private GerritPublicKeyChecker(Factory factory) {
this.db = factory.db;
this.webUrl = factory.webUrl;
this.userFactory = factory.userFactory;
+ if (factory.trusted != null) {
+ enableTrust(factory.maxTrustDepth, factory.trusted);
+ }
+ }
+
+ /**
+ * Set the expected user for this checker.
+ * <p>
+ * If set, the top-level key passed to {@link #check(PGPPublicKey)} must
+ * belong to the given user. (Other keys checked in the course of verifying
+ * the web of trust are checked against the set of identities in the database
+ * belonging to the same user as the key.)
+ */
+ public GerritPublicKeyChecker setExpectedUser(IdentifiedUser expectedUser) {
this.expectedUser = expectedUser;
+ return this;
}
@Override
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPushCertificateChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPushCertificateChecker.java
index fbc3d44..30983ac 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPushCertificateChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPushCertificateChecker.java
@@ -26,8 +26,7 @@
public class GerritPushCertificateChecker extends PushCertificateChecker {
public interface Factory {
- GerritPushCertificateChecker create(IdentifiedUser expectedUser,
- boolean checkNonce);
+ GerritPushCertificateChecker create(IdentifiedUser expectedUser);
}
private final GitRepositoryManager repoManager;
@@ -38,9 +37,8 @@
GerritPublicKeyChecker.Factory keyCheckerFactory,
GitRepositoryManager repoManager,
AllUsersName allUsers,
- @Assisted IdentifiedUser expectedUser,
- @Assisted boolean checkNonce) {
- super(keyCheckerFactory.create(expectedUser), checkNonce);
+ @Assisted IdentifiedUser expectedUser) {
+ super(keyCheckerFactory.create().setExpectedUser(expectedUser));
this.repoManager = repoManager;
this.allUsers = allUsers;
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java
index 725a6e1..e4c81df 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java
@@ -19,20 +19,34 @@
import static com.google.gerrit.extensions.common.GpgKeyInfo.Status.TRUSTED;
import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static org.bouncycastle.bcpg.SignatureSubpacketTags.REVOCATION_KEY;
+import static org.bouncycastle.bcpg.SignatureSubpacketTags.REVOCATION_REASON;
+import static org.bouncycastle.bcpg.sig.RevocationReasonTags.KEY_COMPROMISED;
+import static org.bouncycastle.bcpg.sig.RevocationReasonTags.KEY_RETIRED;
+import static org.bouncycastle.bcpg.sig.RevocationReasonTags.KEY_SUPERSEDED;
+import static org.bouncycastle.bcpg.sig.RevocationReasonTags.NO_REASON;
+import static org.bouncycastle.openpgp.PGPSignature.DIRECT_KEY;
+import static org.bouncycastle.openpgp.PGPSignature.KEY_REVOCATION;
import com.google.gerrit.extensions.common.GpgKeyInfo.Status;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
+import org.bouncycastle.bcpg.sig.RevocationKey;
+import org.bouncycastle.bcpg.sig.RevocationReason;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -42,69 +56,91 @@
/** Checker for GPG public keys for use in a push certificate. */
public class PublicKeyChecker {
+ private static final Logger log =
+ LoggerFactory.getLogger(PublicKeyChecker.class);
+
// https://tools.ietf.org/html/rfc4880#section-5.2.3.13
private static final int COMPLETE_TRUST = 120;
- private final Map<Long, Fingerprint> trusted;
- private final int maxTrustDepth;
-
- /** Create a new checker that does not check the web of trust. */
- public PublicKeyChecker() {
- this(0, null);
- }
+ private PublicKeyStore store;
+ private Map<Long, Fingerprint> trusted;
+ private int maxTrustDepth;
+ private Date effectiveTime = new Date();
/**
+ * Enable web-of-trust checks.
+ * <p>
+ * If enabled, a store must be set with {@link #setStore(PublicKeyStore)}.
+ * (These methods are separate since the store is a closeable resource that
+ * may not be available when reading trusted keys from a config.)
+ *
* @param maxTrustDepth maximum depth to search while looking for a trusted
* key.
- * @param trusted ultimately trusted key fingerprints; may not be empty. If
- * null, disable web-of-trust checks.
+ * @param trusted ultimately trusted key fingerprints, keyed by fingerprint;
+ * may not be empty. To construct a map, see {@link
+ * Fingerprint#byId(Iterable)}.
+ * @return a reference to this object.
*/
- public PublicKeyChecker(int maxTrustDepth, Collection<Fingerprint> trusted) {
- if (trusted != null) {
- if (maxTrustDepth <= 0) {
+ public PublicKeyChecker enableTrust(int maxTrustDepth,
+ Map<Long, Fingerprint> trusted) {
+ if (maxTrustDepth <= 0) {
+ throw new IllegalArgumentException(
+ "maxTrustDepth must be positive, got: " + maxTrustDepth);
+ }
+ if (trusted == null || trusted.isEmpty()) {
throw new IllegalArgumentException(
- "maxTrustDepth must be positive, got: " + maxTrustDepth);
- }
- if (trusted.isEmpty()) {
- throw new IllegalArgumentException("at least one trusted key required");
- }
- this.trusted = new HashMap<>();
- for (Fingerprint fp : trusted) {
- this.trusted.put(fp.getId(), fp);
- }
- } else {
- this.trusted = null;
+ "at least one trusted key is required");
}
this.maxTrustDepth = maxTrustDepth;
+ this.trusted = trusted;
+ return this;
}
- /**
- * Check a public key, including its web of trust.
- *
- * @param key the public key.
- * @param store a store to read public keys from for trust checks. If this
- * store is not configured for web-of-trust checks, this argument is
- * ignored.
- * @return the result of the check.
- */
- public final CheckResult check(PGPPublicKey key, PublicKeyStore store) {
- if (trusted == null) {
- return check(key);
- } else if (store == null) {
- throw new IllegalArgumentException(
- "PublicKeyStore required for web of trust checks");
+ /** Disable web-of-trust checks. */
+ public PublicKeyChecker disableTrust() {
+ trusted = null;
+ return this;
+ }
+
+ /** Set the public key store for reading keys referenced in signatures. */
+ public PublicKeyChecker setStore(PublicKeyStore store) {
+ if (store == null) {
+ throw new IllegalArgumentException("PublicKeyStore is required");
}
- return check(key, store, 0, true, new HashSet<Fingerprint>());
+ this.store = store;
+ return this;
}
/**
- * Check only a public key, not including its web of trust.
+ * Set the effective time for checking the key.
+ * <p>
+ * If set, check whether the key should be considered valid (e.g. unexpired)
+ * as of this time.
+ *
+ * @param effectiveTime effective time.
+ * @return a reference to this object.
+ */
+ public PublicKeyChecker setEffectiveTime(Date effectiveTime) {
+ this.effectiveTime = effectiveTime;
+ return this;
+ }
+
+ protected Date getEffectiveTime() {
+ return effectiveTime;
+ }
+
+ /**
+ * Check a public key.
*
* @param key the public key.
* @return the result of the check.
*/
public final CheckResult check(PGPPublicKey key) {
- return check(key, null, 0, false, null);
+ if (store == null) {
+ throw new IllegalStateException("PublicKeyStore is required");
+ }
+ return check(key, 0, true,
+ trusted != null ? new HashSet<Fingerprint>() : null);
}
/**
@@ -115,17 +151,17 @@
*
* @param key the public key.
* @param depth the depth from the initial key passed to {@link #check(
- * PGPPublicKey, PublicKeyStore)}: 0 if this was the initial key, up to a
- * maximum of {@code maxTrustDepth}.
+ * PGPPublicKey)}: 0 if this was the initial key, up to a maximum of
+ * {@code maxTrustDepth}.
* @return the result of the custom check.
*/
public CheckResult checkCustom(PGPPublicKey key, int depth) {
return CheckResult.ok();
}
- private CheckResult check(PGPPublicKey key, PublicKeyStore store, int depth,
- boolean expand, Set<Fingerprint> seen) {
- CheckResult basicResult = checkBasic(key);
+ private CheckResult check(PGPPublicKey key, int depth, boolean expand,
+ Set<Fingerprint> seen) {
+ CheckResult basicResult = checkBasic(key, effectiveTime);
CheckResult customResult = checkCustom(key, depth);
CheckResult trustResult = checkWebOfTrust(key, store, depth, seen);
if (!expand && !trustResult.isTrusted()) {
@@ -160,28 +196,189 @@
return CheckResult.create(status, problems);
}
- private CheckResult checkBasic(PGPPublicKey key) {
+ private CheckResult checkBasic(PGPPublicKey key, Date now) {
List<String> problems = new ArrayList<>(2);
- if (key.isRevoked()) {
- // TODO(dborowitz): isRevoked is overeager:
- // http://www.bouncycastle.org/jira/browse/BJB-45
- problems.add("Key is revoked");
- }
+ gatherRevocationProblems(key, now, problems);
- long validSecs = key.getValidSeconds();
- if (validSecs != 0) {
- long createdSecs = key.getCreationTime().getTime() / 1000;
- long nowSecs = System.currentTimeMillis() / 1000;
- if (nowSecs - createdSecs > validSecs) {
+ long validMs = key.getValidSeconds() * 1000;
+ if (validMs != 0) {
+ long msSinceCreation = now.getTime() - key.getCreationTime().getTime();
+ if (msSinceCreation > validMs) {
problems.add("Key is expired");
}
}
return CheckResult.create(problems);
}
+ private void gatherRevocationProblems(PGPPublicKey key, Date now,
+ List<String> problems) {
+ try {
+ List<PGPSignature> revocations = new ArrayList<>();
+ Map<Long, RevocationKey> revokers = new HashMap<>();
+ PGPSignature selfRevocation =
+ scanRevocations(key, now, revocations, revokers);
+ if (selfRevocation != null) {
+ RevocationReason reason = getRevocationReason(selfRevocation);
+ if (isRevocationValid(selfRevocation, reason, now)) {
+ problems.add(reasonToString(reason));
+ }
+ } else {
+ checkRevocations(key, revocations, revokers, problems);
+ }
+ } catch (PGPException | IOException e) {
+ problems.add("Error checking key revocation");
+ }
+ }
+
+ private static boolean isRevocationValid(PGPSignature revocation,
+ RevocationReason reason, Date now) {
+ // RFC4880 states:
+ // "If a key has been revoked because of a compromise, all signatures
+ // created by that key are suspect. However, if it was merely superseded or
+ // retired, old signatures are still valid."
+ //
+ // Note that GnuPG does not implement this correctly, as it does not
+ // consider the revocation reason and timestamp when checking whether a
+ // signature (data or certification) is valid.
+ return reason.getRevocationReason() == KEY_COMPROMISED
+ || revocation.getCreationTime().before(now);
+ }
+
+ private PGPSignature scanRevocations(PGPPublicKey key, Date now,
+ List<PGPSignature> revocations, Map<Long, RevocationKey> revokers)
+ throws PGPException {
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> allSigs = key.getSignatures();
+ while (allSigs.hasNext()) {
+ PGPSignature sig = allSigs.next();
+ switch (sig.getSignatureType()) {
+ case KEY_REVOCATION:
+ if (sig.getKeyID() == key.getKeyID()) {
+ sig.init(new BcPGPContentVerifierBuilderProvider(), key);
+ if (sig.verifyCertification(key)) {
+ return sig;
+ }
+ } else {
+ RevocationReason reason = getRevocationReason(sig);
+ if (reason != null && isRevocationValid(sig, reason, now)) {
+ revocations.add(sig);
+ }
+ }
+ break;
+ case DIRECT_KEY:
+ RevocationKey r = getRevocationKey(key, sig);
+ if (r != null) {
+ revokers.put(Fingerprint.getId(r.getFingerprint()), r);
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
+ private RevocationKey getRevocationKey(PGPPublicKey key, PGPSignature sig)
+ throws PGPException {
+ if (sig.getKeyID() != key.getKeyID()) {
+ return null;
+ }
+ SignatureSubpacket sub =
+ sig.getHashedSubPackets().getSubpacket(REVOCATION_KEY);
+ if (sub == null) {
+ return null;
+ }
+ sig.init(new BcPGPContentVerifierBuilderProvider(), key);
+ if (!sig.verifyCertification(key)) {
+ return null;
+ }
+
+ return new RevocationKey(sub.isCritical(), sub.getData());
+ }
+
+ private void checkRevocations(PGPPublicKey key,
+ List<PGPSignature> revocations, Map<Long, RevocationKey> revokers,
+ List<String> problems)
+ throws PGPException, IOException {
+ for (PGPSignature revocation : revocations) {
+ RevocationKey revoker = revokers.get(revocation.getKeyID());
+ if (revoker == null) {
+ continue; // Not a designated revoker.
+ }
+ byte[] rfp = revoker.getFingerprint();
+ PGPPublicKeyRing revokerKeyRing = store.get(rfp);
+ if (revokerKeyRing == null) {
+ // Revoker is authorized and there is a revocation signature by this
+ // revoker, but the key is not in the store so we can't verify the
+ // signature.
+ log.info("Key " + Fingerprint.toString(key.getFingerprint())
+ + " is revoked by " + Fingerprint.toString(rfp)
+ + ", which is not in the store. Assuming revocation is valid.");
+ problems.add(reasonToString(getRevocationReason(revocation)));
+ continue;
+ }
+ PGPPublicKey rk = revokerKeyRing.getPublicKey();
+ if (rk.getAlgorithm() != revoker.getAlgorithm()) {
+ continue;
+ }
+ if (!checkBasic(rk, revocation.getCreationTime()).isOk()) {
+ // Revoker's key was expired or revoked at time of revocation, so the
+ // revocation is invalid.
+ continue;
+ }
+ revocation.init(new BcPGPContentVerifierBuilderProvider(), rk);
+ if (revocation.verifyCertification(key)) {
+ problems.add(reasonToString(getRevocationReason(revocation)));
+ }
+ }
+ }
+
+ private static RevocationReason getRevocationReason(PGPSignature sig) {
+ if (sig.getSignatureType() != KEY_REVOCATION) {
+ throw new IllegalArgumentException(
+ "Expected KEY_REVOCATION signature, got " + sig.getSignatureType());
+ }
+ SignatureSubpacket sub =
+ sig.getHashedSubPackets().getSubpacket(REVOCATION_REASON);
+ if (sub == null) {
+ return null;
+ }
+ return new RevocationReason(sub.isCritical(), sub.getData());
+ }
+
+ private static String reasonToString(RevocationReason reason) {
+ StringBuilder r = new StringBuilder("Key is revoked (");
+ if (reason == null) {
+ return r.append("no reason provided)").toString();
+ }
+ switch (reason.getRevocationReason()) {
+ case NO_REASON:
+ r.append("no reason code specified");
+ break;
+ case KEY_SUPERSEDED:
+ r.append("superseded");
+ break;
+ case KEY_COMPROMISED:
+ r.append("key material has been compromised");
+ break;
+ case KEY_RETIRED:
+ r.append("retired and no longer valid");
+ break;
+ default:
+ r.append("reason code ")
+ .append(Integer.toString(reason.getRevocationReason()))
+ .append(')');
+ break;
+ }
+ r.append(')');
+ String desc = reason.getRevocationDescription();
+ if (!desc.isEmpty()) {
+ r.append(": ").append(desc);
+ }
+ return r.toString();
+ }
+
private CheckResult checkWebOfTrust(PGPPublicKey key, PublicKeyStore store,
int depth, Set<Fingerprint> seen) {
- if (trusted == null || store == null) {
+ if (trusted == null) {
// Trust checking not configured, server trusts all OK keys.
return CheckResult.trusted();
}
@@ -204,8 +401,13 @@
Iterator<String> userIds = key.getUserIDs();
while (userIds.hasNext()) {
String userId = userIds.next();
+
+ // Don't check the timestamp of these certifications. This allows admins
+ // to correct untrusted keys by signing them with a trusted key, such that
+ // older signatures created by those keys retroactively appear valid.
@SuppressWarnings("unchecked")
Iterator<PGPSignature> sigs = key.getSignaturesForID(userId);
+
while (sigs.hasNext()) {
PGPSignature sig = sigs.next();
// TODO(dborowitz): Handle CERTIFICATION_REVOCATION.
@@ -222,8 +424,7 @@
}
String subpacketProblem = checkTrustSubpacket(sig, depth);
if (subpacketProblem == null) {
- CheckResult signerResult =
- check(signer, store, depth + 1, false, seen);
+ CheckResult signerResult = check(signer, depth + 1, false, seen);
if (signerResult.isTrusted()) {
return CheckResult.trusted();
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
index b2798f5..3d939a1 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
@@ -83,7 +83,7 @@
* @param sig signature object.
* @param data signed payload.
* @return the key chosen from {@code keyRings} that was able to verify the
- * signature, or null if none was found.
+ * signature, or {@code null} if none was found.
* @throws PGPException if an error occurred verifying the signature.
*/
public static PGPPublicKey getSigner(Iterable<PGPPublicKeyRing> keyRings,
@@ -107,7 +107,7 @@
* @param userId user ID being certified.
* @param key key being certified.
* @return the key chosen from {@code keyRings} that was able to verify the
- * certification, or null if none was found.
+ * certification, or {@code null} if none was found.
* @throws PGPException if an error occurred verifying the certification.
*/
public static PGPPublicKey getSigner(Iterable<PGPPublicKeyRing> keyRings,
@@ -178,15 +178,39 @@
*/
public PGPPublicKeyRingCollection get(long keyId)
throws PGPException, IOException {
+ return new PGPPublicKeyRingCollection(get(keyId, null));
+ }
+
+ /**
+ * Read public key with the given fingerprint.
+ * <p>
+ * Keys should not be trusted unless checked with {@link PublicKeyChecker}.
+ * <p>
+ * Multiple calls to this method use the same state of the key ref; to reread
+ * the ref, call {@link #close()} first.
+ *
+ * @param fingerprint key fingerprint.
+ * @return the key if found, or {@code null}.
+ * @throws PGPException if an error occurred parsing the key data.
+ * @throws IOException if an error occurred reading the repository data.
+ */
+ public PGPPublicKeyRing get(byte[] fingerprint)
+ throws PGPException, IOException {
+ List<PGPPublicKeyRing> keyRings =
+ get(Fingerprint.getId(fingerprint), fingerprint);
+ return !keyRings.isEmpty() ? keyRings.get(0) : null;
+ }
+
+ private List<PGPPublicKeyRing> get(long keyId, byte[] fp) throws IOException {
if (reader == null) {
load();
}
if (notes == null) {
- return empty();
+ return Collections.emptyList();
}
Note note = notes.getNote(keyObjectId(keyId));
if (note == null) {
- return empty();
+ return Collections.emptyList();
}
List<PGPPublicKeyRing> keys = new ArrayList<>();
@@ -200,12 +224,16 @@
}
Object obj = it.next();
if (obj instanceof PGPPublicKeyRing) {
- keys.add((PGPPublicKeyRing) obj);
+ PGPPublicKeyRing kr = (PGPPublicKeyRing) obj;
+ if (fp == null
+ || Arrays.equals(fp, kr.getPublicKey().getFingerprint())) {
+ keys.add(kr);
+ }
}
checkState(!it.hasNext(),
"expected one PGP object per ArmoredInputStream");
}
- return new PGPPublicKeyRingCollection(keys);
+ return keys;
}
}
@@ -375,12 +403,6 @@
return out.toByteArray();
}
- private static PGPPublicKeyRingCollection empty()
- throws PGPException, IOException {
- return new PGPPublicKeyRingCollection(
- Collections.<PGPPublicKeyRing> emptyList());
- }
-
public static String keyToString(PGPPublicKey key) {
@SuppressWarnings("unchecked")
Iterator<String> it = key.getUserIDs();
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java
index 71068b8..0a0fff7 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java
@@ -67,12 +67,18 @@
}
private final PublicKeyChecker publicKeyChecker;
- private final boolean checkNonce;
- protected PushCertificateChecker(PublicKeyChecker publicKeyChecker,
- boolean checkNonce) {
+ private boolean checkNonce;
+
+ protected PushCertificateChecker(PublicKeyChecker publicKeyChecker) {
this.publicKeyChecker = publicKeyChecker;
+ checkNonce = true;
+ }
+
+ /** Set whether to check the status of the nonce; defaults to true. */
+ public PushCertificateChecker setCheckNonce(boolean checkNonce) {
this.checkNonce = checkNonce;
+ return this;
}
/**
@@ -202,7 +208,10 @@
CheckResult.bad("Signature by " + keyIdToString(sig.getKeyID())
+ " is not valid"));
}
- CheckResult result = publicKeyChecker.check(signer, store);
+ CheckResult result = publicKeyChecker
+ .setStore(store)
+ .setEffectiveTime(sig.getCreationTime())
+ .check(signer);
if (!result.getProblems().isEmpty()) {
StringBuilder err = new StringBuilder("Invalid public key ")
.append(keyToString(signer))
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java
index c694cb9..bc027cd 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java
@@ -15,7 +15,6 @@
package com.google.gerrit.gpg;
import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.EnableSignedPush;
@@ -33,6 +32,7 @@
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PreReceiveHook;
import org.eclipse.jgit.transport.PreReceiveHookChain;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.SignedPushConfig;
@@ -42,6 +42,8 @@
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
class SignedPushModule extends AbstractModule {
@@ -92,15 +94,22 @@
if (!ps.isEnableSignedPush()) {
rp.setSignedPushConfig(null);
return;
- }
- if (signedPushConfig == null) {
+ } else if (signedPushConfig == null) {
log.error("receive.enableSignedPush is true for project {} but"
+ " false in gerrit.config, so signed push verification is"
+ " disabled", project.get());
+ rp.setSignedPushConfig(null);
+ return;
}
rp.setSignedPushConfig(signedPushConfig);
- rp.setPreReceiveHook(PreReceiveHookChain.newChain(Lists.newArrayList(
- hook, rp.getPreReceiveHook())));
+
+ List<PreReceiveHook> hooks = new ArrayList<>(3);
+ if (ps.isRequireSignedPush()) {
+ hooks.add(SignedPushPreReceiveHook.Required.INSTANCE);
+ }
+ hooks.add(hook);
+ hooks.add(rp.getPreReceiveHook());
+ rp.setPreReceiveHook(PreReceiveHookChain.newChain(hooks));
}
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
index 875c77c..cdc3c62 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
@@ -36,6 +36,21 @@
*/
@Singleton
public class SignedPushPreReceiveHook implements PreReceiveHook {
+ public static class Required implements PreReceiveHook {
+ public static final Required INSTANCE = new Required();
+
+ @Override
+ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
+ if (rp.getPushCertificate() == null) {
+ rp.sendMessage("ERROR: Signed push is required");
+ reject(commands, "push cert error");
+ }
+ }
+
+ private Required() {
+ }
+ }
+
private final Provider<IdentifiedUser> user;
private final GerritPushCertificateChecker.Factory checkerFactory;
@@ -54,7 +69,8 @@
if (cert == null) {
return;
}
- CheckResult result = checkerFactory.create(user.get(), true)
+ CheckResult result = checkerFactory.create(user.get())
+ .setCheckNonce(true)
.check(cert)
.getCheckResult();
if (!isAllowed(result, commands)) {
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
index 3821135..e6720db 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -95,8 +95,10 @@
IdentifiedUser expectedUser) throws GpgException {
try {
PushCertificate cert = PushCertificateParser.fromString(certStr);
- PushCertificateChecker.Result result =
- pushCertCheckerFactory.create(expectedUser, false).check(cert);
+ PushCertificateChecker.Result result = pushCertCheckerFactory
+ .create(expectedUser)
+ .setCheckNonce(false)
+ .check(cert);
PushCertificateInfo info = new PushCertificateInfo();
info.certificate = certStr;
info.key = GpgKeys.toJson(result.getPublicKey(), result.getCheckResult());
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java
index 900bcaf..a136007 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -163,7 +163,7 @@
found = true;
GpgKeyInfo info = toJson(
keyRing.getPublicKey(),
- checkerFactory.create(rsrc.getUser()),
+ checkerFactory.create(rsrc.getUser(), store),
store);
keys.put(info.id, info);
info.id = null;
@@ -197,7 +197,7 @@
try (PublicKeyStore store = storeProvider.get()) {
return toJson(
rsrc.getKeyRing().getPublicKey(),
- checkerFactory.create(rsrc.getUser()),
+ checkerFactory.create().setExpectedUser(rsrc.getUser()),
store);
}
}
@@ -261,7 +261,7 @@
static GpgKeyInfo toJson(PGPPublicKey key, PublicKeyChecker checker,
PublicKeyStore store) throws IOException {
- return toJson(key, checker.check(key, store));
+ return toJson(key, checker.setStore(store).check(key));
}
public static void toJson(GpgKeyInfo info, CheckResult checkResult) {
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 9b18ea2..91c4494 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -36,6 +36,7 @@
import com.google.gerrit.gpg.CheckResult;
import com.google.gerrit.gpg.Fingerprint;
import com.google.gerrit.gpg.GerritPublicKeyChecker;
+import com.google.gerrit.gpg.PublicKeyChecker;
import com.google.gerrit.gpg.PublicKeyStore;
import com.google.gerrit.gpg.server.PostGpgKeys.Input;
import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -193,7 +194,9 @@
for (PGPPublicKeyRing keyRing : keyRings) {
PGPPublicKey key = keyRing.getPublicKey();
// Don't check web of trust; admins can fill in certifications later.
- CheckResult result = checkerFactory.create(rsrc.getUser()).check(key);
+ CheckResult result = checkerFactory.create(rsrc.getUser(), store)
+ .disableTrust()
+ .check(key);
if (!result.isOk()) {
throw new BadRequestException(String.format(
"Problems with public key %s:\n%s",
@@ -245,12 +248,12 @@
throws IOException {
// Unlike when storing keys, include web-of-trust checks when producing
// result JSON, so the user at least knows of any issues.
- GerritPublicKeyChecker checker = checkerFactory.create(user);
+ PublicKeyChecker checker = checkerFactory.create(user, store);
Map<String, GpgKeyInfo> infos =
Maps.newHashMapWithExpectedSize(keys.size() + deleted.size());
for (PGPPublicKeyRing keyRing : keys) {
PGPPublicKey key = keyRing.getPublicKey();
- CheckResult result = checker.check(key, store);
+ CheckResult result = checker.check(key);
GpgKeyInfo info = GpgKeys.toJson(key, result);
infos.put(info.id, info);
info.id = null;
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index 05617ff..4df9d37 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -14,10 +14,10 @@
package com.google.gerrit.gpg;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.gpg.GerritPublicKeyChecker.toExtIdKey;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithSecondUserId;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyA;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyB;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyC;
@@ -32,7 +32,6 @@
import com.google.common.collect.Iterators;
import com.google.gerrit.extensions.common.GpgKeyInfo.Status;
import com.google.gerrit.gpg.testutil.TestKey;
-import com.google.gerrit.gpg.testutil.TestKeys;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -67,6 +66,7 @@
import org.junit.Test;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -171,8 +171,9 @@
@Test
public void defaultGpgCertificationMatchesEmail() throws Exception {
- TestKey key = TestKeys.key5();
- GerritPublicKeyChecker checker = checkerFactory.create(user);
+ TestKey key = validKeyWithSecondUserId();
+ PublicKeyChecker checker = checkerFactory.create(user, store)
+ .disableTrust();
assertProblems(
checker.check(key.getPublicKey()), Status.BAD,
"Key must contain a valid certification for one of the following "
@@ -181,16 +182,18 @@
+ " username:user");
addExternalId("test", "test", "test5@example.com");
- checker = checkerFactory.create(user);
+ checker = checkerFactory.create(user, store)
+ .disableTrust();
assertNoProblems(checker.check(key.getPublicKey()));
}
@Test
public void defaultGpgCertificationDoesNotMatchEmail() throws Exception {
addExternalId("test", "test", "nobody@example.com");
- GerritPublicKeyChecker checker = checkerFactory.create(user);
+ PublicKeyChecker checker = checkerFactory.create(user, store)
+ .disableTrust();
assertProblems(
- checker.check(TestKeys.key5().getPublicKey()), Status.BAD,
+ checker.check(validKeyWithSecondUserId().getPublicKey()), Status.BAD,
"Key must contain a valid certification for one of the following "
+ "identities:\n"
+ " gerrit:user\n"
@@ -202,16 +205,18 @@
@Test
public void manualCertificationMatchesExternalId() throws Exception {
addExternalId("foo", "myId", null);
- GerritPublicKeyChecker checker = checkerFactory.create(user);
- assertNoProblems(checker.check(TestKeys.key5().getPublicKey()));
+ PublicKeyChecker checker = checkerFactory.create(user, store)
+ .disableTrust();
+ assertNoProblems(checker.check(validKeyWithSecondUserId().getPublicKey()));
}
@Test
public void manualCertificationDoesNotMatchExternalId() throws Exception {
addExternalId("foo", "otherId", null);
- GerritPublicKeyChecker checker = checkerFactory.create(user);
+ PublicKeyChecker checker = checkerFactory.create(user, store)
+ .disableTrust();
assertProblems(
- checker.check(TestKeys.key5().getPublicKey()), Status.BAD,
+ checker.check(validKeyWithSecondUserId().getPublicKey()), Status.BAD,
"Key must contain a valid certification for one of the following "
+ "identities:\n"
+ " foo:otherId\n"
@@ -225,14 +230,17 @@
db.accountExternalIds().byAccount(user.getAccountId()));
reloadUser();
- TestKey key = TestKeys.key5();
- GerritPublicKeyChecker checker = checkerFactory.create(user);
+ TestKey key = validKeyWithSecondUserId();
+ PublicKeyChecker checker = checkerFactory.create(user, store)
+ .disableTrust();
assertProblems(
checker.check(key.getPublicKey()), Status.BAD,
"No identities found for user; check"
+ " http://test/#/settings/web-identities");
- checker = checkerFactory.create();
+ checker = checkerFactory.create()
+ .setStore(store)
+ .disableTrust();
assertProblems(
checker.check(key.getPublicKey()), Status.BAD,
"Key is not associated with any users");
@@ -264,14 +272,14 @@
add(keyE(), addUser("userE"));
// Checker for A, checking A.
- GerritPublicKeyChecker checkerA = checkerFactory.create(user);
- assertNoProblems(checkerA.check(keyA.getPublicKey(), store));
+ PublicKeyChecker checkerA = checkerFactory.create(user, store);
+ assertNoProblems(checkerA.check(keyA.getPublicKey()));
// Checker for B, checking B. Trust chain and IDs are correct, so the only
// problem is with the key itself.
- GerritPublicKeyChecker checkerB = checkerFactory.create(userB);
+ PublicKeyChecker checkerB = checkerFactory.create(userB, store);
assertProblems(
- checkerB.check(keyB.getPublicKey(), store), Status.BAD,
+ checkerB.check(keyB.getPublicKey()), Status.BAD,
"Key is expired");
}
@@ -294,9 +302,9 @@
add(keyE(), addUser("userE"));
// Checker for A, checking B.
- GerritPublicKeyChecker checkerA = checkerFactory.create(user);
+ PublicKeyChecker checkerA = checkerFactory.create(user, store);
assertProblems(
- checkerA.check(keyB.getPublicKey(), store), Status.BAD,
+ checkerA.check(keyB.getPublicKey()), Status.BAD,
"Key is expired",
"Key must contain a valid certification for one of the following"
+ " identities:\n"
@@ -306,9 +314,9 @@
+ " username:user");
// Checker for B, checking A.
- GerritPublicKeyChecker checkerB = checkerFactory.create(userB);
+ PublicKeyChecker checkerB = checkerFactory.create(userB, store);
assertProblems(
- checkerB.check(keyA.getPublicKey(), store), Status.BAD,
+ checkerB.check(keyA.getPublicKey()), Status.BAD,
"Key must contain a valid certification for one of the following"
+ " identities:\n"
+ " gerrit:userB\n"
@@ -325,9 +333,9 @@
TestKey keyA = add(keyA(), user);
TestKey keyB = add(keyB(), addUser("userB"));
- GerritPublicKeyChecker checker = checkerFactory.create(user);
+ PublicKeyChecker checker = checkerFactory.create(user, store);
assertProblems(
- checker.check(keyA.getPublicKey(), store), Status.OK,
+ checker.check(keyA.getPublicKey()), Status.OK,
"No path to a trusted key",
"Certification by " + keyToString(keyB.getPublicKey())
+ " is valid, but key is not trusted",
@@ -352,15 +360,16 @@
// This checker can check any key, so the only problems come from issues
// with the keys themselves, not having invalid user IDs.
- GerritPublicKeyChecker checker = checkerFactory.create();
- assertNoProblems(checker.check(keyA.getPublicKey(), store));
+ PublicKeyChecker checker = checkerFactory.create()
+ .setStore(store);
+ assertNoProblems(checker.check(keyA.getPublicKey()));
assertProblems(
- checker.check(keyB.getPublicKey(), store), Status.BAD,
+ checker.check(keyB.getPublicKey()), Status.BAD,
"Key is expired");
- assertNoProblems(checker.check(keyC.getPublicKey(), store));
- assertNoProblems(checker.check(keyD.getPublicKey(), store));
+ assertNoProblems(checker.check(keyC.getPublicKey()));
+ assertNoProblems(checker.check(keyD.getPublicKey()));
assertProblems(
- checker.check(keyE.getPublicKey(), store), Status.BAD,
+ checker.check(keyE.getPublicKey()), Status.BAD,
"Key is expired",
"No path to a trusted key");
}
@@ -382,8 +391,8 @@
keyRingB = PGPPublicKeyRing.insertPublicKey(keyRingB, keyB);
add(keyRingB, addUser("userB"));
- GerritPublicKeyChecker checkerA = checkerFactory.create(user);
- assertProblems(checkerA.check(keyA.getPublicKey(), store), Status.OK,
+ PublicKeyChecker checkerA = checkerFactory.create(user, store);
+ assertProblems(checkerA.check(keyA.getPublicKey()), Status.OK,
"No path to a trusted key",
"Certification by " + keyToString(keyB)
+ " is valid, but key is not trusted",
@@ -424,11 +433,13 @@
}
private void assertProblems(CheckResult result, Status expectedStatus,
- String... expectedProblems) throws Exception {
- checkArgument(expectedProblems.length > 0);
+ String first, String... rest) throws Exception {
+ List<String> expectedProblems = new ArrayList<>();
+ expectedProblems.add(first);
+ expectedProblems.addAll(Arrays.asList(rest));
assertThat(result.getStatus()).isEqualTo(expectedStatus);
assertThat(result.getProblems())
- .containsExactly((Object[]) expectedProblems)
+ .containsExactlyElementsIn(expectedProblems)
.inOrder();
}
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
index d1f5731..742bf1a 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
@@ -15,6 +15,14 @@
package com.google.gerrit.gpg;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.testutil.TestKeys.expiredKey;
+import static com.google.gerrit.gpg.testutil.TestKeys.keyRevokedByExpiredKeyAfterExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.keyRevokedByExpiredKeyBeforeExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.revokedCompromisedKey;
+import static com.google.gerrit.gpg.testutil.TestKeys.revokedNoLongerUsedKey;
+import static com.google.gerrit.gpg.testutil.TestKeys.selfRevokedKey;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithoutExpiration;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyA;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyB;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyC;
@@ -25,11 +33,15 @@
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyH;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyI;
import static com.google.gerrit.gpg.testutil.TestTrustKeys.keyJ;
+import static org.bouncycastle.bcpg.SignatureSubpacketTags.REVOCATION_KEY;
+import static org.bouncycastle.openpgp.PGPSignature.DIRECT_KEY;
import static org.junit.Assert.assertEquals;
import com.google.gerrit.gpg.testutil.TestKey;
-import com.google.gerrit.gpg.testutil.TestKeys;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPSignature;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -41,9 +53,15 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
public class PublicKeyCheckerTest {
@Rule
@@ -72,22 +90,33 @@
@Test
public void validKey() throws Exception {
- assertProblems(TestKeys.key1());
+ assertNoProblems(validKeyWithoutExpiration());
}
@Test
public void keyExpiringInFuture() throws Exception {
- assertProblems(TestKeys.key2());
+ TestKey k = validKeyWithExpiration();
+
+ PublicKeyChecker checker = new PublicKeyChecker()
+ .setStore(store);
+ assertNoProblems(checker, k);
+
+ checker.setEffectiveTime(parseDate("2015-07-10 12:00:00 -0400"));
+ assertNoProblems(checker, k);
+
+ checker.setEffectiveTime(parseDate("2075-07-10 12:00:00 -0400"));
+ assertProblems(checker, k, "Key is expired");
}
@Test
- public void expiredKey() throws Exception {
- assertProblems(TestKeys.key3(), "Key is expired");
+ public void expiredKeyIsExpired() throws Exception {
+ assertProblems(expiredKey(), "Key is expired");
}
@Test
- public void selfRevokedKey() throws Exception {
- assertProblems(TestKeys.key4(), "Key is revoked");
+ public void selfRevokedKeyIsRevoked() throws Exception {
+ assertProblems(selfRevokedKey(),
+ "Key is revoked (key material has been compromised)");
}
// Test keys specific to this test are at the bottom of this class. Each test
@@ -112,10 +141,10 @@
save();
PublicKeyChecker checker = newChecker(2, kb, kd);
- assertProblems(checker, ka);
+ assertNoProblems(checker, ka);
assertProblems(checker, kb, "Key is expired");
- assertProblems(checker, kc);
- assertProblems(checker, kd);
+ assertNoProblems(checker, kc);
+ assertNoProblems(checker, kd);
assertProblems(checker, ke, "Key is expired", "No path to a trusted key");
}
@@ -166,17 +195,132 @@
// J trusts I to a depth of 1, so I itself is valid, but I's certification
// of K is not valid.
- assertProblems(checker, ki);
+ assertNoProblems(checker, ki);
assertProblems(checker, kh,
"No path to a trusted key", notTrusted(ki));
}
- private PublicKeyChecker newChecker(int maxTrustDepth, TestKey... trusted) {
- List<Fingerprint> fps = new ArrayList<>(trusted.length);
- for (TestKey k : trusted) {
- fps.add(new Fingerprint(k.getPublicKey().getFingerprint()));
+ @Test
+ public void revokedKeyDueToCompromise() throws Exception {
+ TestKey k = add(revokedCompromisedKey());
+ add(validKeyWithoutExpiration());
+ save();
+
+ assertProblems(k,
+ "Key is revoked (key material has been compromised):"
+ + " test6 compromised");
+
+ PGPPublicKeyRing kr = removeRevokers(k.getPublicKeyRing());
+ store.add(kr);
+ save();
+
+ // Key no longer specified as revoker.
+ assertNoProblems(kr.getPublicKey());
+ }
+
+ @Test
+ public void revokedKeyDueToCompromiseRevokesKeyRetroactively()
+ throws Exception {
+ TestKey k = add(revokedCompromisedKey());
+ add(validKeyWithoutExpiration());
+ save();
+
+ String problem =
+ "Key is revoked (key material has been compromised): test6 compromised";
+ assertProblems(k, problem);
+
+ SimpleDateFormat df = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
+ PublicKeyChecker checker = new PublicKeyChecker()
+ .setStore(store)
+ .setEffectiveTime(df.parse("2010-01-01 12:00:00"));
+ assertProblems(checker, k, problem);
+ }
+
+ @Test
+ public void revokedByKeyNotPresentInStore() throws Exception {
+ TestKey k = add(revokedCompromisedKey());
+ save();
+
+ assertProblems(k,
+ "Key is revoked (key material has been compromised):"
+ + " test6 compromised");
+ }
+
+ @Test
+ public void revokedKeyDueToNoLongerBeingUsed() throws Exception {
+ TestKey k = add(revokedNoLongerUsedKey());
+ add(validKeyWithoutExpiration());
+ save();
+
+ assertProblems(k,
+ "Key is revoked (retired and no longer valid): test7 not used");
+ }
+
+ @Test
+ public void revokedKeyDueToNoLongerBeingUsedDoesNotRevokeKeyRetroactively()
+ throws Exception {
+ TestKey k = add(revokedNoLongerUsedKey());
+ add(validKeyWithoutExpiration());
+ save();
+
+ assertProblems(k,
+ "Key is revoked (retired and no longer valid): test7 not used");
+
+ PublicKeyChecker checker = new PublicKeyChecker()
+ .setStore(store)
+ .setEffectiveTime(parseDate("2010-01-01 12:00:00 -0400"));
+ assertNoProblems(checker, k);
+ }
+
+ @Test
+ public void keyRevokedByExpiredKeyAfterExpirationIsNotRevoked()
+ throws Exception {
+ TestKey k = add(keyRevokedByExpiredKeyAfterExpiration());
+ add(expiredKey());
+ save();
+
+ PublicKeyChecker checker = new PublicKeyChecker().setStore(store);
+ assertNoProblems(checker, k);
+ }
+
+ @Test
+ public void keyRevokedByExpiredKeyBeforeExpirationIsRevoked()
+ throws Exception {
+ TestKey k = add(keyRevokedByExpiredKeyBeforeExpiration());
+ add(expiredKey());
+ save();
+
+ PublicKeyChecker checker = new PublicKeyChecker().setStore(store);
+ assertProblems(checker, k,
+ "Key is revoked (retired and no longer valid): test9 not used");
+
+ // Set time between key creation and revocation.
+ checker.setEffectiveTime(parseDate("2005-08-01 13:00:00 -0400"));
+ assertNoProblems(checker, k);
+ }
+
+ private PGPPublicKeyRing removeRevokers(PGPPublicKeyRing kr) {
+ PGPPublicKey k = kr.getPublicKey();
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> sigs = k.getSignaturesOfType(DIRECT_KEY);
+ while (sigs.hasNext()) {
+ PGPSignature sig = sigs.next();
+ if (sig.getHashedSubPackets().hasSubpacket(REVOCATION_KEY)) {
+ k = PGPPublicKey.removeCertification(k, sig);
+ }
}
- return new PublicKeyChecker(maxTrustDepth, fps);
+ return PGPPublicKeyRing.insertPublicKey(kr, k);
+ }
+
+ private PublicKeyChecker newChecker(int maxTrustDepth, TestKey... trusted) {
+ Map<Long, Fingerprint> fps = new HashMap<>();
+ for (TestKey k : trusted) {
+ Fingerprint fp = new Fingerprint(k.getPublicKey().getFingerprint());
+ fps.put(fp.getId(), fp);
+ }
+ return new PublicKeyChecker()
+ .enableTrust(maxTrustDepth, fps)
+ .setStore(store);
}
private TestKey add(TestKey k) {
@@ -201,18 +345,53 @@
}
private void assertProblems(PublicKeyChecker checker, TestKey k,
- String... expected) {
- CheckResult result = checker.check(k.getPublicKey(), store);
- assertEquals(Arrays.asList(expected), result.getProblems());
+ String first, String... rest) {
+ CheckResult result = checker.setStore(store)
+ .check(k.getPublicKey());
+ assertEquals(list(first, rest), result.getProblems());
}
- private void assertProblems(TestKey tk, String... expected) throws Exception {
- CheckResult result = new PublicKeyChecker().check(tk.getPublicKey(), store);
- assertEquals(Arrays.asList(expected), result.getProblems());
+ private void assertNoProblems(PublicKeyChecker checker, TestKey k) {
+ CheckResult result = checker.setStore(store)
+ .check(k.getPublicKey());
+ assertEquals(Collections.emptyList(), result.getProblems());
+ }
+
+ private void assertProblems(TestKey tk, String first, String... rest) {
+ assertProblems(tk.getPublicKey(), first, rest);
+ }
+
+ private void assertNoProblems(TestKey tk) {
+ assertNoProblems(tk.getPublicKey());
+ }
+
+ private void assertProblems(PGPPublicKey k, String first, String... rest) {
+ CheckResult result = new PublicKeyChecker()
+ .setStore(store)
+ .check(k);
+ assertEquals(list(first, rest), result.getProblems());
+ }
+
+ private void assertNoProblems(PGPPublicKey k) {
+ CheckResult result = new PublicKeyChecker()
+ .setStore(store)
+ .check(k);
+ assertEquals(Collections.emptyList(), result.getProblems());
}
private static String notTrusted(TestKey k) {
return "Certification by " + keyToString(k.getPublicKey())
+ " is valid, but key is not trusted";
}
+
+ private static Date parseDate(String str) throws Exception {
+ return new SimpleDateFormat("YYYY-MM-dd HH:mm:ss Z").parse(str);
+ }
+
+ private static List<String> list(String first, String[] rest) {
+ List<String> all = new ArrayList<>();
+ all.add(first);
+ all.addAll(Arrays.asList(rest));
+ return all;
+ }
}
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java
index b1e6404..9c0a908 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java
@@ -18,13 +18,15 @@
import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
import static com.google.gerrit.gpg.PublicKeyStore.keyObjectId;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithSecondUserId;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithoutExpiration;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.google.gerrit.gpg.testutil.TestKey;
-import com.google.gerrit.gpg.testutil.TestKeys;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
@@ -61,13 +63,13 @@
@Test
public void testKeyIdToString() throws Exception {
- PGPPublicKey key = TestKeys.key1().getPublicKey();
+ PGPPublicKey key = validKeyWithoutExpiration().getPublicKey();
assertEquals("46328A8C", keyIdToString(key.getKeyID()));
}
@Test
public void testKeyToString() throws Exception {
- PGPPublicKey key = TestKeys.key1().getPublicKey();
+ PGPPublicKey key = validKeyWithoutExpiration().getPublicKey();
assertEquals("46328A8C Testuser One <test1@example.com>"
+ " (04AE A7ED 2F82 1133 E5B1 28D1 ED06 25DC 4632 8A8C)",
keyToString(key));
@@ -75,7 +77,7 @@
@Test
public void testKeyObjectId() throws Exception {
- PGPPublicKey key = TestKeys.key1().getPublicKey();
+ PGPPublicKey key = validKeyWithoutExpiration().getPublicKey();
String objId = keyObjectId(key.getKeyID()).name();
assertEquals("ed0625dc46328a8c000000000000000000000000", objId);
assertEquals(keyIdToString(key.getKeyID()).toLowerCase(),
@@ -84,13 +86,13 @@
@Test
public void testGet() throws Exception {
- TestKey key1 = TestKeys.key1();
+ TestKey key1 = validKeyWithoutExpiration();
tr.branch(REFS_GPG_KEYS)
.commit()
.add(keyObjectId(key1.getKeyId()).name(),
key1.getPublicKeyArmored())
.create();
- TestKey key2 = TestKeys.key2();
+ TestKey key2 = validKeyWithExpiration();
tr.branch(REFS_GPG_KEYS)
.commit()
.add(keyObjectId(key2.getKeyId()).name(),
@@ -103,8 +105,8 @@
@Test
public void testGetMultiple() throws Exception {
- TestKey key1 = TestKeys.key1();
- TestKey key2 = TestKeys.key2();
+ TestKey key1 = validKeyWithoutExpiration();
+ TestKey key2 = validKeyWithExpiration();
tr.branch(REFS_GPG_KEYS)
.commit()
.add(keyObjectId(key1.getKeyId()).name(),
@@ -117,8 +119,8 @@
@Test
public void save() throws Exception {
- TestKey key1 = TestKeys.key1();
- TestKey key2 = TestKeys.key2();
+ TestKey key1 = validKeyWithoutExpiration();
+ TestKey key2 = validKeyWithExpiration();
store.add(key1.getPublicKeyRing());
store.add(key2.getPublicKeyRing());
@@ -130,8 +132,8 @@
@Test
public void saveAppendsToExistingList() throws Exception {
- TestKey key1 = TestKeys.key1();
- TestKey key2 = TestKeys.key2();
+ TestKey key1 = validKeyWithoutExpiration();
+ TestKey key2 = validKeyWithExpiration();
tr.branch(REFS_GPG_KEYS)
.commit()
// Mismatched for this key ID, but we can still read it out.
@@ -161,7 +163,7 @@
@Test
public void updateExisting() throws Exception {
- TestKey key5 = TestKeys.key5();
+ TestKey key5 = validKeyWithSecondUserId();
PGPPublicKeyRing keyRing = key5.getPublicKeyRing();
PGPPublicKey key = keyRing.getPublicKey();
store.add(keyRing);
@@ -185,7 +187,7 @@
@Test
public void remove() throws Exception {
- TestKey key1 = TestKeys.key1();
+ TestKey key1 = validKeyWithoutExpiration();
store.add(key1.getPublicKeyRing());
assertEquals(RefUpdate.Result.NEW, store.save(newCommitBuilder()));
assertKeys(key1.getKeyId(), key1);
@@ -197,11 +199,11 @@
@Test
public void removeNonexisting() throws Exception {
- TestKey key1 = TestKeys.key1();
+ TestKey key1 = validKeyWithoutExpiration();
store.add(key1.getPublicKeyRing());
assertEquals(RefUpdate.Result.NEW, store.save(newCommitBuilder()));
- TestKey key2 = TestKeys.key2();
+ TestKey key2 = validKeyWithExpiration();
store.remove(key2.getPublicKey().getFingerprint());
assertEquals(RefUpdate.Result.NO_CHANGE, store.save(newCommitBuilder()));
assertKeys(key1.getKeyId(), key1);
@@ -209,7 +211,7 @@
@Test
public void addThenRemove() throws Exception {
- TestKey key1 = TestKeys.key1();
+ TestKey key1 = validKeyWithoutExpiration();
store.add(key1.getPublicKeyRing());
store.remove(key1.getPublicKey().getFingerprint());
assertEquals(RefUpdate.Result.NO_CHANGE, store.save(newCommitBuilder()));
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java
index 9bee0ef..ee07d55 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java
@@ -14,24 +14,28 @@
package com.google.gerrit.gpg;
-import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.testutil.TestKeys.expiredKey;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithExpiration;
+import static com.google.gerrit.gpg.testutil.TestKeys.validKeyWithoutExpiration;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import com.google.gerrit.gpg.testutil.TestKey;
-import com.google.gerrit.gpg.testutil.TestKeys;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.PushCertificate;
import org.eclipse.jgit.transport.PushCertificateIdent;
@@ -44,25 +48,34 @@
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
public class PushCertificateCheckerTest {
- private TestRepository<?> tr;
+ private InMemoryRepository repo;
+ private PublicKeyStore store;
private SignedPushConfig signedPushConfig;
private PushCertificateChecker checker;
@Before
public void setUp() throws Exception {
- TestKey key1 = TestKeys.key1();
- TestKey key3 = TestKeys.key3();
- tr = new TestRepository<>(new InMemoryRepository(
- new DfsRepositoryDescription("repo")));
- tr.branch(REFS_GPG_KEYS).commit()
- .add(PublicKeyStore.keyObjectId(key1.getPublicKey().getKeyID()).name(),
- key1.getPublicKeyArmored())
- .add(PublicKeyStore.keyObjectId(key3.getPublicKey().getKeyID()).name(),
- key3.getPublicKeyArmored())
- .create();
+ TestKey key1 = validKeyWithoutExpiration();
+ TestKey key3 = expiredKey();
+ repo = new InMemoryRepository(new DfsRepositoryDescription("repo"));
+ store = new PublicKeyStore(repo);
+ store.add(key1.getPublicKeyRing());
+ store.add(key3.getPublicKeyRing());
+
+ PersonIdent ident = new PersonIdent("A U Thor", "author@example.com");
+ CommitBuilder cb = new CommitBuilder();
+ cb.setAuthor(ident);
+ cb.setCommitter(ident);
+ assertEquals(RefUpdate.Result.NEW, store.save(cb));
+
signedPushConfig = new SignedPushConfig();
signedPushConfig.setCertNonceSeed("sekret");
signedPushConfig.setCertNonceSlopLimit(60 * 24);
@@ -70,41 +83,45 @@
}
private PushCertificateChecker newChecker(boolean checkNonce) {
- return new PushCertificateChecker(new PublicKeyChecker(), checkNonce) {
+ PublicKeyChecker keyChecker = new PublicKeyChecker().setStore(store);
+ return new PushCertificateChecker(keyChecker) {
@Override
protected Repository getRepository() {
- return tr.getRepository();
+ return repo;
}
@Override
protected boolean shouldClose(Repository repo) {
return false;
}
- };
+ }.setCheckNonce(checkNonce);
}
@Test
public void validCert() throws Exception {
- PushCertificate cert = newSignedCert(validNonce(), TestKeys.key1());
- assertProblems(cert);
+ PushCertificate cert =
+ newSignedCert(validNonce(), validKeyWithoutExpiration());
+ assertNoProblems(cert);
}
@Test
public void invalidNonce() throws Exception {
- PushCertificate cert = newSignedCert("invalid-nonce", TestKeys.key1());
+ PushCertificate cert =
+ newSignedCert("invalid-nonce", validKeyWithoutExpiration());
assertProblems(cert, "Invalid nonce");
}
@Test
public void invalidNonceNotChecked() throws Exception {
checker = newChecker(false);
- PushCertificate cert = newSignedCert("invalid-nonce", TestKeys.key1());
- assertProblems(cert);
+ PushCertificate cert =
+ newSignedCert("invalid-nonce", validKeyWithoutExpiration());
+ assertNoProblems(cert);
}
@Test
public void missingKey() throws Exception {
- TestKey key2 = TestKeys.key2();
+ TestKey key2 = validKeyWithExpiration();
PushCertificate cert = newSignedCert(validNonce(), key2);
assertProblems(cert,
"No public keys found for key ID " + keyIdToString(key2.getKeyId()));
@@ -112,20 +129,34 @@
@Test
public void invalidKey() throws Exception {
- TestKey key3 = TestKeys.key3();
+ TestKey key3 = expiredKey();
PushCertificate cert = newSignedCert(validNonce(), key3);
assertProblems(cert,
"Invalid public key " + keyToString(key3.getPublicKey())
+ ":\n Key is expired");
}
+ @Test
+ public void signatureByExpiredKeyBeforeExpiration() throws Exception {
+ TestKey key3 = expiredKey();
+ Date now = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss Z")
+ .parse("2005-07-10 12:00:00 -0400");
+ PushCertificate cert = newSignedCert(validNonce(), key3, now);
+ assertNoProblems(cert);
+ }
+
private String validNonce() {
return signedPushConfig.getNonceGenerator()
- .createNonce(tr.getRepository(), System.currentTimeMillis() / 1000);
+ .createNonce(repo, System.currentTimeMillis() / 1000);
}
private PushCertificate newSignedCert(String nonce, TestKey signingKey)
throws Exception {
+ return newSignedCert(nonce, signingKey, null);
+ }
+
+ private PushCertificate newSignedCert(String nonce, TestKey signingKey,
+ Date now) throws Exception {
PushCertificateIdent ident = new PushCertificateIdent(
signingKey.getFirstUserId(), System.currentTimeMillis(), -7 * 60);
String payload = "certificate version 0.1\n"
@@ -139,6 +170,14 @@
PGPSignatureGenerator gen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(
signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1));
+
+ if (now != null) {
+ PGPSignatureSubpacketGenerator subGen =
+ new PGPSignatureSubpacketGenerator();
+ subGen.setSignatureCreationTime(false, now);
+ gen.setHashedSubpackets(subGen.generate());
+ }
+
gen.init(PGPSignature.BINARY_DOCUMENT, signingKey.getPrivateKey());
gen.update(payload.getBytes(UTF_8));
PGPSignature sig = gen.generate();
@@ -153,13 +192,21 @@
Reader reader =
new InputStreamReader(new ByteArrayInputStream(cert.getBytes(UTF_8)));
PushCertificateParser parser =
- new PushCertificateParser(tr.getRepository(), signedPushConfig);
+ new PushCertificateParser(repo, signedPushConfig);
return parser.parse(reader);
}
- private void assertProblems(PushCertificate cert, String... expected)
- throws Exception {
+ private void assertProblems(PushCertificate cert, String first,
+ String... rest) throws Exception {
+ List<String> expected = new ArrayList<>();
+ expected.add(first);
+ expected.addAll(Arrays.asList(rest));
CheckResult result = checker.check(cert).getCheckResult();
- assertEquals(Arrays.asList(expected), result.getProblems());
+ assertEquals(expected, result.getProblems());
+ }
+
+ private void assertNoProblems(PushCertificate cert) {
+ CheckResult result = checker.check(cert).getCheckResult();
+ assertEquals(Collections.emptyList(), result.getProblems());
}
}
diff --git a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKeys.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKeys.java
index 3abe689..ad944c5 100644
--- a/gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKeys.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKeys.java
@@ -20,9 +20,9 @@
public class TestKeys {
public static ImmutableList<TestKey> allValidKeys() {
return ImmutableList.of(
- TestKeys.key1(),
- TestKeys.key2(),
- TestKeys.key5());
+ validKeyWithoutExpiration(),
+ validKeyWithExpiration(),
+ validKeyWithSecondUserId());
}
/**
@@ -35,7 +35,7 @@
* sub 2048R/F0AF69C0 2015-07-08
* </pre>
*/
- public static TestKey key1() {
+ public static TestKey validKeyWithoutExpiration() {
return new TestKey("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ "Version: GnuPG v1\n"
+ "\n"
@@ -135,7 +135,7 @@
* sub 2048R/46D4F204 2015-07-08 [expires: 2065-06-25]
* </pre>
*/
- public static final TestKey key2() {
+ public static final TestKey validKeyWithExpiration() {
return new TestKey(
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ "Version: GnuPG v1\n"
@@ -236,7 +236,7 @@
* uid Testuser Three <test3@example.com>
* </pre>
*/
- public static final TestKey key3() {
+ public static final TestKey expiredKey() {
return new TestKey(
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ "Version: GnuPG v1\n"
@@ -337,7 +337,7 @@
* uid Testuser Four <test4@example.com>
* </pre>
*/
- public static final TestKey key4() {
+ public static final TestKey selfRevokedKey() {
return new TestKey(
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ "Version: GnuPG v1\n"
@@ -445,7 +445,7 @@
* sub 2048R/C781A9E3 2015-07-30
* </pre>
*/
- public static TestKey key5() {
+ public static TestKey validKeyWithSecondUserId() {
return new TestKey("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ "Version: GnuPG v1\n"
+ "\n"
@@ -542,6 +542,487 @@
+ "-----END PGP PRIVATE KEY BLOCK-----\n");
}
- // TODO(dborowitz): Figure out how to get gpg to revoke a key for someone
- // else.
+ /**
+ * A key revoked by a valid key, due to key compromise.
+ * <p>
+ * Revoked by {@link #validKeyWithoutExpiration()}.
+ *
+ * <pre>
+ * pub 2048R/3434B39F 2015-10-20 [revoked: 2015-10-20]
+ * Key fingerprint = 931F 047D 7D01 DDEF 367A 8D90 8C4F D28E 3434 B39F
+ * uid Testuser Six <test6@example.com>
+ * </pre>
+ */
+ public static TestKey revokedCompromisedKey() throws Exception {
+ return new TestKey("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "tULvIEIjNNP8Qizfc4u2O8h7HP2b3yYSrp9MMQ==\n"
+ + "=Dxr7\n"
+ + "-----END PGP PUBLIC KEY BLOCK-----\n",
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "i7Y7yHsc/ZvfJhKun0wx\n"
+ + "=M/kw\n"
+ + "-----END PGP PRIVATE KEY BLOCK-----\n");
+ }
+
+ /**
+ * A key revoked by a valid key, due to no longer being used.
+ * <p>
+ * Revoked by {@link #validKeyWithoutExpiration()}.
+ *
+ * <pre>
+ * pub 2048R/3D6C52D0 2015-10-20 [revoked: 2015-10-20]
+ * Key fingerprint = 32DB 6C31 2ED7 A98D 11B2 43EA FAD2 ABE2 3D6C 52D0
+ * uid Testuser Seven <test7@example.com>
+ * </pre>
+ */
+ public static TestKey revokedNoLongerUsedKey() throws Exception {
+ return new TestKey("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "+B6paw/s8P7XCWznTBRdlFWWgrhcpzQ8fefC\n"
+ + "=CHer\n"
+ + "-----END PGP PUBLIC KEY BLOCK-----\n",
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "bOdMFF2UVZaCuFynNDx958I=\n"
+ + "=aoJv\n"
+ + "-----END PGP PRIVATE KEY BLOCK-----\n");
+ }
+
+ /**
+ * Key revoked by an expired key, after that key's expiration.
+ * <p>
+ * Revoked by {@link #expiredKey()}.
+ *
+ * <pre>
+ * pub 2048R/78BF7D7E 2005-08-01 [revoked: 2015-10-20]
+ * Key fingerprint = 916F AB22 5BE7 7585 F59A 994C 001A DF8B 78BF 7D7E
+ * uid Testuser Eight <test8@example.com>
+ * </pre>
+ */
+ public static TestKey keyRevokedByExpiredKeyAfterExpiration() throws Exception {
+ return new TestKey("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "0d0PyzZT3676mQwmFoebMR9vACAeHjvDxD4F\n"
+ + "=ihWb\n"
+ + "-----END PGP PUBLIC KEY BLOCK-----\n",
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "DCYWh5sxH28AIB4eO8PEPgU=\n"
+ + "=cSfw\n"
+ + "-----END PGP PRIVATE KEY BLOCK-----\n");
+ }
+
+ /**
+ * Key revoked by an expired key, before that key's expiration.
+ * <p>
+ * Revoked by {@link #expiredKey()}.
+ *
+ * <pre>
+ * pub 2048R/C43BF2E1 2005-08-01 [revoked: 2005-08-01]
+ * Key fingerprint = 916D 6AD6 36A5 CBA6 B5A6 7274 6040 8661 C43B F2E1
+ * uid Testuser Nine <test9@example.com>
+ * </pre>
+ */
+ public static TestKey keyRevokedByExpiredKeyBeforeExpiration() throws Exception {
+ return new TestKey("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "REW68wsnWjwTtrPG8bqo6cCsOzqGYVt81eU=\n"
+ + "=FnZg\n"
+ + "-----END PGP PUBLIC KEY BLOCK-----\n",
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
+ + "Version: GnuPG v1\n"
+ + "\n"
+ + "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\n"
+ + "s8bxuqjpwKw7OoZhW3zV5Q==\n"
+ + "=JxsF\n"
+ + "-----END PGP PRIVATE KEY BLOCK-----\n");
+ }
}
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
index 5da8b1e..4b6e7e4 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
@@ -274,6 +274,7 @@
try {
t.setText(getText());
content.add(t);
+ t.setFocus(true);
t.selectAll();
boolean ok = execCommand("copy");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
index 08fe75d..c77b71f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -109,17 +109,28 @@
return new AccountFormatter(Gerrit.info().user().anonymousCowardName());
}
+ /** The returned format string doesn't contain any +/- sign. */
+ public static String formatAbsBytes(long bytes) {
+ return formatBytes(bytes, true);
+ }
+
public static String formatBytes(long bytes) {
+ return formatBytes(bytes, false);
+ }
+
+ private static String formatBytes(long bytes, boolean abs) {
+ bytes = abs ? Math.abs(bytes) : bytes;
+
if (bytes == 0) {
- return "+/- 0 B";
+ return abs ? "0 B" : "+/- 0 B";
}
if (Math.abs(bytes) < 1024) {
- return (bytes > 0 ? "+" : "") + bytes + " B";
+ return (bytes > 0 && !abs ? "+" : "") + bytes + " B";
}
int exp = (int) (Math.log(Math.abs(bytes)) / Math.log(1024));
- return (bytes > 0 ? "+" : "")
+ return (bytes > 0 && !abs ? "+" : "")
+ NumberFormat.getFormat("#.0").format(bytes / Math.pow(1024, exp))
+ " " + "KMGTPE".charAt(exp - 1) + "iB";
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index cd53b8e..ee67116 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -47,9 +47,9 @@
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.HostPageData;
import com.google.gerrit.common.data.SystemInfoService;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GerritTopMenu;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.EntryPoint;
@@ -119,7 +119,7 @@
private static String docUrl;
private static HostPageData.Theme myTheme;
private static String defaultScreenToken;
- private static AccountDiffPreference myAccountDiffPref;
+ private static DiffPreferencesInfo myAccountDiffPref;
private static EditPreferencesInfo editPrefs;
private static String xGerritAuth;
private static boolean isNoteDbEnabled;
@@ -321,12 +321,12 @@
return myPrefs;
}
- /** @return the currently signed in users's diff preferences; null if no diff preferences defined for the account */
- public static AccountDiffPreference getAccountDiffPreference() {
+ /** @return the currently signed in users's diff preferences, or default values */
+ public static DiffPreferencesInfo getDiffPreferences() {
return myAccountDiffPref;
}
- public static void setAccountDiffPreference(AccountDiffPreference accountDiffPref) {
+ public static void setDiffPreferences(DiffPreferencesInfo accountDiffPref) {
myAccountDiffPref = accountDiffPref;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
index 54541e4..07d45e9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
@@ -14,55 +14,58 @@
package com.google.gerrit.client.account;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.Theme;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gwt.core.client.JavaScriptObject;
public class DiffPreferences extends JavaScriptObject {
- public static DiffPreferences create(AccountDiffPreference in) {
+ public static DiffPreferences create(DiffPreferencesInfo in) {
DiffPreferences p = createObject().cast();
- if (in == null) {
- in = AccountDiffPreference.createDefault(null);
- }
- p.ignoreWhitespace(in.getIgnoreWhitespace());
- p.tabSize(in.getTabSize());
- p.lineLength(in.getLineLength());
- p.context(in.getContext());
- p.intralineDifference(in.isIntralineDifference());
- p.showLineEndings(in.isShowLineEndings());
- p.showTabs(in.isShowTabs());
- p.showWhitespaceErrors(in.isShowWhitespaceErrors());
- p.syntaxHighlighting(in.isSyntaxHighlighting());
- p.hideTopMenu(in.isHideTopMenu());
- p.autoHideDiffTableHeader(in.isAutoHideDiffTableHeader());
- p.hideLineNumbers(in.isHideLineNumbers());
- p.expandAllComments(in.isExpandAllComments());
- p.manualReview(in.isManualReview());
- p.renderEntireFile(in.isRenderEntireFile());
- p.theme(in.getTheme());
- p.hideEmptyPane(in.isHideEmptyPane());
+ p.ignoreWhitespace(in.ignoreWhitespace);
+ p.tabSize(in.tabSize);
+ p.lineLength(in.lineLength);
+ p.context(in.context);
+ p.intralineDifference(in.intralineDifference);
+ p.showLineEndings(in.showLineEndings);
+ p.showTabs(in.showTabs);
+ p.showWhitespaceErrors(in.showWhitespaceErrors);
+ p.syntaxHighlighting(in.syntaxHighlighting);
+ p.hideTopMenu(in.hideTopMenu);
+ p.autoHideDiffTableHeader(in.autoHideDiffTableHeader);
+ p.hideLineNumbers(in.hideLineNumbers);
+ p.expandAllComments(in.expandAllComments);
+ p.manualReview(in.manualReview);
+ p.renderEntireFile(in.renderEntireFile);
+ p.theme(in.theme);
+ p.hideEmptyPane(in.hideEmptyPane);
+ p.retainHeader(in.retainHeader);
+ p.skipUncommented(in.skipUncommented);
+ p.skipDeleted(in.skipDeleted);
return p;
}
- public final void copyTo(AccountDiffPreference p) {
- p.setIgnoreWhitespace(ignoreWhitespace());
- p.setTabSize(tabSize());
- p.setLineLength(lineLength());
- p.setContext((short)context());
- p.setIntralineDifference(intralineDifference());
- p.setShowLineEndings(showLineEndings());
- p.setShowTabs(showTabs());
- p.setShowWhitespaceErrors(showWhitespaceErrors());
- p.setSyntaxHighlighting(syntaxHighlighting());
- p.setHideTopMenu(hideTopMenu());
- p.setAutoHideDiffTableHeader(autoHideDiffTableHeader());
- p.setHideLineNumbers(hideLineNumbers());
- p.setExpandAllComments(expandAllComments());
- p.setManualReview(manualReview());
- p.setRenderEntireFile(renderEntireFile());
- p.setTheme(theme());
- p.setHideEmptyPane(hideEmptyPane());
+ public final void copyTo(DiffPreferencesInfo p) {
+ p.context = context();
+ p.tabSize = tabSize();
+ p.lineLength = lineLength();
+ p.expandAllComments = expandAllComments();
+ p.intralineDifference = intralineDifference();
+ p.manualReview = manualReview();
+ p.retainHeader = retainHeader();
+ p.showLineEndings = showLineEndings();
+ p.showTabs = showTabs();
+ p.showWhitespaceErrors = showWhitespaceErrors();
+ p.skipDeleted = skipDeleted();
+ p.skipUncommented = skipUncommented();
+ p.syntaxHighlighting = syntaxHighlighting();
+ p.hideTopMenu = hideTopMenu();
+ p.autoHideDiffTableHeader = autoHideDiffTableHeader();
+ p.hideLineNumbers = hideLineNumbers();
+ p.renderEntireFile = renderEntireFile();
+ p.hideEmptyPane = hideEmptyPane();
+ p.theme = theme();
+ p.ignoreWhitespace = ignoreWhitespace();
}
public final void ignoreWhitespace(Whitespace i) {
@@ -122,6 +125,9 @@
public final native void manualReview(boolean r) /*-{ this.manual_review = r }-*/;
public final native void renderEntireFile(boolean r) /*-{ this.render_entire_file = r }-*/;
public final native void hideEmptyPane(boolean s) /*-{ this.hide_empty_pane = s }-*/;
+ public final native void retainHeader(boolean r) /*-{ this.retain_header = r }-*/;
+ public final native void skipUncommented(boolean s) /*-{ this.skip_uncommented = s }-*/;
+ public final native void skipDeleted(boolean s) /*-{ this.skip_deleted = s }-*/;
public final native boolean intralineDifference() /*-{ return this.intraline_difference || false }-*/;
public final native boolean showLineEndings() /*-{ return this.show_line_endings || false }-*/;
public final native boolean showTabs() /*-{ return this.show_tabs || false }-*/;
@@ -134,6 +140,9 @@
public final native boolean manualReview() /*-{ return this.manual_review || false }-*/;
public final native boolean renderEntireFile() /*-{ return this.render_entire_file || false }-*/;
public final native boolean hideEmptyPane() /*-{ return this.hide_empty_pane || false }-*/;
+ public final native boolean retainHeader() /*-{ return this.retain_header || false }-*/;
+ public final native boolean skipUncommented() /*-{ return this.skip_uncommented || false }-*/;
+ public final native boolean skipDeleted() /*-{ return this.skip_deleted || false }-*/;
private final native void setThemeRaw(String i) /*-{ this.theme = i }-*/;
private final native void setIgnoreWhitespaceRaw(String i) /*-{ this.ignore_whitespace = i }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java
index 73d4ca0..a721441 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java
@@ -25,7 +25,7 @@
super.onInitUI();
PreferencesBox pb = new PreferencesBox(null);
- pb.set(DiffPreferences.create(Gerrit.getAccountDiffPreference()));
+ pb.set(DiffPreferences.create(Gerrit.getDiffPreferences()));
FlowPanel p = new FlowPanel();
p.setStyleName(pb.getStyle().dialog());
p.add(pb);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java
index f83e9ec..f6ac36a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java
@@ -36,6 +36,7 @@
protected void preDisplay(GroupList result) {
groups.display(result);
groups.finishDisplay();
- }});
+ }
+ });
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
index aa72300..af0b1f5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -163,7 +163,8 @@
@Override
public void execute() {
name.setFocus(true);
- }});
+ }
+ });
}
void enableEditing() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index f0928040..511be5f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -43,6 +43,7 @@
String useSignedOffBy();
String createNewChangeForAllNotInTarget();
String enableSignedPush();
+ String requireSignedPush();
String requireChangeID();
String headingMaxObjectSizeLimit();
String headingGroupOptions();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index da260de..7a8888c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -25,6 +25,7 @@
useSignedOffBy = Require <code>Signed-off-by</code> in commit message
createNewChangeForAllNotInTarget = Create a new change for every commit not in the target branch
enableSignedPush = Enable signed push
+requireSignedPush = Require signed push
requireChangeID = Require <code>Change-Id</code> in commit message
headingMaxObjectSizeLimit = Maximum Git object size limit
headingGroupOptions = Group Options
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
index eefd199..c6bd1d1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -84,6 +84,7 @@
private ListBox contentMerge;
private ListBox newChangeForAllNotInTarget;
private ListBox enableSignedPush;
+ private ListBox requireSignedPush;
private NpTextBox maxObjectSizeLimit;
private Label effectiveMaxObjectSizeLimit;
private Map<String, Map<String, HasEnabled>> pluginConfigWidgets;
@@ -247,6 +248,9 @@
enableSignedPush = newInheritedBooleanBox();
saveEnabler.listenTo(enableSignedPush);
grid.add(Util.C.enableSignedPush(), enableSignedPush);
+ requireSignedPush = newInheritedBooleanBox();
+ saveEnabler.listenTo(requireSignedPush);
+ grid.add(Util.C.requireSignedPush(), requireSignedPush);
}
maxObjectSizeLimit = new NpTextBox();
@@ -326,6 +330,9 @@
}
private void setBool(ListBox box, InheritedBooleanInfo inheritedBoolean) {
+ if (box == null) {
+ return;
+ }
int inheritedIndex = -1;
for (int i = 0; i < box.getItemCount(); i++) {
if (box.getValue(i).startsWith(InheritableBoolean.INHERIT.name())) {
@@ -372,8 +379,9 @@
setBool(contentMerge, result.useContentMerge());
setBool(newChangeForAllNotInTarget, result.createNewChangeForAllNotInTarget());
setBool(requireChangeID, result.requireChangeId());
- if (enableSignedPush != null) {
+ if (Gerrit.info().receive().enableSignedPush()) {
setBool(enableSignedPush, result.enableSignedPush());
+ setBool(requireSignedPush, result.requireSignedPush());
}
setSubmitType(result.submitType());
setState(result.state());
@@ -644,12 +652,14 @@
private void doSave() {
enableForm(false);
saveProject.setEnabled(false);
- InheritableBoolean sp = enableSignedPush != null
+ InheritableBoolean esp = enableSignedPush != null
? getBool(enableSignedPush) : null;
+ InheritableBoolean rsp = requireSignedPush != null
+ ? getBool(requireSignedPush) : null;
ProjectApi.setConfig(getProjectKey(), descTxt.getText().trim(),
getBool(contributorAgreements), getBool(contentMerge),
getBool(signedOffBy), getBool(newChangeForAllNotInTarget), getBool(requireChangeID),
- sp,
+ esp, rsp,
maxObjectSizeLimit.getText().trim(),
SubmitType.valueOf(submitType.getValue(submitType.getSelectedIndex())),
ProjectState.valueOf(state.getValue(state.getSelectedIndex())),
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
index 932cda4..d1ca517 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
@@ -14,8 +14,10 @@
package com.google.gerrit.client.change;
+import static com.google.gerrit.client.FormatUtil.formatAbsBytes;
+import static com.google.gerrit.client.FormatUtil.formatBytes;
+
import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.changes.ChangeApi;
@@ -458,8 +460,12 @@
private ProgressBar meter;
private String lastPath = "";
+ private boolean hasBinaryFile;
+ private boolean hasNonBinaryFile;
private int inserted;
private int deleted;
+ private long bytesInserted;
+ private long bytesDeleted;
private DisplayCommand(NativeMap<FileInfo> map,
JsArray<FileInfo> list,
@@ -514,11 +520,23 @@
private void computeInsertedDeleted() {
inserted = 0;
deleted = 0;
+ bytesInserted = 0;
+ bytesDeleted = 0;
for (int i = 0; i < list.length(); i++) {
FileInfo info = list.get(i);
- if (!Patch.COMMIT_MSG.equals(info.path()) && !info.binary()) {
- inserted += info.linesInserted();
- deleted += info.linesDeleted();
+ if (!Patch.COMMIT_MSG.equals(info.path())) {
+ if (!info.binary()) {
+ hasNonBinaryFile = true;
+ inserted += info.linesInserted();
+ deleted += info.linesDeleted();
+ } else {
+ hasBinaryFile = true;
+ if (info.sizeDelta() >= 0) {
+ bytesInserted += info.sizeDelta();
+ } else {
+ bytesDeleted += info.sizeDelta();
+ }
+ }
}
}
}
@@ -752,7 +770,7 @@
}
}
} else if (info.binary()) {
- sb.append(FormatUtil.formatBytes(info.sizeDelta()));
+ sb.append(formatBytes(info.sizeDelta()));
}
sb.closeTd();
}
@@ -801,9 +819,18 @@
sb.openTd().setAttribute("colspan", 3).closeTd(); // comments
// delta1
- sb.openTh().setStyleName(R.css().deltaColumn1())
- .append(Util.M.patchTableSize_Modify(inserted, deleted))
- .closeTh();
+ sb.openTh().setStyleName(R.css().deltaColumn1());
+ if (hasNonBinaryFile) {
+ sb.append(Util.M.patchTableSize_Modify(inserted, deleted));
+ }
+ if (hasBinaryFile) {
+ if (hasNonBinaryFile) {
+ sb.br();
+ }
+ sb.append(Util.M.patchTableSize_ModifyBinaryFiles(
+ formatAbsBytes(bytesInserted), formatAbsBytes(bytesDeleted)));
+ }
+ sb.closeTh();
// delta2
sb.openTh().setStyleName(R.css().deltaColumn2());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index 626e7c0..2ec4b6b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -158,7 +158,8 @@
@Override
public void execute() {
message.setFocus(true);
- }});
+ }
+ });
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
@Override
public boolean execute() {
@@ -167,7 +168,8 @@
message.setCursorPos(t.length());
}
return false;
- }}, 0);
+ }
+ }, 0);
}
@UiHandler("post")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
index f0101cb..bde9755 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
@@ -75,7 +75,7 @@
.deltaColumn1 {
white-space: nowrap;
- text-align: right;
+ text-align: right !important;
}
.deltaColumn2 {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
index 2d3644e..ef74a65 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
@@ -34,6 +34,8 @@
String patchTableComments(@PluralCount int count);
String patchTableDrafts(@PluralCount int count);
String patchTableSize_Modify(int insertions, int deletions);
+ String patchTableSize_ModifyBinaryFiles(String bytesInserted,
+ String bytesDeleted);
String patchTableSize_LongModify(int insertions, int deletions);
String patchTableSize_Lines(@PluralCount int insertions);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
index c109794..67ef2c3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
@@ -17,6 +17,7 @@
patchTableComments = {0} comments
patchTableDrafts = {0} drafts
patchTableSize_Modify = +{0}, -{1}
+patchTableSize_ModifyBinaryFiles = +{0}, -{1}
patchTableSize_LongModify = {0} inserted, {1} deleted
patchTableSize_Lines = {0} lines
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
index 800da91..6d795d3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
@@ -18,7 +18,7 @@
import com.google.gerrit.client.info.FileInfo;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -69,16 +69,16 @@
return this;
}
- public DiffApi ignoreWhitespace(AccountDiffPreference.Whitespace w) {
+ public DiffApi ignoreWhitespace(DiffPreferencesInfo.Whitespace w) {
switch (w) {
default:
case IGNORE_NONE:
return ignoreWhitespace(IgnoreWhitespace.NONE);
- case IGNORE_SPACE_AT_EOL:
+ case IGNORE_TRAILING:
return ignoreWhitespace(IgnoreWhitespace.TRAILING);
- case IGNORE_SPACE_CHANGE:
+ case IGNORE_LEADING_AND_TRAILING:
return ignoreWhitespace(IgnoreWhitespace.CHANGED);
- case IGNORE_ALL_SPACE:
+ case IGNORE_ALL:
return ignoreWhitespace(IgnoreWhitespace.ALL);
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
index 3364490..a50768d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
@@ -16,10 +16,10 @@
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.DEFAULT_CONTEXT;
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.WHOLE_FILE_CONTEXT;
-import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_ALL_SPACE;
+import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_ALL;
+import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_LEADING_AND_TRAILING;
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_NONE;
-import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_SPACE_AT_EOL;
-import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_SPACE_CHANGE;
+import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_TRAILING;
import static com.google.gwt.event.dom.client.KeyCodes.KEY_ESCAPE;
import com.google.gerrit.client.Gerrit;
@@ -29,9 +29,9 @@
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.NpIntTextBox;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.client.Theme;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Patch.ChangeType;
import com.google.gwt.core.client.GWT;
@@ -503,12 +503,9 @@
AccountApi.putDiffPreferences(prefs, new GerritCallback<DiffPreferences>() {
@Override
public void onSuccess(DiffPreferences result) {
- AccountDiffPreference p = Gerrit.getAccountDiffPreference();
- if (p == null) {
- p = AccountDiffPreference.createDefault(Gerrit.getUserAccount().getId());
- }
+ DiffPreferencesInfo p = Gerrit.getDiffPreferences();
result.copyTo(p);
- Gerrit.setAccountDiffPreference(p);
+ Gerrit.setDiffPreferences(p);
}
});
if (view != null) {
@@ -546,14 +543,14 @@
PatchUtil.C.whitespaceIGNORE_NONE(),
IGNORE_NONE.name());
ignoreWhitespace.addItem(
- PatchUtil.C.whitespaceIGNORE_SPACE_AT_EOL(),
- IGNORE_SPACE_AT_EOL.name());
+ PatchUtil.C.whitespaceIGNORE_TRAILING(),
+ IGNORE_TRAILING.name());
ignoreWhitespace.addItem(
- PatchUtil.C.whitespaceIGNORE_SPACE_CHANGE(),
- IGNORE_SPACE_CHANGE.name());
+ PatchUtil.C.whitespaceIGNORE_LEADING_AND_TRAILING(),
+ IGNORE_LEADING_AND_TRAILING.name());
ignoreWhitespace.addItem(
- PatchUtil.C.whitespaceIGNORE_ALL_SPACE(),
- IGNORE_ALL_SPACE.name());
+ PatchUtil.C.whitespaceIGNORE_ALL(),
+ IGNORE_ALL.name());
}
private void initMode() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
index 6ff93e4..7fc84f0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
@@ -14,7 +14,8 @@
package com.google.gerrit.client.diff;
-import static com.google.gerrit.reviewdb.client.AccountDiffPreference.WHOLE_FILE_CONTEXT;
+import static com.google.gerrit.extensions.client.DiffPreferencesInfo.WHOLE_FILE_CONTEXT;
+
import static java.lang.Double.POSITIVE_INFINITY;
import com.google.gerrit.client.Dispatcher;
@@ -151,7 +152,7 @@
this.startSide = startSide;
this.startLine = startLine;
- prefs = DiffPreferences.create(Gerrit.getAccountDiffPreference());
+ prefs = DiffPreferences.create(Gerrit.getDiffPreferences());
handlers = new ArrayList<>(6);
keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
header = new Header(keysNavigation, base, revision, path);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
index a344e6b..5376588 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
@@ -16,7 +16,7 @@
import com.google.gerrit.client.diff.DiffInfo.Region;
import com.google.gerrit.client.patches.SkippedLine;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gwt.core.client.JsArray;
import net.codemirror.lib.CodeMirror;
@@ -39,7 +39,7 @@
}
void render(int context, DiffInfo diff) {
- if (context == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
+ if (context == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
return;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
index 5cb3fbc..a546c62 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -518,6 +518,13 @@
if (!cm.isClean(generation)) {
close.setEnabled(false);
String text = cm.getValue();
+ if (Patch.COMMIT_MSG.equals(path)) {
+ String trimmed = text.trim() + "\r";
+ if (!trimmed.equals(text)) {
+ text = trimmed;
+ cm.setValue(text);
+ }
+ }
final int g = cm.changeGeneration(false);
ChangeEditApi.put(revision.getParentKey().get(), path, text,
new GerritCallback<VoidResult>() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
index 2420e7a..5b9203a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
@@ -28,11 +28,11 @@
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.prettify.client.ClientSideFormatter;
import com.google.gerrit.prettify.client.PrettyFormatter;
import com.google.gerrit.prettify.client.SparseHtmlFile;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -286,8 +286,8 @@
}
protected SparseHtmlFile getSparseHtmlFileA(PatchScript s) {
- AccountDiffPreference dp = new AccountDiffPreference(s.getDiffPrefs());
- dp.setShowWhitespaceErrors(false);
+ DiffPreferencesInfo dp = s.getDiffPrefs();
+ dp.showWhitespaceErrors = false;
PrettyFormatter f = ClientSideFormatter.FACTORY.get();
f.setDiffPrefs(dp);
@@ -299,7 +299,7 @@
}
protected SparseHtmlFile getSparseHtmlFileB(PatchScript s) {
- AccountDiffPreference dp = new AccountDiffPreference(s.getDiffPrefs());
+ DiffPreferencesInfo dp = s.getDiffPrefs();
SparseFileContent b = s.getB();
PrettyFormatter f = ClientSideFormatter.FACTORY.get();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
index 39aadc3..422a4dd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
@@ -69,9 +69,9 @@
String commentCancelEdit();
String whitespaceIGNORE_NONE();
- String whitespaceIGNORE_SPACE_AT_EOL();
- String whitespaceIGNORE_SPACE_CHANGE();
- String whitespaceIGNORE_ALL_SPACE();
+ String whitespaceIGNORE_TRAILING();
+ String whitespaceIGNORE_LEADING_AND_TRAILING();
+ String whitespaceIGNORE_ALL();
String previousFileHelp();
String nextFileHelp();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
index 2f68822..aa6177b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
@@ -50,9 +50,9 @@
commentCancelEdit = Cancel comment edit
whitespaceIGNORE_NONE=None
-whitespaceIGNORE_SPACE_AT_EOL=At Line End
-whitespaceIGNORE_SPACE_CHANGE=Leading, At Line End
-whitespaceIGNORE_ALL_SPACE=All
+whitespaceIGNORE_TRAILING=At Line End
+whitespaceIGNORE_LEADING_AND_TRAILING=Leading, At Line End
+whitespaceIGNORE_ALL=All
previousFileHelp = Previous file
nextFileHelp = Next file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
index 264e043..b7ba64b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScriptSettingsPanel.java
@@ -20,8 +20,8 @@
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.ListenableAccountDiffPreference;
import com.google.gerrit.client.ui.NpIntTextBox;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.OptionElement;
@@ -156,7 +156,7 @@
public void setEnableSmallFileFeatures(final boolean on) {
enableSmallFileFeatures = on;
if (enableSmallFileFeatures) {
- syntaxHighlighting.setValue(getValue().isSyntaxHighlighting());
+ syntaxHighlighting.setValue(getValue().syntaxHighlighting);
} else {
syntaxHighlighting.setValue(false);
}
@@ -181,7 +181,7 @@
public void setEnableIntralineDifference(final boolean on) {
enableIntralineDifference = on;
if (enableIntralineDifference) {
- intralineDifference.setValue(getValue().isIntralineDifference());
+ intralineDifference.setValue(getValue().intralineDifference);
} else {
intralineDifference.setValue(false);
}
@@ -197,36 +197,36 @@
syntaxHighlighting.setTitle(title);
}
- public AccountDiffPreference getValue() {
+ public DiffPreferencesInfo getValue() {
return listenablePrefs.get();
}
- public void setValue(final AccountDiffPreference dp) {
+ public void setValue(final DiffPreferencesInfo dp) {
listenablePrefs.set(dp);
display();
}
protected void display() {
- final AccountDiffPreference dp = getValue();
- setIgnoreWhitespace(dp.getIgnoreWhitespace());
+ final DiffPreferencesInfo dp = getValue();
+ setIgnoreWhitespace(dp.ignoreWhitespace);
if (enableSmallFileFeatures) {
- syntaxHighlighting.setValue(dp.isSyntaxHighlighting());
+ syntaxHighlighting.setValue(dp.syntaxHighlighting);
} else {
syntaxHighlighting.setValue(false);
}
- setContext(dp.getContext());
+ setContext(dp.context);
- tabWidth.setIntValue(dp.getTabSize());
- colWidth.setIntValue(dp.getLineLength());
- intralineDifference.setValue(dp.isIntralineDifference());
- whitespaceErrors.setValue(dp.isShowWhitespaceErrors());
- showLineEndings.setValue(dp.isShowLineEndings());
- showTabs.setValue(dp.isShowTabs());
- skipDeleted.setValue(dp.isSkipDeleted());
- skipUncommented.setValue(dp.isSkipUncommented());
- expandAllComments.setValue(dp.isExpandAllComments());
- retainHeader.setValue(dp.isRetainHeader());
- manualReview.setValue(dp.isManualReview());
+ tabWidth.setIntValue(dp.tabSize);
+ colWidth.setIntValue(dp.lineLength);
+ intralineDifference.setValue(dp.intralineDifference);
+ whitespaceErrors.setValue(dp.showWhitespaceErrors);
+ showLineEndings.setValue(dp.showLineEndings);
+ showTabs.setValue(dp.showTabs);
+ skipDeleted.setValue(dp.skipDeleted);
+ skipUncommented.setValue(dp.skipUncommented);
+ expandAllComments.setValue(dp.expandAllComments);
+ retainHeader.setValue(dp.retainHeader);
+ manualReview.setValue(dp.manualReview);
}
@UiHandler("update")
@@ -244,22 +244,21 @@
new ErrorDialog(PatchUtil.C.illegalNumberOfColumns()).center();
return;
}
-
- AccountDiffPreference dp = new AccountDiffPreference(getValue());
- dp.setIgnoreWhitespace(getIgnoreWhitespace());
- dp.setContext(getContext());
- dp.setTabSize(tabWidth.getIntValue());
- dp.setLineLength(colWidth.getIntValue());
- dp.setSyntaxHighlighting(syntaxHighlighting.getValue());
- dp.setIntralineDifference(intralineDifference.getValue());
- dp.setShowWhitespaceErrors(whitespaceErrors.getValue());
- dp.setShowLineEndings(showLineEndings.getValue());
- dp.setShowTabs(showTabs.getValue());
- dp.setSkipDeleted(skipDeleted.getValue());
- dp.setSkipUncommented(skipUncommented.getValue());
- dp.setExpandAllComments(expandAllComments.getValue());
- dp.setRetainHeader(retainHeader.getValue());
- dp.setManualReview(manualReview.getValue());
+ DiffPreferencesInfo dp = getValue();
+ dp.ignoreWhitespace = getIgnoreWhitespace();
+ dp.context = getContext();
+ dp.tabSize = tabWidth.getIntValue();
+ dp.lineLength = colWidth.getIntValue();
+ dp.syntaxHighlighting = syntaxHighlighting.getValue();
+ dp.intralineDifference = intralineDifference.getValue();
+ dp.showWhitespaceErrors = whitespaceErrors.getValue();
+ dp.showLineEndings = showLineEndings.getValue();
+ dp.showTabs = showTabs.getValue();
+ dp.skipDeleted = skipDeleted.getValue();
+ dp.skipUncommented = skipUncommented.getValue();
+ dp.expandAllComments = expandAllComments.getValue();
+ dp.retainHeader = retainHeader.getValue();
+ dp.manualReview = manualReview.getValue();
listenablePrefs.set(dp);
}
@@ -289,18 +288,18 @@
private void initIgnoreWhitespace(ListBox ws) {
ws.addItem(PatchUtil.C.whitespaceIGNORE_NONE(), //
Whitespace.IGNORE_NONE.name());
- ws.addItem(PatchUtil.C.whitespaceIGNORE_SPACE_AT_EOL(), //
- Whitespace.IGNORE_SPACE_AT_EOL.name());
- ws.addItem(PatchUtil.C.whitespaceIGNORE_SPACE_CHANGE(), //
- Whitespace.IGNORE_SPACE_CHANGE.name());
- ws.addItem(PatchUtil.C.whitespaceIGNORE_ALL_SPACE(), //
- Whitespace.IGNORE_ALL_SPACE.name());
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_TRAILING(), //
+ Whitespace.IGNORE_TRAILING.name());
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_LEADING_AND_TRAILING(), //
+ Whitespace.IGNORE_LEADING_AND_TRAILING.name());
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_ALL(), //
+ Whitespace.IGNORE_ALL.name());
}
private void initContext(ListBox context) {
- for (final short v : AccountDiffPreference.CONTEXT_CHOICES) {
+ for (final short v : DiffPreferencesInfo.CONTEXT_CHOICES) {
final String label;
- if (v == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
+ if (v == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
label = Util.C.contextWholeFile();
} else {
label = Util.M.lines(v);
@@ -314,7 +313,7 @@
if (0 <= sel) {
return Whitespace.valueOf(ignoreWhitespace.getValue(sel));
}
- return getValue().getIgnoreWhitespace();
+ return getValue().ignoreWhitespace;
}
private void setIgnoreWhitespace(Whitespace s) {
@@ -327,12 +326,12 @@
ignoreWhitespace.setSelectedIndex(0);
}
- private short getContext() {
+ private int getContext() {
final int sel = context.getSelectedIndex();
if (0 <= sel) {
return Short.parseShort(context.getValue(sel));
}
- return getValue().getContext();
+ return getValue().context;
}
private void setContext(int ctx) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTable.java
index f3bc092..d84f799 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTable.java
@@ -65,9 +65,9 @@
new PatchValidator() {
@Override
public boolean isValid(Patch patch) {
- return !((listenablePrefs.get().isSkipDeleted()
+ return !((listenablePrefs.get().skipDeleted
&& patch.getChangeType().equals(ChangeType.DELETED))
- || (listenablePrefs.get().isSkipUncommented()
+ || (listenablePrefs.get().skipUncommented
&& patch.getCommentCount() == 0));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
index 10c029f..b3617d5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
@@ -294,7 +294,7 @@
private void appendImageDifferences(final PatchScript script,
final SafeHtmlBuilder nc) {
final boolean syntaxHighlighting =
- script.getDiffPrefs().isSyntaxHighlighting();
+ script.getDiffPrefs().syntaxHighlighting;
if (script.getDisplayMethodA() == DisplayMethod.IMG) {
final String url = getUrlA();
appendImageLine(nc, url, syntaxHighlighting, false);
@@ -310,7 +310,7 @@
final SparseHtmlFile a = getSparseHtmlFileA(script);
final SparseHtmlFile b = getSparseHtmlFileB(script);
final boolean syntaxHighlighting =
- script.getDiffPrefs().isSyntaxHighlighting();
+ script.getDiffPrefs().syntaxHighlighting;
for (final EditList.Hunk hunk : script.getHunks()) {
appendHunkHeader(nc, hunk);
while (hunk.next()) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
index bfa308a..e587aac 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedPatchScreen.java
@@ -31,9 +31,9 @@
import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.prettify.client.ClientSideFormatter;
import com.google.gerrit.prettify.client.PrettyFactory;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.Scheduler;
@@ -125,10 +125,10 @@
lastScript = null;
}
- private void update(AccountDiffPreference dp) {
+ private void update(DiffPreferencesInfo dp) {
// Did the user just turn on auto-review?
- if (!reviewedPanels.getValue() && prefs.getOld().isManualReview()
- && !dp.isManualReview()) {
+ if (!reviewedPanels.getValue() && prefs.getOld().manualReview
+ && !dp.manualReview) {
reviewedPanels.setValue(true);
reviewedPanels.setReviewedByCurrentUser(true);
}
@@ -152,25 +152,25 @@
}
}
- private boolean canReuse(AccountDiffPreference dp, PatchScript last) {
- if (last.getDiffPrefs().getIgnoreWhitespace() != dp.getIgnoreWhitespace()) {
+ private boolean canReuse(DiffPreferencesInfo dp, PatchScript last) {
+ if (last.getDiffPrefs().ignoreWhitespace != dp.ignoreWhitespace) {
// Whitespace ignore setting requires server computation.
return false;
}
- final int ctx = dp.getContext();
- if (ctx == AccountDiffPreference.WHOLE_FILE_CONTEXT && !last.getA().isWholeFile()) {
+ final int ctx = dp.context;
+ if (ctx == DiffPreferencesInfo.WHOLE_FILE_CONTEXT
+ && !last.getA().isWholeFile()) {
// We don't have the entire file here, so we can't render it.
return false;
}
- if (last.getDiffPrefs().getContext() < ctx && !last.getA().isWholeFile()) {
+ if (last.getDiffPrefs().context < ctx && !last.getA().isWholeFile()) {
// We don't have sufficient context.
return false;
}
- if (dp.isSyntaxHighlighting()
- && !last.getA().isWholeFile()) {
+ if (dp.syntaxHighlighting && !last.getA().isWholeFile()) {
// We need the whole file to syntax highlight accurately.
return false;
}
@@ -425,15 +425,15 @@
}
if (script.isHugeFile()) {
- AccountDiffPreference dp = script.getDiffPrefs();
- int context = dp.getContext();
- if (context == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
+ DiffPreferencesInfo dp = script.getDiffPrefs();
+ int context = dp.context;
+ if (context == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
context = Short.MAX_VALUE;
} else if (context > Short.MAX_VALUE) {
context = Short.MAX_VALUE;
}
- dp.setContext((short) Math.min(context, LARGE_FILE_CONTEXT));
- dp.setSyntaxHighlighting(false);
+ dp.context = Math.min(context, LARGE_FILE_CONTEXT);
+ dp.syntaxHighlighting = false;
script.setDiffPrefs(dp);
}
@@ -453,7 +453,7 @@
if (Gerrit.isSignedIn()) {
boolean isReviewed = false;
- if (isFirst && !prefs.get().isManualReview()) {
+ if (isFirst && !prefs.get().manualReview) {
isReviewed = true;
reviewedPanels.setReviewedByCurrentUser(isReviewed);
} else {
@@ -476,9 +476,9 @@
super.onShowView();
if (prefsHandler == null) {
prefsHandler = prefs.addValueChangeHandler(
- new ValueChangeHandler<AccountDiffPreference>() {
+ new ValueChangeHandler<DiffPreferencesInfo>() {
@Override
- public void onValueChange(ValueChangeEvent<AccountDiffPreference> event) {
+ public void onValueChange(ValueChangeEvent<DiffPreferencesInfo> event) {
update(event.getValue());
}
});
@@ -491,7 +491,7 @@
new ErrorDialog(PatchUtil.C.intralineTimeout()).setText(
Gerrit.C.warnTitle()).show();
}
- if (topView != null && prefs.get().isRetainHeader()) {
+ if (topView != null && prefs.get().retainHeader) {
setTopView(topView);
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
index 3f3875a..322a354 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
@@ -53,6 +53,9 @@
public final native InheritedBooleanInfo enableSignedPush()
/*-{ return this.enable_signed_push; }-*/;
+ public final native InheritedBooleanInfo requireSignedPush()
+ /*-{ return this.require_signed_push; }-*/;
+
public final SubmitType submitType() {
return SubmitType.valueOf(submitTypeRaw());
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
index f9c3640..fffdd3f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -117,6 +117,7 @@
InheritableBoolean createNewChangeForAllNotInTarget,
InheritableBoolean requireChangeId,
InheritableBoolean enableSignedPush,
+ InheritableBoolean requireSignedPush,
String maxObjectSizeLimit,
SubmitType submitType, ProjectState state,
Map<String, Map<String, ConfigParameterValue>> pluginConfigValues,
@@ -131,6 +132,9 @@
if (enableSignedPush != null) {
in.setEnableSignedPush(enableSignedPush);
}
+ if (requireSignedPush != null) {
+ in.setRequireSignedPush(requireSignedPush);
+ }
in.setMaxObjectSizeLimit(maxObjectSizeLimit);
in.setSubmitType(submitType);
in.setState(state);
@@ -257,6 +261,12 @@
private final native void setEnableSignedPushRaw(String v)
/*-{ if(v)this.enable_signed_push=v; }-*/;
+ final void setRequireSignedPush(InheritableBoolean v) {
+ setRequireSignedPushRaw(v.name());
+ }
+ private final native void setRequireSignedPushRaw(String v)
+ /*-{ if(v)this.require_signed_push=v; }-*/;
+
final native void setMaxObjectSizeLimit(String l)
/*-{ if(l)this.max_object_size_limit=l; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java
index 27bc107..0c31f41 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ListenableAccountDiffPreference.java
@@ -17,11 +17,11 @@
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.account.Util;
import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gwtjsonrpc.common.VoidResult;
public class ListenableAccountDiffPreference
- extends ListenableOldValue<AccountDiffPreference> {
+ extends ListenableOldValue<DiffPreferencesInfo> {
public ListenableAccountDiffPreference() {
reset();
@@ -33,7 +33,7 @@
new GerritCallback<VoidResult>() {
@Override
public void onSuccess(VoidResult result) {
- Gerrit.setAccountDiffPreference(get());
+ Gerrit.setDiffPreferences(get());
cb.onSuccess(result);
}
@@ -46,10 +46,10 @@
}
public void reset() {
- if (Gerrit.isSignedIn() && Gerrit.getAccountDiffPreference() != null) {
- set(Gerrit.getAccountDiffPreference());
+ if (Gerrit.isSignedIn() && Gerrit.getDiffPreferences() != null) {
+ set(Gerrit.getDiffPreferences());
} else {
- set(AccountDiffPreference.createDefault(null));
+ set(DiffPreferencesInfo.defaults());
}
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
index c1a113f..389dcc7 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -24,13 +24,18 @@
import com.google.common.primitives.Bytes;
import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.HostPageData;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.systemstatus.MessageOfTheDay;
import com.google.gerrit.extensions.webui.WebUiPlugin;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.GetDiffPreferences;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -38,10 +43,12 @@
import com.google.gwtexpui.server.CacheHeaders;
import com.google.gwtjsonrpc.server.JsonServlet;
import com.google.gwtjsonrpc.server.RPCServletUtils;
+import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -87,6 +94,7 @@
private final StaticServlet staticServlet;
private final boolean isNoteDbEnabled;
private final Integer pluginsLoadTimeout;
+ private final GetDiffPreferences getDiff;
private volatile Page page;
@Inject
@@ -100,7 +108,8 @@
DynamicSet<MessageOfTheDay> motd,
@GerritServerConfig Config cfg,
StaticServlet ss,
- NotesMigration migration)
+ NotesMigration migration,
+ GetDiffPreferences diffPref)
throws IOException, ServletException {
currentUser = cu;
session = w;
@@ -113,6 +122,7 @@
staticServlet = ss;
isNoteDbEnabled = migration.enabled();
pluginsLoadTimeout = getPluginsLoadTimeout(cfg);
+ getDiff = diffPref;
final String pageName = "HostPage.html";
template = HtmlDomUtil.parseFile(getClass(), pageName);
@@ -187,7 +197,7 @@
w.write(";");
w.write(HPD_ID + ".accountDiffPref=");
- json(user.asIdentifiedUser().getAccountDiffPreference(), w);
+ json(getDiffPreferences(user.asIdentifiedUser()), w);
w.write(";");
w.write(HPD_ID + ".theme=");
@@ -220,6 +230,16 @@
}
}
+ private DiffPreferencesInfo getDiffPreferences(IdentifiedUser user) {
+ try {
+ return getDiff.apply(new AccountResource(user));
+ } catch (AuthException | OrmException | ConfigInvalidException
+ | IOException e) {
+ log.warn("Cannot query account diff preferences", e);
+ }
+ return DiffPreferencesInfo.defaults();
+ }
+
private void plugins(StringWriter w) {
List<String> urls = Lists.newArrayList();
for (WebUiPlugin u : plugins) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
index af20ea5..3b064e2 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
@@ -166,14 +166,18 @@
}
private String extractWhat(final Audit note, final GerritCall call) {
- String methodClass = call.getMethodClass().getName();
- methodClass = methodClass.substring(methodClass.lastIndexOf(".")+1);
+ Class<?> methodClass = call.getMethodClass();
+ String methodClassName = methodClass != null
+ ? methodClass.getName()
+ : "<UNKNOWN_CLASS>";
+ methodClassName =
+ methodClassName.substring(methodClassName.lastIndexOf(".") + 1);
String what = note.action();
if (what.length() == 0) {
what = call.getMethod().getName();
}
- return methodClass + "." + what;
+ return methodClassName + "." + what;
}
static class GerritCall extends ActiveCall {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java
index 968029c..0da66bf 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java
@@ -19,13 +19,17 @@
import com.google.gerrit.common.data.AgreementInfo;
import com.google.gerrit.common.errors.InvalidQueryException;
import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountProjectWatch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.account.SetDiffPreferences;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.query.QueryParseException;
@@ -37,6 +41,9 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -49,18 +56,21 @@
private final ProjectControl.Factory projectControlFactory;
private final AgreementInfoFactory.Factory agreementInfoFactory;
private final ChangeQueryBuilder queryBuilder;
+ private final SetDiffPreferences setDiff;
@Inject
AccountServiceImpl(final Provider<ReviewDb> schema,
final Provider<IdentifiedUser> identifiedUser,
final ProjectControl.Factory projectControlFactory,
final AgreementInfoFactory.Factory agreementInfoFactory,
- final ChangeQueryBuilder queryBuilder) {
+ final ChangeQueryBuilder queryBuilder,
+ SetDiffPreferences setDiff) {
super(schema, identifiedUser);
this.currentUser = identifiedUser;
this.projectControlFactory = projectControlFactory;
this.agreementInfoFactory = agreementInfoFactory;
this.queryBuilder = queryBuilder;
+ this.setDiff = setDiff;
}
@Override
@@ -74,17 +84,21 @@
}
@Override
- public void changeDiffPreferences(final AccountDiffPreference diffPref,
+ public void changeDiffPreferences(final DiffPreferencesInfo diffPref,
AsyncCallback<VoidResult> callback) {
run(callback, new Action<VoidResult>(){
@Override
public VoidResult run(ReviewDb db) throws OrmException {
- if (!diffPref.getAccountId().equals(getAccountId())) {
- throw new IllegalArgumentException("diffPref.getAccountId() "
- + diffPref.getAccountId() + " doesn't match"
- + " the accountId of the signed in user " + getAccountId());
+ if (!getUser().isIdentifiedUser()) {
+ throw new IllegalArgumentException("Not authenticated");
}
- db.accountDiffPreferences().upsert(Collections.singleton(diffPref));
+ IdentifiedUser me = getUser().asIdentifiedUser();
+ try {
+ setDiff.apply(new AccountResource(me), diffPref);
+ } catch (AuthException | BadRequestException | ConfigInvalidException
+ | IOException e) {
+ throw new OrmException("Cannot save diff preferences", e);
+ }
return VoidResult.INSTANCE;
}
});
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
index d0c042c..37ca524 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
@@ -16,7 +16,7 @@
import com.google.gerrit.common.data.ChangeDetailService;
import com.google.gerrit.common.data.PatchSetDetail;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.inject.Inject;
@@ -38,7 +38,7 @@
@Override
public void patchSetDetail2(PatchSet.Id baseId, PatchSet.Id id,
- AccountDiffPreference diffPrefs, AsyncCallback<PatchSetDetail> callback) {
+ DiffPreferencesInfo diffPrefs, AsyncCallback<PatchSetDetail> callback) {
patchSetDetail.create(baseId, id, diffPrefs).to(callback);
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
index 2bf5fe4..d2dc384 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
@@ -18,11 +18,11 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.AccountPatchReview;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
@@ -67,7 +67,7 @@
PatchSetDetailFactory create(
@Assisted("psIdBase") @Nullable PatchSet.Id psIdBase,
@Assisted("psIdNew") PatchSet.Id psIdNew,
- @Nullable AccountDiffPreference diffPrefs);
+ @Nullable DiffPreferencesInfo diffPrefs);
}
private final PatchSetInfoFactory infoFactory;
@@ -81,7 +81,7 @@
private Project.NameKey project;
private final PatchSet.Id psIdBase;
private final PatchSet.Id psIdNew;
- private final AccountDiffPreference diffPrefs;
+ private final DiffPreferencesInfo diffPrefs;
private ObjectId oldId;
private ObjectId newId;
@@ -98,7 +98,7 @@
ChangeEditUtil editUtil,
@Assisted("psIdBase") @Nullable final PatchSet.Id psIdBase,
@Assisted("psIdNew") final PatchSet.Id psIdNew,
- @Assisted @Nullable final AccountDiffPreference diffPrefs) {
+ @Assisted @Nullable final DiffPreferencesInfo diffPrefs) {
this.infoFactory = psif;
this.db = db;
this.patchListCache = patchListCache;
@@ -145,7 +145,7 @@
newId = toObjectId(psIdNew);
}
- list = listFor(keyFor(diffPrefs.getIgnoreWhitespace()));
+ list = listFor(keyFor(diffPrefs.ignoreWhitespace));
} else { // OK, means use base to compare
list = patchListCache.get(control.getChange(), patchSet);
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
index 7ff3782..f9180f7 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
@@ -17,9 +17,9 @@
import com.google.gerrit.common.data.PatchDetailService;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -48,7 +48,7 @@
@Override
public void patchScript(final Patch.Key patchKey, final PatchSet.Id psa,
- final PatchSet.Id psb, final AccountDiffPreference dp,
+ final PatchSet.Id psb, final DiffPreferencesInfo dp,
final AsyncCallback<PatchScript> callback) {
if (psb == null) {
callback.onFailure(new NoSuchEntityException());
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 99003ee..5c3b629 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -18,8 +18,8 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
-import static com.google.gerrit.server.index.IndexRewriteImpl.CLOSED_STATUSES;
-import static com.google.gerrit.server.index.IndexRewriteImpl.OPEN_STATUSES;
+import static com.google.gerrit.server.index.IndexRewriter.CLOSED_STATUSES;
+import static com.google.gerrit.server.index.IndexRewriter.OPEN_STATUSES;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
@@ -47,7 +47,7 @@
import com.google.gerrit.server.index.FieldDef.FillArgs;
import com.google.gerrit.server.index.FieldType;
import com.google.gerrit.server.index.IndexExecutor;
-import com.google.gerrit.server.index.IndexRewriteImpl;
+import com.google.gerrit.server.index.IndexRewriter;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.Schema.Values;
import com.google.gerrit.server.query.Predicate;
@@ -55,6 +55,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
import com.google.gerrit.server.query.change.LegacyChangeIdPredicate;
+import com.google.gerrit.server.query.change.QueryOptions;
import com.google.gwtorm.protobuf.ProtobufCodec;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -352,9 +353,9 @@
}
@Override
- public ChangeDataSource getSource(Predicate<ChangeData> p, int start,
- int limit) throws QueryParseException {
- Set<Change.Status> statuses = IndexRewriteImpl.getPossibleStatus(p);
+ public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts)
+ throws QueryParseException {
+ Set<Change.Status> statuses = IndexRewriter.getPossibleStatus(p);
List<SubIndex> indexes = Lists.newArrayListWithCapacity(2);
if (!Sets.intersection(statuses, OPEN_STATUSES).isEmpty()) {
indexes.add(openIndex);
@@ -362,7 +363,7 @@
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
indexes.add(closedIndex);
}
- return new QuerySource(indexes, queryBuilder.toQuery(p), start, limit,
+ return new QuerySource(indexes, queryBuilder.toQuery(p), opts,
getSort());
}
@@ -397,16 +398,14 @@
private class QuerySource implements ChangeDataSource {
private final List<SubIndex> indexes;
private final Query query;
- private final int start;
- private final int limit;
+ private final QueryOptions opts;
private final Sort sort;
- private QuerySource(List<SubIndex> indexes, Query query, int start,
- int limit, Sort sort) {
+ private QuerySource(List<SubIndex> indexes, Query query, QueryOptions opts,
+ Sort sort) {
this.indexes = indexes;
this.query = checkNotNull(query, "null query from Lucene");
- this.start = start;
- this.limit = limit;
+ this.opts = opts;
this.sort = sort;
}
@@ -429,7 +428,7 @@
public ResultSet<ChangeData> read() throws OrmException {
IndexSearcher[] searchers = new IndexSearcher[indexes.size()];
try {
- int realLimit = start + limit;
+ int realLimit = opts.start() + opts.limit();
TopFieldDocs[] hits = new TopFieldDocs[indexes.size()];
for (int i = 0; i < indexes.size(); i++) {
searchers[i] = indexes.get(i).acquire();
@@ -439,7 +438,7 @@
List<ChangeData> result =
Lists.newArrayListWithCapacity(docs.scoreDocs.length);
- for (int i = start; i < docs.scoreDocs.length; i++) {
+ for (int i = opts.start(); i < docs.scoreDocs.length; i++) {
ScoreDoc sd = docs.scoreDocs[i];
Document doc = searchers[sd.shardIndex].doc(sd.doc, FIELDS);
result.add(toChangeData(doc));
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
index 1c70468..cd1c7a3 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -28,6 +28,7 @@
import com.google.gerrit.pgm.init.api.InstallPlugins;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -222,6 +223,7 @@
throw die(err);
}
+ m.add(new GerritServerConfigModule());
m.add(new InitModule(standalone, initDb));
m.add(new AbstractModule() {
@Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
index 6eb313e..a6f1f93 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -145,7 +145,8 @@
@Override
protected void configure() {
bind(DataSourceType.class).toInstance(dst);
- }});
+ }
+ });
modules.add(new DatabaseModule());
modules.add(new SchemaModule());
modules.add(new LocalDiskRepositoryManager.Module());
diff --git a/gerrit-prettify/BUCK b/gerrit-prettify/BUCK
index 6728ba6..9a0136e 100644
--- a/gerrit-prettify/BUCK
+++ b/gerrit-prettify/BUCK
@@ -15,6 +15,7 @@
'//gerrit-gwtexpui:SafeHtml',
],
exported_deps = [
+ '//gerrit-extension-api:client',
'//gerrit-patch-jgit:client',
'//gerrit-reviewdb:client',
'//lib:gwtjsonrpc',
diff --git a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java
index cdf800c..49dc2fc 100644
--- a/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java
+++ b/gerrit-prettify/src/main/java/com/google/gerrit/prettify/client/PrettyFormatter.java
@@ -14,8 +14,8 @@
package com.google.gerrit.prettify.client;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
@@ -73,7 +73,7 @@
protected SparseFileContent content;
protected EditFilter side;
protected List<Edit> edits;
- protected AccountDiffPreference diffPrefs;
+ protected DiffPreferencesInfo diffPrefs;
protected String fileName;
protected Set<Integer> trailingEdits;
@@ -110,7 +110,7 @@
edits = all;
}
- public void setDiffPrefs(AccountDiffPreference how) {
+ public void setDiffPrefs(DiffPreferencesInfo how) {
diffPrefs = how;
}
@@ -132,7 +132,7 @@
String html = toHTML(src);
html = expandTabs(html);
- if (diffPrefs.isSyntaxHighlighting() && getFileType() != null
+ if (diffPrefs.syntaxHighlighting && getFileType() != null
&& src.isWholeFile()) {
// The prettify parsers don't like ' as an entity for the
// single quote character. Replace them all out so we don't
@@ -233,7 +233,7 @@
cleanText(txt, pos, start);
pos = txt.indexOf(';', start + 1) + 1;
- if (diffPrefs.getLineLength() <= col) {
+ if (diffPrefs.lineLength <= col) {
buf.append("<br />");
col = 0;
}
@@ -247,14 +247,14 @@
private void cleanText(String txt, int pos, int end) {
while (pos < end) {
- int free = diffPrefs.getLineLength() - col;
+ int free = diffPrefs.lineLength - col;
if (free <= 0) {
// The current line is full. Throw an explicit line break
// onto the end, and we'll continue on the next line.
//
buf.append("<br />");
col = 0;
- free = diffPrefs.getLineLength();
+ free = diffPrefs.lineLength;
}
int n = Math.min(end - pos, free);
@@ -326,7 +326,7 @@
private String toHTML(SparseFileContent src) {
SafeHtml html;
- if (diffPrefs.isIntralineDifference()) {
+ if (diffPrefs.intralineDifference) {
html = colorLineEdits(src);
} else {
SafeHtmlBuilder b = new SafeHtmlBuilder();
@@ -342,7 +342,7 @@
html = html.replaceAll("\r([^\n])", r);
}
- if (diffPrefs.isShowWhitespaceErrors()) {
+ if (diffPrefs.showWhitespaceErrors) {
// We need to do whitespace errors before showing tabs, because
// these patterns rely on \t as a literal, before it expands.
//
@@ -350,12 +350,12 @@
html = showTrailingWhitespace(html);
}
- if (diffPrefs.isShowLineEndings()){
+ if (diffPrefs.showLineEndings){
html = showLineEndings(html);
}
- if (diffPrefs.isShowTabs()) {
- String t = 1 < diffPrefs.getTabSize() ? "\t" : "";
+ if (diffPrefs.showTabs) {
+ String t = 1 < diffPrefs.tabSize ? "\t" : "";
html = html.replaceAll("\t", "<span class=\"vt\">\u00BB</span>" + t);
}
@@ -528,10 +528,10 @@
private String expandTabs(String html) {
StringBuilder tmp = new StringBuilder();
int i = 0;
- if (diffPrefs.isShowTabs()) {
+ if (diffPrefs.showTabs) {
i = 1;
}
- for (; i < diffPrefs.getTabSize(); i++) {
+ for (; i < diffPrefs.tabSize; i++) {
tmp.append(" ");
}
return html.replaceAll("\t", tmp.toString());
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
index 3e18bc8..295239f 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -16,6 +16,7 @@
import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.IntKey;
@@ -51,7 +52,7 @@
* notifications of updates on that change, or just book-marking it for faster
* future reference. One record per starred change.</li>
*
- * <li>{@link AccountDiffPreference}: user's preferences for rendering side-to-side
+ * <li>{@link DiffPreferencesInfo}: user's preferences for rendering side-to-side
* and unified diff</li>
*
* </ul>
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountDiffPreference.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountDiffPreference.java
index 7948080..cc0cbf0 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountDiffPreference.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountDiffPreference.java
@@ -32,9 +32,9 @@
public static enum Whitespace implements CodedEnum {
IGNORE_NONE('N'), //
- IGNORE_SPACE_AT_EOL('E'), //
- IGNORE_SPACE_CHANGE('S'), //
- IGNORE_ALL_SPACE('A');
+ IGNORE_TRAILING('E'), //
+ IGNORE_LEADING_AND_TRAILING('S'), //
+ IGNORE_ALL('A');
private final char code;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
index 26d31da..d2906d3 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Change.java
@@ -39,8 +39,6 @@
* |
* +- {@link PatchSetApproval}: a +/- vote on the change's current state.
* |
- * +- {@link PatchSetAncestor}: parents of this change's commit.
- * |
* +- {@link PatchLineComment}: comment about a specific line
* </pre>
* <p>
@@ -51,11 +49,6 @@
* {@link Account} is usually also listed as the author and committer in the
* PatchSetInfo.
* <p>
- * The {@link PatchSetAncestor} entities are a mirror of the Git commit
- * metadata, providing access to the information without needing direct
- * accessing Git. These entities are actually legacy artifacts from Gerrit 1.x
- * and could be removed, replaced by direct RevCommit access.
- * <p>
* Each PatchSet contains zero or more Patch records, detailing the file paths
* impacted by the change (otherwise known as, the file paths the author
* added/deleted/modified). Sometimes a merge commit can contain zero patches,
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
index acf8b45..3ecd539 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
@@ -52,7 +52,7 @@
}
@Override
- protected void set(String newValue) {
+ public void set(String newValue) {
uuid = newValue;
}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetAncestor.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetAncestor.java
deleted file mode 100644
index 8412788..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchSetAncestor.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2008 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.reviewdb.client;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-
-/** Ancestors of a {@link PatchSet} that the PatchSet depends upon. */
-public final class PatchSetAncestor {
- public static class Id extends IntKey<PatchSet.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1, name = Column.NONE)
- protected PatchSet.Id patchSetId;
-
- @Column(id = 2)
- protected int position;
-
- protected Id() {
- patchSetId = new PatchSet.Id();
- }
-
- public Id(final PatchSet.Id psId, final int pos) {
- this.patchSetId = psId;
- this.position = pos;
- }
-
- @Override
- public PatchSet.Id getParentKey() {
- return patchSetId;
- }
-
- @Override
- public int get() {
- return position;
- }
-
- @Override
- protected void set(int newValue) {
- position = newValue;
- }
- }
-
- @Column(id = 1, name = Column.NONE)
- protected Id key;
-
- @Column(id = 2)
- protected RevId ancestorRevision;
-
- protected PatchSetAncestor() {
- }
-
- public PatchSetAncestor(final PatchSetAncestor.Id k) {
- key = k;
- }
-
- public PatchSetAncestor.Id getId() {
- return key;
- }
-
- public PatchSet.Id getPatchSet() {
- return key.patchSetId;
- }
-
- public int getPosition() {
- return key.position;
- }
-
- public RevId getAncestorRevision() {
- return ancestorRevision;
- }
-
- public void setAncestorRevision(final RevId id) {
- ancestorRevision = id;
- }
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
index ce1b27f..af9e75c 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Project.java
@@ -97,6 +97,7 @@
protected InheritableBoolean createNewChangeForAllNotInTarget;
protected InheritableBoolean enableSignedPush;
+ protected InheritableBoolean requireSignedPush;
protected Project() {
}
@@ -111,6 +112,7 @@
useContentMerge = InheritableBoolean.INHERIT;
createNewChangeForAllNotInTarget = InheritableBoolean.INHERIT;
enableSignedPush = InheritableBoolean.INHERIT;
+ requireSignedPush = InheritableBoolean.INHERIT;
}
public Project.NameKey getNameKey() {
@@ -182,6 +184,14 @@
enableSignedPush = enable;
}
+ public InheritableBoolean getRequireSignedPush() {
+ return requireSignedPush;
+ }
+
+ public void setRequireSignedPush(InheritableBoolean require) {
+ requireSignedPush = require;
+ }
+
public void setMaxObjectSizeLimit(final String limit) {
maxObjectSizeLimit = limit;
}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/PatchSetAncestorAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/PatchSetAncestorAccess.java
deleted file mode 100644
index 02459d9..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/PatchSetAncestorAccess.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2008 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.reviewdb.server;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetAncestor;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface PatchSetAncestorAccess extends
- Access<PatchSetAncestor, PatchSetAncestor.Id> {
- @Override
- @PrimaryKey("key")
- PatchSetAncestor get(PatchSetAncestor.Id key) throws OrmException;
-
- @Query("WHERE key.patchSetId = ? ORDER BY key.position")
- ResultSet<PatchSetAncestor> ancestorsOf(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE key.patchSetId.changeId = ?")
- ResultSet<PatchSetAncestor> byChange(Change.Id id) throws OrmException;
-
- @Query("WHERE key.patchSetId = ?")
- ResultSet<PatchSetAncestor> byPatchSet(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE ancestorRevision = ?")
- ResultSet<PatchSetAncestor> descendantsOf(RevId revision)
- throws OrmException;
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
index 85f2b26..37dcc59 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/ReviewDb.java
@@ -92,8 +92,7 @@
@Relation(id = 24)
PatchSetAccess patchSets();
- @Relation(id = 25)
- PatchSetAncestorAccess patchSetAncestors();
+ // Deleted @Relation(id = 25)
@Relation(id = 26)
PatchLineCommentAccess patchComments();
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
index a62c762..1162a5f 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
@@ -88,14 +88,6 @@
ON patch_sets (revision);
-- *********************************************************************
--- PatchSetAncestorAccess
--- @PrimaryKey covers: ancestorsOf
--- covers: descendantsOf
-CREATE INDEX patch_set_ancestors_desc
-ON patch_set_ancestors (ancestor_revision);
-
-
--- *********************************************************************
-- StarredChangeAccess
-- @PrimaryKey covers: byAccount
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
index f88c169..258b7be 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
@@ -97,15 +97,6 @@
#
-- *********************************************************************
--- PatchSetAncestorAccess
--- @PrimaryKey covers: ancestorsOf
--- covers: descendantsOf
-CREATE INDEX patch_set_ancestors_desc
-ON patch_set_ancestors (ancestor_revision)
-#
-
-
--- *********************************************************************
-- StarredChangeAccess
-- @PrimaryKey covers: byAccount
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
index a6b21ee..1fe7dce 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
@@ -137,13 +137,6 @@
ON patch_sets (revision);
-- *********************************************************************
--- PatchSetAncestorAccess
--- @PrimaryKey covers: ancestorsOf
--- covers: descendantsOf
-CREATE INDEX patch_set_ancestors_desc
-ON patch_set_ancestors (ancestor_revision);
-
--- *********************************************************************
-- StarredChangeAccess
-- @PrimaryKey covers: byAccount
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 285f1d4..889f008 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -36,6 +36,7 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.data.ApprovalAttribute;
+import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.events.ChangeAbandonedEvent;
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent;
@@ -62,6 +63,7 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -334,6 +336,16 @@
}
}
+ private PatchSetAttribute asPatchSetAttribute(Change change,
+ PatchSet patchSet, ReviewDb db) throws OrmException {
+ try (Repository repo = repoManager.openRepository(change.getProject());
+ RevWalk revWalk = new RevWalk(repo)) {
+ return eventFactory.asPatchSetAttribute(db, revWalk, patchSet);
+ } catch (IOException e) {
+ throw new OrmException(e);
+ }
+ }
+
/**
* Fire the update hook
*
@@ -380,8 +392,8 @@
AccountState uploader = accountCache.get(patchSet.getUploader());
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.change = eventFactory.asChangeAttribute(db, change);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.uploader = eventFactory.asAccountAttribute(uploader.getAccount());
fireEvent(change, event, db);
@@ -408,8 +420,8 @@
AccountState uploader = accountCache.get(patchSet.getUploader());
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.change = eventFactory.asChangeAttribute(db, change);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.uploader = eventFactory.asAccountAttribute(uploader.getAccount());
fireEvent(change, event, db);
@@ -434,9 +446,9 @@
CommentAddedEvent event = new CommentAddedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.author = eventFactory.asAccountAttribute(account);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.comment = comment;
LabelTypes labelTypes = projectCache.get(change.getProject()).getLabelTypes();
@@ -478,9 +490,9 @@
ChangeMergedEvent event = new ChangeMergedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.submitter = eventFactory.asAccountAttribute(account);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.newRev = mergeResultRev;
fireEvent(change, event, db);
@@ -505,9 +517,9 @@
MergeFailedEvent event = new MergeFailedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.submitter = eventFactory.asAccountAttribute(account);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.reason = reason;
fireEvent(change, event, db);
@@ -532,9 +544,9 @@
ChangeAbandonedEvent event = new ChangeAbandonedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.abandoner = eventFactory.asAccountAttribute(account);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.reason = reason;
fireEvent(change, event, db);
@@ -559,9 +571,9 @@
ChangeRestoredEvent event = new ChangeRestoredEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.restorer = eventFactory.asAccountAttribute(account);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.reason = reason;
fireEvent(change, event, db);
@@ -615,8 +627,8 @@
ReviewerAddedEvent event = new ReviewerAddedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
- event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+ event.change = eventFactory.asChangeAttribute(db, change);
+ event.patchSet = asPatchSetAttribute(change, patchSet, db);
event.reviewer = eventFactory.asAccountAttribute(account);
fireEvent(change, event, db);
@@ -638,7 +650,7 @@
TopicChangedEvent event = new TopicChangedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.changer = eventFactory.asAccountAttribute(account);
event.oldTopic = oldTopic;
fireEvent(change, event, db);
@@ -670,7 +682,7 @@
HashtagsChangedEvent event = new HashtagsChangedEvent();
AccountState owner = accountCache.get(change.getOwner());
- event.change = eventFactory.asChangeAttribute(change);
+ event.change = eventFactory.asChangeAttribute(db, change);
event.editor = eventFactory.asAccountAttribute(account);
event.hashtags = hashtagArray(hashtags);
event.added = hashtagArray(added);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 961cc45..44c1f01 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -17,8 +17,8 @@
import static com.google.gerrit.rules.StoredValue.create;
import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetInfo;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
index 9a64369..1d1f571 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalCopier.java
@@ -46,6 +46,7 @@
import java.io.IOException;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.NavigableSet;
import java.util.Objects;
@@ -87,7 +88,11 @@
Iterable<PatchSetApproval> getForPatchSet(ReviewDb db,
ChangeControl ctl, PatchSet.Id psId) throws OrmException {
- return getForPatchSet(db, ctl, db.patchSets().get(psId));
+ PatchSet ps = db.patchSets().get(psId);
+ if (ps == null) {
+ return Collections.emptyList();
+ }
+ return getForPatchSet(db, ctl, ps);
}
private Iterable<PatchSetApproval> getForPatchSet(ReviewDb db,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 0de7e38..6c80d26 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -21,13 +21,13 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetAncestor;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeMessages;
@@ -39,6 +39,7 @@
import com.google.gerrit.server.git.validators.CommitValidators;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.RevertedSender;
+import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.RefControl;
@@ -72,7 +73,6 @@
import java.io.IOException;
import java.text.MessageFormat;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -149,19 +149,6 @@
c.setLastUpdatedOn(TimeUtil.nowTs());
}
- public static void insertAncestors(ReviewDb db, PatchSet.Id id, RevCommit src)
- throws OrmException {
- int cnt = src.getParentCount();
- List<PatchSetAncestor> toInsert = new ArrayList<>(cnt);
- for (int p = 0; p < cnt; p++) {
- PatchSetAncestor a =
- new PatchSetAncestor(new PatchSetAncestor.Id(id, p + 1));
- a.setAncestorRevision(new RevId(src.getParent(p).getId().getName()));
- toInsert.add(a);
- }
- db.patchSetAncestors().insert(toInsert);
- }
-
public static PatchSet.Id nextPatchSetId(Map<String, Ref> allRefs,
PatchSet.Id id) {
PatchSet.Id next = nextPatchSetId(id);
@@ -199,6 +186,8 @@
private final GitReferenceUpdated gitRefUpdated;
private final ChangeIndexer indexer;
private final BatchUpdate.Factory updateFactory;
+ private final ChangeMessagesUtil changeMessagesUtil;
+ private final ChangeUpdate.Factory changeUpdateFactory;
@Inject
ChangeUtil(Provider<IdentifiedUser> user,
@@ -209,7 +198,9 @@
GitRepositoryManager gitManager,
GitReferenceUpdated gitRefUpdated,
ChangeIndexer indexer,
- BatchUpdate.Factory updateFactory) {
+ BatchUpdate.Factory updateFactory,
+ ChangeMessagesUtil changeMessagesUtil,
+ ChangeUpdate.Factory changeUpdateFactory) {
this.user = user;
this.db = db;
this.queryProvider = queryProvider;
@@ -219,6 +210,8 @@
this.gitRefUpdated = gitRefUpdated;
this.indexer = indexer;
this.updateFactory = updateFactory;
+ this.changeMessagesUtil = changeMessagesUtil;
+ this.changeUpdateFactory = changeUpdateFactory;
}
public Change.Id revert(ChangeControl ctl, PatchSet.Id patchSetId,
@@ -242,6 +235,10 @@
PersonIdent authorIdent = user.get()
.newCommitterIdent(myIdent.getWhen(), myIdent.getTimeZone());
+ if (commitToRevert.getParentCount() == 0) {
+ throw new ResourceConflictException("Cannot revert initial commit");
+ }
+
RevCommit parentToCommitToRevert = commitToRevert.getParent(0);
revWalk.parseHeaders(parentToCommitToRevert);
@@ -281,12 +278,22 @@
ins = changeInserterFactory.create(
refControl, change, revertCommit)
.setValidatePolicy(CommitValidators.Policy.GERRIT);
+
+ ChangeMessage changeMessage = new ChangeMessage(
+ new ChangeMessage.Key(
+ patchSetId.getParentKey(), ChangeUtil.messageUUID(db.get())),
+ user.get().getAccountId(), TimeUtil.nowTs(), patchSetId);
StringBuilder msgBuf = new StringBuilder();
msgBuf.append("Patch Set ").append(patchSetId.get()).append(": Reverted");
msgBuf.append("\n\n");
msgBuf.append("This patchset was reverted in change: ")
.append(change.getKey().get());
- ins.setMessage(msgBuf.toString());
+ changeMessage.setMessage(msgBuf.toString());
+ ChangeUpdate update = changeUpdateFactory.create(ctl, TimeUtil.nowTs());
+ changeMessagesUtil.addChangeMessage(db.get(), update, changeMessage);
+ update.commit();
+
+ ins.setMessage("Uploaded patch set 1.");
try (BatchUpdate bu = updateFactory.create(
db.get(), change.getProject(), refControl.getUser(),
change.getCreatedOn())) {
@@ -354,7 +361,6 @@
db.patchComments().delete(db.patchComments().byChange(changeId));
db.patchSetApprovals().delete(db.patchSetApprovals().byChange(changeId));
- db.patchSetAncestors().delete(db.patchSetAncestors().byChange(changeId));
db.patchSets().delete(patchSets);
db.changeMessages().delete(db.changeMessages().byChange(changeId));
db.starredChanges().delete(db.starredChanges().byChange(changeId));
@@ -461,7 +467,6 @@
// No need to delete from notedb; draft patch sets will be filtered out.
db.patchComments().delete(db.patchComments().byPatchSet(patchSetId));
db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(patchSetId));
- db.patchSetAncestors().delete(db.patchSetAncestors().byPatchSet(patchSetId));
db.patchSets().delete(Collections.singleton(patch));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
index df25042..9e5ff12 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -21,7 +21,6 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccountInfo;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountProjectWatch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.StarredChange;
@@ -110,14 +109,16 @@
public IdentifiedUser create(SocketAddress remotePeer, Account.Id id) {
return new IdentifiedUser(capabilityControlFactory, authConfig, realm,
anonymousCowardName, canonicalUrl, accountCache, groupBackend,
- disableReverseDnsLookup, Providers.of(remotePeer), null, id, null);
+ disableReverseDnsLookup, Providers.of(remotePeer), null,
+ id, null);
}
public CurrentUser runAs(SocketAddress remotePeer, Account.Id id,
@Nullable CurrentUser caller) {
return new IdentifiedUser(capabilityControlFactory, authConfig, realm,
anonymousCowardName, canonicalUrl, accountCache, groupBackend,
- disableReverseDnsLookup, Providers.of(remotePeer), null, id, caller);
+ disableReverseDnsLookup, Providers.of(remotePeer), null,
+ id, caller);
}
}
@@ -151,7 +152,6 @@
final AccountCache accountCache,
final GroupBackend groupBackend,
@DisableReverseDnsLookup final Boolean disableReverseDnsLookup,
-
@RemotePeer final Provider<SocketAddress> remotePeerProvider,
final Provider<ReviewDb> dbProvider) {
this.capabilityControlFactory = capabilityControlFactory;
@@ -162,7 +162,6 @@
this.accountCache = accountCache;
this.groupBackend = groupBackend;
this.disableReverseDnsLookup = disableReverseDnsLookup;
-
this.remotePeerProvider = remotePeerProvider;
this.dbProvider = dbProvider;
}
@@ -170,13 +169,15 @@
public IdentifiedUser create(Account.Id id) {
return new IdentifiedUser(capabilityControlFactory, authConfig, realm,
anonymousCowardName, canonicalUrl, accountCache, groupBackend,
- disableReverseDnsLookup, remotePeerProvider, dbProvider, id, null);
+ disableReverseDnsLookup, remotePeerProvider, dbProvider,
+ id, null);
}
public IdentifiedUser runAs(Account.Id id, CurrentUser caller) {
return new IdentifiedUser(capabilityControlFactory, authConfig, realm,
anonymousCowardName, canonicalUrl, accountCache, groupBackend,
- disableReverseDnsLookup, remotePeerProvider, dbProvider, id, caller);
+ disableReverseDnsLookup, remotePeerProvider, dbProvider,
+ id, caller);
}
}
@@ -274,20 +275,6 @@
return state().getAccount();
}
- public AccountDiffPreference getAccountDiffPreference() {
- AccountDiffPreference diffPref;
- try {
- diffPref = dbProvider.get().accountDiffPreferences().get(getAccountId());
- if (diffPref == null) {
- diffPref = AccountDiffPreference.createDefault(getAccountId());
- }
- } catch (OrmException e) {
- log.warn("Cannot query account diff preferences", e);
- diffPref = AccountDiffPreference.createDefault(getAccountId());
- }
- return diffPref;
- }
-
public boolean hasEmailAddress(String email) {
if (validEmails.contains(email)) {
return true;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
index 19aeefc..d2d6bf2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
@@ -14,91 +14,140 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.extensions.client.Theme;
+import static com.google.gerrit.server.config.ConfigUtil.loadSection;
+
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.UserConfigSections;
+import com.google.gerrit.server.patch.PatchListKey;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+
@Singleton
public class GetDiffPreferences implements RestReadView<AccountResource> {
private final Provider<CurrentUser> self;
private final Provider<ReviewDb> db;
+ private final Provider<AllUsersName> allUsersName;
+ private final GitRepositoryManager gitMgr;
+ private final boolean readFromGit;
@Inject
- GetDiffPreferences(Provider<CurrentUser> self, Provider<ReviewDb> db) {
+ GetDiffPreferences(Provider<CurrentUser> self,
+ Provider<ReviewDb> db,
+ @GerritServerConfig Config cfg,
+ Provider<AllUsersName> allUsersName,
+ GitRepositoryManager gitMgr) {
this.self = self;
this.db = db;
+ this.allUsersName = allUsersName;
+ this.gitMgr = gitMgr;
+ readFromGit = cfg.getBoolean("user", null, "readPrefsFromGit", false);
}
@Override
public DiffPreferencesInfo apply(AccountResource rsrc)
- throws AuthException, OrmException {
+ throws AuthException, OrmException, ConfigInvalidException, IOException {
if (self.get() != rsrc.getUser()
&& !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("restricted to administrator");
}
Account.Id userId = rsrc.getUser().getAccountId();
- AccountDiffPreference a = db.get().accountDiffPreferences().get(userId);
- if (a == null) {
- a = new AccountDiffPreference(userId);
- }
- return DiffPreferencesInfo.parse(a);
+ return readFromGit
+ ? readFromGit(userId, gitMgr, allUsersName.get(), null)
+ : readFromDb(userId);
}
- public static class DiffPreferencesInfo {
- static DiffPreferencesInfo parse(AccountDiffPreference p) {
- DiffPreferencesInfo info = new DiffPreferencesInfo();
- info.context = p.getContext();
- info.expandAllComments = p.isExpandAllComments() ? true : null;
- info.ignoreWhitespace = p.getIgnoreWhitespace();
- info.intralineDifference = p.isIntralineDifference() ? true : null;
- info.lineLength = p.getLineLength();
- info.manualReview = p.isManualReview() ? true : null;
- info.retainHeader = p.isRetainHeader() ? true : null;
- info.showLineEndings = p.isShowLineEndings() ? true : null;
- info.showTabs = p.isShowTabs() ? true : null;
- info.showWhitespaceErrors = p.isShowWhitespaceErrors() ? true : null;
- info.skipDeleted = p.isSkipDeleted() ? true : null;
- info.skipUncommented = p.isSkipUncommented() ? true : null;
- info.hideTopMenu = p.isHideTopMenu() ? true : null;
- info.autoHideDiffTableHeader = p.isAutoHideDiffTableHeader() ? true : null;
- info.hideLineNumbers = p.isHideLineNumbers() ? true : null;
- info.syntaxHighlighting = p.isSyntaxHighlighting() ? true : null;
- info.tabSize = p.getTabSize();
- info.renderEntireFile = p.isRenderEntireFile() ? true : null;
- info.hideEmptyPane = p.isHideEmptyPane() ? true : null;
- info.theme = p.getTheme();
- return info;
+ static DiffPreferencesInfo readFromGit(Account.Id id,
+ GitRepositoryManager gitMgr, AllUsersName allUsersName,
+ DiffPreferencesInfo in)
+ throws IOException, ConfigInvalidException, RepositoryNotFoundException {
+ try (Repository git = gitMgr.openRepository(allUsersName)) {
+ VersionedAccountPreferences p =
+ VersionedAccountPreferences.forUser(id);
+ p.load(git);
+ DiffPreferencesInfo prefs = new DiffPreferencesInfo();
+ loadSection(p.getConfig(), UserConfigSections.DIFF, null, prefs,
+ DiffPreferencesInfo.defaults(), in);
+ return prefs;
+ }
+ }
+
+ private DiffPreferencesInfo readFromDb(Account.Id id)
+ throws OrmException {
+ AccountDiffPreference a = db.get().accountDiffPreferences().get(id);
+ return nullify(initFromDb(a));
+ }
+
+ static DiffPreferencesInfo initFromDb(AccountDiffPreference a) {
+ DiffPreferencesInfo prefs = DiffPreferencesInfo.defaults();
+ if (a != null) {
+ prefs.context = (int)a.getContext();
+ prefs.expandAllComments = a.isExpandAllComments();
+ prefs.hideLineNumbers = a.isHideLineNumbers();
+ prefs.hideTopMenu = a.isHideTopMenu();
+ prefs.ignoreWhitespace = PatchListKey.WHITESPACE_TYPES.inverse().get(
+ a.getIgnoreWhitespace().getCode());
+ prefs.intralineDifference = a.isIntralineDifference();
+ prefs.lineLength = a.getLineLength();
+ prefs.manualReview = a.isManualReview();
+ prefs.renderEntireFile = a.isRenderEntireFile();
+ prefs.retainHeader = a.isRetainHeader();
+ prefs.showLineEndings = a.isShowLineEndings();
+ prefs.showTabs = a.isShowTabs();
+ prefs.showWhitespaceErrors = a.isShowWhitespaceErrors();
+ prefs.skipDeleted = a.isSkipDeleted();
+ prefs.skipUncommented = a.isSkipUncommented();
+ prefs.syntaxHighlighting = a.isSyntaxHighlighting();
+ prefs.tabSize = a.getTabSize();
+ prefs.theme = a.getTheme();
+ prefs.hideEmptyPane = a.isHideEmptyPane();
+ prefs.autoHideDiffTableHeader = a.isAutoHideDiffTableHeader();
}
- public short context;
- public Boolean expandAllComments;
- public Whitespace ignoreWhitespace;
- public Boolean intralineDifference;
- public int lineLength;
- public Boolean manualReview;
- public Boolean retainHeader;
- public Boolean showLineEndings;
- public Boolean showTabs;
- public Boolean showWhitespaceErrors;
- public Boolean skipDeleted;
- public Boolean skipUncommented;
- public Boolean syntaxHighlighting;
- public Boolean hideTopMenu;
- public Boolean autoHideDiffTableHeader;
- public Boolean hideLineNumbers;
- public Boolean renderEntireFile;
- public Boolean hideEmptyPane;
- public int tabSize;
- public Theme theme;
+ return prefs;
+ }
+
+ private static DiffPreferencesInfo nullify(DiffPreferencesInfo prefs) {
+ prefs.expandAllComments = b(prefs.expandAllComments);
+ prefs.hideLineNumbers = b(prefs.hideLineNumbers);
+ prefs.hideTopMenu = b(prefs.hideTopMenu);
+ prefs.intralineDifference = b(prefs.intralineDifference);
+ prefs.manualReview = b(prefs.manualReview);
+ prefs.renderEntireFile = b(prefs.renderEntireFile);
+ prefs.retainHeader = b(prefs.retainHeader);
+ prefs.showLineEndings = b(prefs.showLineEndings);
+ prefs.showTabs = b(prefs.showTabs);
+ prefs.showWhitespaceErrors = b(prefs.showWhitespaceErrors);
+ prefs.skipDeleted = b(prefs.skipDeleted);
+ prefs.skipUncommented = b(prefs.skipUncommented);
+ prefs.syntaxHighlighting = b(prefs.syntaxHighlighting);
+ prefs.hideEmptyPane = b(prefs.hideEmptyPane);
+ prefs.autoHideDiffTableHeader = b(prefs.autoHideDiffTableHeader);
+ return prefs;
+ }
+
+ private static Boolean b(Boolean b) {
+ if (b == null) {
+ return null;
+ }
+ return b ? Boolean.TRUE : null;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java
index e6e7644..d99b68f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEditPreferences.java
@@ -19,6 +19,7 @@
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -28,6 +29,7 @@
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
@@ -55,13 +57,21 @@
throw new AuthException("restricted to members of Modify Accounts");
}
+ return readFromGit(
+ rsrc.getUser().getAccountId(), gitMgr, allUsersName, null);
+ }
+
+ static EditPreferencesInfo readFromGit(Account.Id id,
+ GitRepositoryManager gitMgr, AllUsersName allUsersName,
+ EditPreferencesInfo in) throws IOException, ConfigInvalidException,
+ RepositoryNotFoundException {
try (Repository git = gitMgr.openRepository(allUsersName)) {
VersionedAccountPreferences p =
- VersionedAccountPreferences.forUser(rsrc.getUser().getAccountId());
+ VersionedAccountPreferences.forUser(id);
p.load(git);
return loadSection(p.getConfig(), UserConfigSections.EDIT, null,
- new EditPreferencesInfo(), EditPreferencesInfo.defaults());
+ new EditPreferencesInfo(), EditPreferencesInfo.defaults(), in);
}
}
}
\ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java
index 4e08756..7b4d133 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetDiffPreferences.java
@@ -14,144 +14,224 @@
package com.google.gerrit.server.account;
-import com.google.gerrit.extensions.client.Theme;
+import static com.google.gerrit.server.account.GetDiffPreferences.initFromDb;
+import static com.google.gerrit.server.account.GetDiffPreferences.readFromGit;
+import static com.google.gerrit.server.config.ConfigUtil.loadSection;
+import static com.google.gerrit.server.config.ConfigUtil.storeSection;
+
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.GetDiffPreferences.DiffPreferencesInfo;
-import com.google.gerrit.server.account.SetDiffPreferences.Input;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.UserConfigSections;
+import com.google.gerrit.server.patch.PatchListKey;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Config;
+
+import java.io.IOException;
import java.util.Collections;
@Singleton
-public class SetDiffPreferences implements RestModifyView<AccountResource, Input> {
- static class Input {
- Short context;
- Boolean expandAllComments;
- Whitespace ignoreWhitespace;
- Boolean intralineDifference;
- Integer lineLength;
- Boolean manualReview;
- Boolean retainHeader;
- Boolean showLineEndings;
- Boolean showTabs;
- Boolean showWhitespaceErrors;
- Boolean skipDeleted;
- Boolean skipUncommented;
- Boolean syntaxHighlighting;
- Boolean hideTopMenu;
- Boolean autoHideDiffTableHeader;
- Boolean hideLineNumbers;
- Boolean renderEntireFile;
- Integer tabSize;
- Theme theme;
- Boolean hideEmptyPane;
- }
-
+public class SetDiffPreferences implements
+ RestModifyView<AccountResource, DiffPreferencesInfo> {
private final Provider<CurrentUser> self;
private final Provider<ReviewDb> db;
+ private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
+ private final AllUsersName allUsersName;
+ private final GitRepositoryManager gitMgr;
+ private final boolean readFromGit;
@Inject
- SetDiffPreferences(Provider<CurrentUser> self, Provider<ReviewDb> db) {
+ SetDiffPreferences(Provider<CurrentUser> self,
+ Provider<ReviewDb> db,
+ @GerritServerConfig Config cfg,
+ Provider<MetaDataUpdate.User> metaDataUpdateFactory,
+ AllUsersName allUsersName,
+ GitRepositoryManager gitMgr) {
this.self = self;
this.db = db;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.allUsersName = allUsersName;
+ this.gitMgr = gitMgr;
+ readFromGit = cfg.getBoolean("user", null, "readPrefsFromGit", false);
}
@Override
- public DiffPreferencesInfo apply(AccountResource rsrc, Input input)
- throws AuthException, OrmException {
+ public DiffPreferencesInfo apply(AccountResource rsrc, DiffPreferencesInfo in)
+ throws AuthException, BadRequestException, ConfigInvalidException,
+ RepositoryNotFoundException, IOException, OrmException {
if (self.get() != rsrc.getUser()
&& !self.get().getCapabilities().canModifyAccount()) {
throw new AuthException("restricted to members of Modify Accounts");
}
- if (input == null) {
- input = new Input();
+
+ if (in == null) {
+ throw new BadRequestException("input must be provided");
}
- Account.Id accountId = rsrc.getUser().getAccountId();
- AccountDiffPreference p;
+ Account.Id userId = rsrc.getUser().getAccountId();
+ DiffPreferencesInfo n = readFromGit
+ ? readFromGit(userId, gitMgr, allUsersName, in)
+ : merge(initFromDb(db.get().accountDiffPreferences().get(userId)), in);
+ DiffPreferencesInfo out = writeToGit(n, userId);
+ writeToDb(n, userId);
+ return out;
+ }
- db.get().accounts().beginTransaction(accountId);
+ private void writeToDb(DiffPreferencesInfo in, Account.Id id)
+ throws OrmException {
+ db.get().accounts().beginTransaction(id);
try {
- p = db.get().accountDiffPreferences().get(accountId);
- if (p == null) {
- p = new AccountDiffPreference(accountId);
- }
-
- if (input.context != null) {
- p.setContext(input.context);
- }
- if (input.ignoreWhitespace != null) {
- p.setIgnoreWhitespace(input.ignoreWhitespace);
- }
- if (input.expandAllComments != null) {
- p.setExpandAllComments(input.expandAllComments);
- }
- if (input.intralineDifference != null) {
- p.setIntralineDifference(input.intralineDifference);
- }
- if (input.lineLength != null) {
- p.setLineLength(input.lineLength);
- }
- if (input.manualReview != null) {
- p.setManualReview(input.manualReview);
- }
- if (input.retainHeader != null) {
- p.setRetainHeader(input.retainHeader);
- }
- if (input.showLineEndings != null) {
- p.setShowLineEndings(input.showLineEndings);
- }
- if (input.showTabs != null) {
- p.setShowTabs(input.showTabs);
- }
- if (input.showWhitespaceErrors != null) {
- p.setShowWhitespaceErrors(input.showWhitespaceErrors);
- }
- if (input.skipDeleted != null) {
- p.setSkipDeleted(input.skipDeleted);
- }
- if (input.skipUncommented != null) {
- p.setSkipUncommented(input.skipUncommented);
- }
- if (input.syntaxHighlighting != null) {
- p.setSyntaxHighlighting(input.syntaxHighlighting);
- }
- if (input.hideTopMenu != null) {
- p.setHideTopMenu(input.hideTopMenu);
- }
- if (input.autoHideDiffTableHeader != null) {
- p.setAutoHideDiffTableHeader(input.autoHideDiffTableHeader);
- }
- if (input.hideLineNumbers != null) {
- p.setHideLineNumbers(input.hideLineNumbers);
- }
- if (input.renderEntireFile != null) {
- p.setRenderEntireFile(input.renderEntireFile);
- }
- if (input.tabSize != null) {
- p.setTabSize(input.tabSize);
- }
- if (input.theme != null) {
- p.setTheme(input.theme);
- }
- if (input.hideEmptyPane != null) {
- p.setHideEmptyPane(input.hideEmptyPane);
- }
-
+ AccountDiffPreference p = db.get().accountDiffPreferences().get(id);
+ p = initAccountDiffPreferences(p, in, id);
db.get().accountDiffPreferences().upsert(Collections.singleton(p));
db.get().commit();
} finally {
db.get().rollback();
}
- return DiffPreferencesInfo.parse(p);
+ }
+
+ private DiffPreferencesInfo writeToGit(DiffPreferencesInfo in,
+ Account.Id useId) throws RepositoryNotFoundException, IOException,
+ ConfigInvalidException {
+ MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName);
+
+ VersionedAccountPreferences prefs;
+ DiffPreferencesInfo out = new DiffPreferencesInfo();
+ try {
+ prefs = VersionedAccountPreferences.forUser(useId);
+ prefs.load(md);
+ storeSection(prefs.getConfig(), UserConfigSections.DIFF, null, in,
+ DiffPreferencesInfo.defaults());
+ prefs.commit(md);
+ loadSection(prefs.getConfig(), UserConfigSections.DIFF, null, out,
+ DiffPreferencesInfo.defaults(), null);
+ } finally {
+ md.close();
+ }
+ return out;
+ }
+
+ // TODO(davido): Remove manual merging in follow-up change
+ private DiffPreferencesInfo merge(DiffPreferencesInfo n,
+ DiffPreferencesInfo i) {
+ if (i.context != null) {
+ n.context = i.context;
+ }
+ if (i.expandAllComments != null) {
+ n.expandAllComments = i.expandAllComments;
+ }
+ if (i.hideLineNumbers != null) {
+ n.hideLineNumbers = i.hideLineNumbers;
+ }
+ if (i.hideTopMenu != null) {
+ n.hideTopMenu = i.hideTopMenu;
+ }
+ if (i.ignoreWhitespace != null) {
+ n.ignoreWhitespace = i.ignoreWhitespace;
+ }
+ if (i.intralineDifference != null) {
+ n.intralineDifference = i.intralineDifference;
+ }
+ if (i.lineLength != null) {
+ n.lineLength = i.lineLength;
+ }
+ if (i.manualReview != null) {
+ n.manualReview = i.manualReview;
+ }
+ if (i.renderEntireFile != null) {
+ n.renderEntireFile = i.renderEntireFile;
+ }
+ if (i.retainHeader != null) {
+ n.retainHeader = i.retainHeader;
+ }
+ if (i.showLineEndings != null) {
+ n.showLineEndings = i.showLineEndings;
+ }
+ if (i.showTabs != null) {
+ n.showTabs = i.showTabs;
+ }
+ if (i.showWhitespaceErrors != null) {
+ n.showWhitespaceErrors = i.showWhitespaceErrors;
+ }
+ if (i.skipDeleted != null) {
+ n.skipDeleted = i.skipDeleted;
+ }
+ if (i.skipUncommented != null) {
+ n.skipUncommented = i.skipUncommented;
+ }
+ if (i.syntaxHighlighting != null) {
+ n.syntaxHighlighting = i.syntaxHighlighting;
+ }
+ if (i.tabSize != null) {
+ n.tabSize = i.tabSize;
+ }
+ if (i.theme != null) {
+ n.theme = i.theme;
+ }
+ if (i.hideEmptyPane != null) {
+ n.hideEmptyPane = i.hideEmptyPane;
+ }
+ if (i.autoHideDiffTableHeader != null) {
+ n.autoHideDiffTableHeader = i.autoHideDiffTableHeader;
+ }
+ return n;
+ }
+
+ private static AccountDiffPreference initAccountDiffPreferences(
+ AccountDiffPreference a, DiffPreferencesInfo i, Account.Id id) {
+ if (a == null) {
+ a = AccountDiffPreference.createDefault(id);
+ }
+ int context = i.context == null
+ ? DiffPreferencesInfo.DEFAULT_CONTEXT
+ : i.context;
+ a.setContext((short)context);
+ a.setExpandAllComments(b(i.expandAllComments));
+ a.setHideLineNumbers(b(i.hideLineNumbers));
+ a.setHideTopMenu(b(i.hideTopMenu));
+ a.setIgnoreWhitespace(i.ignoreWhitespace == null
+ ? Whitespace.IGNORE_NONE
+ : Whitespace.forCode(
+ PatchListKey.WHITESPACE_TYPES.get(i.ignoreWhitespace)));
+ a.setIntralineDifference(b(i.intralineDifference));
+ a.setLineLength(i.lineLength == null
+ ? DiffPreferencesInfo.DEFAULT_LINE_LENGTH
+ : i.lineLength);
+ a.setManualReview(b(i.manualReview));
+ a.setRenderEntireFile(b(i.renderEntireFile));
+ a.setRetainHeader(b(i.retainHeader));
+ a.setShowLineEndings(b(i.showLineEndings));
+ a.setShowTabs(b(i.showTabs));
+ a.setShowWhitespaceErrors(b(i.showWhitespaceErrors));
+ a.setSkipDeleted(b(i.skipDeleted));
+ a.setSkipUncommented(b(i.skipUncommented));
+ a.setSyntaxHighlighting(b(i.syntaxHighlighting));
+ a.setTabSize(i.tabSize == null
+ ? DiffPreferencesInfo.DEFAULT_TAB_SIZE
+ : i.tabSize);
+ a.setTheme(i.theme);
+ a.setHideEmptyPane(b(i.hideEmptyPane));
+ a.setAutoHideDiffTableHeader(b(i.autoHideDiffTableHeader));
+ return a;
+ }
+
+ private static boolean b(Boolean b) {
+ return b == null ? false : b;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java
index 2df1a77..eabe31d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetEditPreferences.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.account;
+import static com.google.gerrit.server.account.GetEditPreferences.readFromGit;
import static com.google.gerrit.server.config.ConfigUtil.storeSection;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
@@ -24,6 +25,7 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.UserConfigSections;
import com.google.inject.Inject;
@@ -41,14 +43,17 @@
private final Provider<CurrentUser> self;
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
+ private final GitRepositoryManager gitMgr;
private final AllUsersName allUsersName;
@Inject
SetEditPreferences(Provider<CurrentUser> self,
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
+ GitRepositoryManager gitMgr,
AllUsersName allUsersName) {
this.self = self;
this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.gitMgr = gitMgr;
this.allUsersName = allUsersName;
}
@@ -72,7 +77,8 @@
try {
prefs = VersionedAccountPreferences.forUser(accountId);
prefs.load(md);
- storeSection(prefs.getConfig(), UserConfigSections.EDIT, null, in,
+ storeSection(prefs.getConfig(), UserConfigSections.EDIT, null,
+ readFromGit(accountId, gitMgr, allUsersName, in),
EditPreferencesInfo.defaults());
prefs.commit(md);
} finally {
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 344eda1..90d6aae 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
@@ -245,7 +245,6 @@
ReviewDb db = ctx.getDb();
ChangeControl ctl = ctx.getChangeControl();
ChangeUpdate update = ctx.getChangeUpdate();
- ChangeUtil.insertAncestors(db, patchSet.getId(), commit);
if (patchSet.getGroups() == null) {
patchSet.setGroups(GroupCollector.getDefaultGroups(patchSet));
}
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 7d126e3..733d7a2 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,7 +118,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
@@ -785,14 +784,6 @@
return Collections.emptyList();
}
- // chronological order
- Collections.sort(messages, new Comparator<ChangeMessage>() {
- @Override
- public int compare(ChangeMessage a, ChangeMessage b) {
- return a.getWrittenOn().compareTo(b.getWrittenOn());
- }
- });
-
List<ChangeMessageInfo> result =
Lists.newArrayListWithCapacity(messages.size());
for (ChangeMessage message : messages) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
index b0f14af..82aaecb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
@@ -25,7 +25,7 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.UpdateException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException;
@@ -87,7 +87,7 @@
return json.create(ChangeJson.NO_OPTIONS).format(cherryPickedChangeId);
} catch (InvalidChangeOperationException e) {
throw new BadRequestException(e.getMessage());
- } catch (MergeException | NoSuchChangeException e) {
+ } catch (IntegrationException | NoSuchChangeException e) {
throw new ResourceConflictException(e.getMessage());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index e559964..fc4893c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -17,6 +17,7 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
@@ -33,8 +34,7 @@
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MergeConflictException;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeIdenticalTreeException;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.UpdateException;
@@ -112,7 +112,7 @@
final RefControl refControl) throws NoSuchChangeException,
OrmException, MissingObjectException,
IncorrectObjectTypeException, IOException,
- InvalidChangeOperationException, MergeException, UpdateException,
+ InvalidChangeOperationException, IntegrationException, UpdateException,
RestApiException {
if (Strings.isNullOrEmpty(ref)) {
@@ -195,7 +195,7 @@
return newChange.getId();
}
} catch (MergeIdenticalTreeException | MergeConflictException e) {
- throw new MergeException("Cherry pick failed: " + e.getMessage());
+ throw new IntegrationException("Cherry pick failed: " + e.getMessage());
}
} catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(change.getId(), e);
@@ -254,7 +254,8 @@
private void addMessageToSourceChange(Change change, PatchSet.Id patchSetId,
String destinationBranch, CodeReviewCommit cherryPickCommit,
- IdentifiedUser identifiedUser, RefControl refControl) throws OrmException {
+ IdentifiedUser identifiedUser, RefControl refControl)
+ throws OrmException, IOException {
ChangeMessage changeMessage = new ChangeMessage(
new ChangeMessage.Key(
patchSetId.getParentKey(), ChangeUtil.messageUUID(db.get())),
@@ -270,8 +271,9 @@
changeMessage.setMessage(sb.toString());
ChangeControl ctl = refControl.getProjectControl().controlFor(change);
- ChangeUpdate update = updateFactory.create(ctl, change.getCreatedOn());
+ ChangeUpdate update = updateFactory.create(ctl, TimeUtil.nowTs());
changeMessagesUtil.addChangeMessage(db.get(), update, changeMessage);
+ update.commit();
}
private String messageForDestinationChange(PatchSet.Id patchSetId,
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 8a0a47e..ddeb5c0 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
@@ -583,8 +583,6 @@
// historical information.
db.accountPatchReviews().delete(
db.accountPatchReviews().byPatchSet(psId));
- db.patchSetAncestors().delete(
- db.patchSetAncestors().byPatchSet(psId));
db.patchSetApprovals().delete(
db.patchSetApprovals().byPatchSet(psId));
db.patchComments().delete(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java
index 84f8a04..71974c4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileInfoJson.java
@@ -16,8 +16,8 @@
import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.common.FileInfo;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
index eef0533..3dcc442 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDiff.java
@@ -24,6 +24,7 @@
import com.google.common.collect.Maps;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.common.ChangeType;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.DiffInfo.ContentEntry;
@@ -39,8 +40,6 @@
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
@@ -52,7 +51,6 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.ReplaceEdit;
@@ -69,6 +67,8 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
+
public class GetDiff implements RestReadView<FileResource> {
private static final ImmutableMap<Patch.ChangeType, ChangeType> CHANGE_TYPE =
Maps.immutableEnumMap(
@@ -93,7 +93,7 @@
IgnoreWhitespace ignoreWhitespace = IgnoreWhitespace.NONE;
@Option(name = "--context", handler = ContextOptionHandler.class)
- short context = AccountDiffPreference.DEFAULT_CONTEXT;
+ int context = DiffPreferencesInfo.DEFAULT_CONTEXT;
@Option(name = "--intraline")
boolean intraline;
@@ -122,10 +122,10 @@
resource.getRevision().getChangeResource(), IdString.fromDecoded(base));
basePatchSet = baseResource.getPatchSet();
}
- AccountDiffPreference prefs = new AccountDiffPreference(new Account.Id(0));
- prefs.setIgnoreWhitespace(ignoreWhitespace.whitespace);
- prefs.setContext(context);
- prefs.setIntralineDifference(intraline);
+ DiffPreferencesInfo prefs = new DiffPreferencesInfo();
+ prefs.ignoreWhitespace = ignoreWhitespace.whitespace;
+ prefs.context = context;
+ prefs.intralineDifference = intraline;
try {
PatchScriptFactory psf = patchScriptFactoryFactory.create(
@@ -135,7 +135,7 @@
resource.getPatchKey().getParentKey(),
prefs);
psf.setLoadHistory(false);
- psf.setLoadComments(context != AccountDiffPreference.WHOLE_FILE_CONTEXT);
+ psf.setLoadComments(context != DiffPreferencesInfo.WHOLE_FILE_CONTEXT);
PatchScript ps = psf.call();
Content content = new Content(ps);
for (Edit edit : ps.getEdits()) {
@@ -369,14 +369,14 @@
}
enum IgnoreWhitespace {
- NONE(AccountDiffPreference.Whitespace.IGNORE_NONE),
- TRAILING(AccountDiffPreference.Whitespace.IGNORE_SPACE_AT_EOL),
- CHANGED(AccountDiffPreference.Whitespace.IGNORE_SPACE_CHANGE),
- ALL(AccountDiffPreference.Whitespace.IGNORE_ALL_SPACE);
+ NONE(DiffPreferencesInfo.Whitespace.IGNORE_NONE),
+ TRAILING(DiffPreferencesInfo.Whitespace.IGNORE_TRAILING),
+ CHANGED(DiffPreferencesInfo.Whitespace.IGNORE_LEADING_AND_TRAILING),
+ ALL(DiffPreferencesInfo.Whitespace.IGNORE_ALL);
- private final AccountDiffPreference.Whitespace whitespace;
+ private final DiffPreferencesInfo.Whitespace whitespace;
- private IgnoreWhitespace(AccountDiffPreference.Whitespace whitespace) {
+ private IgnoreWhitespace(DiffPreferencesInfo.Whitespace whitespace) {
this.whitespace = whitespace;
}
}
@@ -393,7 +393,7 @@
final String value = params.getParameter(0);
short context;
if ("all".equalsIgnoreCase(value)) {
- context = AccountDiffPreference.WHOLE_FILE_CONTEXT;
+ context = DiffPreferencesInfo.WHOLE_FILE_CONTEXT;
} else {
try {
context = Short.parseShort(value, 10);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
index 77f7bda..6aa1a47 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelated.java
@@ -14,8 +14,6 @@
package com.google.gerrit.server.change;
-import static com.google.gerrit.server.index.ChangeField.GROUP;
-
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.gerrit.common.Nullable;
@@ -25,10 +23,7 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommonConverters;
-import com.google.gerrit.server.change.PatchSetAncestorSorter.PatchSetData;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.GroupCollector;
-import com.google.gerrit.server.index.IndexCollection;
+import com.google.gerrit.server.change.RelatedChangesSorter.PatchSetData;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
@@ -37,7 +32,6 @@
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException;
@@ -50,53 +44,36 @@
@Singleton
public class GetRelated implements RestReadView<RevisionResource> {
private final Provider<ReviewDb> db;
- private final GetRelatedByAncestors byAncestors;
private final Provider<InternalChangeQuery> queryProvider;
- private final PatchSetAncestorSorter sorter;
- private final IndexCollection indexes;
- private final boolean byAncestorsOnly;
+ private final RelatedChangesSorter sorter;
@Inject
GetRelated(Provider<ReviewDb> db,
- @GerritServerConfig Config cfg,
- GetRelatedByAncestors byAncestors,
Provider<InternalChangeQuery> queryProvider,
- PatchSetAncestorSorter sorter,
- IndexCollection indexes) {
+ RelatedChangesSorter sorter) {
this.db = db;
- this.byAncestors = byAncestors;
this.queryProvider = queryProvider;
this.sorter = sorter;
- this.indexes = indexes;
- byAncestorsOnly =
- cfg.getBoolean("change", null, "getRelatedByAncestors", false);
}
@Override
public RelatedInfo apply(RevisionResource rsrc)
throws RepositoryNotFoundException, IOException, OrmException {
- List<String> thisPatchSetGroups = GroupCollector.getGroups(rsrc);
- if (byAncestorsOnly
- || thisPatchSetGroups == null
- || !indexes.getSearchIndex().getSchema().hasField(GROUP)) {
- return byAncestors.getRelated(rsrc);
- }
RelatedInfo relatedInfo = new RelatedInfo();
- relatedInfo.changes = getRelated(rsrc, thisPatchSetGroups);
+ relatedInfo.changes = getRelated(rsrc);
return relatedInfo;
}
- private List<ChangeAndCommit> getRelated(RevisionResource rsrc,
- List<String> thisPatchSetGroups) throws OrmException, IOException {
- if (thisPatchSetGroups.isEmpty()) {
+ private List<ChangeAndCommit> getRelated(RevisionResource rsrc)
+ throws OrmException, IOException {
+ Set<String> groups = getAllGroups(rsrc.getChange().getId());
+ if (groups.isEmpty()) {
return Collections.emptyList();
}
List<ChangeData> cds = queryProvider.get()
.enforceVisibility(true)
- .byProjectGroups(
- rsrc.getChange().getProject(),
- getAllGroups(rsrc.getChange().getId()));
+ .byProjectGroups(rsrc.getChange().getProject(), groups);
if (cds.isEmpty()) {
return Collections.emptyList();
} if (cds.size() == 1
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelatedByAncestors.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelatedByAncestors.java
deleted file mode 100644
index 09d1ef0..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRelatedByAncestors.java
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright (C) 2015 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.change;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetAncestor;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.GetRelated.ChangeAndCommit;
-import com.google.gerrit.server.change.GetRelated.RelatedInfo;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Implementation of {@link GetRelated} using {@link PatchSetAncestor}s. */
-class GetRelatedByAncestors {
- private static final Logger log = LoggerFactory.getLogger(GetRelated.class);
-
- private final GitRepositoryManager gitMgr;
- private final Provider<ReviewDb> dbProvider;
- private final Provider<InternalChangeQuery> queryProvider;
-
- @Inject
- GetRelatedByAncestors(GitRepositoryManager gitMgr,
- Provider<ReviewDb> db,
- Provider<InternalChangeQuery> queryProvider) {
- this.gitMgr = gitMgr;
- this.dbProvider = db;
- this.queryProvider = queryProvider;
- }
-
- public RelatedInfo getRelated(RevisionResource rsrc)
- throws RepositoryNotFoundException, IOException, OrmException {
- try (Repository git = gitMgr.openRepository(rsrc.getChange().getProject());
- RevWalk rw = new RevWalk(git)) {
- Ref ref = git.getRefDatabase().exactRef(rsrc.getChange().getDest().get());
- RelatedInfo info = new RelatedInfo();
- info.changes = walk(rsrc, rw, ref);
- return info;
- }
- }
-
- private List<ChangeAndCommit> walk(RevisionResource rsrc, RevWalk rw, Ref ref)
- throws OrmException, IOException {
- Map<Change.Id, ChangeData> changes = allOpenChanges(rsrc);
- Map<PatchSet.Id, PatchSet> patchSets = allPatchSets(rsrc, changes.values());
-
- Map<String, PatchSet> commits = Maps.newHashMap();
- for (PatchSet p : patchSets.values()) {
- commits.put(p.getRevision().get(), p);
- }
-
- RevCommit rev = rw.parseCommit(ObjectId.fromString(
- rsrc.getPatchSet().getRevision().get()));
- rw.sort(RevSort.TOPO);
- rw.markStart(rev);
-
- if (ref != null && ref.getObjectId() != null) {
- try {
- rw.markUninteresting(rw.parseCommit(ref.getObjectId()));
- } catch (IncorrectObjectTypeException notCommit) {
- // Ignore and treat as new branch.
- }
- }
-
- Set<Change.Id> added = Sets.newHashSet();
- List<ChangeAndCommit> parents = Lists.newArrayList();
- for (RevCommit c; (c = rw.next()) != null;) {
- PatchSet p = commits.get(c.name());
- Change g = null;
- if (p != null) {
- g = changes.get(p.getId().getParentKey()).change();
- added.add(p.getId().getParentKey());
- }
- parents.add(new ChangeAndCommit(g, p, c));
- }
- List<ChangeAndCommit> list = children(rsrc, rw, changes, patchSets, added);
- list.addAll(parents);
-
- if (list.size() == 1) {
- ChangeAndCommit r = list.get(0);
- if (r.commit != null && r.commit.commit.equals(rsrc.getPatchSet().getRevision().get())) {
- return Collections.emptyList();
- }
- }
- return list;
- }
-
- private Map<Change.Id, ChangeData> allOpenChanges(RevisionResource rsrc)
- throws OrmException {
- return ChangeData.asMap(
- queryProvider.get().byBranchOpen(rsrc.getChange().getDest()));
- }
-
- private Map<PatchSet.Id, PatchSet> allPatchSets(RevisionResource rsrc,
- Collection<ChangeData> cds) throws OrmException {
- Map<PatchSet.Id, PatchSet> r =
- Maps.newHashMapWithExpectedSize(cds.size() * 2);
- for (ChangeData cd : cds) {
- for (PatchSet p : cd.patchSets()) {
- r.put(p.getId(), p);
- }
- }
-
- if (rsrc.getEdit().isPresent()) {
- r.put(rsrc.getPatchSet().getId(), rsrc.getPatchSet());
- }
- return r;
- }
-
- private List<ChangeAndCommit> children(RevisionResource rsrc, RevWalk rw,
- Map<Change.Id, ChangeData> changes, Map<PatchSet.Id, PatchSet> patchSets,
- Set<Change.Id> added)
- throws OrmException, IOException {
- // children is a map of parent commit name to PatchSet built on it.
- Multimap<String, PatchSet.Id> children = allChildren(changes.keySet());
-
- RevFlag seenCommit = rw.newFlag("seenCommit");
- LinkedList<String> q = Lists.newLinkedList();
- seedQueue(rsrc, rw, seenCommit, patchSets, q);
-
- ProjectControl projectCtl = rsrc.getControl().getProjectControl();
- Set<Change.Id> seenChange = Sets.newHashSet();
- List<ChangeAndCommit> graph = Lists.newArrayList();
- while (!q.isEmpty()) {
- String id = q.remove();
-
- // For every matching change find the most recent patch set.
- Map<Change.Id, PatchSet.Id> matches = Maps.newHashMap();
- for (PatchSet.Id psId : children.get(id)) {
- PatchSet.Id e = matches.get(psId.getParentKey());
- if ((e == null || e.get() < psId.get())
- && isVisible(projectCtl, changes, patchSets, psId)) {
- matches.put(psId.getParentKey(), psId);
- }
- }
-
- for (Map.Entry<Change.Id, PatchSet.Id> e : matches.entrySet()) {
- ChangeData cd = changes.get(e.getKey());
- PatchSet ps = patchSets.get(e.getValue());
- if (cd == null || ps == null || !seenChange.add(e.getKey())) {
- continue;
- }
-
- RevCommit c = rw.parseCommit(ObjectId.fromString(
- ps.getRevision().get()));
- if (!c.has(seenCommit)) {
- c.add(seenCommit);
- q.addFirst(ps.getRevision().get());
- if (added.add(ps.getId().getParentKey())) {
- rw.parseBody(c);
- graph.add(new ChangeAndCommit(cd.change(), ps, c));
- }
- }
- }
- }
- Collections.reverse(graph);
- return graph;
- }
-
- private boolean isVisible(ProjectControl projectCtl,
- Map<Change.Id, ChangeData> changes,
- Map<PatchSet.Id, PatchSet> patchSets,
- PatchSet.Id psId) throws OrmException {
- ChangeData cd = changes.get(psId.getParentKey());
- PatchSet ps = patchSets.get(psId);
- if (cd != null && ps != null) {
- // Related changes are in the same project, so reuse the existing
- // ProjectControl.
- ChangeControl ctl = projectCtl.controlFor(cd.change());
- return ctl.isVisible(dbProvider.get())
- && ctl.isPatchVisible(ps, dbProvider.get());
- }
- return false;
- }
-
- private void seedQueue(RevisionResource rsrc, RevWalk rw,
- RevFlag seenCommit, Map<PatchSet.Id, PatchSet> patchSets,
- LinkedList<String> q) throws IOException {
- RevCommit tip = rw.parseCommit(ObjectId.fromString(
- rsrc.getPatchSet().getRevision().get()));
- tip.add(seenCommit);
- q.add(tip.name());
-
- Change.Id cId = rsrc.getChange().getId();
- for (PatchSet p : patchSets.values()) {
- if (cId.equals(p.getId().getParentKey())) {
- try {
- RevCommit c = rw.parseCommit(ObjectId.fromString(
- p.getRevision().get()));
- if (!c.has(seenCommit)) {
- c.add(seenCommit);
- q.add(c.name());
- }
- } catch (IOException e) {
- log.warn(String.format(
- "Cannot read patch set %d of %d",
- p.getPatchSetId(), cId.get()), e);
- }
- }
- }
- }
-
- private Multimap<String, PatchSet.Id> allChildren(Collection<Change.Id> ids)
- throws OrmException {
- ReviewDb db = dbProvider.get();
- List<ResultSet<PatchSetAncestor>> t =
- Lists.newArrayListWithCapacity(ids.size());
- for (Change.Id id : ids) {
- t.add(db.patchSetAncestors().byChange(id));
- }
-
- Multimap<String, PatchSet.Id> r = ArrayListMultimap.create();
- for (ResultSet<PatchSetAncestor> rs : t) {
- for (PatchSetAncestor a : rs) {
- r.put(a.getAncestorRevision().get(), a.getPatchSet());
- }
- }
- return r;
- }
-
-
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
index 8d92beb..826ebdd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
@@ -34,7 +34,7 @@
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.inject.Inject;
@@ -199,7 +199,7 @@
@Override
public Boolean call()
- throws NoSuchProjectException, MergeException, IOException {
+ throws NoSuchProjectException, IntegrationException, IOException {
if (key.into.equals(ObjectId.zeroId())) {
return true; // Assume yes on new branch.
}
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 68f1497..d4737bd 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
@@ -221,7 +221,6 @@
patchSet.setRevision(new RevId(commit.name()));
patchSet.setDraft(draft);
- ChangeUtil.insertAncestors(db, patchSet.getId(), commit);
if (groups != null) {
patchSet.setGroups(groups);
} else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 7a24dbc..e2473e7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -17,12 +17,17 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hashing;
import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
@@ -75,6 +80,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -299,6 +305,32 @@
}
}
+ /**
+ * Used to compare PatchLineComments with CommentInput comments.
+ */
+ @AutoValue
+ abstract static class CommentSetEntry {
+ private static CommentSetEntry create(Patch.Key key,
+ Integer line, Side side, HashCode message, CommentRange range) {
+ return new AutoValue_PostReview_CommentSetEntry(key, line, side, message,
+ range);
+ }
+
+ public static CommentSetEntry create(PatchLineComment comment) {
+ return create(comment.getKey().getParentKey(),
+ comment.getLine(),
+ Side.fromShort(comment.getSide()),
+ Hashing.sha1().hashString(comment.getMessage(), UTF_8),
+ comment.getRange());
+ }
+
+ abstract Patch.Key key();
+ @Nullable abstract Integer line();
+ abstract Side side();
+ abstract HashCode message();
+ @Nullable abstract CommentRange range();
+ }
+
private class Op extends BatchUpdate.Op {
private final PatchSet.Id psId;
private final ReviewInput in;
@@ -374,6 +406,10 @@
List<PatchLineComment> del = Lists.newArrayList();
List<PatchLineComment> ups = Lists.newArrayList();
+ Set<CommentSetEntry> existingIds = in.omitDuplicateComments
+ ? readExistingComments(ctx)
+ : Collections.<CommentSetEntry>emptySet();
+
for (Map.Entry<String, List<CommentInput>> ent : map.entrySet()) {
String path = ent.getKey();
for (CommentInput c : ent.getValue()) {
@@ -381,9 +417,7 @@
PatchLineComment e = drafts.remove(Url.decode(c.id));
if (e == null) {
e = new PatchLineComment(
- new PatchLineComment.Key(
- new Patch.Key(psId, path),
- ChangeUtil.messageUUID(ctx.getDb())),
+ new PatchLineComment.Key(new Patch.Key(psId, path), null),
c.line != null ? c.line : 0,
user.getAccountId(),
parent, ctx.getWhen());
@@ -403,6 +437,12 @@
c.range.endCharacter));
e.setLine(c.range.endLine);
}
+ if (existingIds.contains(CommentSetEntry.create(e))) {
+ continue;
+ }
+ if (e.getKey().get() == null) {
+ e.getKey().set(ChangeUtil.messageUUID(ctx.getDb()));
+ }
ups.add(e);
}
}
@@ -430,6 +470,16 @@
return !del.isEmpty() || !ups.isEmpty();
}
+ private Set<CommentSetEntry> readExistingComments(ChangeContext ctx)
+ throws OrmException {
+ Set<CommentSetEntry> r = new HashSet<>();
+ for (PatchLineComment c : plcUtil.publishedByChange(ctx.getDb(),
+ ctx.getChangeNotes())) {
+ r.add(CommentSetEntry.create(c));
+ }
+ return r;
+ }
+
private Map<String, PatchLineComment> changeDrafts(ChangeContext ctx)
throws OrmException {
Map<String, PatchLineComment> drafts = Maps.newHashMap();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java
index 622c99d..6820115 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PublishDraftPatchSet.java
@@ -209,6 +209,7 @@
throw new ResourceConflictException("Patch set is not a draft");
}
patchSet.setDraft(false);
+ ctx.getDb().patchSets().update(Collections.singleton(patchSet));
}
private void addReviewers(ChangeContext ctx) throws OrmException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java
index 933ac73..31bfc50 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkState;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -25,7 +26,6 @@
import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
import com.google.gerrit.server.git.BatchUpdate.RepoContext;
-import com.google.gerrit.server.git.MergeConflictException;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.validators.CommitValidators;
import com.google.gerrit.server.project.ChangeControl;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetAncestorSorter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RelatedChangesSorter.java
similarity index 95%
rename from gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetAncestorSorter.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/change/RelatedChangesSorter.java
index 8b0ceb2..a0e641e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetAncestorSorter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RelatedChangesSorter.java
@@ -52,11 +52,11 @@
import java.util.Set;
@Singleton
-class PatchSetAncestorSorter {
+class RelatedChangesSorter {
private final GitRepositoryManager repoManager;
@Inject
- PatchSetAncestorSorter(GitRepositoryManager repoManager) {
+ RelatedChangesSorter(GitRepositoryManager repoManager) {
this.repoManager = repoManager;
}
@@ -199,10 +199,10 @@
}
allPatchSets.add(psd);
}
- // Breadth-first search with oldest children first.
- // TODO(dborowitz): After killing PatchSetAncestors, consider DFS to keep
- // parallel history together.
- pending.addAll(Lists.reverse(children.get(psd)));
+ // Depth-first search with newest children first.
+ for (PatchSetData child : children.get(psd)) {
+ pending.addFirst(child);
+ }
}
// If we saw the same change multiple times, prefer the latest patch set.
@@ -227,7 +227,7 @@
abstract static class PatchSetData {
@VisibleForTesting
static PatchSetData create(ChangeData cd, PatchSet ps, RevCommit commit) {
- return new AutoValue_PatchSetAncestorSorter_PatchSetData(cd, ps, commit);
+ return new AutoValue_RelatedChangesSorter_PatchSetData(cd, ps, commit);
}
abstract ChangeData data();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
index 2b6ee41..be9d984 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
@@ -314,19 +314,19 @@
* The loading is performed eagerly: all values are set.
* <p>
* Fields marked with final or transient modifiers are skipped.
- * <p>
- * Boolean fields are only set when their values are true.
*
* @param cfg config from which the values are loaded
* @param section section
* @param sub subsection
* @param s instance of class in which the values are set
* @param defaults instance of class with default values
+ * @param i instance to merge during the load. When present, the
+ * boolean fields are not nullified when their values are false
* @return loaded instance
* @throws ConfigInvalidException
*/
public static <T> T loadSection(Config cfg, String section, String sub,
- T s, T defaults) throws ConfigInvalidException {
+ T s, T defaults, T i) throws ConfigInvalidException {
try {
for (Field f : s.getClass().getDeclaredFields()) {
if (skipField(f)) {
@@ -345,7 +345,7 @@
f.set(s, cfg.getLong(section, sub, n, (Long) d));
} else if (isBoolean(t)) {
boolean b = cfg.getBoolean(section, sub, n, (Boolean) d);
- if (b) {
+ if (b || i != null) {
f.set(s, b);
}
} else if (t.isEnum()) {
@@ -353,6 +353,12 @@
} else {
throw new ConfigInvalidException("type is unknown: " + t.getName());
}
+ if (i != null) {
+ Object o = f.get(i);
+ if (o != null) {
+ f.set(s, o);
+ }
+ }
}
} catch (SecurityException | IllegalArgumentException
| IllegalAccessException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
index c99a3af..722eb91 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
@@ -94,9 +94,12 @@
int tasksSleeping = 0;
for (Task<?> task : pending) {
switch (task.getState()) {
- case RUNNING: tasksRunning++; break;
- case READY: tasksReady++; break;
- case SLEEPING: tasksSleeping++; break;
+ case RUNNING: tasksRunning++;
+ break;
+ case READY: tasksReady++;
+ break;
+ case SLEEPING: tasksSleeping++;
+ break;
case CANCELLED:
case DONE:
case OTHER:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index d0c11e7..e44c810 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -227,6 +227,7 @@
public RefUpdate.Result modifyMessage(ChangeEdit edit, String msg)
throws AuthException, InvalidChangeOperationException, IOException,
UnchangedCommitMessageException {
+ msg = msg.trim() + "\n";
checkState(!Strings.isNullOrEmpty(msg), "message cannot be null");
if (!currentUser.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index 38d3188..d52f831 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -14,8 +14,12 @@
package com.google.gerrit.server.events;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
+import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
@@ -27,9 +31,7 @@
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetAncestor;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
@@ -57,56 +59,59 @@
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@Singleton
public class EventFactory {
private static final Logger log = LoggerFactory.getLogger(EventFactory.class);
+
private final AccountCache accountCache;
private final Provider<String> urlProvider;
private final PatchListCache patchListCache;
- private final SchemaFactory<ReviewDb> schema;
private final PatchSetInfoFactory psInfoFactory;
private final PersonIdent myIdent;
- private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final ApprovalsUtil approvalsUtil;
private final ChangeKindCache changeKindCache;
+ private final Provider<InternalChangeQuery> queryProvider;
@Inject
EventFactory(AccountCache accountCache,
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
PatchSetInfoFactory psif,
- PatchListCache patchListCache, SchemaFactory<ReviewDb> schema,
+ PatchListCache patchListCache,
@GerritPersonIdent PersonIdent myIdent,
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
ApprovalsUtil approvalsUtil,
- ChangeKindCache changeKindCache) {
+ ChangeKindCache changeKindCache,
+ Provider<InternalChangeQuery> queryProvider) {
this.accountCache = accountCache;
this.urlProvider = urlProvider;
this.patchListCache = patchListCache;
- this.schema = schema;
this.psInfoFactory = psif;
this.myIdent = myIdent;
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.approvalsUtil = approvalsUtil;
this.changeKindCache = changeKindCache;
+ this.queryProvider = queryProvider;
}
/**
@@ -116,7 +121,7 @@
* @param change
* @return object suitable for serialization to JSON
*/
- public ChangeAttribute asChangeAttribute(final Change change) {
+ public ChangeAttribute asChangeAttribute(ReviewDb db, Change change) {
ChangeAttribute a = new ChangeAttribute();
a.project = change.getProject().get();
a.branch = change.getDest().getShortName();
@@ -125,8 +130,7 @@
a.number = change.getId().toString();
a.subject = change.getSubject();
try {
- a.commitMessage =
- changeDataFactory.create(db.get(), change).commitMessage();
+ a.commitMessage = changeDataFactory.create(db, change).commitMessage();
} catch (Exception e) {
log.error("Error while getting full commit message for"
+ " change " + a.number);
@@ -146,7 +150,8 @@
* @param refName
* @return object suitable for serialization to JSON
*/
- public RefUpdateAttribute asRefUpdateAttribute(final ObjectId oldId, final ObjectId newId, final Branch.NameKey refName) {
+ public RefUpdateAttribute asRefUpdateAttribute(ObjectId oldId, ObjectId newId,
+ Branch.NameKey refName) {
RefUpdateAttribute ru = new RefUpdateAttribute();
ru.newRev = newId != null ? newId.getName() : ObjectId.zeroId().getName();
ru.oldRev = oldId != null ? oldId.getName() : ObjectId.zeroId().getName();
@@ -173,10 +178,10 @@
* @param a
* @param notes
*/
- public void addAllReviewers(ChangeAttribute a, ChangeNotes notes)
+ public void addAllReviewers(ReviewDb db, ChangeAttribute a, ChangeNotes notes)
throws OrmException {
Collection<Account.Id> reviewers =
- approvalsUtil.getReviewers(db.get(), notes).values();
+ approvalsUtil.getReviewers(db, notes).values();
if (!reviewers.isEmpty()) {
a.allReviewers = Lists.newArrayListWithCapacity(reviewers.size());
for (Account.Id id : reviewers) {
@@ -226,51 +231,17 @@
}
}
- public void addDependencies(ChangeAttribute ca, Change change) {
+ public void addDependencies(RevWalk rw, ChangeAttribute ca, Change change,
+ PatchSet currentPs) {
+ if (change == null || currentPs == null) {
+ return;
+ }
ca.dependsOn = new ArrayList<>();
ca.neededBy = new ArrayList<>();
- try (ReviewDb db = schema.open()) {
- final PatchSet.Id psId = change.currentPatchSetId();
- for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(psId)) {
- for (PatchSet p :
- db.patchSets().byRevision(a.getAncestorRevision())) {
- Change c = db.changes().get(p.getId().getParentKey());
- if (c == null) {
- log.error("Error while generating the ancestor change for"
- + " revision " + a.getAncestorRevision() + ": Cannot find"
- + " Change entry in database for " + p.getId().getParentKey());
- continue;
- }
- ca.dependsOn.add(newDependsOn(c, p));
- }
- }
-
- final PatchSet ps = db.patchSets().get(psId);
- if (ps == null) {
- log.error("Error while generating the list of descendants for"
- + " PatchSet " + psId + ": Cannot find PatchSet entry in"
- + " database.");
- } else {
- final RevId revId = ps.getRevision();
- for (PatchSetAncestor a : db.patchSetAncestors().descendantsOf(revId)) {
- final PatchSet p = db.patchSets().get(a.getPatchSet());
- if (p == null) {
- log.error("Error while generating the list of descendants for"
- + " revision " + revId.get() + ": Cannot find PatchSet entry in"
- + " database for " + a.getPatchSet());
- continue;
- }
- final Change c = db.changes().get(p.getId().getParentKey());
- if (c == null) {
- log.error("Error while generating the list of descendants for"
- + " revision " + revId.get() + ": Cannot find Change entry in"
- + " database for " + p.getId().getParentKey());
- continue;
- }
- ca.neededBy.add(newNeededBy(c, p));
- }
- }
- } catch (OrmException e) {
+ try {
+ addDependsOn(rw, ca, change, currentPs);
+ addNeededBy(rw, ca, change, currentPs);
+ } catch (OrmException | IOException e) {
// Squash DB exceptions and leave dependency lists partially filled.
}
// Remove empty lists so a confusing label won't be displayed in the output.
@@ -282,6 +253,67 @@
}
}
+ private void addDependsOn(RevWalk rw, ChangeAttribute ca, Change change,
+ PatchSet currentPs) throws OrmException, IOException {
+ RevCommit commit =
+ rw.parseCommit(ObjectId.fromString(currentPs.getRevision().get()));
+ final List<String> parentNames = new ArrayList<>(commit.getParentCount());
+ for (RevCommit p : commit.getParents()) {
+ parentNames.add(p.name());
+ }
+
+ // Find changes in this project having a patch set matching any parent of
+ // this patch set's revision.
+ for (ChangeData cd : queryProvider.get().byProjectCommits(
+ change.getProject(), parentNames)) {
+ for (PatchSet ps : cd.patchSets()) {
+ for (String p : parentNames) {
+ if (!ps.getRevision().get().equals(p)) {
+ continue;
+ }
+ ca.dependsOn.add(newDependsOn(checkNotNull(cd.change()), ps));
+ }
+ }
+ }
+ // Sort by original parent order.
+ Collections.sort(ca.dependsOn, Ordering.natural().onResultOf(
+ new Function<DependencyAttribute, Integer>() {
+ @Override
+ public Integer apply(DependencyAttribute d) {
+ for (int i = 0; i < parentNames.size(); i++) {
+ if (parentNames.get(i).equals(d.revision)) {
+ return i;
+ }
+ }
+ return parentNames.size() + 1;
+ }
+ }));
+ }
+
+ private void addNeededBy(RevWalk rw, ChangeAttribute ca, Change change,
+ PatchSet currentPs) throws OrmException, IOException {
+ if (currentPs.getGroups() == null || currentPs.getGroups().isEmpty()) {
+ return;
+ }
+ String rev = currentPs.getRevision().get();
+ // Find changes in the same related group as this patch set, having a patch
+ // set whose parent matches this patch set's revision.
+ for (ChangeData cd : queryProvider.get().byProjectGroups(
+ change.getProject(), currentPs.getGroups())) {
+ patchSets: for (PatchSet ps : cd.patchSets()) {
+ RevCommit commit =
+ rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
+ for (RevCommit p : commit.getParents()) {
+ if (!p.name().equals(rev)) {
+ continue;
+ }
+ ca.neededBy.add(newNeededBy(checkNotNull(cd.change()), ps));
+ continue patchSets;
+ }
+ }
+ }
+ }
+
private DependencyAttribute newDependsOn(Change c, PatchSet ps) {
DependencyAttribute d = newDependencyAttribute(c, ps);
d.isCurrentPatchSet = ps.getId().equals(c.currentPatchSetId());
@@ -319,24 +351,21 @@
a.commitMessage = commitMessage;
}
- public void addPatchSets(ChangeAttribute a, Collection<PatchSet> ps,
- LabelTypes labelTypes) {
- addPatchSets(a, ps, null, false, null, labelTypes);
- }
-
- public void addPatchSets(ChangeAttribute ca, Collection<PatchSet> ps,
+ public void addPatchSets(ReviewDb db, RevWalk revWalk, ChangeAttribute ca,
+ Collection<PatchSet> ps,
Map<PatchSet.Id, Collection<PatchSetApproval>> approvals,
LabelTypes labelTypes) {
- addPatchSets(ca, ps, approvals, false, null, labelTypes);
+ addPatchSets(db, revWalk, ca, ps, approvals, false, null, labelTypes);
}
- public void addPatchSets(ChangeAttribute ca, Collection<PatchSet> ps,
+ public void addPatchSets(ReviewDb db, RevWalk revWalk, ChangeAttribute ca,
+ Collection<PatchSet> ps,
Map<PatchSet.Id, Collection<PatchSetApproval>> approvals,
boolean includeFiles, Change change, LabelTypes labelTypes) {
if (!ps.isEmpty()) {
ca.patchSets = new ArrayList<>(ps.size());
for (PatchSet p : ps) {
- PatchSetAttribute psa = asPatchSetAttribute(p);
+ PatchSetAttribute psa = asPatchSetAttribute(db, revWalk, p);
if (approvals != null) {
addApprovals(psa, p.getId(), approvals, labelTypes);
}
@@ -400,7 +429,8 @@
* @param patchSet
* @return object suitable for serialization to JSON
*/
- public PatchSetAttribute asPatchSetAttribute(final PatchSet patchSet) {
+ public PatchSetAttribute asPatchSetAttribute(ReviewDb db, RevWalk revWalk,
+ PatchSet patchSet) {
PatchSetAttribute p = new PatchSetAttribute();
p.revision = patchSet.getRevision().get();
p.number = Integer.toString(patchSet.getPatchSetId());
@@ -408,12 +438,12 @@
p.uploader = asAccountAttribute(patchSet.getUploader());
p.createdOn = patchSet.getCreatedOn().getTime() / 1000L;
p.isDraft = patchSet.isDraft();
- final PatchSet.Id pId = patchSet.getId();
- try (ReviewDb db = schema.open()) {
+ PatchSet.Id pId = patchSet.getId();
+ try {
p.parents = new ArrayList<>();
- for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(
- patchSet.getId())) {
- p.parents.add(a.getAncestorRevision().get());
+ RevCommit c = revWalk.parseCommit(ObjectId.fromString(p.revision));
+ for (RevCommit parent : c.getParents()) {
+ p.parents.add(parent.name());
}
UserIdentity author = psInfoFactory.get(db, pId).getAuthor();
@@ -436,7 +466,7 @@
}
}
p.kind = changeKindCache.getChangeKind(db, change, patchSet);
- } catch (OrmException e) {
+ } catch (OrmException | IOException e) {
log.error("Cannot load patch set data for " + patchSet.getId(), e);
} catch (PatchSetInfoNotAvailableException e) {
log.error(String.format("Cannot get authorEmail for %s.", pId), e);
@@ -491,7 +521,7 @@
* @param account
* @return object suitable for serialization to JSON
*/
- public AccountAttribute asAccountAttribute(final Account account) {
+ public AccountAttribute asAccountAttribute(Account account) {
if (account == null) {
return null;
}
@@ -560,9 +590,9 @@
}
/** Get a link to the change; null if the server doesn't know its own address. */
- private String getChangeUrl(final Change change) {
+ private String getChangeUrl(Change change) {
if (change != null && urlProvider.get() != null) {
- final StringBuilder r = new StringBuilder();
+ StringBuilder r = new StringBuilder();
r.append(urlProvider.get());
r.append(change.getChangeId());
return r.toString();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/IntegrationException.java
similarity index 73%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/IntegrationException.java
index d3ebb95..58d4e6e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/IntegrationException.java
@@ -14,19 +14,19 @@
package com.google.gerrit.server.git;
-/** Indicates the current branch's queue cannot be processed at this time. */
-public class MergeException extends Exception {
+/** Indicates an integration operation (see {@link MergeOp}) failed. */
+public class IntegrationException extends Exception {
private static final long serialVersionUID = 1L;
- public MergeException(String msg) {
+ public IntegrationException(String msg) {
super(msg);
}
- public MergeException(Throwable why) {
+ public IntegrationException(Throwable why) {
super(why);
}
- public MergeException(String msg, Throwable why) {
+ public IntegrationException(String msg, Throwable why) {
super(msg, why);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index 624e30c..eebae70 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -201,7 +201,7 @@
public Repository createRepository(Project.NameKey name)
throws RepositoryNotFoundException, RepositoryCaseMismatchException {
Repository repo = createRepository(basePath, name);
- if (noteDbPath != null) {
+ if (noteDbPath != null && !noteDbPath.equals(basePath)) {
createRepository(noteDbPath, name);
}
return repo;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index 776cee9..74474f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -226,10 +226,12 @@
mergeTips = new HashMap<>();
}
- private void setDestProject(Branch.NameKey destBranch) throws MergeException {
+ private void setDestProject(Branch.NameKey destBranch)
+ throws IntegrationException {
destProject = projectCache.get(destBranch.getParentKey());
if (destProject == null) {
- throw new MergeException("No such project: " + destBranch.getParentKey());
+ throw new IntegrationException(
+ "No such project: " + destBranch.getParentKey());
}
}
@@ -376,7 +378,7 @@
}
try {
integrateIntoHistory(cs, caller);
- } catch (MergeException e) {
+ } catch (IntegrationException e) {
logError("Merge Conflict", e);
throw new ResourceConflictException("Merge Conflict", e);
}
@@ -387,7 +389,8 @@
}
private void integrateIntoHistory(ChangeSet cs, IdentifiedUser caller)
- throws MergeException, NoSuchChangeException, ResourceConflictException {
+ throws IntegrationException, NoSuchChangeException,
+ ResourceConflictException {
logDebug("Beginning merge attempt on {}", cs);
Map<Branch.NameKey, ListMultimap<SubmitType, ChangeData>> toSubmit =
new HashMap<>();
@@ -450,9 +453,9 @@
+ "abandoning open changes");
abandonAllOpenChanges(noProject.project());
} catch (OrmException e) {
- throw new MergeException("Cannot query the database", e);
+ throw new IntegrationException("Cannot query the database", e);
} catch (IOException e) {
- throw new MergeException("Cannot query the database", e);
+ throw new IntegrationException("Cannot query the database", e);
} finally {
closeRepository();
}
@@ -460,7 +463,7 @@
private MergeTip preMerge(SubmitStrategy strategy,
List<ChangeData> submitted, CodeReviewCommit branchTip)
- throws MergeException, OrmException {
+ throws IntegrationException, OrmException {
logDebug("Running submit strategy {} for {} commits {}",
strategy.getClass().getSimpleName(), submitted.size(), submitted);
List<CodeReviewCommit> toMerge = new ArrayList<>(submitted.size());
@@ -479,20 +482,20 @@
private SubmitStrategy createStrategy(Branch.NameKey destBranch,
SubmitType submitType, CodeReviewCommit branchTip, IdentifiedUser caller)
- throws MergeException, NoSuchProjectException {
+ throws IntegrationException, NoSuchProjectException {
return submitStrategyFactory.create(submitType, db, repo, rw, inserter,
canMergeFlag, getAlreadyAccepted(branchTip), destBranch, caller);
}
private void openRepository(Project.NameKey name)
- throws MergeException, NoSuchProjectException {
+ throws IntegrationException, NoSuchProjectException {
try {
repo = repoManager.openRepository(name);
} catch (RepositoryNotFoundException notFound) {
throw new NoSuchProjectException(name, notFound);
} catch (IOException err) {
String m = "Error opening repository \"" + name.get() + '"';
- throw new MergeException(m, err);
+ throw new IntegrationException(m, err);
}
rw = CodeReviewCommit.newRevWalk(repo);
@@ -517,7 +520,7 @@
}
private RefUpdate getPendingRefUpdate(Branch.NameKey destBranch)
- throws MergeException {
+ throws IntegrationException {
if (pendingRefUpdates.containsKey(destBranch)) {
logDebug("Access cached open branch {}: {}", destBranch.get(),
@@ -534,8 +537,8 @@
branchTip = null;
branchUpdate.setExpectedOldObjectId(ObjectId.zeroId());
} else {
- throw new MergeException("The destination branch " + destBranch.get()
- + " does not exist anymore.");
+ throw new IntegrationException("The destination branch "
+ + destBranch.get() + " does not exist anymore.");
}
logDebug("Opened branch {}: {}", destBranch.get(), branchTip);
@@ -543,12 +546,12 @@
openBranches.put(destBranch, branchTip);
return branchUpdate;
} catch (IOException e) {
- throw new MergeException("Cannot open branch", e);
+ throw new IntegrationException("Cannot open branch", e);
}
}
private CodeReviewCommit getBranchTip(Branch.NameKey destBranch)
- throws MergeException {
+ throws IntegrationException {
if (openBranches.containsKey(destBranch)) {
return openBranches.get(destBranch);
} else {
@@ -558,7 +561,7 @@
}
private Set<RevCommit> getAlreadyAccepted(CodeReviewCommit branchTip)
- throws MergeException {
+ throws IntegrationException {
Set<RevCommit> alreadyAccepted = new HashSet<>();
if (branchTip != null) {
@@ -574,7 +577,7 @@
}
}
} catch (IOException e) {
- throw new MergeException(
+ throw new IntegrationException(
"Failed to determine already accepted commits.", e);
}
@@ -583,7 +586,7 @@
}
private ListMultimap<SubmitType, ChangeData> validateChangeList(
- Collection<ChangeData> submitted) throws MergeException {
+ Collection<ChangeData> submitted) throws IntegrationException {
logDebug("Validating {} changes", submitted.size());
ListMultimap<SubmitType, ChangeData> toSubmit = ArrayListMultimap.create();
@@ -591,7 +594,7 @@
try {
allRefs = repo.getRefDatabase().getRefs(ALL);
} catch (IOException e) {
- throw new MergeException(e.getMessage(), e);
+ throw new IntegrationException(e.getMessage(), e);
}
Set<ObjectId> tips = new HashSet<>();
@@ -607,7 +610,7 @@
// Reload change in case index was stale.
chg = cd.reloadChange();
} catch (OrmException e) {
- throw new MergeException("Failed to validate changes", e);
+ throw new IntegrationException("Failed to validate changes", e);
}
Change.Id changeId = cd.getId();
if (chg.getStatus() != Change.Status.NEW) {
@@ -625,7 +628,7 @@
try {
ps = cd.currentPatchSet();
} catch (OrmException e) {
- throw new MergeException("Cannot query the database", e);
+ throw new IntegrationException("Cannot query the database", e);
}
if (ps == null || ps.getRevision() == null
|| ps.getRevision().get() == null) {
@@ -718,7 +721,7 @@
}
private RefUpdate updateBranch(Branch.NameKey destBranch)
- throws MergeException {
+ throws IntegrationException {
RefUpdate branchUpdate = getPendingRefUpdate(destBranch);
CodeReviewCommit branchTip = getBranchTip(destBranch);
@@ -746,7 +749,7 @@
new ProjectConfig(destProject.getProject().getNameKey());
cfg.load(repo, currentTip);
} catch (Exception e) {
- throw new MergeException("Submit would store invalid"
+ throw new IntegrationException("Submit would store invalid"
+ " project configuration " + currentTip.name() + " for "
+ destProject.getProject().getName(), e);
}
@@ -782,13 +785,13 @@
return branchUpdate;
case LOCK_FAILURE:
- throw new MergeException("Failed to lock " + branchUpdate.getName());
+ throw new IntegrationException("Failed to lock " + branchUpdate.getName());
default:
throw new IOException(branchUpdate.getResult().name()
+ '\n' + branchUpdate);
}
} catch (IOException e) {
- throw new MergeException("Cannot update " + branchUpdate.getName(), e);
+ throw new IntegrationException("Cannot update " + branchUpdate.getName(), e);
}
}
@@ -820,7 +823,7 @@
private void updateChangeStatus(List<ChangeData> submitted,
Branch.NameKey destBranch, boolean dryRun, IdentifiedUser caller)
- throws NoSuchChangeException, MergeException, ResourceConflictException,
+ throws NoSuchChangeException, IntegrationException, ResourceConflictException,
OrmException {
if (!dryRun) {
logDebug("Updating change status for {} changes", submitted.size());
@@ -896,8 +899,8 @@
case MISSING_DEPENDENCY:
logDebug("Change {} is missing dependency", c.getId());
- throw new MergeException("Cannot merge " + commit.name() + "\n"
- + s.getMessage());
+ throw new IntegrationException(
+ "Cannot merge " + commit.name() + "\n" + s.getMessage());
case REVISION_GONE:
logDebug("Commit not found for change {}", c.getId());
@@ -910,12 +913,12 @@
c.currentPatchSetId());
msg.setMessage("Failed to read commit for this patch set");
setNew(commit.notes(), msg);
- throw new MergeException(msg.getMessage());
+ throw new IntegrationException(msg.getMessage());
default:
msg = message(c, "Unspecified merge failure: " + s.name());
setNew(commit.notes(), msg);
- throw new MergeException(msg.getMessage());
+ throw new IntegrationException(msg.getMessage());
}
} catch (OrmException | IOException err) {
logWarn("Error updating change status for " + c.getId(), err);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index 5fdefbe..436147c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -23,6 +23,7 @@
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
@@ -138,7 +139,7 @@
public CodeReviewCommit getFirstFastForward(
final CodeReviewCommit mergeTip, final RevWalk rw,
- final List<CodeReviewCommit> toMerge) throws MergeException {
+ final List<CodeReviewCommit> toMerge) throws IntegrationException {
for (final Iterator<CodeReviewCommit> i = toMerge.iterator(); i.hasNext();) {
try {
final CodeReviewCommit n = i.next();
@@ -147,19 +148,20 @@
return n;
}
} catch (IOException e) {
- throw new MergeException("Cannot fast-forward test during merge", e);
+ throw new IntegrationException(
+ "Cannot fast-forward test during merge", e);
}
}
return mergeTip;
}
public List<CodeReviewCommit> reduceToMinimalMerge(MergeSorter mergeSorter,
- Collection<CodeReviewCommit> toSort) throws MergeException {
+ Collection<CodeReviewCommit> toSort) throws IntegrationException {
List<CodeReviewCommit> result = new ArrayList<>();
try {
result.addAll(mergeSorter.sort(toSort));
} catch (IOException e) {
- throw new MergeException("Branch head sorting failed", e);
+ throw new IntegrationException("Branch head sorting failed", e);
}
Collections.sort(result, CodeReviewCommit.ORDER);
return result;
@@ -345,7 +347,7 @@
public boolean canMerge(final MergeSorter mergeSorter,
final Repository repo, final CodeReviewCommit mergeTip,
final CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
if (hasMissingDependencies(mergeSorter, toMerge)) {
return false;
}
@@ -359,13 +361,13 @@
} catch (NoMergeBaseException e) {
return false;
} catch (IOException e) {
- throw new MergeException("Cannot merge " + toMerge.name(), e);
+ throw new IntegrationException("Cannot merge " + toMerge.name(), e);
}
}
public boolean canFastForward(MergeSorter mergeSorter,
CodeReviewCommit mergeTip, CodeReviewRevWalk rw, CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
if (hasMissingDependencies(mergeSorter, toMerge)) {
return false;
}
@@ -373,13 +375,13 @@
try {
return mergeTip == null || rw.isMergedInto(mergeTip, toMerge);
} catch (IOException e) {
- throw new MergeException("Cannot fast-forward test during merge", e);
+ throw new IntegrationException("Cannot fast-forward test during merge", e);
}
}
public boolean canCherryPick(MergeSorter mergeSorter, Repository repo,
CodeReviewCommit mergeTip, CodeReviewRevWalk rw, CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
if (mergeTip == null) {
// The branch is unborn. Fast-forward is possible.
//
@@ -403,7 +405,7 @@
m.setBase(toMerge.getParent(0));
return m.merge(mergeTip, toMerge);
} catch (IOException e) {
- throw new MergeException("Cannot merge " + toMerge.name(), e);
+ throw new IntegrationException("Cannot merge " + toMerge.name(), e);
}
}
@@ -418,11 +420,11 @@
}
public boolean hasMissingDependencies(final MergeSorter mergeSorter,
- final CodeReviewCommit toMerge) throws MergeException {
+ final CodeReviewCommit toMerge) throws IntegrationException {
try {
return !mergeSorter.sort(Collections.singleton(toMerge)).contains(toMerge);
} catch (IOException e) {
- throw new MergeException("Branch head sorting failed", e);
+ throw new IntegrationException("Branch head sorting failed", e);
}
}
@@ -449,7 +451,8 @@
public CodeReviewCommit mergeOneCommit(PersonIdent author,
PersonIdent committer, Repository repo, CodeReviewRevWalk rw,
ObjectInserter inserter, RevFlag canMergeFlag, Branch.NameKey destBranch,
- CodeReviewCommit mergeTip, CodeReviewCommit n) throws MergeException {
+ CodeReviewCommit mergeTip, CodeReviewCommit n)
+ throws IntegrationException {
final ThreeWayMerger m = newThreeWayMerger(repo, inserter);
try {
if (m.merge(new AnyObjectId[] {mergeTip, n})) {
@@ -463,10 +466,10 @@
failed(rw, canMergeFlag, mergeTip, n,
getCommitMergeStatus(e.getReason()));
} catch (IOException e2) {
- throw new MergeException("Cannot merge " + n.name(), e);
+ throw new IntegrationException("Cannot merge " + n.name(), e);
}
} catch (IOException e) {
- throw new MergeException("Cannot merge " + n.name(), e);
+ throw new IntegrationException("Cannot merge " + n.name(), e);
}
return mergeTip;
}
@@ -639,7 +642,7 @@
public void markCleanMerges(final RevWalk rw,
final RevFlag canMergeFlag, final CodeReviewCommit mergeTip,
- final Set<RevCommit> alreadyAccepted) throws MergeException {
+ final Set<RevCommit> alreadyAccepted) throws IntegrationException {
if (mergeTip == null) {
// If mergeTip is null here, branchTip was null, indicating a new branch
// at the start of the merge process. We also elected to merge nothing,
@@ -664,7 +667,7 @@
}
}
} catch (IOException e) {
- throw new MergeException("Cannot mark clean merges", e);
+ throw new IntegrationException("Cannot mark clean merges", e);
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index d12d30a..addbc1b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -118,6 +118,7 @@
"requireContributorAgreement";
private static final String KEY_CHECK_RECEIVED_OBJECTS = "checkReceivedObjects";
private static final String KEY_ENABLE_SIGNED_PUSH = "enableSignedPush";
+ private static final String KEY_REQUIRE_SIGNED_PUSH = "requireSignedPush";
private static final String SUBMIT = "submit";
private static final String KEY_ACTION = "action";
@@ -420,6 +421,8 @@
p.setCreateNewChangeForAllNotInTarget(getEnum(rc, RECEIVE, null, KEY_USE_ALL_NOT_IN_TARGET, InheritableBoolean.INHERIT));
p.setEnableSignedPush(getEnum(rc, RECEIVE, null,
KEY_ENABLE_SIGNED_PUSH, InheritableBoolean.INHERIT));
+ p.setRequireSignedPush(getEnum(rc, RECEIVE, null,
+ KEY_REQUIRE_SIGNED_PUSH, InheritableBoolean.INHERIT));
p.setMaxObjectSizeLimit(rc.getString(RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction));
@@ -775,6 +778,17 @@
Config pluginConfig = new Config();
pluginConfigs.put(plugin, pluginConfig);
for (String name : rc.getNames(PLUGIN, plugin)) {
+ String value = rc.getString(PLUGIN, plugin, name);
+ if (value.startsWith("Group[")) {
+ GroupReference refFromString = GroupReference.fromString(value);
+ GroupReference ref = groupList.byUUID(refFromString.getUUID());
+ if (ref == null) {
+ ref = refFromString;
+ error(new ValidationError(PROJECT_CONFIG,
+ "group \"" + ref.getName() + "\" not in " + GroupList.FILE_NAME));
+ }
+ rc.setString(PLUGIN, plugin, name, ref.toString());
+ }
pluginConfig.setStringList(PLUGIN, plugin, name,
Arrays.asList(rc.getStringList(PLUGIN, plugin, name)));
}
@@ -828,6 +842,8 @@
set(rc, RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT, validMaxObjectSizeLimit(p.getMaxObjectSizeLimit()));
set(rc, RECEIVE, null, KEY_ENABLE_SIGNED_PUSH,
p.getEnableSignedPush(), InheritableBoolean.INHERIT);
+ set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_PUSH,
+ p.getRequireSignedPush(), InheritableBoolean.INHERIT);
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.getUseContentMerge(), InheritableBoolean.INHERIT);
@@ -842,9 +858,9 @@
saveContributorAgreements(rc, keepGroups);
saveAccessSections(rc, keepGroups);
saveNotifySections(rc, keepGroups);
+ savePluginSections(rc, keepGroups);
groupList.retainUUIDs(keepGroups);
saveLabelSections(rc);
- savePluginSections(rc);
saveConfig(PROJECT_CONFIG, rc);
saveGroupList();
@@ -1096,7 +1112,7 @@
}
}
- private void savePluginSections(Config rc) {
+ private void savePluginSections(Config rc, Set<AccountGroup.UUID> keepGroups) {
List<String> existing = Lists.newArrayList(rc.getSubsections(PLUGIN));
for (String name : existing) {
rc.unsetSection(PLUGIN, name);
@@ -1106,6 +1122,14 @@
String plugin = e.getKey();
Config pluginConfig = e.getValue();
for (String name : pluginConfig.getNames(PLUGIN, plugin)) {
+ String value = pluginConfig.getString(PLUGIN, plugin, name);
+ if (value.startsWith("Group[")) {
+ GroupReference ref = resolve(GroupReference.fromString(value));
+ if (ref.getUUID() != null) {
+ keepGroups.add(ref.getUUID());
+ pluginConfig.setString(PLUGIN, plugin, name, ref.toString());
+ }
+ }
rc.setStringList(PLUGIN, plugin, name,
Arrays.asList(pluginConfig.getStringList(PLUGIN, plugin, name)));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index c87ed00..5da4ec7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -2252,7 +2252,6 @@
return null;
}
- ChangeUtil.insertAncestors(db, newPatchSet.getId(), newCommit);
if (newPatchSet.getGroups() == null) {
newPatchSet.setGroups(GroupCollector.getCurrentGroups(db, change));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java
index 29b5373..a1c5b8a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/UserConfigSections.java
@@ -22,6 +22,9 @@
/** The edit user preferences. */
public static final String EDIT = "edit";
+ /** The diff user preferences. */
+ public static final String DIFF = "diff";
+
private UserConfigSections() {
}
}
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 a2e9c03..61b1b00 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
@@ -16,14 +16,13 @@
import com.google.common.collect.Lists;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetAncestor;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
@@ -31,8 +30,7 @@
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.git.GroupCollector;
-import com.google.gerrit.server.git.MergeConflictException;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeIdenticalTreeException;
import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.UpdateException;
@@ -42,11 +40,9 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.ReceiveCommand;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -66,7 +62,7 @@
@Override
protected MergeTip _run(CodeReviewCommit branchTip,
- Collection<CodeReviewCommit> toMerge) throws MergeException {
+ Collection<CodeReviewCommit> toMerge) throws IntegrationException {
MergeTip mergeTip = new MergeTip(branchTip, toMerge);
List<CodeReviewCommit> sorted = CodeReviewCommit.ORDER.sortedCopy(toMerge);
boolean first = true;
@@ -87,7 +83,8 @@
}
u.execute();
} catch (UpdateException | RestApiException e) {
- throw new MergeException("Cannot cherry-pick onto " + args.destBranch);
+ throw new IntegrationException(
+ "Cannot cherry-pick onto " + args.destBranch);
}
// TODO(dborowitz): When BatchUpdate is hoisted out of CherryPick,
// SubmitStrategy should probably no longer return MergeTip, instead just
@@ -187,7 +184,6 @@
Change c = toMerge.change();
ps.setGroups(GroupCollector.getCurrentGroups(args.db, c));
args.db.patchSets().insert(Collections.singleton(ps));
- insertAncestors(args.db, ps.getId(), newCommit);
c.setCurrentPatchSet(patchSetInfo);
args.db.changes().update(Collections.singletonList(c));
@@ -219,7 +215,8 @@
}
@Override
- public void updateRepo(RepoContext ctx) throws MergeException, IOException {
+ public void updateRepo(RepoContext ctx)
+ throws IntegrationException, IOException {
if (args.mergeUtil.hasMissingDependencies(args.mergeSorter, toMerge)) {
// One or more dependencies were not met. The status was already marked
// on the commit so we have nothing further to perform at this time.
@@ -247,20 +244,6 @@
}
}
- private static void insertAncestors(ReviewDb db, PatchSet.Id id,
- RevCommit src) throws OrmException {
- int cnt = src.getParentCount();
- List<PatchSetAncestor> toInsert = new ArrayList<>(cnt);
- for (int p = 0; p < cnt; p++) {
- PatchSetAncestor a;
-
- a = new PatchSetAncestor(new PatchSetAncestor.Id(id, p + 1));
- a.setAncestorRevision(new RevId(src.getParent(p).getId().name()));
- toInsert.add(a);
- }
- db.patchSetAncestors().insert(toInsert);
- }
-
@Override
public Map<Change.Id, CodeReviewCommit> getNewCommits() {
return newCommits;
@@ -268,7 +251,7 @@
@Override
public boolean dryRun(CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
return args.mergeUtil.canCherryPick(args.mergeSorter, args.repo,
mergeTip, args.rw, toMerge);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/FastForwardOnly.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/FastForwardOnly.java
index f7d8ab1..1709659 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/FastForwardOnly.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/FastForwardOnly.java
@@ -16,7 +16,7 @@
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeTip;
import java.util.Collection;
@@ -29,7 +29,7 @@
@Override
protected MergeTip _run(final CodeReviewCommit branchTip,
- final Collection<CodeReviewCommit> toMerge) throws MergeException {
+ final Collection<CodeReviewCommit> toMerge) throws IntegrationException {
MergeTip mergeTip = new MergeTip(branchTip, toMerge);
List<CodeReviewCommit> sorted = args.mergeUtil.reduceToMinimalMerge(
args.mergeSorter, toMerge);
@@ -56,7 +56,7 @@
@Override
public boolean dryRun(CodeReviewCommit mergeTip,
- CodeReviewCommit toMerge) throws MergeException {
+ CodeReviewCommit toMerge) throws IntegrationException {
return args.mergeUtil.canFastForward(args.mergeSorter, mergeTip, args.rw,
toMerge);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeAlways.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeAlways.java
index d3a72e3..9f5f521 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeAlways.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeAlways.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git.strategy;
import com.google.gerrit.server.git.CodeReviewCommit;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeTip;
import org.eclipse.jgit.lib.PersonIdent;
@@ -30,7 +30,7 @@
@Override
protected MergeTip _run(CodeReviewCommit branchTip,
- Collection<CodeReviewCommit> toMerge) throws MergeException {
+ Collection<CodeReviewCommit> toMerge) throws IntegrationException {
List<CodeReviewCommit> sorted = args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
MergeTip mergeTip;
if (branchTip == null) {
@@ -62,7 +62,7 @@
@Override
public boolean dryRun(CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
return args.mergeUtil.canMerge(args.mergeSorter, args.repo, mergeTip,
toMerge);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeIfNecessary.java
index 688fa3e..4ebe461 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeIfNecessary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/MergeIfNecessary.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git.strategy;
import com.google.gerrit.server.git.CodeReviewCommit;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeTip;
import org.eclipse.jgit.lib.PersonIdent;
@@ -30,7 +30,7 @@
@Override
protected MergeTip _run(CodeReviewCommit branchTip,
- Collection<CodeReviewCommit> toMerge) throws MergeException {
+ Collection<CodeReviewCommit> toMerge) throws IntegrationException {
List<CodeReviewCommit> sorted =
args.mergeUtil.reduceToMinimalMerge(args.mergeSorter, toMerge);
MergeTip mergeTip;
@@ -67,7 +67,7 @@
@Override
public boolean dryRun(CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
return args.mergeUtil.canFastForward(
args.mergeSorter, mergeTip, args.rw, toMerge)
|| args.mergeUtil.canMerge(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
index 5ef33a6..cd6b5bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/RebaseIfNecessary.java
@@ -16,6 +16,7 @@
import com.google.common.collect.Lists;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -24,8 +25,7 @@
import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus;
-import com.google.gerrit.server.git.MergeConflictException;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.RebaseSorter;
import com.google.gerrit.server.git.UpdateException;
@@ -60,7 +60,7 @@
@Override
protected MergeTip _run(final CodeReviewCommit branchTip,
- final Collection<CodeReviewCommit> toMerge) throws MergeException {
+ final Collection<CodeReviewCommit> toMerge) throws IntegrationException {
MergeTip mergeTip = new MergeTip(branchTip, toMerge);
List<CodeReviewCommit> sorted = sort(toMerge);
while (!sorted.isEmpty()) {
@@ -111,16 +111,13 @@
newCommits.put(newPatchSet.getId().getParentKey(),
mergeTip.getCurrentTip());
setRefLogIdent();
- } catch (UpdateException e) {
- if (e.getCause() instanceof MergeConflictException) {
- n.setStatusCode(CommitMergeStatus.REBASE_MERGE_CONFLICT);
- }
- throw new MergeException("Cannot rebase " + n.name(), e);
+ } catch (MergeConflictException e) {
+ n.setStatusCode(CommitMergeStatus.REBASE_MERGE_CONFLICT);
+ throw new IntegrationException(
+ "Cannot rebase " + n.name() + ": " + e.getMessage(), e);
} catch (NoSuchChangeException | OrmException | IOException
- | RestApiException e) {
- // TODO(dborowitz): Allow Submit to unwrap ResourceConflictException
- // so it can turn into a 409.
- throw new MergeException("Cannot rebase " + n.name(), e);
+ | RestApiException | UpdateException e) {
+ throw new IntegrationException("Cannot rebase " + n.name(), e);
}
}
@@ -145,7 +142,7 @@
mergeTip.getCurrentTip(), args.alreadyAccepted);
setRefLogIdent();
} catch (IOException e) {
- throw new MergeException("Cannot merge " + n.name(), e);
+ throw new IntegrationException("Cannot merge " + n.name(), e);
}
}
@@ -156,14 +153,14 @@
}
private List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort)
- throws MergeException {
+ throws IntegrationException {
try {
List<CodeReviewCommit> result = new RebaseSorter(
args.rw, args.alreadyAccepted, args.canMergeFlag).sort(toSort);
Collections.sort(result, CodeReviewCommit.ORDER);
return result;
} catch (IOException e) {
- throw new MergeException("Commit sorting failed", e);
+ throw new IntegrationException("Commit sorting failed", e);
}
}
@@ -190,7 +187,7 @@
@Override
public boolean dryRun(CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
- throws MergeException {
+ throws IntegrationException {
return !args.mergeUtil.hasMissingDependencies(args.mergeSorter, toMerge)
&& args.mergeUtil.canCherryPick(args.mergeSorter, args.repo, mergeTip,
args.rw, toMerge);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
index e3247c7..5215f55 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
@@ -25,7 +25,7 @@
import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeSorter;
import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.MergeUtil;
@@ -124,10 +124,10 @@
* this submit strategy. Implementations are responsible for ordering
* of commits, and should not modify the input in place.
* @return the new merge tip.
- * @throws MergeException
+ * @throws IntegrationException
*/
public final MergeTip run(final CodeReviewCommit currentTip,
- final Collection<CodeReviewCommit> toMerge) throws MergeException {
+ final Collection<CodeReviewCommit> toMerge) throws IntegrationException {
refLogIdent = null;
checkState(args.caller != null);
return _run(currentTip, toMerge);
@@ -135,7 +135,7 @@
/** @see #run(CodeReviewCommit, Collection) */
protected abstract MergeTip _run(CodeReviewCommit currentTip,
- Collection<CodeReviewCommit> toMerge) throws MergeException;
+ Collection<CodeReviewCommit> toMerge) throws IntegrationException;
/**
* Checks whether the given commit can be merged.
@@ -147,10 +147,10 @@
* @param toMerge the commit that should be checked.
* @return {@code true} if the given commit can be merged, otherwise
* {@code false}
- * @throws MergeException
+ * @throws IntegrationException
*/
public abstract boolean dryRun(CodeReviewCommit mergeTip,
- CodeReviewCommit toMerge) throws MergeException;
+ CodeReviewCommit toMerge) throws IntegrationException;
/**
* Returns the identity that should be used for reflog entries when updating
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
index 05aee25..0c8c2f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategyFactory.java
@@ -23,7 +23,7 @@
import com.google.gerrit.server.change.RebaseChangeOp;
import com.google.gerrit.server.git.BatchUpdate;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -90,7 +90,7 @@
Repository repo, CodeReviewRevWalk rw, ObjectInserter inserter,
RevFlag canMergeFlag, Set<RevCommit> alreadyAccepted,
Branch.NameKey destBranch, IdentifiedUser caller)
- throws MergeException, NoSuchProjectException {
+ throws IntegrationException, NoSuchProjectException {
ProjectState project = getProject(destBranch);
SubmitStrategy.Arguments args = new SubmitStrategy.Arguments(
identifiedUserFactory, myIdent, db, batchUpdateFactory,
@@ -111,7 +111,7 @@
default:
final String errorMsg = "No submit strategy for: " + submitType;
log.error(errorMsg);
- throw new MergeException(errorMsg);
+ throw new IntegrationException(errorMsg);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java
index 3b04f05..02e737a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeIndex.java
@@ -19,6 +19,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.QueryOptions;
import java.io.IOException;
@@ -81,16 +82,16 @@
* @param p the predicate to match. Must be a tree containing only AND, OR,
* or NOT predicates as internal nodes, and {@link IndexPredicate}s as
* leaves.
- * @param start offset in results list at which to start returning results.
- * @param limit maximum number of results to return.
+ * @param opts query options not implied by the predicate, such as start and
+ * limit.
* @return a source of documents matching the predicate. Documents must be
* returned in descending updated timestamp order.
*
* @throws QueryParseException if the predicate could not be converted to an
* indexed data source.
*/
- public ChangeDataSource getSource(Predicate<ChangeData> p, int start,
- int limit) throws QueryParseException;
+ public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts)
+ throws QueryParseException;
/**
* Mark whether this index is up-to-date and ready to serve reads.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java
index 09226bd..d204905 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/DummyIndex.java
@@ -16,9 +16,9 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.query.Predicate;
-import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.QueryOptions;
import java.io.IOException;
@@ -45,8 +45,7 @@
}
@Override
- public ChangeDataSource getSource(Predicate<ChangeData> p, int start,
- int limit) throws QueryParseException {
+ public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts) {
throw new UnsupportedOperationException();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index afb7c22..2799144 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -22,7 +22,6 @@
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.query.change.ChangeQueryRewriter;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
@@ -67,7 +66,7 @@
@Override
protected void configure() {
- bind(ChangeQueryRewriter.class).to(IndexRewriteImpl.class);
+ bind(IndexRewriter.class);
bind(IndexCollection.class);
listener().to(IndexCollection.class);
factory(ChangeIndexer.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriter.java
similarity index 88%
rename from gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriter.java
index 8dd7aa3..d56348b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriteImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexRewriter.java
@@ -14,8 +14,6 @@
package com.google.gerrit.server.index;
-import static com.google.common.base.Preconditions.checkArgument;
-
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.Change;
@@ -27,11 +25,12 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.AndSource;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.ChangeQueryRewriter;
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
import com.google.gerrit.server.query.change.LimitPredicate;
import com.google.gerrit.server.query.change.OrSource;
+import com.google.gerrit.server.query.change.QueryOptions;
import com.google.inject.Inject;
+import com.google.inject.Singleton;
import org.eclipse.jgit.util.MutableInteger;
@@ -41,7 +40,8 @@
import java.util.Set;
/** Rewriter that pushes boolean logic into the secondary index. */
-public class IndexRewriteImpl implements ChangeQueryRewriter {
+@Singleton
+public class IndexRewriter {
/** Set of all open change statuses. */
public static final Set<Change.Status> OPEN_STATUSES;
@@ -122,25 +122,20 @@
private final IndexConfig config;
@Inject
- IndexRewriteImpl(IndexCollection indexes,
+ IndexRewriter(IndexCollection indexes,
IndexConfig config) {
this.indexes = indexes;
this.config = config;
}
- @Override
- public Predicate<ChangeData> rewrite(Predicate<ChangeData> in, int start,
- int limit) throws QueryParseException {
- checkArgument(limit > 0, "limit must be positive: %s", limit);
+ public Predicate<ChangeData> rewrite(Predicate<ChangeData> in,
+ QueryOptions opts) throws QueryParseException {
ChangeIndex index = indexes.getSearchIndex();
- // Increase the limit rather than skipping, since we don't know how many
- // skipped results would have been filtered out by the enclosing AndSource.
- limit += start;
MutableInteger leafTerms = new MutableInteger();
- Predicate<ChangeData> out = rewriteImpl(in, index, limit, leafTerms);
+ Predicate<ChangeData> out = rewriteImpl(in, index, opts, leafTerms);
if (in == out || out instanceof IndexPredicate) {
- return new IndexedChangeQuery(index, out, limit);
+ return new IndexedChangeQuery(index, out, opts);
} else if (out == null /* cannot rewrite */) {
return in;
} else {
@@ -153,7 +148,7 @@
*
* @param in predicate to rewrite.
* @param index index whose schema determines which fields are indexed.
- * @param limit maximum number of results to return.
+ * @param opts other query options.
* @param leafTerms number of leaf index query terms encountered so far.
* @return {@code null} if no part of this subtree can be queried in the
* index directly. {@code in} if this subtree and all its children can be
@@ -164,7 +159,7 @@
* support this predicate.
*/
private Predicate<ChangeData> rewriteImpl(Predicate<ChangeData> in,
- ChangeIndex index, int limit, MutableInteger leafTerms)
+ ChangeIndex index, QueryOptions opts, MutableInteger leafTerms)
throws QueryParseException {
if (isIndexPredicate(in, index)) {
if (++leafTerms.value > config.maxTerms()) {
@@ -172,8 +167,10 @@
}
return in;
} else if (in instanceof LimitPredicate) {
- // Replace any limits with the limit provided by the caller.
- return new LimitPredicate(limit);
+ // Replace any limits with the limit provided by the caller. The caller
+ // should have already searched the predicate tree for limit predicates
+ // and included that in their limit computation.
+ return new LimitPredicate(opts.limit());
} else if (!isRewritePossible(in)) {
return null; // magic to indicate "in" cannot be rewritten
}
@@ -185,7 +182,7 @@
List<Predicate<ChangeData>> newChildren = Lists.newArrayListWithCapacity(n);
for (int i = 0; i < n; i++) {
Predicate<ChangeData> c = in.getChild(i);
- Predicate<ChangeData> nc = rewriteImpl(c, index, limit, leafTerms);
+ Predicate<ChangeData> nc = rewriteImpl(c, index, opts, leafTerms);
if (nc == c) {
isIndexed.set(i);
newChildren.add(c);
@@ -205,7 +202,7 @@
} else if (rewritten.cardinality() == n) {
return in.copy(newChildren); // All children were rewritten.
}
- return partitionChildren(in, newChildren, isIndexed, index, limit);
+ return partitionChildren(in, newChildren, isIndexed, index, opts);
}
private boolean isIndexPredicate(Predicate<ChangeData> in, ChangeIndex index) {
@@ -221,11 +218,11 @@
List<Predicate<ChangeData>> newChildren,
BitSet isIndexed,
ChangeIndex index,
- int limit) throws QueryParseException {
+ QueryOptions opts) throws QueryParseException {
if (isIndexed.cardinality() == 1) {
int i = isIndexed.nextSetBit(0);
newChildren.add(
- 0, new IndexedChangeQuery(index, newChildren.remove(i), limit));
+ 0, new IndexedChangeQuery(index, newChildren.remove(i), opts));
return copy(in, newChildren);
}
@@ -245,7 +242,7 @@
all.add(c);
}
}
- all.add(0, new IndexedChangeQuery(index, in.copy(indexed), limit));
+ all.add(0, new IndexedChangeQuery(index, in.copy(indexed), opts));
return copy(in, all);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
index 81be0fd..683f8cf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
@@ -14,15 +14,18 @@
package com.google.gerrit.server.index;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.primitives.Ints;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
import com.google.gerrit.server.query.change.Paginated;
+import com.google.gerrit.server.query.change.QueryOptions;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -40,19 +43,28 @@
*/
public class IndexedChangeQuery extends Predicate<ChangeData>
implements ChangeDataSource, Paginated {
+ @VisibleForTesting
+ static QueryOptions convertOptions(QueryOptions opts) {
+ // Increase the limit rather than skipping, since we don't know how many
+ // skipped results would have been filtered out by the enclosing AndSource.
+ int backendLimit = opts.config().maxLimit();
+ int limit = Ints.saturatedCast((long) opts.limit() + opts.start());
+ limit = Math.min(limit, backendLimit);
+ return QueryOptions.create(opts.config(), 0, limit);
+ }
private final ChangeIndex index;
- private final int limit;
+ private QueryOptions opts;
private Predicate<ChangeData> pred;
private ChangeDataSource source;
public IndexedChangeQuery(ChangeIndex index, Predicate<ChangeData> pred,
- int limit) throws QueryParseException {
+ QueryOptions opts) throws QueryParseException {
this.index = index;
- this.limit = limit;
+ this.opts = convertOptions(opts);
this.pred = pred;
- this.source = index.getSource(pred, 0, limit);
+ this.source = index.getSource(pred, this.opts);
}
@Override
@@ -74,13 +86,13 @@
}
@Override
- public int limit() {
- return limit;
+ public QueryOptions getOptions() {
+ return opts;
}
@Override
public int getCardinality() {
- return source != null ? source.getCardinality() : limit();
+ return source != null ? source.getCardinality() : opts.limit();
}
@Override
@@ -126,14 +138,17 @@
@Override
public ResultSet<ChangeData> restart(int start) throws OrmException {
+ opts = opts.withStart(start);
try {
- source = index.getSource(pred, start, limit);
+ source = index.getSource(pred, opts);
} catch (QueryParseException e) {
// Don't need to show this exception to the user; the only thing that
// changed about pred was its start, and any other QPEs that might happen
// should have already thrown from the constructor.
throw new OrmException(e);
}
+ // Don't convert start to a limit, since the caller of this method (see
+ // AndSource) has calculated the actual number to skip.
return read();
}
@@ -168,14 +183,14 @@
}
IndexedChangeQuery o = (IndexedChangeQuery) other;
return pred.equals(o.pred)
- && limit == o.limit;
+ && opts.equals(o.opts);
}
@Override
public String toString() {
return MoreObjects.toStringHelper("index")
.add("p", pred)
- .add("limit", limit)
+ .add("opts", opts)
.toString();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
index 2c4f30c..da1e7b5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
@@ -16,7 +16,7 @@
package com.google.gerrit.server.patch;
import com.google.common.cache.Cache;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
index 6f957da..15277b2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
@@ -14,15 +14,16 @@
package com.google.gerrit.server.patch;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum;
+import static com.google.common.base.Preconditions.checkState;
import static org.eclipse.jgit.lib.ObjectIdSerialization.readCanBeNull;
import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
import static org.eclipse.jgit.lib.ObjectIdSerialization.writeCanBeNull;
import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
@@ -35,6 +36,16 @@
public class PatchListKey implements Serializable {
static final long serialVersionUID = 18L;
+ public static final BiMap<Whitespace, Character> WHITESPACE_TYPES = ImmutableBiMap.of(
+ Whitespace.IGNORE_NONE, 'N',
+ Whitespace.IGNORE_TRAILING, 'E',
+ Whitespace.IGNORE_LEADING_AND_TRAILING, 'S',
+ Whitespace.IGNORE_ALL, 'A');
+
+ static {
+ checkState(WHITESPACE_TYPES.size() == Whitespace.values().length);
+ }
+
private transient ObjectId oldId;
private transient ObjectId newId;
private transient Whitespace whitespace;
@@ -108,12 +119,20 @@
private void writeObject(final ObjectOutputStream out) throws IOException {
writeCanBeNull(out, oldId);
writeNotNull(out, newId);
- writeEnum(out, whitespace);
+ Character c = WHITESPACE_TYPES.get(whitespace);
+ if (c == null) {
+ throw new IOException("Invalid whitespace type: " + whitespace);
+ }
+ out.writeChar(c);
}
private void readObject(final ObjectInputStream in) throws IOException {
oldId = readCanBeNull(in);
newId = readNotNull(in);
- whitespace = readEnum(in, Whitespace.values());
+ char t = in.readChar();
+ whitespace = WHITESPACE_TYPES.inverse().get(t);
+ if (whitespace == null) {
+ throw new IOException("Invalid whitespace type code: " + t);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
index 0e421db..aa2a8a8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -21,7 +21,7 @@
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
@@ -129,13 +129,13 @@
private static RawTextComparator comparatorFor(Whitespace ws) {
switch (ws) {
- case IGNORE_ALL_SPACE:
+ case IGNORE_ALL:
return RawTextComparator.WS_IGNORE_ALL;
- case IGNORE_SPACE_AT_EOL:
+ case IGNORE_TRAILING:
return RawTextComparator.WS_IGNORE_TRAILING;
- case IGNORE_SPACE_CHANGE:
+ case IGNORE_LEADING_AND_TRAILING:
return RawTextComparator.WS_IGNORE_CHANGE;
case IGNORE_NONE:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index ff4496f..d0c3d09 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -17,10 +17,10 @@
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.common.data.PatchScript.DisplayMethod;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.prettify.common.EditList;
import com.google.gerrit.prettify.common.SparseFileContent;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
@@ -65,7 +65,7 @@
private Project.NameKey projectKey;
private ObjectReader reader;
private Change change;
- private AccountDiffPreference diffPrefs;
+ private DiffPreferencesInfo diffPrefs;
private boolean againstParent;
private ObjectId aId;
private ObjectId bId;
@@ -95,11 +95,11 @@
this.change = c;
}
- void setDiffPrefs(final AccountDiffPreference dp) {
+ void setDiffPrefs(final DiffPreferencesInfo dp) {
diffPrefs = dp;
- context = diffPrefs.getContext();
- if (context == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
+ context = diffPrefs.context;
+ if (context == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
context = MAX_CONTEXT;
} else if (context > MAX_CONTEXT) {
context = MAX_CONTEXT;
@@ -140,12 +140,12 @@
if (!isModify(content)) {
intralineDifferenceIsPossible = false;
- } else if (diffPrefs.isIntralineDifference()) {
+ } else if (diffPrefs.intralineDifference) {
IntraLineDiff d =
patchListCache.getIntraLineDiff(
new IntraLineDiffKey(
a.id, b.id,
- diffPrefs.getIgnoreWhitespace() != Whitespace.IGNORE_NONE),
+ diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE),
IntraLineDiffArgs.create(
a.src, b.src, edits, projectKey, bId, b.path));
if (d != null) {
@@ -208,7 +208,7 @@
//
context = MAX_CONTEXT;
- packContent(diffPrefs.getIgnoreWhitespace() != Whitespace.IGNORE_NONE);
+ packContent(diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE);
}
return new PatchScript(change.getKey(), content.getChangeType(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index 2169671..5836df5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -18,10 +18,10 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo;
+import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Patch.ChangeType;
@@ -66,7 +66,7 @@
String fileName,
@Assisted("patchSetA") PatchSet.Id patchSetA,
@Assisted("patchSetB") PatchSet.Id patchSetB,
- AccountDiffPreference diffPrefs);
+ DiffPreferencesInfo diffPrefs);
}
private static final Logger log =
@@ -83,7 +83,7 @@
@Nullable
private final PatchSet.Id psa;
private final PatchSet.Id psb;
- private final AccountDiffPreference diffPrefs;
+ private final DiffPreferencesInfo diffPrefs;
private final ChangeEditUtil editReader;
private Optional<ChangeEdit> edit;
@@ -110,7 +110,7 @@
@Assisted final String fileName,
@Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
@Assisted("patchSetB") final PatchSet.Id patchSetB,
- @Assisted final AccountDiffPreference diffPrefs) {
+ @Assisted DiffPreferencesInfo diffPrefs) {
this.repoManager = grm;
this.builderFactory = builderFactory;
this.patchListCache = patchListCache;
@@ -156,7 +156,7 @@
try (Repository git = repoManager.openRepository(project)) {
try {
- final PatchList list = listFor(keyFor(diffPrefs.getIgnoreWhitespace()));
+ final PatchList list = listFor(keyFor(diffPrefs.ignoreWhitespace));
final PatchScriptBuilder b = newBuilder(list, git);
final PatchListEntry content = list.get(fileName);
@@ -192,11 +192,10 @@
}
private PatchScriptBuilder newBuilder(final PatchList list, Repository git) {
- final AccountDiffPreference dp = new AccountDiffPreference(diffPrefs);
final PatchScriptBuilder b = builderFactory.get();
b.setRepository(git, project);
b.setChange(change);
- b.setDiffPrefs(dp);
+ b.setDiffPrefs(diffPrefs);
b.setTrees(list.isAgainstParent(), list.getOldId(), list.getNewId());
return b;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
index 13f8098..2f780bf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
@@ -60,7 +60,12 @@
}
try {
try (InputStream in = openStream(input)) {
- loader.installPluginFromStream(name, in);
+ String pluginName = loader.installPluginFromStream(name, in);
+ ListPlugins.PluginInfo info =
+ new ListPlugins.PluginInfo(loader.get(pluginName));
+ return created
+ ? Response.created(info)
+ : Response.ok(info);
}
} catch (PluginInstallException e) {
StringWriter buf = new StringWriter();
@@ -76,9 +81,6 @@
}
throw new BadRequestException(buf.toString());
}
-
- ListPlugins.PluginInfo info = new ListPlugins.PluginInfo(loader.get(name));
- return created ? Response.created(info) : Response.ok(info);
}
private InputStream openStream(Input input)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
index 6eb336d..d94df9c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -102,19 +102,19 @@
throw new InvalidPluginException("Cannot auto-register", err);
} catch (RuntimeException err) {
PluginLoader.log.warn(String.format(
- "Plugin %s has invaild class file %s inside of %s", pluginName,
+ "Plugin %s has invalid class file %s inside of %s", pluginName,
entry.getName(), jarFile.getName()), err);
continue;
}
- if (def.isConcrete()) {
- if (!Strings.isNullOrEmpty(def.annotationName)) {
- rawMap.put(def.annotationName, def);
+ if (!Strings.isNullOrEmpty(def.annotationName)) {
+ if (def.isConcrete()) {
+ rawMap.put(def.annotationName, def);
+ } else {
+ PluginLoader.log.warn(String.format(
+ "Plugin %s tries to @%s(\"%s\") abstract class %s", pluginName,
+ def.annotationName, def.annotationValue, def.className));
}
- } else {
- PluginLoader.log.warn(String.format(
- "Plugin %s tries to @%s(\"%s\") abstract class %s", pluginName,
- def.annotationName, def.annotationValue, def.className));
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
index 4e651c2..5006401 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -157,7 +157,7 @@
}
}
- public void installPluginFromStream(String originalName, InputStream in)
+ public String installPluginFromStream(String originalName, InputStream in)
throws IOException, PluginInstallException {
checkRemoteInstall();
@@ -197,6 +197,8 @@
cleanInBackground();
}
+
+ return name;
}
static Path asTemp(InputStream in, String prefix, String suffix, Path dir)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
index 212bb1c..6cbf7c6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
@@ -46,6 +46,7 @@
public InheritedBooleanInfo createNewChangeForAllNotInTarget;
public InheritedBooleanInfo requireChangeId;
public InheritedBooleanInfo enableSignedPush;
+ public InheritedBooleanInfo requireSignedPush;
public MaxObjectSizeLimitInfo maxObjectSizeLimit;
public SubmitType submitType;
public com.google.gerrit.extensions.client.ProjectState state;
@@ -74,6 +75,7 @@
InheritedBooleanInfo createNewChangeForAllNotInTarget =
new InheritedBooleanInfo();
InheritedBooleanInfo enableSignedPush = new InheritedBooleanInfo();
+ InheritedBooleanInfo requireSignedPush = new InheritedBooleanInfo();
useContributorAgreements.value = projectState.isUseContributorAgreements();
useSignedOffBy.value = projectState.isUseSignedOffBy();
@@ -90,6 +92,7 @@
createNewChangeForAllNotInTarget.configuredValue =
p.getCreateNewChangeForAllNotInTarget();
enableSignedPush.configuredValue = p.getEnableSignedPush();
+ requireSignedPush.configuredValue = p.getRequireSignedPush();
ProjectState parentState = Iterables.getFirst(projectState
.parents(), null);
@@ -102,6 +105,7 @@
createNewChangeForAllNotInTarget.inheritedValue =
parentState.isCreateNewChangeForAllNotInTarget();
enableSignedPush.inheritedValue = projectState.isEnableSignedPush();
+ requireSignedPush.inheritedValue = projectState.isRequireSignedPush();
}
this.useContributorAgreements = useContributorAgreements;
@@ -111,6 +115,7 @@
this.createNewChangeForAllNotInTarget = createNewChangeForAllNotInTarget;
if (serverEnableSignedPush) {
this.enableSignedPush = enableSignedPush;
+ this.requireSignedPush = requireSignedPush;
}
MaxObjectSizeLimitInfo maxObjectSizeLimit = new MaxObjectSizeLimitInfo();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java
index 41d4f94..b957ba1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java
@@ -106,7 +106,8 @@
@Override
public ReflogEntryInfo apply(ReflogEntry e) {
return new ReflogEntryInfo(e);
- }});
+ }
+ });
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
index 455a42b..7094828 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -413,6 +413,15 @@
});
}
+ public boolean isRequireSignedPush() {
+ return getInheritableBoolean(new Function<Project, InheritableBoolean>() {
+ @Override
+ public InheritableBoolean apply(Project input) {
+ return input.getRequireSignedPush();
+ }
+ });
+ }
+
public LabelTypes getLabelTypes() {
Map<String, LabelType> types = Maps.newLinkedHashMap();
for (ProjectState s : treeInOrder()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index 724dc45..76ad2f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -70,6 +70,7 @@
public InheritableBoolean createNewChangeForAllNotInTarget;
public InheritableBoolean requireChangeId;
public InheritableBoolean enableSignedPush;
+ public InheritableBoolean requireSignedPush;
public String maxObjectSizeLimit;
public SubmitType submitType;
public com.google.gerrit.extensions.client.ProjectState state;
@@ -166,8 +167,13 @@
p.setRequireChangeID(input.requireChangeId);
}
- if (input.enableSignedPush != null) {
- p.setEnableSignedPush(input.enableSignedPush);
+ if (serverEnableSignedPush) {
+ if (input.enableSignedPush != null) {
+ p.setEnableSignedPush(input.enableSignedPush);
+ }
+ if (input.requireSignedPush != null) {
+ p.setRequireSignedPush(input.requireSignedPush);
+ }
}
if (input.maxObjectSizeLimit != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java
index f48cfd8..9ed0447 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AndSource.java
@@ -128,7 +128,7 @@
// limit the caller wants. Restart the source and continue.
//
Paginated p = (Paginated) source;
- while (skipped && r.size() < p.limit() + start) {
+ while (skipped && r.size() < p.getOptions().limit() + start) {
skipped = false;
ResultSet<ChangeData> next = p.restart(nextStart);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index f52edc1..2fdeea4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -47,6 +47,7 @@
import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.IndexCollection;
+import com.google.gerrit.server.index.IndexRewriter;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.ChangeControl;
@@ -143,7 +144,7 @@
public static class Arguments {
final Provider<ReviewDb> db;
final Provider<InternalChangeQuery> queryProvider;
- final Provider<ChangeQueryRewriter> rewriter;
+ final IndexRewriter rewriter;
final IdentifiedUser.GenericFactory userFactory;
final CapabilityControl.Factory capabilityControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory;
@@ -170,7 +171,7 @@
@VisibleForTesting
public Arguments(Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
- Provider<ChangeQueryRewriter> rewriter,
+ IndexRewriter rewriter,
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
CapabilityControl.Factory capabilityControlFactory,
@@ -204,7 +205,7 @@
private Arguments(
Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
- Provider<ChangeQueryRewriter> rewriter,
+ IndexRewriter rewriter,
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
CapabilityControl.Factory capabilityControlFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
deleted file mode 100644
index 83492d2..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (C) 2013 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.server.query.Predicate;
-import com.google.gerrit.server.query.QueryParseException;
-
-public interface ChangeQueryRewriter {
- Predicate<ChangeData> rewrite(Predicate<ChangeData> in, int start, int limit)
- throws QueryParseException;
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java
index e829e53..dd3c3b3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentPredicate.java
@@ -32,9 +32,11 @@
@Override
public boolean match(ChangeData object) throws OrmException {
try {
- for (ChangeData cData : index.getSource(
- Predicate.and(new LegacyChangeIdPredicate(
- index.getSchema(), object.getId()), this), 0, 1).read()) {
+ Predicate<ChangeData> p = Predicate.and(
+ new LegacyChangeIdPredicate(index.getSchema(), object.getId()),
+ this);
+ for (ChangeData cData
+ : index.getSource(p, QueryOptions.oneResult()).read()) {
if (cData.getId().equals(object.getId())) {
return true;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index ea8a3ef..9b47302 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -22,7 +22,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
-import com.google.gerrit.server.git.MergeException;
+import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.strategy.SubmitStrategy;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
@@ -129,7 +129,8 @@
conflicts = !strategy.dryRun(commit, otherCommit);
args.conflictsCache.put(conflictsKey, conflicts);
return conflicts;
- } catch (MergeException | NoSuchProjectException | IOException e) {
+ } catch (IntegrationException | NoSuchProjectException
+ | IOException e) {
throw new IllegalStateException(e);
}
}
@@ -148,7 +149,7 @@
}
private Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw,
- CodeReviewCommit tip) throws MergeException {
+ CodeReviewCommit tip) throws IntegrationException {
Set<RevCommit> alreadyAccepted = Sets.newHashSet();
if (tip != null) {
@@ -164,7 +165,7 @@
}
}
} catch (IOException e) {
- throw new MergeException(
+ throw new IntegrationException(
"Failed to determine already accepted commits.", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
index 5655154..5b9b94c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
@@ -58,7 +58,7 @@
Predicate<ChangeData> thisId =
new LegacyChangeIdPredicate(index.getSchema(), cd.getId());
Iterable<ChangeData> results =
- index.getSource(and(thisId, this), 0, 1).read();
+ index.getSource(and(thisId, this), QueryOptions.oneResult()).read();
return !Iterables.isEmpty(results);
} catch (QueryParseException e) {
throw new OrmException(e);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index aa977dd..a2ccc3a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.change;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.server.index.ChangeField.SUBMISSIONID;
import static com.google.gerrit.server.query.Predicate.and;
import static com.google.gerrit.server.query.Predicate.not;
@@ -215,6 +216,13 @@
return query(commit(schema(indexes), id.name()));
}
+ public List<ChangeData> byProjectCommits(Project.NameKey project,
+ List<String> hashes) throws OrmException {
+ int n = indexConfig.maxTerms() - 1;
+ checkArgument(hashes.size() <= n, "cannot exceed %s commits", n);
+ return query(and(project(project), or(commits(schema(indexes), hashes))));
+ }
+
public List<ChangeData> bySubmissionId(String cs) throws OrmException {
if (Strings.isNullOrEmpty(cs) || !schema(indexes).hasField(SUBMISSIONID)) {
return Collections.emptyList();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java
index 72230f6..cf3140a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/MessagePredicate.java
@@ -36,9 +36,11 @@
@Override
public boolean match(ChangeData object) throws OrmException {
try {
- for (ChangeData cData : index.getSource(
- Predicate.and(new LegacyChangeIdPredicate(
- index.getSchema(), object.getId()), this), 0, 1).read()) {
+ Predicate<ChangeData> p = Predicate.and(
+ new LegacyChangeIdPredicate(index.getSchema(), object.getId()),
+ this);
+ for (ChangeData cData
+ : index.getSource(p, QueryOptions.oneResult()).read()) {
if (cData.getId().equals(object.getId())) {
return true;
}
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 b84cc79..b068030 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
@@ -14,24 +14,31 @@
package com.google.gerrit.server.query.change;
+import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.data.QueryStatsAttribute;
import com.google.gerrit.server.events.EventFactory;
+import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gson.Gson;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.io.DisabledOutputStream;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
@@ -47,7 +54,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Change query implementation that outputs to a stream in the style of an SSH
@@ -64,6 +73,8 @@
TEXT, JSON
}
+ private final Provider<ReviewDb> db;
+ private final GitRepositoryManager repoManager;
private final ChangeQueryBuilder queryBuilder;
private final QueryProcessor queryProcessor;
private final EventFactory eventFactory;
@@ -86,11 +97,15 @@
@Inject
OutputStreamQuery(
+ Provider<ReviewDb> db,
+ GitRepositoryManager repoManager,
ChangeQueryBuilder queryBuilder,
QueryProcessor queryProcessor,
EventFactory eventFactory,
TrackingFooters trackingFooters,
CurrentUser user) {
+ this.db = db;
+ this.repoManager = repoManager;
this.queryBuilder = queryBuilder;
this.queryProcessor = queryProcessor;
this.eventFactory = eventFactory;
@@ -179,76 +194,16 @@
final QueryStatsAttribute stats = new QueryStatsAttribute();
stats.runTimeMilliseconds = TimeUtil.nowMs();
+ Map<Project.NameKey, Repository> repos = new HashMap<>();
+ Map<Project.NameKey, RevWalk> revWalks = new HashMap<>();
QueryResult results =
queryProcessor.queryChanges(queryBuilder.parse(queryString));
- ChangeAttribute c = null;
- for (ChangeData d : results.changes()) {
- ChangeControl cc = d.changeControl().forUser(user);
-
- LabelTypes labelTypes = cc.getLabelTypes();
- c = eventFactory.asChangeAttribute(d.change());
- eventFactory.extend(c, d.change());
-
- if (!trackingFooters.isEmpty()) {
- eventFactory.addTrackingIds(c,
- trackingFooters.extract(d.commitFooters()));
+ try {
+ for (ChangeData d : results.changes()) {
+ show(buildChangeAttribute(d, repos, revWalks));
}
-
- if (includeAllReviewers) {
- eventFactory.addAllReviewers(c, d.notes());
- }
-
- if (includeSubmitRecords) {
- eventFactory.addSubmitRecords(c, new SubmitRuleEvaluator(d)
- .setAllowClosed(true)
- .setAllowDraft(true)
- .evaluate());
- }
-
- if (includeCommitMessage) {
- eventFactory.addCommitMessage(c, d.commitMessage());
- }
-
- if (includePatchSets) {
- if (includeFiles) {
- eventFactory.addPatchSets(c, d.patchSets(),
- includeApprovals ? d.approvals().asMap() : null,
- includeFiles, d.change(), labelTypes);
- } else {
- eventFactory.addPatchSets(c, d.patchSets(),
- includeApprovals ? d.approvals().asMap() : null,
- labelTypes);
- }
- }
-
- if (includeCurrentPatchSet) {
- PatchSet current = d.currentPatchSet();
- if (current != null) {
- c.currentPatchSet = eventFactory.asPatchSetAttribute(current);
- eventFactory.addApprovals(c.currentPatchSet,
- d.currentApprovals(), labelTypes);
-
- if (includeFiles) {
- eventFactory.addPatchSetFileNames(c.currentPatchSet,
- d.change(), d.currentPatchSet());
- }
- }
- }
-
- if (includeComments) {
- eventFactory.addComments(c, d.messages());
- if (includePatchSets) {
- for (PatchSetAttribute attribute : c.patchSets) {
- eventFactory.addPatchSetComments(attribute, d.publishedComments());
- }
- }
- }
-
- if (includeDependencies) {
- eventFactory.addDependencies(c, d.change());
- }
-
- show(c);
+ } finally {
+ closeAll(revWalks.values(), repos.values());
}
stats.rowCount = results.changes().size();
@@ -277,6 +232,107 @@
}
}
+ private ChangeAttribute buildChangeAttribute(ChangeData d,
+ Map<Project.NameKey, Repository> repos,
+ Map<Project.NameKey, RevWalk> revWalks)
+ throws OrmException, IOException {
+ ChangeControl cc = d.changeControl().forUser(user);
+
+ LabelTypes labelTypes = cc.getLabelTypes();
+ ChangeAttribute c = eventFactory.asChangeAttribute(db.get(), d.change());
+ eventFactory.extend(c, d.change());
+
+ if (!trackingFooters.isEmpty()) {
+ eventFactory.addTrackingIds(c,
+ trackingFooters.extract(d.commitFooters()));
+ }
+
+ if (includeAllReviewers) {
+ eventFactory.addAllReviewers(db.get(), c, d.notes());
+ }
+
+ if (includeSubmitRecords) {
+ eventFactory.addSubmitRecords(c, new SubmitRuleEvaluator(d)
+ .setAllowClosed(true)
+ .setAllowDraft(true)
+ .evaluate());
+ }
+
+ if (includeCommitMessage) {
+ eventFactory.addCommitMessage(c, d.commitMessage());
+ }
+
+ RevWalk rw = null;
+ if (includePatchSets || includeCurrentPatchSet || includeDependencies) {
+ Project.NameKey p = d.change().getProject();
+ rw = revWalks.get(p);
+ // Cache and reuse repos and revwalks.
+ if (rw == null) {
+ Repository repo = repoManager.openRepository(p);
+ checkState(repos.put(p, repo) == null);
+ rw = new RevWalk(repo);
+ revWalks.put(p, rw);
+ }
+ }
+
+ if (includePatchSets) {
+ if (includeFiles) {
+ eventFactory.addPatchSets(db.get(), rw, c, d.patchSets(),
+ includeApprovals ? d.approvals().asMap() : null,
+ includeFiles, d.change(), labelTypes);
+ } else {
+ eventFactory.addPatchSets(db.get(), rw, c, d.patchSets(),
+ includeApprovals ? d.approvals().asMap() : null,
+ labelTypes);
+ }
+ }
+
+ if (includeCurrentPatchSet) {
+ PatchSet current = d.currentPatchSet();
+ if (current != null) {
+ c.currentPatchSet =
+ eventFactory.asPatchSetAttribute(db.get(), rw, current);
+ eventFactory.addApprovals(c.currentPatchSet,
+ d.currentApprovals(), labelTypes);
+
+ if (includeFiles) {
+ eventFactory.addPatchSetFileNames(c.currentPatchSet,
+ d.change(), d.currentPatchSet());
+ }
+ }
+ }
+
+ if (includeComments) {
+ eventFactory.addComments(c, d.messages());
+ if (includePatchSets) {
+ for (PatchSetAttribute attribute : c.patchSets) {
+ eventFactory.addPatchSetComments(
+ attribute, d.publishedComments());
+ }
+ }
+ }
+
+ if (includeDependencies) {
+ eventFactory.addDependencies(rw, c, d.change(), d.currentPatchSet());
+ }
+
+ return c;
+ }
+
+ private static void closeAll(Iterable<RevWalk> revWalks,
+ Iterable<Repository> repos) {
+ if (repos != null) {
+ for (Repository repo : repos) {
+ repo.close();
+ }
+ }
+ if (revWalks != null) {
+ for (RevWalk revWalk : revWalks) {
+ revWalk.close();
+ }
+ }
+ }
+
private void show(Object data) {
switch (outputFormat) {
default:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java
index 7afd934..3278b7f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/Paginated.java
@@ -18,7 +18,7 @@
import com.google.gwtorm.server.ResultSet;
public interface Paginated {
- int limit();
+ QueryOptions getOptions();
ResultSet<ChangeData> restart(int start) throws OrmException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryOptions.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryOptions.java
new file mode 100644
index 0000000..1964fa5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryOptions.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2015 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 static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.server.index.IndexConfig;
+
+@AutoValue
+public abstract class QueryOptions {
+ public static QueryOptions create(IndexConfig config, int start, int limit) {
+ checkArgument(start >= 0, "start must be nonnegative: %s", start);
+ checkArgument(limit > 0, "limit must be positive: %s", limit);
+ return new AutoValue_QueryOptions(config, start, limit);
+ }
+
+ public static QueryOptions oneResult() {
+ return create(IndexConfig.createDefault(), 0, 1);
+ }
+
+ public abstract IndexConfig config();
+ public abstract int start();
+ public abstract int limit();
+
+ public QueryOptions withLimit(int newLimit) {
+ return create(config(), start(), newLimit);
+ }
+
+ public QueryOptions withStart(int newStart) {
+ return create(config(), newStart, limit());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index 6068dd0..a2c8b81 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -24,6 +24,7 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.index.IndexConfig;
import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.index.IndexRewriter;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
@@ -39,7 +40,7 @@
private final Provider<ReviewDb> db;
private final Provider<CurrentUser> userProvider;
private final ChangeControl.GenericFactory changeControlFactory;
- private final ChangeQueryRewriter queryRewriter;
+ private final IndexRewriter rewriter;
private final IndexConfig indexConfig;
private int limitFromCaller;
@@ -50,12 +51,12 @@
QueryProcessor(Provider<ReviewDb> db,
Provider<CurrentUser> userProvider,
ChangeControl.GenericFactory changeControlFactory,
- ChangeQueryRewriter queryRewriter,
+ IndexRewriter rewriter,
IndexConfig indexConfig) {
this.db = db;
this.userProvider = userProvider;
this.changeControlFactory = changeControlFactory;
- this.queryRewriter = queryRewriter;
+ this.rewriter = rewriter;
this.indexConfig = indexConfig;
}
@@ -139,10 +140,11 @@
"Cannot go beyond page " + indexConfig.maxPages() + "of results");
}
- Predicate<ChangeData> s = queryRewriter.rewrite(q, start, limit + 1);
+ QueryOptions opts = QueryOptions.create(indexConfig, start, limit + 1);
+ Predicate<ChangeData> s = rewriter.rewrite(q, opts);
if (!(s instanceof ChangeDataSource)) {
q = Predicate.and(open(), q);
- s = queryRewriter.rewrite(q, start, limit);
+ s = rewriter.rewrite(q, opts);
}
if (!(s instanceof ChangeDataSource)) {
throw new QueryParseException("invalid query: " + s);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
index 8da9e2a..148d1df 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -19,6 +19,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -76,6 +77,7 @@
for (Class<?> c : new Class<?>[] {
AllProjectsName.class,
AllUsersCreator.class,
+ AllUsersName.class,
GitRepositoryManager.class,
SitePaths.class,
}) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index a4a7ddc..1c29988 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_113> C = Schema_113.class;
+ public static final Class<Schema_114> C = Schema_114.class;
public static int getBinaryVersion() {
return guessVersion(C);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeConflictException.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_114.java
similarity index 62%
copy from gerrit-server/src/main/java/com/google/gerrit/server/git/MergeConflictException.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_114.java
index 02bc8dc..85c93d2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeConflictException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_114.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2014 The Android Open Source Project
+// Copyright (C) 2015 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.
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.schema;
-/** Indicates that the commit cannot be merged without conflicts. */
-public class MergeConflictException extends Exception {
- private static final long serialVersionUID = 1L;
- public MergeConflictException(String msg) {
- super(msg, null);
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class Schema_114 extends SchemaVersion {
+ @Inject
+ Schema_114(Provider<Schema_113> prior) {
+ super(prior);
}
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
index 1e752d4..f5e6d74 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -401,7 +401,8 @@
@Override
public ResultSet<PatchLineComment> answer() throws Throwable {
return new ListResultSet<>(Lists.newArrayList(comments));
- }};
+ }
+ };
}
private void assertListComments(RevisionResource res,
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java
index ab00ba8..4f409d1 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/ConfigUtilTest.java
@@ -98,7 +98,7 @@
assertThat(cfg.getString(SECT, SUB, "sd")).isNull();
SectionInfo out = new SectionInfo();
- ConfigUtil.loadSection(cfg, SECT, SUB, out, d);
+ ConfigUtil.loadSection(cfg, SECT, SUB, out, d, null);
assertThat(out.i).isEqualTo(in.i);
assertThat(out.ii).isEqualTo(in.ii);
assertThat(out.id).isEqualTo(d.id);
@@ -115,6 +115,25 @@
}
@Test
+ public void mergeSection() throws Exception {
+ SectionInfo d = SectionInfo.defaults();
+ Config cfg = new Config();
+ ConfigUtil.storeSection(cfg, SECT, SUB, d, d);
+
+ SectionInfo in = new SectionInfo();
+ in.i = 42;
+
+ SectionInfo out = new SectionInfo();
+ ConfigUtil.loadSection(cfg, SECT, SUB, out, d, in);
+ // Check original values preserved
+ assertThat(out.id).isEqualTo(d.id);
+ // Check merged values
+ assertThat(out.i).isEqualTo(in.i);
+ // Check that boolean attribute not nullified
+ assertThat(out.bb).isFalse();
+ }
+
+ @Test
public void testTimeUnit() {
assertEquals(ms(0, MILLISECONDS), parse("0"));
assertEquals(ms(2, MILLISECONDS), parse("2ms"));
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
index 9b3d5ed..d38da43 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/FakeIndex.java
@@ -20,6 +20,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
+import com.google.gerrit.server.query.change.QueryOptions;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -84,8 +85,8 @@
}
@Override
- public ChangeDataSource getSource(Predicate<ChangeData> p, int start,
- int limit) throws QueryParseException {
+ public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts)
+ throws QueryParseException {
return new FakeIndex.Source(p);
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
deleted file mode 100644
index 128c2a7..0000000
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriteTest.java
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright (C) 2013 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.index;
-
-import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
-import static com.google.gerrit.reviewdb.client.Change.Status.ABANDONED;
-import static com.google.gerrit.reviewdb.client.Change.Status.DRAFT;
-import static com.google.gerrit.reviewdb.client.Change.Status.MERGED;
-import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.query.AndPredicate;
-import com.google.gerrit.server.query.Predicate;
-import com.google.gerrit.server.query.QueryParseException;
-import com.google.gerrit.server.query.change.AndSource;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.ChangeQueryBuilder;
-import com.google.gerrit.server.query.change.OrSource;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Set;
-
-public class IndexRewriteTest {
- @Rule
- public ExpectedException exception = ExpectedException.none();
-
- private FakeIndex index;
- private IndexCollection indexes;
- private ChangeQueryBuilder queryBuilder;
- private IndexRewriteImpl rewrite;
-
- @Before
- public void setUp() throws Exception {
- index = new FakeIndex(FakeIndex.V2);
- indexes = new IndexCollection();
- indexes.setSearchIndex(index);
- queryBuilder = new FakeQueryBuilder(indexes);
- rewrite = new IndexRewriteImpl(indexes,
- IndexConfig.create(0, 0, 3, 100));
- }
-
- @Test
- public void testIndexPredicate() throws Exception {
- Predicate<ChangeData> in = parse("file:a");
- assertEquals(query(in), rewrite(in));
- }
-
- @Test
- public void testNonIndexPredicate() throws Exception {
- Predicate<ChangeData> in = parse("foo:a");
- assertSame(in, rewrite(in));
- }
-
- @Test
- public void testIndexPredicates() throws Exception {
- Predicate<ChangeData> in = parse("file:a file:b");
- assertEquals(query(in), rewrite(in));
- }
-
- @Test
- public void testNonIndexPredicates() throws Exception {
- Predicate<ChangeData> in = parse("foo:a OR foo:b");
- assertEquals(in, rewrite(in));
- }
-
- @Test
- public void testOneIndexPredicate() throws Exception {
- Predicate<ChangeData> in = parse("foo:a file:b");
- Predicate<ChangeData> out = rewrite(in);
- assertSame(AndSource.class, out.getClass());
- assertEquals(
- ImmutableList.of(query(in.getChild(1)), in.getChild(0)),
- out.getChildren());
- }
-
- @Test
- public void testThreeLevelTreeWithAllIndexPredicates() throws Exception {
- Predicate<ChangeData> in =
- parse("-status:abandoned (file:a OR file:b)");
- assertEquals(
- query(in),
- rewrite.rewrite(in, 0, DEFAULT_MAX_QUERY_LIMIT));
- }
-
- @Test
- public void testThreeLevelTreeWithSomeIndexPredicates() throws Exception {
- Predicate<ChangeData> in = parse("-foo:a (file:b OR file:c)");
- Predicate<ChangeData> out = rewrite(in);
- assertEquals(AndSource.class, out.getClass());
- assertEquals(
- ImmutableList.of(query(in.getChild(1)), in.getChild(0)),
- out.getChildren());
- }
-
- @Test
- public void testMultipleIndexPredicates() throws Exception {
- Predicate<ChangeData> in =
- parse("file:a OR foo:b OR file:c OR foo:d");
- Predicate<ChangeData> out = rewrite(in);
- assertSame(OrSource.class, out.getClass());
- assertEquals(ImmutableList.of(
- query(Predicate.or(in.getChild(0), in.getChild(2))),
- in.getChild(1), in.getChild(3)),
- out.getChildren());
- }
-
- @Test
- public void testIndexAndNonIndexPredicates() throws Exception {
- Predicate<ChangeData> in = parse("status:new bar:p file:a");
- Predicate<ChangeData> out = rewrite(in);
- assertSame(AndSource.class, out.getClass());
- assertEquals(ImmutableList.of(
- query(Predicate.and(in.getChild(0), in.getChild(2))),
- in.getChild(1)),
- out.getChildren());
- }
-
- @Test
- public void testDuplicateCompoundNonIndexOnlyPredicates() throws Exception {
- Predicate<ChangeData> in =
- parse("(status:new OR status:draft) bar:p file:a");
- Predicate<ChangeData> out = rewrite(in);
- assertSame(AndSource.class, out.getClass());
- assertEquals(ImmutableList.of(
- query(Predicate.and(in.getChild(0), in.getChild(2))),
- in.getChild(1)),
- out.getChildren());
- }
-
- @Test
- public void testDuplicateCompoundIndexOnlyPredicates() throws Exception {
- Predicate<ChangeData> in =
- parse("(status:new OR file:a) bar:p file:b");
- Predicate<ChangeData> out = rewrite(in);
- assertSame(AndSource.class, out.getClass());
- assertEquals(ImmutableList.of(
- query(Predicate.and(in.getChild(0), in.getChild(2))),
- in.getChild(1)),
- out.getChildren());
- }
-
- @Test
- public void testLimitArgumentOverridesAllLimitPredicates() throws Exception {
- Predicate<ChangeData> in = parse("limit:1 file:a limit:3");
- Predicate<ChangeData> out = rewrite(in, 5);
- assertSame(AndSource.class, out.getClass());
- assertEquals(ImmutableList.of(
- query(in.getChild(1), 5),
- parse("limit:5"),
- parse("limit:5")),
- out.getChildren());
- }
-
- @Test
- public void testStartIncreasesLimit() throws Exception {
- int n = 3;
- Predicate<ChangeData> f = parse("file:a");
- Predicate<ChangeData> l = parse("limit:" + n);
- Predicate<ChangeData> in = and(f, l);
- assertEquals(and(query(f, 3), parse("limit:3")), rewrite.rewrite(in, 0, n));
- assertEquals(and(query(f, 4), parse("limit:4")), rewrite.rewrite(in, 1, n));
- assertEquals(and(query(f, 5), parse("limit:5")), rewrite.rewrite(in, 2, n));
- }
-
- @Test
- public void testGetPossibleStatus() throws Exception {
- assertEquals(EnumSet.allOf(Change.Status.class), status("file:a"));
- assertEquals(EnumSet.of(NEW), status("is:new"));
- assertEquals(EnumSet.of(DRAFT, MERGED, ABANDONED),
- status("-is:new"));
- assertEquals(EnumSet.of(NEW, MERGED), status("is:new OR is:merged"));
-
- EnumSet<Change.Status> none = EnumSet.noneOf(Change.Status.class);
- assertEquals(none, status("is:new is:merged"));
- assertEquals(none, status("(is:new is:draft) (is:merged)"));
- assertEquals(none, status("(is:new is:draft) (is:merged)"));
-
- assertEquals(EnumSet.of(MERGED),
- status("(is:new is:draft) OR (is:merged)"));
- }
-
- @Test
- public void testUnsupportedIndexOperator() throws Exception {
- Predicate<ChangeData> in = parse("status:merged file:a");
- assertEquals(query(in), rewrite(in));
-
- indexes.setSearchIndex(new FakeIndex(FakeIndex.V1));
- Predicate<ChangeData> out = rewrite(in);
- assertTrue(out instanceof AndPredicate);
- assertEquals(ImmutableList.of(
- query(in.getChild(0)),
- in.getChild(1)),
- out.getChildren());
- }
-
- @Test
- public void testTooManyTerms() throws Exception {
- String q = "file:a OR file:b OR file:c";
- Predicate<ChangeData> in = parse(q);
- assertEquals(query(in), rewrite(in));
-
- exception.expect(QueryParseException.class);
- exception.expectMessage("too many terms in query");
- rewrite(parse(q + " OR file:d"));
- }
-
- private Predicate<ChangeData> parse(String query) throws QueryParseException {
- return queryBuilder.parse(query);
- }
-
- @SafeVarargs
- private static AndSource and(Predicate<ChangeData>... preds) {
- return new AndSource(Arrays.asList(preds));
- }
-
- private Predicate<ChangeData> rewrite(Predicate<ChangeData> in)
- throws QueryParseException {
- return rewrite.rewrite(in, 0, DEFAULT_MAX_QUERY_LIMIT);
- }
-
- private Predicate<ChangeData> rewrite(Predicate<ChangeData> in, int limit)
- throws QueryParseException {
- return rewrite.rewrite(in, 0, limit);
- }
-
- private IndexedChangeQuery query(Predicate<ChangeData> p)
- throws QueryParseException {
- return query(p, DEFAULT_MAX_QUERY_LIMIT);
- }
-
- private IndexedChangeQuery query(Predicate<ChangeData> p, int limit)
- throws QueryParseException {
- return new IndexedChangeQuery(index, p, limit);
- }
-
- private Set<Change.Status> status(String query) throws QueryParseException {
- return IndexRewriteImpl.getPossibleStatus(parse(query));
- }
-}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriterTest.java
new file mode 100644
index 0000000..9ac83d5
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/IndexRewriterTest.java
@@ -0,0 +1,298 @@
+// Copyright (C) 2013 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.index;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.common.data.GlobalCapability.DEFAULT_MAX_QUERY_LIMIT;
+import static com.google.gerrit.reviewdb.client.Change.Status.ABANDONED;
+import static com.google.gerrit.reviewdb.client.Change.Status.DRAFT;
+import static com.google.gerrit.reviewdb.client.Change.Status.MERGED;
+import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
+import static com.google.gerrit.server.index.IndexedChangeQuery.convertOptions;
+import static com.google.gerrit.server.query.Predicate.and;
+import static com.google.gerrit.server.query.Predicate.or;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.query.AndPredicate;
+import com.google.gerrit.server.query.Predicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.change.AndSource;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder;
+import com.google.gerrit.server.query.change.OrSource;
+import com.google.gerrit.server.query.change.QueryOptions;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Set;
+
+public class IndexRewriterTest {
+ private static final IndexConfig CONFIG = IndexConfig.createDefault();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ private FakeIndex index;
+ private IndexCollection indexes;
+ private ChangeQueryBuilder queryBuilder;
+ private IndexRewriter rewrite;
+
+ @Before
+ public void setUp() throws Exception {
+ index = new FakeIndex(FakeIndex.V2);
+ indexes = new IndexCollection();
+ indexes.setSearchIndex(index);
+ queryBuilder = new FakeQueryBuilder(indexes);
+ rewrite = new IndexRewriter(indexes,
+ IndexConfig.create(0, 0, 3, 100));
+ }
+
+ @Test
+ public void testIndexPredicate() throws Exception {
+ Predicate<ChangeData> in = parse("file:a");
+ assertThat(rewrite(in)).isEqualTo(query(in));
+ }
+
+ @Test
+ public void testNonIndexPredicate() throws Exception {
+ Predicate<ChangeData> in = parse("foo:a");
+ assertThat(in).isSameAs(rewrite(in));
+ }
+
+ @Test
+ public void testIndexPredicates() throws Exception {
+ Predicate<ChangeData> in = parse("file:a file:b");
+ assertThat(rewrite(in)).isEqualTo(query(in));
+ }
+
+ @Test
+ public void testNonIndexPredicates() throws Exception {
+ Predicate<ChangeData> in = parse("foo:a OR foo:b");
+ assertThat(in).isEqualTo(rewrite(in));
+ }
+
+ @Test
+ public void testOneIndexPredicate() throws Exception {
+ Predicate<ChangeData> in = parse("foo:a file:b");
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(AndSource.class).isSameAs(out.getClass());
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(in.getChild(1)),
+ in.getChild(0))
+ .inOrder();
+ }
+
+ @Test
+ public void testThreeLevelTreeWithAllIndexPredicates() throws Exception {
+ Predicate<ChangeData> in =
+ parse("-status:abandoned (file:a OR file:b)");
+ assertThat(rewrite.rewrite(in, options(0, DEFAULT_MAX_QUERY_LIMIT)))
+ .isEqualTo(query(in));
+ }
+
+ @Test
+ public void testThreeLevelTreeWithSomeIndexPredicates() throws Exception {
+ Predicate<ChangeData> in = parse("-foo:a (file:b OR file:c)");
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(out.getClass()).isSameAs(AndSource.class);
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(in.getChild(1)),
+ in.getChild(0))
+ .inOrder();
+ }
+
+ @Test
+ public void testMultipleIndexPredicates() throws Exception {
+ Predicate<ChangeData> in =
+ parse("file:a OR foo:b OR file:c OR foo:d");
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(out.getClass()).isSameAs(OrSource.class);
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(or(in.getChild(0), in.getChild(2))),
+ in.getChild(1),
+ in.getChild(3))
+ .inOrder();
+ }
+
+ @Test
+ public void testIndexAndNonIndexPredicates() throws Exception {
+ Predicate<ChangeData> in = parse("status:new bar:p file:a");
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(AndSource.class).isSameAs(out.getClass());
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(and(in.getChild(0), in.getChild(2))),
+ in.getChild(1))
+ .inOrder();
+ }
+
+ @Test
+ public void testDuplicateCompoundNonIndexOnlyPredicates() throws Exception {
+ Predicate<ChangeData> in =
+ parse("(status:new OR status:draft) bar:p file:a");
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(out.getClass()).isEqualTo(AndSource.class);
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(and(in.getChild(0), in.getChild(2))),
+ in.getChild(1))
+ .inOrder();
+ }
+
+ @Test
+ public void testDuplicateCompoundIndexOnlyPredicates() throws Exception {
+ Predicate<ChangeData> in =
+ parse("(status:new OR file:a) bar:p file:b");
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(out.getClass()).isEqualTo(AndSource.class);
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(and(in.getChild(0), in.getChild(2))),
+ in.getChild(1))
+ .inOrder();
+ }
+
+ @Test
+ public void testOptionsArgumentOverridesAllLimitPredicates()
+ throws Exception {
+ Predicate<ChangeData> in = parse("limit:1 file:a limit:3");
+ Predicate<ChangeData> out = rewrite(in, options(0, 5));
+ assertThat(out.getClass()).isEqualTo(AndSource.class);
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(in.getChild(1), 5),
+ parse("limit:5"),
+ parse("limit:5"))
+ .inOrder();
+ }
+
+ @Test
+ public void testStartIncreasesLimitInQueryButNotPredicate() throws Exception {
+ int n = 3;
+ Predicate<ChangeData> f = parse("file:a");
+ Predicate<ChangeData> l = parse("limit:" + n);
+ Predicate<ChangeData> in = andSource(f, l);
+ assertThat(rewrite.rewrite(in, options(0, n)))
+ .isEqualTo(andSource(query(f, 3), l));
+ assertThat(rewrite.rewrite(in, options(1, n)))
+ .isEqualTo(andSource(query(f, 4), l));
+ assertThat(rewrite.rewrite(in, options(2, n)))
+ .isEqualTo(andSource(query(f, 5), l));
+ }
+
+ @Test
+ public void testGetPossibleStatus() throws Exception {
+ assertThat(status("file:a")).isEqualTo(EnumSet.allOf(Change.Status.class));
+ assertThat(status("is:new")).containsExactly(NEW);
+ assertThat(status("-is:new"))
+ .containsExactly(DRAFT, MERGED, ABANDONED);
+ assertThat(status("is:new OR is:merged")).containsExactly(NEW, MERGED);
+
+ assertThat(status("is:new is:merged")).isEmpty();
+ assertThat(status("(is:new is:draft) (is:merged)")).isEmpty();
+ assertThat(status("(is:new is:draft) (is:merged)")).isEmpty();
+
+ assertThat(status("(is:new is:draft) OR (is:merged)"))
+ .containsExactly(MERGED);
+ }
+
+ @Test
+ public void testUnsupportedIndexOperator() throws Exception {
+ Predicate<ChangeData> in = parse("status:merged file:a");
+ assertThat(rewrite(in)).isEqualTo(query(in));
+
+ indexes.setSearchIndex(new FakeIndex(FakeIndex.V1));
+ Predicate<ChangeData> out = rewrite(in);
+ assertThat(out).isInstanceOf(AndPredicate.class);
+ assertThat(out.getChildren())
+ .containsExactly(
+ query(in.getChild(0)),
+ in.getChild(1))
+ .inOrder();
+ }
+
+ @Test
+ public void testTooManyTerms() throws Exception {
+ String q = "file:a OR file:b OR file:c";
+ Predicate<ChangeData> in = parse(q);
+ assertEquals(query(in), rewrite(in));
+
+ exception.expect(QueryParseException.class);
+ exception.expectMessage("too many terms in query");
+ rewrite(parse(q + " OR file:d"));
+ }
+
+ @Test
+ public void testConvertOptions() throws Exception {
+ assertEquals(options(0, 3), convertOptions(options(0, 3)));
+ assertEquals(options(0, 4), convertOptions(options(1, 3)));
+ assertEquals(options(0, 5), convertOptions(options(2, 3)));
+ }
+
+ @Test
+ public void testAddingStartToLimitDoesNotExceedBackendLimit() throws Exception {
+ int max = CONFIG.maxLimit();
+ assertEquals(options(0, max), convertOptions(options(0, max)));
+ assertEquals(options(0, max), convertOptions(options(1, max)));
+ assertEquals(options(0, max), convertOptions(options(1, max - 1)));
+ assertEquals(options(0, max), convertOptions(options(2, max - 1)));
+ }
+
+ private Predicate<ChangeData> parse(String query) throws QueryParseException {
+ return queryBuilder.parse(query);
+ }
+
+ @SafeVarargs
+ private static AndSource andSource(Predicate<ChangeData>... preds) {
+ return new AndSource(Arrays.asList(preds));
+ }
+
+ private Predicate<ChangeData> rewrite(Predicate<ChangeData> in)
+ throws QueryParseException {
+ return rewrite.rewrite(in, options(0, DEFAULT_MAX_QUERY_LIMIT));
+ }
+
+ private Predicate<ChangeData> rewrite(Predicate<ChangeData> in,
+ QueryOptions opts) throws QueryParseException {
+ return rewrite.rewrite(in, opts);
+ }
+
+ private IndexedChangeQuery query(Predicate<ChangeData> p)
+ throws QueryParseException {
+ return query(p, DEFAULT_MAX_QUERY_LIMIT);
+ }
+
+ private IndexedChangeQuery query(Predicate<ChangeData> p, int limit)
+ throws QueryParseException {
+ return new IndexedChangeQuery(index, p, options(0, limit));
+ }
+
+ private static QueryOptions options(int start, int limit) {
+ return QueryOptions.create(CONFIG, start, limit);
+ }
+
+ private Set<Change.Status> status(String query) throws QueryParseException {
+ return IndexRewriter.getPossibleStatus(parse(query));
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 2f2f234..feca80b 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -17,7 +17,6 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.extensions.client.ListChangesOption.REVIEWED;
-
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -1274,13 +1273,8 @@
// necessary for notedb anyway.
cd.isMergeable();
- // Don't use ExpectedException since that wouldn't distinguish between
- // failures here and on the previous calls.
- try {
- cd.messages();
- } catch (AssertionError e) {
- assertThat(e.getMessage()).isEqualTo(DisabledReviewDb.MESSAGE);
- }
+ exception.expect(DisabledReviewDb.Disabled.class);
+ cd.messages();
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java
index d5444e3..9acae6d 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/DisabledReviewDb.java
@@ -30,7 +30,6 @@
import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
import com.google.gerrit.reviewdb.server.PatchSetAccess;
-import com.google.gerrit.reviewdb.server.PatchSetAncestorAccess;
import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
@@ -42,7 +41,13 @@
/** ReviewDb that is disabled for testing. */
public class DisabledReviewDb implements ReviewDb {
- public static final String MESSAGE = "ReviewDb is disabled for this test";
+ public static class Disabled extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private Disabled() {
+ super("ReviewDb is disabled for this test");
+ }
+ }
@Override
public void close() {
@@ -51,156 +56,151 @@
@Override
public void commit() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public void rollback() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public void updateSchema(StatementExecutor e) {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public void pruneSchema(StatementExecutor e) {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public Access<?, ?>[] allRelations() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public SchemaVersionAccess schemaVersion() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public SystemConfigAccess systemConfig() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountAccess accounts() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountExternalIdAccess accountExternalIds() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountSshKeyAccess accountSshKeys() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountGroupAccess accountGroups() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountGroupNameAccess accountGroupNames() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountGroupMemberAccess accountGroupMembers() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountGroupMemberAuditAccess accountGroupMembersAudit() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountDiffPreferenceAccess accountDiffPreferences() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public StarredChangeAccess starredChanges() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountProjectWatchAccess accountProjectWatches() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountPatchReviewAccess accountPatchReviews() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public ChangeAccess changes() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public PatchSetApprovalAccess patchSetApprovals() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public ChangeMessageAccess changeMessages() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public PatchSetAccess patchSets() {
- throw new AssertionError(MESSAGE);
- }
-
- @Override
- public PatchSetAncestorAccess patchSetAncestors() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public PatchLineCommentAccess patchComments() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public SubmoduleSubscriptionAccess submoduleSubscriptions() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountGroupByIdAccess accountGroupById() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public AccountGroupByIdAudAccess accountGroupByIdAud() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public int nextAccountId() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public int nextAccountGroupId() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public int nextChangeId() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
@Override
public int nextChangeMessageId() {
- throw new AssertionError(MESSAGE);
+ throw new Disabled();
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
index 577eb55..3afb208 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -712,7 +712,8 @@
@Override
public FileSystemView getNormalizedView() {
return this;
- }};
+ }
+ };
}
});
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index d20a879..acbc50e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -75,7 +75,8 @@
@Override
public String apply(AccountGroup.Id id) {
return id.toString();
- }});
+ }
+ });
try {
createAccountFactory.create(username).apply(TopLevelResource.INSTANCE, input);
} catch (RestApiException e) {
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 226f4d4..82eefc2 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 226f4d41673257bc5b6f95deae49a49aaabde750
+Subproject commit 82eefc2048a4dd69ab589213190dd8403295fb7d