Merge "dev-release: update the instructions for sticky announcement"
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index 286d59b..0fa494d 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -17,7 +17,7 @@
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 1a0c0db..cd9bb4d 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -768,8 +768,7 @@
[[category_submit]]
=== Submit
-This category permits users to push the `Submit Patch Set n` button
-on the web UI.
+This category permits users to submit changes.
Submitting a change causes it to be merged into the destination
branch as soon as possible, making it a permanent part of the
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index bfab875..2fe47a3 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -134,7 +134,12 @@
./tools/version.py 2.5
----
-Commit the change and create the release tag on the new commit:
+Also check and update the referenced `archetypeVersion` and the
+`archetypeRepository` in the `Documentation/dev-plugins.txt` file.
+If the referenced `archetypeVersion` will be available in the Maven central,
+delete the line with the `archetypeRepository`.
+
+Commit the changes and create the release tag on the new commit:
----
git tag -a v2.5
@@ -313,13 +318,13 @@
For an `RC`:
+
----
- git push gerrit-review refs/tags/v2.5-rc0:refs/tags/v2.5-rc0
+ git push gerrit-review tag v2.5-rc0
----
+
For a final `stable` release:
+
----
- git push gerrit-review refs/tags/v2.5:refs/tags/v2.5
+ git push gerrit-review tag v2.5
----
diff --git a/Documentation/images/user-review-ui-change-screen-edit-commit-message.png b/Documentation/images/user-review-ui-change-screen-edit-commit-message.png
deleted file mode 100644
index 615e9a7..0000000
--- a/Documentation/images/user-review-ui-change-screen-edit-commit-message.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 9966c8e..b5c1557 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1550,6 +1550,8 @@
Whether to show the change sizes as colored bars in the change table.
|`legacycid_in_change_table` |not set if `false`|
Whether to show change number in the change table.
+|`mute_common_path_prefixes` |not set if `false`|
+Whether to mute common path prefixes in file names in the file table.
|`review_category_strategy` ||
The strategy used to displayed info in the review category column.
Allowed values are `NONE`, `NAME`, `EMAIL`, `USERNAME`, `ABBREV`.
@@ -1591,6 +1593,8 @@
Whether to show the change sizes as colored bars in the change table.
|`legacycid_in_change_table` |optional|
Whether to show change number in the change table.
+|`mute_common_path_prefixes` |optional|
+Whether to mute common path prefixes in file names in the file table.
|`review_category_strategy` |optional|
The strategy used to displayed info in the review category column.
Allowed values are `NONE`, `NAME`, `EMAIL`, `USERNAME`, `ABBREV`.
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 576cec6..bd72353 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1445,6 +1445,10 @@
RnJvbSA3ZGFkY2MxNTNmZGVhMTdhYTg0ZmYzMmE2ZTI0NWRiYjY...
----
+Alternatively, if the only value of the Accept request header is
+`application/json` the content is returned as JSON string and
+`X-FYI-Content-Encoding` is set to `json`.
+
[[get-edit-meta-data]]
=== Retrieve meta data of a file from Change Edit
--
@@ -1512,6 +1516,18 @@
M2JhNjcxZTk0OTBmNzUxNDU5ZGUzCg==
----
+Alternatively, if the only value of the Accept request header is
+`application/json` the commit message is returned as JSON string:
+
+.Response
+----
+ HTTP/1.1 200 OK
+
+)]}'
+"Subject of the commit message\n\nThis is the body of the commit message.\n\nChange-Id: Iaf1ba916bf843c175673d675bf7f52862f452db9\n"
+----
+
+
[[publish-edit]]
=== Publish Change Edit
--
@@ -2861,6 +2877,10 @@
Ly8gQ29weXJpZ2h0IChDKSAyMDEwIFRoZSBBbmRyb2lkIE9wZW4gU291cmNlIFByb2plY...
----
+Alternatively, if the only value of the Accept request header is
+`application/json` the content is returned as JSON string and
+`X-FYI-Content-Encoding` is set to `json`.
+
[[get-diff]]
=== Get Diff
--
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index c6215c9..ef2cdd9 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1121,6 +1121,38 @@
HTTP/1.1 204 No Content
----
+[[delete-branches]]
+=== Delete Branches
+--
+'POST /projects/link:#project-name[\{project-name\}]/branches:delete'
+--
+
+Delete one or more branches.
+
+The branches to be deleted must be provided in the request body as a
+link:#delete-branches-input[DeleteBranchesInput] entity.
+
+.Request
+----
+ POST /projects/MyProject/branches:delete HTTP/1.0
+ Content-Type: application/json;charset=UTF-8
+
+ {
+ "branches": [
+ "stable-1.0",
+ "stable-2.0"
+ ]
+ }
+----
+
+.Response
+----
+ HTTP/1.1 204 No Content
+----
+
+If some branches could not be deleted, the response is "`409 Conflict`" and the
+error message is contained in the response body.
+
[[get-content]]
=== Get Content
--
@@ -2060,6 +2092,18 @@
Tokens such as `${project}` are not resolved.
|===========================
+[[delete-branches-input]]
+=== DeleteBranchesInput
+The `DeleteBranchesInput` entity contains information about branches that should
+be deleted.
+
+[options="header",width="50%",cols="1,6"]
+|==========================
+|Field Name |Description
+|`branches` |A list of branch names that identify the branches that should be
+deleted.
+|==========================
+
[[gc-input]]
=== GCInput
The `GCInput` entity contains information to run the Git garbage
@@ -2172,7 +2216,8 @@
|Field Name ||Description
|`name` |optional|
The name of the project (not encoded). +
-If set, must match the project name in the URL.
+If set, must match the project name in the URL. +
+If name ends with `.git` the suffix will be automatically removed.
|`parent` |optional|
The name of the parent project. +
If not set, the `All-Projects` project will be the parent project.
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt
index 9be89a2..4c0c5a8 100644
--- a/Documentation/user-inline-edit.txt
+++ b/Documentation/user-inline-edit.txt
@@ -5,12 +5,16 @@
[[create-change]]
-== Creating a New Empty Change
+== Creating a New Change
A new change can be created directly in the browser, meaning it is not necessary
to clone the whole repository to make trivial changes.
-There are two different ways to create an empty change:
+The new change is created as a draft change, unless
+link:config-gerrit.html#change.allowDrafts[change.allowDrafts] is set to false,
+in which case the change is created as a normal new change.
+
+There are two different ways to create a new change:
By clicking on the 'Create Change' button in the project screen:
@@ -22,8 +26,8 @@
image::images/inline-edit-create-change-project-screen-dialog.png[width=800, link="images/inline-edit-create-change-project-screen-dialog.png"]
-By clicking the 'Follow-Up' button on the change screen, to create an empty
-change based on the selected change.
+By clicking the 'Follow-Up' button on the change screen, to create a new change
+based on the selected change.
[[create-change-from-change-screen]]
diff --git a/Documentation/user-review-ui.txt b/Documentation/user-review-ui.txt
index bb38f6a..ab1070a 100644
--- a/Documentation/user-review-ui.txt
+++ b/Documentation/user-review-ui.txt
@@ -22,16 +22,6 @@
image::images/user-review-ui-change-screen-commit-message.png[width=800, link="images/user-review-ui-change-screen-commit-message.png"]
-[[edit-commit-message]]
-The commit message can be edited directly in the Web UI by clicking on
-the `Edit Message` button in the change header. This opens a drop-down
-editor box in which the commit message can be edited. Saving
-modifications of the commit message automatically creates a change edit
-that must be published to become a new patch set. The commit message may
-only be edited on the current patch set.
-
-image::images/user-review-ui-change-screen-edit-commit-message.png[width=800, link="images/user-review-ui-change-screen-edit-commit-message.png"]
-
[[permalink]]
The numeric change ID is a link to the change and clicking on it
refreshes the change screen. By copying the link location you can get
@@ -609,10 +599,6 @@
each label on which the user is allowed to vote. Voting on non-current
patch sets is not possible.
-Typing "LGTM" (acronym for 'Looks Good To Me') in the summary comment
-text box automatically selects the highest possible score for the
-'Code-Review' label.
-
The inline draft comments that will be published are displayed in a
separate section so that they can be reviewed before publishing. There
are links to navigate to the inline comments which can be used if a
diff --git a/ReleaseNotes/ReleaseNotes-2.11.txt b/ReleaseNotes/ReleaseNotes-2.11.txt
index b8e51ea..3a7a56e 100644
--- a/ReleaseNotes/ReleaseNotes-2.11.txt
+++ b/ReleaseNotes/ReleaseNotes-2.11.txt
@@ -17,9 +17,9 @@
java -jar gerrit.war reindex --recheck-mergeable -d site_path
----
-*WARNING:* Upgrading to 2.11.x requires the server be first upgraded to 2.1.7 (or
-a later 2.1.x version), and then to 2.11.x. If you are upgrading from 2.2.x.x or
-later, you may ignore this warning and upgrade directly to 2.11.x.
+*WARNING:* Upgrading to 2.11.x requires the server be first upgraded to 2.8 (or
+2.9) and then to 2.11.x. If you are upgrading from 2.8.x or later, you may ignore
+this warning and upgrade directly to 2.11.x.
*WARNING:* The 'Generate HTTP Password' capability has been
link:#remove-generate-http-password-capability[removed].
@@ -34,6 +34,9 @@
account and ask the site administrator to
link:https://code.google.com/p/gerrit/wiki/SqlMergeUserAccounts[merge it].
+*WARNING:* The
+link:https://gerrit-review.googlesource.com/Documentation/2.10/rest-api-changes.html#message[
+Edit Commit Message] REST API endpoint is removed
Release Highlights
------------------
@@ -48,37 +51,6 @@
* The deprecated '/query' URL is removed and will now return `Not Found`.
-Experimental Features
----------------------
-
-The following new features are experimental. They are not fully documented yet,
-and it is not recommended to enable them in live production systems.
-
-* Migration of review information from database to git notes.
-+
-Groundwork has been done to implement migration of review information from the
-database to a git notes based backend.
-+
-Existing review information can be migrated from the review database to
-git notes with the `RebuildNotedb` program.
-+
-This feature can be enabled with the following settings in `gerrit.config`:
-----
-[gerrit]
- notedbpath = notedb
-[notedb "changes"]
- write = true
- read = true
-----
-
-* Hashtags.
-+
-Hashtags can be added to changes. The hashtags are stored in git notes and
-are indexed in the secondary index.
-+
-This feature requires the notedb to be enabled.
-
-
New Features
------------
@@ -100,26 +72,23 @@
* New follow-up changes can be created via a 'Follow-Up' button on the change
screen.
-* File content can be edited in a full screen CodeMirror window with support for
-themes, syntax highlighting, different key maps (Emacs, Vim, Default).
+* File content can be edited in a full screen CodeMirror editor with support for
+themes and syntax highlighting.
* The CodeMirror screen can be configured in the same way as the side-by-side
diff screen.
-* The file table in the change screen supports edit mode with seamless navigation
-to CodeMirror for editing.
+* The file table in the change screen supports seamless navigation to the
+CodeMirror editor.
* Edit mode can be started from the side-by-side diff screen with seamless
-navigation to CodeMirror.
+navigation to the CodeMirror editor.
-* The commit message can be changed in context of change edit. The 'Edit Message'
-button is still supported, but now it creates a change edit that must be published.
+* The commit message must now be changed in the context of a change edit. The
+'Edit Message' button is removed from the change screen.
* Files can be added, deleted, restored and modified directly in browser.
-* User-specific configuration dedicated to edit mode in CodeMirror are stored in
-the `All-Users` repository rather than in the database.
-
Change Screen
^^^^^^^^^^^^^
@@ -171,8 +140,8 @@
Changes
^^^^^^^
-* The https://gerrit-review.googlesource.com/Documentation/2.11/rest-api-changes.html#message[
-Edit Commit Message] endpoint is deprecated in favor of the new
+* The link:https://gerrit-review.googlesource.com/Documentation/2.10/rest-api-changes.html#message[
+Edit Commit Message] endpoint is removed in favor of the new
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11/rest-api-changes.html#put-change-edit-message[
Change commit message in Change Edit] and
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11/rest-api-changes.html#publish-edit[
@@ -500,12 +469,6 @@
If a change/revision is a draft the natural next step is to publish (or delete)
it, hence these buttons should be displayed in a more prominent place.
-** Move 'Submit' button into header.
-+
-If a change is ready to submit the natural next step is to submit it, hence the
-'Submit' button should be displayed in a more prominent place. This is consistent
-with displaying other buttons in the header.
-
** Highlight the 'Publish' button in blue.
+
If a change is a draft the natural next step is to publish it, hence
@@ -640,11 +603,13 @@
* Update GWT to 2.7.
+* Update gwtjsonrpc to 1.7-2-g272ca32.
+
* Update gwtorm to 1.14-14-gf54f1f1.
* Update Jetty to 9.2.6.
-* Update JGit to 3.6.0.201412230720-r.
+* Update JGit to 3.6.2.201501210735-r.40-g23ad3a3.
* Update Lucene to 4.10.2.
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index b6b4d8b..84c2674 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -39,6 +39,7 @@
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.AllProjectsName;
+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.ProjectConfig;
@@ -116,6 +117,9 @@
@Inject
protected Provider<InternalChangeQuery> queryProvider;
+ @Inject
+ protected @GerritServerConfig Config cfg;
+
protected Git git;
protected GerritServer server;
protected TestAccount admin;
@@ -159,12 +163,26 @@
}
}
- protected static Config wholeTopicEnabledConfig() {
+ protected static Config submitWholeTopicEnabledConfig() {
Config cfg = new Config();
cfg.setBoolean("change", null, "submitWholeTopic", true);
return cfg;
}
+ protected static Config allowDraftsDisabledConfig() {
+ Config cfg = new Config();
+ cfg.setBoolean("change", null, "allowDrafts", false);
+ return cfg;
+ }
+
+ protected boolean isAllowDrafts() {
+ return cfg.getBoolean("change", "allowDrafts", true);
+ }
+
+ protected boolean isSubmitWholeTopicEnabled() {
+ return cfg.getBoolean("change", null, "submitWholeTopic", false);
+ }
+
private void beforeTest(Config cfg, boolean memory, boolean enableHttpd) throws Exception {
server = startServer(cfg, memory, enableHttpd);
server.getTestInjector().injectMembers(this);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java
index 584186c..bf6f928 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/RestSession.java
@@ -16,9 +16,11 @@
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
+import com.google.common.net.HttpHeaders;
import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.server.OutputFormat;
+import org.apache.http.Header;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
@@ -41,7 +43,20 @@
@Override
public RestResponse get(String endPoint) throws IOException {
+ return getWithHeader(endPoint, null);
+ }
+
+ public RestResponse getJsonAccept(String endPoint) throws IOException {
+ return getWithHeader(endPoint,
+ new BasicHeader(HttpHeaders.ACCEPT, "application/json"));
+ }
+
+ private RestResponse getWithHeader(String endPoint, Header header)
+ throws IOException {
HttpGet get = new HttpGet(url + "/a" + endPoint);
+ if (header != null) {
+ get.addHeader(header);
+ }
return new RestResponse(getClient().execute(get));
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
index d2e70c3..7de4712 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -44,6 +44,17 @@
.name);
}
+ @Test
+ public void createProjectFooWithGitSuffix() throws Exception {
+ String name = "foo";
+ assertThat(name).isEqualTo(
+ gApi.projects()
+ .name(name + ".git")
+ .create()
+ .get()
+ .name);
+ }
+
@Test(expected = RestApiException.class)
public void createProjectFooBar() throws Exception {
ProjectInput in = new ProjectInput();
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 ff2944f..a177d00 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
@@ -36,6 +36,7 @@
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.MergeableInfo;
@@ -55,6 +56,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -157,6 +159,21 @@
.cherryPick(in);
assertThat((Iterable<?>)orig.get().messages).hasSize(2);
+ String cherryPickedRevision = cherry.get().currentRevision;
+ String expectedMessage = String.format(
+ "Patch Set 1: Cherry Picked\n\n" +
+ "This patchset was cherry picked to branch %s as commit %s",
+ in.destination, cherryPickedRevision);
+
+ Iterator<ChangeMessageInfo> origIt = orig.get().messages.iterator();
+ origIt.next();
+ assertThat(origIt.next().message).isEqualTo(expectedMessage);
+
+ assertThat((Iterable<?>)cherry.get().messages).hasSize(1);
+ Iterator<ChangeMessageInfo> cherryIt = cherry.get().messages.iterator();
+ expectedMessage = "Patch Set 1: Cherry Picked from branch master.";
+ assertThat(cherryIt.next().message).isEqualTo(expectedMessage);
+
assertThat(cherry.get().subject).contains(in.message);
assertThat(cherry.get().topic).isEqualTo("someTopic");
cherry.current().review(ReviewInput.approve());
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 2640645..371a472 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
@@ -53,10 +53,10 @@
import com.google.gerrit.server.edit.UnchangedCommitMessageException;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.InvalidChangeOperationException;
+import com.google.gson.stream.JsonReader;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
-import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.ObjectId;
@@ -82,7 +82,7 @@
private final static String FILE_NAME3 = "foo3";
private final static byte[] CONTENT_OLD = "bar".getBytes(UTF_8);
private final static byte[] CONTENT_NEW = "baz".getBytes(UTF_8);
- private final static byte[] CONTENT_NEW2 = "qux".getBytes(UTF_8);
+ private final static byte[] CONTENT_NEW2 = "quxÄÜÖßµ".getBytes(UTF_8);
@Inject
private SchemaFactory<ReviewDb> reviewDbProvider;
@@ -301,16 +301,14 @@
assertThat(adminSession.get(urlEditMessage()).getStatusCode())
.isEqualTo(SC_NOT_FOUND);
EditMessage.Input in = new EditMessage.Input();
- in.message = String.format("New commit message\n\nChange-Id: %s",
+ in.message = String.format("New commit message\n\n" +
+ CONTENT_NEW2 + "\n\nChange-Id: %s",
change.getKey());
assertThat(adminSession.put(urlEditMessage(), in).getStatusCode())
.isEqualTo(SC_NO_CONTENT);
- RestResponse r = adminSession.get(urlEditMessage());
- assertThat(adminSession.get(urlEditMessage()).getStatusCode())
- .isEqualTo(SC_OK);
- String content = r.getEntityContent();
- assertThat(StringUtils.newStringUtf8(Base64.decodeBase64(content)))
- .isEqualTo(in.message);
+ RestResponse r = adminSession.getJsonAccept(urlEditMessage());
+ assertThat(r.getStatusCode()).isEqualTo(SC_OK);
+ assertThat(readContentFromJson(r)).isEqualTo(in.message);
Optional<ChangeEdit> edit = editUtil.byChange(change);
assertThat(edit.get().getEditCommit().getFullMessage())
.isEqualTo(in.message);
@@ -538,11 +536,10 @@
assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW2)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
- RestResponse r = adminSession.get(urlEditFile());
+ RestResponse r = adminSession.getJsonAccept(urlEditFile());
assertThat(r.getStatusCode()).isEqualTo(SC_OK);
- String content = r.getEntityContent();
- assertThat(StringUtils.newStringUtf8(Base64.decodeBase64(content)))
- .isEqualTo(StringUtils.newStringUtf8(CONTENT_NEW2));
+ assertThat(readContentFromJson(r)).isEqualTo(
+ StringUtils.newStringUtf8(CONTENT_NEW2));
}
@Test
@@ -718,4 +715,10 @@
assertThat(r.getStatusCode()).isEqualTo(SC_OK);
return newGson().fromJson(r.getReader(), EditInfo.class);
}
+
+ private String readContentFromJson(RestResponse r) throws IOException {
+ JsonReader jsonReader = new JsonReader(r.getReader());
+ jsonReader.setLenient(true);
+ return newGson().fromJson(jsonReader, String.class);
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 94f965a..41918e3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -17,6 +17,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.cloneProject;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
@@ -82,7 +83,7 @@
public abstract class AbstractSubmit extends AbstractDaemonTest {
@ConfigSuite.Config
public static Config submitWholeTopicEnabled() {
- return wholeTopicEnabledConfig();
+ return submitWholeTopicEnabledConfig();
}
private Map<String, String> mergeResults;
@@ -134,6 +135,21 @@
assertThat(getRemoteHead().getId()).isEqualTo(change.getCommitId());
}
+ @Test
+ public void submitWholeTopic() throws Exception {
+ assume().that(isSubmitWholeTopicEnabled()).isTrue();
+ Git git = createProject();
+ PushOneCommit.Result change1 =
+ createChange(git, "Change 1", "a.txt", "content", "test-topic");
+ PushOneCommit.Result change2 =
+ createChange(git, "Change 2", "b.txt", "content", "test-topic");
+ approve(change1.getChangeId());
+ approve(change2.getChangeId());
+ submit(change2.getChangeId());
+ change1.assertChange(Change.Status.MERGED, "test-topic", admin);
+ change2.assertChange(Change.Status.MERGED, "test-topic", admin);
+ }
+
protected Git createProject() throws JSchException, IOException,
GitAPIException {
return createProject(true);
@@ -183,6 +199,14 @@
return push.to(git, "refs/for/master");
}
+ protected PushOneCommit.Result createChange(Git git, String subject,
+ String fileName, String content, String topic)
+ throws GitAPIException, IOException {
+ PushOneCommit push =
+ pushFactory.create(db, admin.getIdent(), subject, fileName, content);
+ return push.to(git, "refs/for/master/" + topic);
+ }
+
protected void submit(String changeId) throws IOException {
submit(changeId, HttpStatus.SC_OK);
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 18b33bb..311161a 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -15,17 +15,24 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.testutil.ConfigSuite;
import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lib.Config;
import org.junit.Test;
public class CreateChangeIT extends AbstractDaemonTest {
+ @ConfigSuite.Config
+ public static Config allowDraftsDisabled() {
+ return allowDraftsDisabledConfig();
+ }
@Test
public void createEmptyChange_MissingBranch() throws Exception {
@@ -61,9 +68,19 @@
@Test
public void createDraftChange() throws Exception {
+ assume().that(isAllowDrafts()).isTrue();
assertChange(newChangeInfo(ChangeStatus.DRAFT));
}
+ @Test
+ public void createDraftChangeNotAllowed() throws Exception {
+ assume().that(isAllowDrafts()).isFalse();
+ ChangeInfo ci = newChangeInfo(ChangeStatus.DRAFT);
+ RestResponse r = adminSession.post("/changes/", ci);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_METHOD_NOT_ALLOWED);
+ assertThat(r.getEntityContent()).contains("draft workflow is disabled");
+ }
+
private ChangeInfo newChangeInfo(ChangeStatus status) {
ChangeInfo in = new ChangeInfo();
in.project = project.get();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
similarity index 61%
rename from gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftChangeIT.java
rename to gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
index 564bdf6..a3809a9 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DraftChangeIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -24,66 +25,89 @@
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.testutil.ConfigSuite;
import com.google.gwtorm.server.OrmException;
import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lib.Config;
import org.junit.Test;
import java.io.IOException;
-public class DeleteDraftChangeIT extends AbstractDaemonTest {
+public class DraftChangeIT extends AbstractDaemonTest {
+ @ConfigSuite.Config
+ public static Config allowDraftsDisabled() {
+ return allowDraftsDisabledConfig();
+ }
@Test
public void deleteChange() throws Exception {
- String changeId = createChange().getChangeId();
+ PushOneCommit.Result result = createChange();
+ result.assertOkStatus();
+ String changeId = result.getChangeId();
String triplet = "p~master~" + changeId;
ChangeInfo c = get(triplet);
assertThat(c.id).isEqualTo(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.NEW);
- RestResponse r = deleteChange(changeId, adminSession);
- assertThat(r.getEntityContent()).isEqualTo("Change is not a draft");
- assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT);
+ RestResponse response = deleteChange(changeId, adminSession);
+ assertThat(response.getEntityContent()).isEqualTo("Change is not a draft");
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_CONFLICT);
}
@Test
public void deleteDraftChange() throws Exception {
- String changeId = createDraftChange();
+ assume().that(isAllowDrafts()).isTrue();
+ PushOneCommit.Result result = createDraftChange();
+ result.assertOkStatus();
+ String changeId = result.getChangeId();
String triplet = "p~master~" + changeId;
ChangeInfo c = get(triplet);
assertThat(c.id).isEqualTo(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.DRAFT);
- RestResponse r = deleteChange(changeId, adminSession);
- assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
+ RestResponse response = deleteChange(changeId, adminSession);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
}
@Test
public void publishDraftChange() throws Exception {
- String changeId = createDraftChange();
+ assume().that(isAllowDrafts()).isTrue();
+ PushOneCommit.Result result = createDraftChange();
+ result.assertOkStatus();
+ String changeId = result.getChangeId();
String triplet = "p~master~" + changeId;
ChangeInfo c = get(triplet);
assertThat(c.id).isEqualTo(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.DRAFT);
- RestResponse r = publishChange(changeId);
- assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
+ RestResponse response = publishChange(changeId);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
c = get(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.NEW);
}
@Test
public void publishDraftPatchSet() throws Exception {
- String changeId = createDraftChange();
+ assume().that(isAllowDrafts()).isTrue();
+ PushOneCommit.Result result = createDraftChange();
+ result.assertOkStatus();
+ String changeId = result.getChangeId();
String triplet = "p~master~" + changeId;
ChangeInfo c = get(triplet);
assertThat(c.id).isEqualTo(triplet);
assertThat(c.status).isEqualTo(ChangeStatus.DRAFT);
- RestResponse r = publishPatchSet(changeId);
- assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
+ RestResponse response = publishPatchSet(changeId);
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
assertThat(get(triplet).status).isEqualTo(ChangeStatus.NEW);
}
- private String createDraftChange() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent());
- return push.to(git, "refs/drafts/master").getChangeId();
+ @Test
+ public void createDraftChangeWhenDraftsNotAllowed() throws Exception {
+ assume().that(isAllowDrafts()).isFalse();
+ PushOneCommit.Result r = createDraftChange();
+ r.assertErrorStatus("draft workflow is disabled");
+ }
+
+ private PushOneCommit.Result createDraftChange() throws Exception {
+ return pushTo("refs/drafts/master");
}
private static RestResponse deleteChange(String changeId,
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index 9492c437..efd16a4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -17,8 +17,12 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.checkout;
+import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.extensions.client.ChangeStatus;
+import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.common.ChangeInfo;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -184,4 +188,57 @@
assertThat(log.get(3).getId()).isEqualTo(initialHead.getId());
}
+
+ @Test
+ public void submitDependentNonConflictingChangesOutOfOrder() throws Exception {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b");
+ PushOneCommit.Result change3 = createChange(git, "Change 3", "c", "c");
+ assertThat(change3.getCommit().getParent(0)).isEqualTo(change2.getCommit());
+
+ // Submit succeeds; change3 is successfully cherry-picked onto head.
+ submit(change3.getChangeId());
+ // Submit succeeds; change2 is successfully cherry-picked onto head
+ // (which was change3's cherry-pick).
+ submit(change2.getChangeId());
+
+ // change2 is the new tip.
+ List<RevCommit> log = getRemoteLog();
+ assertThat(log.get(0).getShortMessage()).isEqualTo(
+ change2.getCommit().getShortMessage());
+ assertThat(log.get(0).getParent(0)).isEqualTo(log.get(1));
+
+ assertThat(log.get(1).getShortMessage()).isEqualTo(
+ change3.getCommit().getShortMessage());
+ assertThat(log.get(1).getParent(0)).isEqualTo(log.get(2));
+
+ assertThat(log.get(2).getId()).isEqualTo(initialHead.getId());
+ }
+
+ @Test
+ public void submitDependentConflictingChangesOutOfOrder() throws Exception {
+ Git git = createProject();
+ RevCommit initialHead = getRemoteHead();
+
+ checkout(git, initialHead.getId().getName());
+ PushOneCommit.Result change2 = createChange(git, "Change 2", "b", "b1");
+ PushOneCommit.Result change3 = createChange(git, "Change 3", "b", "b2");
+ assertThat(change3.getCommit().getParent(0)).isEqualTo(change2.getCommit());
+
+ // Submit fails; change3 contains the delta "b1" -> "b2", which cannot be
+ // applied against tip.
+ submitWithConflict(change3.getChangeId());
+
+ ChangeInfo info3 = get(change3.getChangeId(), ListChangesOption.MESSAGES);
+ assertThat(info3.status).isEqualTo(ChangeStatus.NEW);
+ assertThat(Iterables.getLast(info3.messages).message.toLowerCase())
+ .contains("path conflict");
+
+ // Tip has not changed.
+ List<RevCommit> log = getRemoteLog();
+ assertThat(log.get(0)).isEqualTo(initialHead.getId());
+ }
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index a87e972..998c5ea 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -58,6 +58,17 @@
}
@Test
+ public void testCreateProjectApiWithGitSuffix() throws Exception {
+ final String newProjectName = "newProject";
+ ProjectInfo p = gApi.projects().name(newProjectName + ".git").create().get();
+ assertThat(p.name).isEqualTo(newProjectName);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ }
+
+ @Test
public void testCreateProject() throws Exception {
final String newProjectName = "newProject";
RestResponse r = adminSession.put("/projects/" + newProjectName);
@@ -71,6 +82,19 @@
}
@Test
+ public void testCreateProjectWithGitSuffix() throws Exception {
+ final String newProjectName = "newProject";
+ RestResponse r = adminSession.put("/projects/" + newProjectName + ".git");
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_CREATED);
+ ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class);
+ assertThat(p.name).isEqualTo(newProjectName);
+ ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+ assertThat(projectState).isNotNull();
+ assertProjectInfo(projectState.getProject(), p);
+ assertHead(newProjectName, "refs/heads/master");
+ }
+
+ @Test
public void testCreateProjectWithNameMismatch_BadRequest() throws Exception {
ProjectInput in = new ProjectInput();
in.name = "otherName";
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
new file mode 100644
index 0000000..761e282
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/GetProjectIT.java
@@ -0,0 +1,51 @@
+// 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.project;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.extensions.common.ProjectInfo;
+
+import org.apache.http.HttpStatus;
+import org.junit.Test;
+
+public class GetProjectIT extends AbstractDaemonTest {
+
+ @Test
+ public void getProject() throws Exception {
+ String name = project.get();
+ RestResponse r = adminSession.get("/projects/" + name);
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+ ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class);
+ assertThat(p.name).isEqualTo(name);
+ }
+
+ @Test
+ public void getProjectWithGitSuffix() throws Exception {
+ String name = project.get();
+ RestResponse r = adminSession.get("/projects/" + name + ".git");
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+ ProjectInfo p = newGson().fromJson(r.getReader(), ProjectInfo.class);
+ assertThat(p.name).isEqualTo(name);
+ }
+
+ @Test
+ public void getProjectNotExisting() throws Exception {
+ RestResponse r = adminSession.get("/projects/does-not-exist");
+ assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND);
+ }
+}
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 dfce940..fbd3c29 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
@@ -22,6 +22,7 @@
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.client.Comment;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.server.notedb.NotesMigration;
@@ -44,83 +45,95 @@
return NotesMigration.allEnabledConfig();
}
+ private final Integer lines[] = {0, 1};
+
@Test
public void createDraft() throws Exception {
- PushOneCommit.Result r = createChange();
- String changeId = r.getChangeId();
- String revId = r.getCommit().getName();
- ReviewInput.CommentInput comment = newCommentInfo(
- "file1", Side.REVISION, 1, "comment 1");
- addDraft(changeId, revId, comment);
- Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
- assertThat(result).hasSize(1);
- CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
- assertCommentInfo(comment, actual);
+ for (Integer line : lines) {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ String revId = r.getCommit().getName();
+ ReviewInput.CommentInput comment = newCommentInfo(
+ "file1", Side.REVISION, line, "comment 1");
+ addDraft(changeId, revId, comment);
+ Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
+ assertThat(result).hasSize(1);
+ CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
+ assertCommentInfo(comment, actual);
+ }
}
@Test
public void postComment() throws Exception {
- String file = "file";
- String contents = "contents";
- PushOneCommit push = pushFactory.create(db, admin.getIdent(),
- "first subject", file, contents);
- PushOneCommit.Result r = push.to(git, "refs/for/master");
- String changeId = r.getChangeId();
- String revId = r.getCommit().getName();
- ReviewInput input = new ReviewInput();
- ReviewInput.CommentInput comment = newCommentInfo(
- file, Side.REVISION, 1, "comment 1");
- input.comments = new HashMap<>();
- input.comments.put(comment.path, Lists.newArrayList(comment));
- revision(r).review(input);
- Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
- assertThat(result).isNotEmpty();
- CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
- assertCommentInfo(comment, actual);
+ for (Integer line : lines) {
+ String file = "file";
+ String contents = "contents " + line;
+ PushOneCommit push = pushFactory.create(db, admin.getIdent(),
+ "first subject", file, contents);
+ PushOneCommit.Result r = push.to(git, "refs/for/master");
+ String changeId = r.getChangeId();
+ String revId = r.getCommit().getName();
+ ReviewInput input = new ReviewInput();
+ ReviewInput.CommentInput comment = newCommentInfo(
+ file, Side.REVISION, line, "comment 1");
+ input.comments = new HashMap<>();
+ input.comments.put(comment.path, Lists.newArrayList(comment));
+ revision(r).review(input);
+ Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
+ assertThat(result).isNotEmpty();
+ CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
+ assertCommentInfo(comment, actual);
+ }
}
@Test
public void putDraft() throws Exception {
- PushOneCommit.Result r = createChange();
- String changeId = r.getChangeId();
- String revId = r.getCommit().getName();
- ReviewInput.CommentInput comment = newCommentInfo(
- "file1", Side.REVISION, 1, "comment 1");
- addDraft(changeId, revId, comment);
- Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
- CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
- assertCommentInfo(comment, actual);
- String uuid = actual.id;
- comment.message = "updated comment 1";
- updateDraft(changeId, revId, comment, uuid);
- result = getDraftComments(changeId, revId);
- actual = Iterables.getOnlyElement(result.get(comment.path));
- assertCommentInfo(comment, actual);
+ for (Integer line : lines) {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ String revId = r.getCommit().getName();
+ ReviewInput.CommentInput comment = newCommentInfo(
+ "file1", Side.REVISION, line, "comment 1");
+ addDraft(changeId, revId, comment);
+ Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
+ CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
+ assertCommentInfo(comment, actual);
+ String uuid = actual.id;
+ comment.message = "updated comment 1";
+ updateDraft(changeId, revId, comment, uuid);
+ result = getDraftComments(changeId, revId);
+ actual = Iterables.getOnlyElement(result.get(comment.path));
+ assertCommentInfo(comment, actual);
+ }
}
@Test
public void getDraft() throws Exception {
- PushOneCommit.Result r = createChange();
- String changeId = r.getChangeId();
- String revId = r.getCommit().getName();
- ReviewInput.CommentInput comment = newCommentInfo(
- "file1", Side.REVISION, 1, "comment 1");
- CommentInfo returned = addDraft(changeId, revId, comment);
- CommentInfo actual = getDraftComment(changeId, revId, returned.id);
- assertCommentInfo(comment, actual);
+ for (Integer line : lines) {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ String revId = r.getCommit().getName();
+ ReviewInput.CommentInput comment = newCommentInfo(
+ "file1", Side.REVISION, line, "comment 1");
+ CommentInfo returned = addDraft(changeId, revId, comment);
+ CommentInfo actual = getDraftComment(changeId, revId, returned.id);
+ assertCommentInfo(comment, actual);
+ }
}
@Test
public void deleteDraft() throws Exception {
- PushOneCommit.Result r = createChange();
- String changeId = r.getChangeId();
- String revId = r.getCommit().getName();
- ReviewInput.CommentInput comment = newCommentInfo(
- "file1", Side.REVISION, 1, "comment 1");
- CommentInfo returned = addDraft(changeId, revId, comment);
- deleteDraft(changeId, revId, returned.id);
- Map<String, List<CommentInfo>> drafts = getDraftComments(changeId, revId);
- assertThat(drafts).isEmpty();
+ for (Integer line : lines) {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ String revId = r.getCommit().getName();
+ ReviewInput.CommentInput comment = newCommentInfo(
+ "file1", Side.REVISION, line, "comment 1");
+ CommentInfo returned = addDraft(changeId, revId, comment);
+ deleteDraft(changeId, revId, returned.id);
+ Map<String, List<CommentInfo>> drafts = getDraftComments(changeId, revId);
+ assertThat(drafts).isEmpty();
+ }
}
private CommentInfo addDraft(String changeId, String revId,
@@ -176,18 +189,40 @@
assertThat(actual.line).isEqualTo(expected.line);
assertThat(actual.message).isEqualTo(expected.message);
assertThat(actual.inReplyTo).isEqualTo(expected.inReplyTo);
+ assertCommentRange(expected.range, actual.range);
if (actual.side == null) {
assertThat(Side.REVISION).isEqualTo(expected.side);
}
}
+ private static void assertCommentRange(Comment.Range expected,
+ Comment.Range actual) {
+ if (expected == null) {
+ assertThat(actual).isNull();
+ } else {
+ assertThat(actual).isNotNull();
+ assertThat(actual.startLine).isEqualTo(expected.startLine);
+ assertThat(actual.startCharacter).isEqualTo(expected.startCharacter);
+ assertThat(actual.endLine).isEqualTo(expected.endLine);
+ assertThat(actual.endCharacter).isEqualTo(expected.endCharacter);
+ }
+ }
+
private ReviewInput.CommentInput newCommentInfo(String path,
Side side, int line, String message) {
ReviewInput.CommentInput input = new ReviewInput.CommentInput();
input.path = path;
input.side = side;
- input.line = line;
+ input.line = line != 0 ? line : null;
input.message = message;
+ if (line != 0) {
+ Comment.Range range = new Comment.Range();
+ range.startLine = 1;
+ range.startCharacter = 1;
+ range.endLine = 1;
+ range.endCharacter = 5;
+ input.range = range;
+ }
return input;
}
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
index b93ad3e..d911390 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -56,6 +56,7 @@
protected int largeChangeSize;
protected String replyLabel;
protected String replyTitle;
+ protected boolean allowDraftChanges;
public String getLoginUrl() {
return loginUrl;
@@ -307,4 +308,12 @@
public void setReplyLabel(String r) {
replyLabel = r;
}
+
+ public boolean isAllowDraftChanges() {
+ return allowDraftChanges;
+ }
+
+ public void setAllowDraftChanges(boolean b) {
+ allowDraftChanges = b;
+ }
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java
index e264b31..18f356b 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/BinaryResult.java
@@ -14,11 +14,17 @@
package com.google.gerrit.extensions.restapi;
+import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
/**
* Wrapper around a non-JSON result from a {@link RestView}.
@@ -34,13 +40,7 @@
/** Produce a UTF-8 encoded result from a string. */
public static BinaryResult create(String data) {
- try {
- return create(data.getBytes("UTF-8"))
- .setContentType("text/plain")
- .setCharacterEncoding("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("JVM does not support UTF-8", e);
- }
+ return new StringResult(data);
}
/** Produce an {@code application/octet-stream} result from a byte array. */
@@ -144,6 +144,28 @@
*/
public abstract void writeTo(OutputStream os) throws IOException;
+ /**
+ * Return a copy of the result as a String.
+ * <p>
+ * The default version of this method copies the result into a temporary byte
+ * array and then tries to decode it using the configured encoding.
+ *
+ * @return string version of the result.
+ * @throws IOException if the data cannot be produced or could not be
+ * decoded to a String.
+ */
+ public String asString() throws IOException {
+ long len = getContentLength();
+ ByteArrayOutputStream buf;
+ if (0 < len) {
+ buf = new ByteArrayOutputStream((int) len);
+ } else {
+ buf = new ByteArrayOutputStream();
+ }
+ writeTo(buf);
+ return decode(buf.toByteArray(), getCharacterEncoding());
+ }
+
/** Close the result and release any resources it holds. */
@Override
public void close() throws IOException {
@@ -161,6 +183,25 @@
getContentType());
}
+ private static String decode(byte[] data, String enc) {
+ try {
+ Charset cs = enc != null
+ ? Charset.forName(enc)
+ : StandardCharsets.UTF_8;
+ return cs.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .decode(ByteBuffer.wrap(data))
+ .toString();
+ } catch (UnsupportedCharsetException | CharacterCodingException e) {
+ // Fallback to ISO-8850-1 style encoding.
+ StringBuilder r = new StringBuilder(data.length);
+ for (byte b : data)
+ r.append((char) (b & 0xff));
+ return r.toString();
+ }
+ }
+
private static class Array extends BinaryResult {
private final byte[] data;
@@ -173,6 +214,27 @@
public void writeTo(OutputStream os) throws IOException {
os.write(data);
}
+
+ @Override
+ public String asString() {
+ return decode(data, getCharacterEncoding());
+ }
+ }
+
+ private static class StringResult extends Array {
+ private final String str;
+
+ StringResult(String str) {
+ super(str.getBytes(StandardCharsets.UTF_8));
+ setContentType("text/plain");
+ setCharacterEncoding("UTF-8");
+ this.str = str;
+ }
+
+ @Override
+ public String asString() {
+ return str;
+ }
}
private static class Stream extends BinaryResult {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index abcfa0c..4c3cc29 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
@@ -42,6 +42,7 @@
String showRelativeDateInChangeTable();
String showSizeBarInChangeTable();
String showLegacycidInChangeTable();
+ String muteCommonPathPrefixes();
String myMenu();
String myMenuInfo();
String myMenuName();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index 8f151a9..5596ace 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -23,6 +23,7 @@
showRelativeDateInChangeTable = Show Relative Dates In Changes Table
showSizeBarInChangeTable = Show Change Sizes As Colored Bars In Changes Table
showLegacycidInChangeTable = Show Change Number In Changes Table
+muteCommonPathPrefixes = Mute Common Path Prefixes In File List
myMenu = My Menu
myMenuInfo = \
Menu items for the 'My' top level menu. \
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
index af3129b..5cee767 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
@@ -50,6 +50,7 @@
private CheckBox relativeDateInChangeTable;
private CheckBox sizeBarInChangeTable;
private CheckBox legacycidInChangeTable;
+ private CheckBox muteCommonPathPrefixes;
private ListBox maximumPageSize;
private ListBox dateFormat;
private ListBox timeFormat;
@@ -132,6 +133,7 @@
relativeDateInChangeTable = new CheckBox(Util.C.showRelativeDateInChangeTable());
sizeBarInChangeTable = new CheckBox(Util.C.showSizeBarInChangeTable());
legacycidInChangeTable = new CheckBox(Util.C.showLegacycidInChangeTable());
+ muteCommonPathPrefixes = new CheckBox(Util.C.muteCommonPathPrefixes());
final Grid formGrid = new Grid(11, 2);
@@ -173,7 +175,7 @@
row++;
formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, legacycidInChangeTable);
+ formGrid.setWidget(row, fieldIdx, muteCommonPathPrefixes);
row++;
formGrid.setText(row, labelIdx, Util.C.diffViewLabel());
@@ -205,6 +207,7 @@
e.listenTo(relativeDateInChangeTable);
e.listenTo(sizeBarInChangeTable);
e.listenTo(legacycidInChangeTable);
+ e.listenTo(muteCommonPathPrefixes);
e.listenTo(diffView);
}
@@ -230,6 +233,7 @@
relativeDateInChangeTable.setEnabled(on);
sizeBarInChangeTable.setEnabled(on);
legacycidInChangeTable.setEnabled(on);
+ muteCommonPathPrefixes.setEnabled(on);
reviewCategoryStrategy.setEnabled(on);
diffView.setEnabled(on);
}
@@ -246,6 +250,7 @@
relativeDateInChangeTable.setValue(p.relativeDateInChangeTable());
sizeBarInChangeTable.setValue(p.sizeBarInChangeTable());
legacycidInChangeTable.setValue(p.legacycidInChangeTable());
+ muteCommonPathPrefixes.setValue(p.muteCommonPathPrefixes());
setListBox(reviewCategoryStrategy,
AccountGeneralPreferences.ReviewCategoryStrategy.NONE,
p.reviewCategoryStrategy());
@@ -329,6 +334,7 @@
p.setRelativeDateInChangeTable(relativeDateInChangeTable.getValue());
p.setSizeBarInChangeTable(sizeBarInChangeTable.getValue());
p.setLegacycidInChangeTable(legacycidInChangeTable.getValue());
+ p.setMuteCommonPathPrefixes(muteCommonPathPrefixes.getValue());
p.setReviewCategoryStrategy(getListBox(reviewCategoryStrategy,
ReviewCategoryStrategy.NONE,
ReviewCategoryStrategy.values()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Preferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Preferences.java
index aec679c..e9bd5f1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Preferences.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Preferences.java
@@ -44,6 +44,7 @@
p.relativeDateInChangeTable(in.isRelativeDateInChangeTable());
p.sizeBarInChangeTable(in.isSizeBarInChangeTable());
p.legacycidInChangeTable(in.isLegacycidInChangeTable());
+ p.muteCommonPathPrefixes(in.isMuteCommonPathPrefixes());
p.reviewCategoryStrategy(in.getReviewCategoryStrategy());
p.diffView(in.getDiffView());
p.setMyMenus(myMenus);
@@ -102,6 +103,9 @@
public final native boolean legacycidInChangeTable()
/*-{ return this.legacycid_in_change_table || false }-*/;
+ public final native boolean muteCommonPathPrefixes()
+ /*-{ return this.mute_common_path_prefixes || false }-*/;
+
public final ReviewCategoryStrategy reviewCategoryStrategy() {
String s = reviewCategeoryStrategyRaw();
return s != null ? ReviewCategoryStrategy.valueOf(s) : ReviewCategoryStrategy.NONE;
@@ -164,6 +168,9 @@
public final native void legacycidInChangeTable(boolean s)
/*-{ this.legacycid_in_change_table = s }-*/;
+ public final native void muteCommonPathPrefixes(boolean s)
+ /*-{ this.mute_common_path_prefixes = s }-*/;
+
public final void reviewCategoryStrategy(ReviewCategoryStrategy s) {
reviewCategoryStrategyRaw(s != null ? s.toString() : null);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
index 30e2e3d..1ffd6f0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
@@ -21,10 +21,12 @@
import com.google.gerrit.client.ui.CreateChangeDialog;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Project;
+import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.PopupPanel;
class CreateChangeAction {
- static void call(Button b, final String project) {
+ static void call(final Button b, final String project) {
// TODO Replace CreateChangeDialog with a nicer looking display.
b.setEnabled(false);
new CreateChangeDialog(new Project.NameKey(project)) {
@@ -35,7 +37,7 @@
@Override
public void onSend() {
- ChangeApi.createDraftChange(project, getDestinationBranch(),
+ ChangeApi.createChange(project, getDestinationBranch(),
message.getText(), null,
new GerritCallback<ChangeInfo>() {
@Override
@@ -52,6 +54,13 @@
}
});
}
+
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ super.onClose(event);
+ b.setEnabled(true);
+ }
+
}.center();
}
}
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 aa50f02..5dab3ac 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
@@ -630,7 +630,8 @@
if (Patch.COMMIT_MSG.equals(path)) {
sb.append(Util.C.commitMessage());
- } else {
+ } else if (!hasUser || Gerrit.getUserAccount().getGeneralPreferences()
+ .isMuteCommonPathPrefixes()) {
int commonPrefixLen = commonPrefix(path);
if (commonPrefixLen > 0) {
sb.openSpan().setStyleName(R.css().commonPrefix())
@@ -639,6 +640,8 @@
}
sb.append(path.substring(commonPrefixLen));
lastPath = path;
+ } else {
+ sb.append(path);
}
sb.closeAnchor();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
index 1f5c516..5a7df72 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
@@ -35,7 +35,7 @@
@Override
void send(String message) {
- ChangeApi.createDraftChange(project, branch, message, base,
+ ChangeApi.createChange(project, branch, message, base,
new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
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 5eb936c..fd9f4bc 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
@@ -76,8 +76,6 @@
import java.util.TreeSet;
class ReplyBox extends Composite {
- private static final String CODE_REVIEW = "Code-Review";
-
interface Binder extends UiBinder<HTMLPanel, ReplyBox> {}
private static final Binder uiBinder = GWT.create(Binder.class);
@@ -92,7 +90,6 @@
private final String revision;
private ReviewInput in = ReviewInput.create();
private int labelHelpColumn;
- private Runnable lgtm;
@UiField Styles style;
@UiField TextArea message;
@@ -173,20 +170,6 @@
}}, 0);
}
- @UiHandler("message")
- void onMessageKey(KeyPressEvent event) {
- if (lgtm != null
- && event.getCharCode() == 'M'
- && message.getValue().equals("LGT")) {
- Scheduler.get().scheduleDeferred(new ScheduledCommand() {
- @Override
- public void execute() {
- lgtm.run();
- }
- });
- }
- }
-
@UiHandler("post")
void onPost(@SuppressWarnings("unused") ClickEvent e) {
postReview();
@@ -358,15 +341,6 @@
labelsTable.setWidget(row, 1 + i, b);
}
}
-
- if (CODE_REVIEW.equalsIgnoreCase(id) && !group.buttons.isEmpty()) {
- lgtm = new Runnable() {
- @Override
- public void run() {
- group.selectMax();
- }
- };
- }
}
private void renderCheckBox(int row, LabelAndValues lv) {
@@ -390,15 +364,6 @@
});
b.setStyleName(style.label_name());
labelsTable.setWidget(row, 0, b);
-
- if (CODE_REVIEW.equalsIgnoreCase(id)) {
- lgtm = new Runnable() {
- @Override
- public void run() {
- b.setValue(true, true);
- }
- };
- }
}
private static boolean isCheckBox(Set<Short> values) {
@@ -467,16 +432,6 @@
selected = b;
labelsTable.setText(row, labelHelpColumn, b.text);
}
-
- void selectMax() {
- for (int i = 0; i < buttons.size() - 1; i++) {
- buttons.get(i).setValue(false, false);
- }
-
- LabelRadioButton max = buttons.get(buttons.size() - 1);
- max.setValue(true, true);
- max.select();
- }
}
private class LabelRadioButton extends RadioButton implements
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index 0078ed3..2036942 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -14,6 +14,7 @@
package com.google.gerrit.client.changes;
+import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.IncludedInInfo;
@@ -37,15 +38,24 @@
call(id, "abandon").post(input, cb);
}
- /** Create a draft change. */
- public static void createDraftChange(String project, String branch,
+ /** Create a new change.
+ *
+ * The new change is created as DRAFT unless the draft workflow is disabled
+ * by `change.allowDrafts = false` in the configuration, in which case the
+ * new change is created as NEW.
+ *
+ */
+ public static void createChange(String project, String branch,
String subject, String base, AsyncCallback<ChangeInfo> cb) {
CreateChangeInput input = CreateChangeInput.create();
input.project(emptyToNull(project));
input.branch(emptyToNull(branch));
input.subject(emptyToNull(subject));
input.base_change(emptyToNull(base));
- input.status(Change.Status.DRAFT.toString());
+
+ if (Gerrit.getConfig().isAllowDraftChanges()) {
+ input.status(Change.Status.DRAFT.toString());
+ }
new RestApi("/changes/").post(input, cb);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
index 7cc080a..514a3be 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
@@ -52,16 +52,19 @@
private final Set<DraftBox> unsavedDrafts;
private boolean attached;
private boolean expandAll;
+ private boolean open;
CommentManager(SideBySide host,
PatchSet.Id base, PatchSet.Id revision,
String path,
- CommentLinkProcessor clp) {
+ CommentLinkProcessor clp,
+ boolean open) {
this.host = host;
this.base = base;
this.revision = revision;
this.path = path;
this.commentLinkProcessor = clp;
+ this.open = open;
published = new HashMap<>();
sideA = new TreeMap<>();
@@ -157,7 +160,8 @@
group,
commentLinkProcessor,
getPatchSetIdFromSide(side),
- info);
+ info,
+ open);
group.add(box);
box.setAnnotation(host.diffTable.scrollbar.comment(
host.getCmFromSide(side),
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
index dc52aa3..c4459b6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/FileInfo.java
@@ -43,6 +43,20 @@
} else if (Patch.COMMIT_MSG.equals(b.path())) {
return 1;
}
+ // Look at file suffixes to check if it makes sense to use a different order
+ int s1 = a.path().lastIndexOf('.');
+ int s2 = b.path().lastIndexOf('.');
+ if (s1 > 0 && s2 > 0 &&
+ a.path().substring(0, s1).equals(b.path().substring(0, s2))) {
+ String suffixA = a.path().substring(s1);
+ String suffixB = b.path().substring(s2);
+ // C++ and C: give priority to header files (.h/.hpp/...)
+ if (suffixA.indexOf(".h") == 0) {
+ return -1;
+ } else if (suffixB.indexOf(".h") == 0) {
+ return 1;
+ }
+ }
return a.path().compareTo(b.path());
}
});
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
index 5e79683..7d74c2b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
@@ -70,7 +70,8 @@
CommentGroup group,
CommentLinkProcessor clp,
PatchSet.Id psId,
- CommentInfo info) {
+ CommentInfo info,
+ boolean open) {
super(group, info.range());
this.psId = psId;
@@ -99,6 +100,8 @@
message.setInnerSafeHtml(clp.apply(
new SafeHtmlBuilder().append(msg).wikify()));
}
+
+ fix.setVisible(open);
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml
index 4495102..bcc34e2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml
@@ -66,7 +66,7 @@
<ui:attribute name='title'/>
<div><ui:msg>Done</ui:msg></div>
</g:Button>
- <g:Button ui:field='fix' styleName=''
+ <g:Button ui:field='fix' styleName='' visible='false'
title='Fix this comment in the inline editor'>
<ui:attribute name='title'/>
<div><ui:msg>Fix</ui:msg></div>
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 34e92e9..f405cb6 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
@@ -116,6 +116,7 @@
private DisplaySide startSide;
private int startLine;
private DiffPreferences prefs;
+ private Change.Status changeStatus;
private CodeMirror cmA;
private CodeMirror cmB;
@@ -234,6 +235,7 @@
call.get(group2.add(new AsyncCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo info) {
+ changeStatus = info.status();
info.revisions().copyKeysIntoChildren("name");
if (edit != null) {
edit.set_name(edit.commit().commit());
@@ -244,7 +246,7 @@
JsArray<RevisionInfo> list = info.revisions().values();
RevisionInfo.sortRevisionInfoByNumber(list);
diffTable.set(prefs, list, diff, edit != null, currentPatchSet,
- info.status().isOpen());
+ changeStatus.isOpen());
header.setChangeInfo(info);
}
@@ -260,7 +262,8 @@
commentManager = new CommentManager(
SideBySide.this,
base, revision, path,
- result.getCommentLinkProcessor());
+ result.getCommentLinkProcessor(),
+ changeStatus.isOpen());
setTheme(result.getTheme());
display(comments);
}
@@ -357,11 +360,9 @@
}
private void registerCmEvents(final CodeMirror cm) {
- cm.on("beforeSelectionChange", onSelectionChange(cm));
cm.on("cursorActivity", updateActiveLine(cm));
- cm.on("gutterClick", onGutterClick(cm));
cm.on("focus", updateActiveLine(cm));
- cm.addKeyMap(KeyMap.create()
+ KeyMap keyMap = KeyMap.create()
.on("A", upToChange(true))
.on("U", upToChange(false))
.on("[", header.navigate(Direction.PREV))
@@ -369,7 +370,6 @@
.on("R", header.toggleReviewed())
.on("O", commentManager.toggleOpenBox(cm))
.on("Enter", commentManager.toggleOpenBox(cm))
- .on("C", commentManager.insertNewDraft(cm))
.on("N", maybeNextVimSearch(cm))
.on("M", modifyInEditScreen(cm))
.on("P", chunkManager.diffChunkNav(cm, Direction.PREV))
@@ -428,7 +428,13 @@
public void run() {
cm.execCommand("selectAll");
}
- }));
+ });
+ if (revision.get() != 0) {
+ cm.on("beforeSelectionChange", onSelectionChange(cm));
+ cm.on("gutterClick", onGutterClick(cm));
+ keyMap.on("C", commentManager.insertNewDraft(cm));
+ }
+ cm.addKeyMap(keyMap);
if (prefs.renderEntireFile()) {
cm.addKeyMap(RENDER_ENTIRE_FILE_KEYMAP);
}
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 218a6c3..178a583 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
@@ -261,8 +261,9 @@
private static boolean isUnifiedPatchLink(final Patch patch) {
return (patch.getPatchType().equals(PatchType.BINARY)
- || Gerrit.getUserAccount().getGeneralPreferences().getDiffView()
- .equals(DiffView.UNIFIED_DIFF));
+ || (Gerrit.isSignedIn()
+ && Gerrit.getUserAccount().getGeneralPreferences().getDiffView()
+ .equals(DiffView.UNIFIED_DIFF)));
}
private static String getFileNameOnly(Patch patch) {
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 ee8ea69..f4f7087 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
@@ -15,7 +15,6 @@
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterValue;
-import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi;
@@ -60,21 +59,20 @@
}
/**
- * Delete branches. For each branch to be deleted a separate DELETE request is
- * fired to the server. The {@code onSuccess} method of the provided callback
- * is invoked once after all requests succeeded. If any request fails the
- * callbacks' {@code onFailure} method is invoked. In a failure case it can be
- * that still some of the branches were successfully deleted.
+ * Delete branches. One call is fired to the server to delete all the
+ * branches.
*/
public static void deleteBranches(Project.NameKey name,
Set<String> refs, AsyncCallback<VoidResult> cb) {
- CallbackGroup group = new CallbackGroup();
- for (String ref : refs) {
- project(name).view("branches").id(ref)
- .delete(group.add(cb));
- cb = CallbackGroup.emptyCallback();
+ if (refs.size() == 1) {
+ project(name).view("branches").id(refs.iterator().next()).delete(cb);
+ } else {
+ DeleteBranchesInput d = DeleteBranchesInput.create();
+ for (String ref : refs) {
+ d.add_branch(ref);
+ }
+ project(name).view("branches:delete").post(d, cb);
}
- group.done();
}
public static void getConfig(Project.NameKey name,
@@ -292,4 +290,18 @@
final native void setRef(String r) /*-{ if(r)this.ref=r; }-*/;
}
+
+ private static class DeleteBranchesInput extends JavaScriptObject {
+ static DeleteBranchesInput create() {
+ DeleteBranchesInput d = createObject().cast();
+ d.init();
+ return d;
+ }
+
+ protected DeleteBranchesInput() {
+ }
+
+ final native void init() /*-{ this.branches = []; }-*/;
+ final native void add_branch(String b) /*-{ this.branches.push(b); }-*/;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
index e87853b..e48477f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
@@ -45,6 +45,7 @@
private static final String JSON_TYPE = "application/json";
private static final String JSON_UTF8 = JSON_TYPE + "; charset=utf-8";
private static final String TEXT_TYPE = "text/plain";
+ private static final String TEXT_UTF8 = TEXT_TYPE + "; charset=utf-8";
/**
* Expected JSON content body prefix that prevents XSSI.
@@ -129,9 +130,14 @@
final String type;
if (isJsonBody(res)) {
try {
- // javac generics bug
- data = RestApi.<T> cast(parseJson(res));
- type = JSON_TYPE;
+ JSONValue val = parseJson(res);
+ if (isJsonEncoded(res) && val.isString() != null) {
+ data = NativeString.wrap(val.isString().stringValue()).cast();
+ type = simpleType(res.getHeader("X-FYI-Content-Type"));
+ } else {
+ data = RestApi.<T> cast(val);
+ type = JSON_TYPE;
+ }
} catch (JSONException e) {
if (!background) {
RpcStatus.INSTANCE.onRpcComplete();
@@ -140,9 +146,6 @@
"Invalid JSON: " + e.getMessage()));
return;
}
- } else if (isEncodedBase64(res)) {
- data = NativeString.wrap(decodeBase64(res.getText())).cast();
- type = simpleType(res.getHeader("X-FYI-Content-Type"));
} else if (isTextBody(res)) {
data = NativeString.wrap(res.getText()).cast();
type = TEXT_TYPE;
@@ -371,7 +374,7 @@
public <T extends JavaScriptObject> void post(String content,
HttpCallback<T> cb) {
- sendRaw(POST, content, cb);
+ sendText(POST, content, cb);
}
public <T extends JavaScriptObject> void put(AsyncCallback<T> cb) {
@@ -389,7 +392,7 @@
public <T extends JavaScriptObject> void put(String content,
HttpCallback<T> cb) {
- sendRaw(PUT, content, cb);
+ sendText(PUT, content, cb);
}
public <T extends JavaScriptObject> void put(
@@ -423,10 +426,7 @@
private static native String str(JavaScriptObject jso)
/*-{ return JSON.stringify(jso) }-*/;
- private static native String decodeBase64(String a)
- /*-{ return $wnd.atob(a) }-*/;
-
- private <T extends JavaScriptObject> void sendRaw(Method method, String body,
+ private <T extends JavaScriptObject> void sendText(Method method, String body,
HttpCallback<T> cb) {
HttpImpl<T> httpCallback = new HttpImpl<>(background, cb);
try {
@@ -434,7 +434,7 @@
RpcStatus.INSTANCE.onRpcStart();
}
RequestBuilder req = request(method);
- req.setHeader("Content-Type", TEXT_TYPE);
+ req.setHeader("Content-Type", TEXT_UTF8);
req.sendRequest(body, httpCallback);
} catch (RequestException e) {
httpCallback.onError(null, e);
@@ -461,9 +461,8 @@
return isContentType(res, TEXT_TYPE);
}
- private static boolean isEncodedBase64(Response res) {
- return "base64".equals(res.getHeader("X-FYI-Content-Encoding"))
- && isTextBody(res);
+ private static boolean isJsonEncoded(Response res) {
+ return "json".equals(res.getHeader("X-FYI-Content-Encoding"));
}
private static boolean isContentType(Response res, String want) {
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
index 2dc034b..d6b194b 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
@@ -51,6 +51,7 @@
Modes.I.gas(),
Modes.I.gerrit_commit(),
Modes.I.gfm(),
+ Modes.I.go(),
Modes.I.groovy(),
Modes.I.haskell(),
Modes.I.htmlmixed(),
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
index 2bd5b00..e511be5 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
@@ -36,6 +36,7 @@
@Source("gas.js") @DoNotEmbed DataResource gas();
@Source("gerrit/commit.js") @DoNotEmbed DataResource gerrit_commit();
@Source("gfm.js") @DoNotEmbed DataResource gfm();
+ @Source("go.js") @DoNotEmbed DataResource go();
@Source("groovy.js") @DoNotEmbed DataResource groovy();
@Source("haskell.js") @DoNotEmbed DataResource haskell();
@Source("htmlmixed.js") @DoNotEmbed DataResource htmlmixed();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index eca6e22..2b283ca 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -177,6 +177,8 @@
config.setReplyTitle(replyTitle);
config.setReplyLabel(replyLabel);
+ config.setAllowDraftChanges(cfg.getBoolean("change", "allowDrafts", true));
+
return config;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 0b66e58..e916e2c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -76,8 +76,8 @@
}
install(new RunAsFilter.Module());
+ installAuthModule();
if (options.enableMasterFeatures()) {
- installAuthModule();
install(new UrlModule(options));
install(new UiRpcModule());
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 5055d47..dd36495 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -91,6 +91,7 @@
import com.google.gson.JsonPrimitive;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import com.google.gwtexpui.server.CacheHeaders;
import com.google.inject.Inject;
@@ -105,6 +106,7 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.EOFException;
+import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -718,6 +720,7 @@
}
}
+ @SuppressWarnings("resource")
static void replyBinaryResult(
@Nullable HttpServletRequest req,
HttpServletResponse res,
@@ -730,7 +733,11 @@
"attachment; filename=\"" + bin.getAttachmentName() + "\"");
}
if (bin.isBase64()) {
- bin = stackBase64(res, bin);
+ if (req != null && JSON_TYPE.equals(req.getHeader(HttpHeaders.ACCEPT))) {
+ bin = stackJsonString(res, bin);
+ } else {
+ bin = stackBase64(res, bin);
+ }
}
if (bin.canGzip() && acceptsGzip(req)) {
bin = stackGzip(res, bin);
@@ -757,6 +764,24 @@
}
}
+ private static BinaryResult stackJsonString(HttpServletResponse res,
+ final BinaryResult src) throws IOException {
+ TemporaryBuffer.Heap buf = heap(Integer.MAX_VALUE);
+ buf.write(JSON_MAGIC);
+ try(Writer w = new BufferedWriter(new OutputStreamWriter(buf, UTF_8));
+ JsonWriter json = new JsonWriter(w)) {
+ json.setLenient(true);
+ json.setHtmlSafe(true);
+ json.value(src.asString());
+ w.write('\n');
+ }
+ res.setHeader("X-FYI-Content-Encoding", "json");
+ res.setHeader("X-FYI-Content-Type", src.getContentType());
+ return asBinaryResult(buf)
+ .setContentType(JSON_TYPE)
+ .setCharacterEncoding(UTF_8.name());
+ }
+
private static BinaryResult stackBase64(HttpServletResponse res,
final BinaryResult src) throws IOException {
BinaryResult b64;
@@ -767,10 +792,16 @@
b64 = new BinaryResult() {
@Override
public void writeTo(OutputStream out) throws IOException {
- OutputStream e = BaseEncoding.base64().encodingStream(
- new OutputStreamWriter(out, ISO_8859_1));
- src.writeTo(e);
- e.flush();
+ try (OutputStreamWriter w = new OutputStreamWriter(
+ new FilterOutputStream(out) {
+ @Override
+ public void close() {
+ // Do not close out, but only w and e.
+ }
+ }, ISO_8859_1);
+ OutputStream e = BaseEncoding.base64().encodingStream(w)) {
+ src.writeTo(e);
+ }
}
};
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/WarDistribution.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/WarDistribution.java
index 2c34711..3328a54 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/WarDistribution.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/WarDistribution.java
@@ -49,8 +49,9 @@
String pluginJarName = new File(ze.getName()).getName();
String pluginName = pluginJarName.substring(0,
pluginJarName.length() - JAR.length());
- final InputStream in = zf.getInputStream(ze);
- processor.process(pluginName, in);
+ try (InputStream in = zf.getInputStream(ze)) {
+ processor.process(pluginName, in);
+ }
}
}
}
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh
index a5a8ed5..7e6f943 100755
--- a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/gerrit.sh
@@ -265,7 +265,9 @@
exit 1
fi
-JSTACK=${JAVA:0:${#JAVA}-5}/jstack
+if test -z "$JSTACK"; then
+ JSTACK="$JAVA_HOME/bin/jstack"
+fi
#####################################################
# Add Gerrit properties to Java VM options.
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java
index a0cc2bf..31494ba 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGeneralPreferences.java
@@ -157,6 +157,9 @@
@Column(id = 18, length = 20, notNull = false)
protected String reviewCategoryStrategy;
+ @Column(id = 19)
+ protected boolean muteCommonPathPrefixes;
+
public AccountGeneralPreferences() {
}
@@ -295,6 +298,15 @@
this.legacycidInChangeTable = legacycidInChangeTable;
}
+ public boolean isMuteCommonPathPrefixes() {
+ return muteCommonPathPrefixes;
+ }
+
+ public void setMuteCommonPathPrefixes(
+ boolean muteCommonPathPrefixes) {
+ this.muteCommonPathPrefixes = muteCommonPathPrefixes;
+ }
+
public void resetToDefaults() {
maximumPageSize = DEFAULT_PAGESIZE;
showSiteHeader = true;
@@ -309,5 +321,6 @@
diffView = null;
sizeBarInChangeTable = true;
legacycidInChangeTable = false;
+ muteCommonPathPrefixes = true;
}
}
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 4c0bb69..fe2cd96 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
@@ -598,4 +598,14 @@
public void setTopic(String topic) {
this.topic = topic;
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder(getClass().getSimpleName())
+ .append('{').append(changeId)
+ .append(" (").append(changeKey).append("), ")
+ .append("dest=").append(dest).append(", ")
+ .append("status=").append(status).append('}')
+ .toString();
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java
index 8983013..e45e7cc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetPreferences.java
@@ -109,6 +109,7 @@
Boolean relativeDateInChangeTable;
Boolean sizeBarInChangeTable;
Boolean legacycidInChangeTable;
+ Boolean muteCommonPathPrefixes;
ReviewCategoryStrategy reviewCategoryStrategy;
DiffView diffView;
List<TopMenu.MenuItem> my;
@@ -127,6 +128,7 @@
relativeDateInChangeTable = p.isRelativeDateInChangeTable() ? true : null;
sizeBarInChangeTable = p.isSizeBarInChangeTable() ? true : null;
legacycidInChangeTable = p.isLegacycidInChangeTable() ? true : null;
+ muteCommonPathPrefixes = p.isMuteCommonPathPrefixes() ? true : null;
reviewCategoryStrategy = p.getReviewCategoryStrategy();
diffView = p.getDiffView();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java
index 08450f3..d75c5a2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SetPreferences.java
@@ -63,6 +63,7 @@
public Boolean relativeDateInChangeTable;
public Boolean sizeBarInChangeTable;
public Boolean legacycidInChangeTable;
+ public Boolean muteCommonPathPrefixes;
public ReviewCategoryStrategy reviewCategoryStrategy;
public DiffView diffView;
public List<TopMenu.MenuItem> my;
@@ -150,6 +151,9 @@
if (i.legacycidInChangeTable != null) {
p.setLegacycidInChangeTable(i.legacycidInChangeTable);
}
+ if (i.muteCommonPathPrefixes != null) {
+ p.setMuteCommonPathPrefixes(i.muteCommonPathPrefixes);
+ }
if (i.reviewCategoryStrategy != null) {
p.setReviewCategoryStrategy(i.reviewCategoryStrategy);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
index 0698203..730a86f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -279,7 +279,8 @@
try {
final Name compositeGroupName = new CompositeName().add(groupDN);
final Attribute in =
- ctx.getAttributes(compositeGroupName).get(schema.accountMemberField);
+ ctx.getAttributes(compositeGroupName, schema.accountMemberFieldArray)
+ .get(schema.accountMemberField);
if (in != null) {
final NamingEnumeration<?> groups = in.getAll();
try {
@@ -308,6 +309,7 @@
final ParameterizedString accountEmailAddress;
final ParameterizedString accountSshUserName;
final String accountMemberField;
+ final String[] accountMemberFieldArray;
final List<LdapQuery> accountQueryList;
final List<String> groupBases;
@@ -372,7 +374,10 @@
accountMemberField =
LdapRealm.optdef(config, "accountMemberField", type.accountMemberField());
if (accountMemberField != null) {
+ accountMemberFieldArray = new String[] {accountMemberField};
accountAtts.add(accountMemberField);
+ } else {
+ accountMemberFieldArray = null;
}
final SearchScope accountScope = LdapRealm.scope(config, "accountScope");
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 262e7bd..9606397 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
@@ -22,6 +22,7 @@
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.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -34,6 +35,7 @@
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidators;
+import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -76,7 +78,9 @@
private final CommitValidators.Factory commitValidatorsFactory;
private final ChangeInserter.Factory changeInserterFactory;
private final PatchSetInserter.Factory patchSetInserterFactory;
- final MergeUtil.Factory mergeUtilFactory;
+ private final MergeUtil.Factory mergeUtilFactory;
+ private final ChangeMessagesUtil changeMessagesUtil;
+ private final ChangeUpdate.Factory updateFactory;
@Inject
CherryPickChange(Provider<ReviewDb> db,
@@ -87,7 +91,9 @@
CommitValidators.Factory commitValidatorsFactory,
ChangeInserter.Factory changeInserterFactory,
PatchSetInserter.Factory patchSetInserterFactory,
- MergeUtil.Factory mergeUtilFactory) {
+ MergeUtil.Factory mergeUtilFactory,
+ ChangeMessagesUtil changeMessagesUtil,
+ ChangeUpdate.Factory updateFactory) {
this.db = db;
this.queryProvider = queryProvider;
this.gitManager = gitManager;
@@ -97,6 +103,8 @@
this.changeInserterFactory = changeInserterFactory;
this.patchSetInserterFactory = patchSetInserterFactory;
this.mergeUtilFactory = mergeUtilFactory;
+ this.changeMessagesUtil = changeMessagesUtil;
+ this.updateFactory = updateFactory;
}
public Change.Id cherryPick(Change change, PatchSet patch,
@@ -113,89 +121,95 @@
Project.NameKey project = change.getProject();
IdentifiedUser identifiedUser = (IdentifiedUser) currentUser.get();
- Repository git = null;
- ObjectInserter oi = null;
- RevWalk revWalk = null;
-
+ final Repository git;
try {
git = gitManager.openRepository(project);
- oi = git.newObjectInserter();
- revWalk = new RevWalk(oi.newReader());
-
- Ref destRef = git.getRef(destinationBranch);
- if (destRef == null) {
- throw new InvalidChangeOperationException("Branch "
- + destinationBranch + " does not exist.");
- }
-
- final RevCommit mergeTip = revWalk.parseCommit(destRef.getObjectId());
-
- RevCommit commitToCherryPick =
- revWalk.parseCommit(ObjectId.fromString(patch.getRevision().get()));
-
- PersonIdent committerIdent =
- identifiedUser.newCommitterIdent(TimeUtil.nowTs(),
- serverTimeZone);
-
- final ObjectId computedChangeId =
- ChangeIdUtil
- .computeChangeId(commitToCherryPick.getTree(), mergeTip,
- commitToCherryPick.getAuthorIdent(), committerIdent, message);
- String commitMessage =
- ChangeIdUtil.insertId(message, computedChangeId).trim() + '\n';
-
- RevCommit cherryPickCommit;
- ProjectState projectState = refControl.getProjectControl().getProjectState();
- cherryPickCommit =
- mergeUtilFactory.create(projectState).createCherryPickFromCommit(git, oi, mergeTip,
- commitToCherryPick, committerIdent, commitMessage, revWalk);
- oi.flush();
-
- Change.Key changeKey;
- final List<String> idList = cherryPickCommit.getFooterLines(
- FooterConstants.CHANGE_ID);
- if (!idList.isEmpty()) {
- final String idStr = idList.get(idList.size() - 1).trim();
- changeKey = new Change.Key(idStr);
- } else {
- changeKey = new Change.Key("I" + computedChangeId.name());
- }
-
- Branch.NameKey newDest =
- new Branch.NameKey(change.getProject(), destRef.getName());
- List<ChangeData> destChanges = queryProvider.get()
- .setLimit(2)
- .byBranchKey(newDest, changeKey);
- if (destChanges.size() > 1) {
- throw new InvalidChangeOperationException("Several changes with key "
- + changeKey + " reside on the same branch. "
- + "Cannot create a new patch set.");
- } else if (destChanges.size() == 1) {
- // The change key exists on the destination branch. The cherry pick
- // will be added as a new patch set.
- return insertPatchSet(git, revWalk, destChanges.get(0).change(),
- cherryPickCommit, refControl, identifiedUser);
- } else {
- // Change key not found on destination branch. We can create a new
- // change.
- return createNewChange(git, revWalk, changeKey, project,
- patch.getId(), destRef, cherryPickCommit, refControl,
- identifiedUser, change.getTopic());
- }
- } catch (MergeIdenticalTreeException | MergeConflictException e) {
- throw new MergeException("Cherry pick failed: " + e.getMessage());
} catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(change.getId(), e);
- } finally {
- if (revWalk != null) {
+ }
+
+ try {
+ RevWalk revWalk = new RevWalk(git);
+ try {
+ Ref destRef = git.getRef(destinationBranch);
+ if (destRef == null) {
+ throw new InvalidChangeOperationException("Branch "
+ + destinationBranch + " does not exist.");
+ }
+
+ final RevCommit mergeTip = revWalk.parseCommit(destRef.getObjectId());
+
+ RevCommit commitToCherryPick =
+ revWalk.parseCommit(ObjectId.fromString(patch.getRevision().get()));
+
+ PersonIdent committerIdent =
+ identifiedUser.newCommitterIdent(TimeUtil.nowTs(),
+ serverTimeZone);
+
+ final ObjectId computedChangeId =
+ ChangeIdUtil
+ .computeChangeId(commitToCherryPick.getTree(), mergeTip,
+ commitToCherryPick.getAuthorIdent(), committerIdent, message);
+ String commitMessage =
+ ChangeIdUtil.insertId(message, computedChangeId).trim() + '\n';
+
+ RevCommit cherryPickCommit;
+ ObjectInserter oi = git.newObjectInserter();
+ try {
+ ProjectState projectState = refControl.getProjectControl().getProjectState();
+ cherryPickCommit =
+ mergeUtilFactory.create(projectState).createCherryPickFromCommit(git, oi, mergeTip,
+ commitToCherryPick, committerIdent, commitMessage, revWalk);
+ } catch (MergeIdenticalTreeException | MergeConflictException e) {
+ throw new MergeException("Cherry pick failed: " + e.getMessage());
+ } finally {
+ oi.release();
+ }
+
+ Change.Key changeKey;
+ final List<String> idList = cherryPickCommit.getFooterLines(
+ FooterConstants.CHANGE_ID);
+ if (!idList.isEmpty()) {
+ final String idStr = idList.get(idList.size() - 1).trim();
+ changeKey = new Change.Key(idStr);
+ } else {
+ changeKey = new Change.Key("I" + computedChangeId.name());
+ }
+
+ Branch.NameKey newDest =
+ new Branch.NameKey(change.getProject(), destRef.getName());
+ List<ChangeData> destChanges = queryProvider.get()
+ .setLimit(2)
+ .byBranchKey(newDest, changeKey);
+ if (destChanges.size() > 1) {
+ throw new InvalidChangeOperationException("Several changes with key "
+ + changeKey + " reside on the same branch. "
+ + "Cannot create a new patch set.");
+ } else if (destChanges.size() == 1) {
+ // The change key exists on the destination branch. The cherry pick
+ // will be added as a new patch set.
+ return insertPatchSet(git, revWalk, destChanges.get(0).change(),
+ cherryPickCommit, refControl, identifiedUser);
+ } else {
+ // Change key not found on destination branch. We can create a new
+ // change.
+ Change newChange = createNewChange(git, revWalk, changeKey, project,
+ destRef, cherryPickCommit, refControl,
+ identifiedUser, change.getTopic());
+
+ addMessageToSourceChange(change, patch.getId(), destinationBranch,
+ cherryPickCommit, identifiedUser, refControl);
+
+ addMessageToDestinationChange(newChange, change.getDest().getShortName(),
+ identifiedUser, refControl);
+
+ return newChange.getId();
+ }
+ } finally {
revWalk.release();
}
- if (oi != null) {
- oi.release();
- }
- if (git != null) {
- git.close();
- }
+ } finally {
+ git.close();
}
}
@@ -218,8 +232,8 @@
return change.getId();
}
- private Change.Id createNewChange(Repository git, RevWalk revWalk,
- Change.Key changeKey, Project.NameKey project, PatchSet.Id patchSetId,
+ private Change createNewChange(Repository git, RevWalk revWalk,
+ Change.Key changeKey, Project.NameKey project,
Ref destRef, RevCommit cherryPickCommit, RefControl refControl,
IdentifiedUser identifiedUser, String topic)
throws OrmException, InvalidChangeOperationException, IOException {
@@ -256,31 +270,51 @@
change.getDest().getParentKey().get(), ru.getResult()));
}
- ins.setMessage(buildChangeMessage(patchSetId, change, cherryPickCommit,
- identifiedUser))
- .insert();
+ ins.insert();
- return change.getId();
+ return change;
}
- private ChangeMessage buildChangeMessage(PatchSet.Id patchSetId, Change dest,
- RevCommit cherryPickCommit, IdentifiedUser identifiedUser)
- throws OrmException {
- ChangeMessage cmsg = new ChangeMessage(
+ private void addMessageToSourceChange(Change change, PatchSet.Id patchSetId,
+ String destinationBranch, RevCommit cherryPickCommit,
+ IdentifiedUser identifiedUser, RefControl refControl) throws OrmException {
+ ChangeMessage changeMessage = new ChangeMessage(
new ChangeMessage.Key(
patchSetId.getParentKey(), ChangeUtil.messageUUID(db.get())),
identifiedUser.getAccountId(), TimeUtil.nowTs(), patchSetId);
- String destBranchName = dest.getDest().get();
- StringBuilder msgBuf = new StringBuilder("Patch Set ")
+ StringBuilder sb = new StringBuilder("Patch Set ")
.append(patchSetId.get())
.append(": Cherry Picked")
.append("\n\n")
.append("This patchset was cherry picked to branch ")
- .append(destBranchName.substring(
- destBranchName.indexOf("refs/heads/") + "refs/heads/".length()))
+ .append(destinationBranch)
.append(" as commit ")
.append(cherryPickCommit.getId().getName());
- cmsg.setMessage(msgBuf.toString());
- return cmsg;
+ changeMessage.setMessage(sb.toString());
+
+ ChangeControl ctl = refControl.getProjectControl().controlFor(change);
+ ChangeUpdate update = updateFactory.create(ctl, change.getCreatedOn());
+ changeMessagesUtil.addChangeMessage(db.get(), update, changeMessage);
+ }
+
+ private void addMessageToDestinationChange(Change change, String sourceBranch,
+ IdentifiedUser identifiedUser, RefControl refControl) throws OrmException {
+ PatchSet.Id patchSetId =
+ db.get().patchSets().get(change.currentPatchSetId()).getId();
+ ChangeMessage changeMessage = new ChangeMessage(
+ new ChangeMessage.Key(
+ patchSetId.getParentKey(), ChangeUtil.messageUUID(db.get())),
+ identifiedUser.getAccountId(), TimeUtil.nowTs(), patchSetId);
+
+ StringBuilder sb = new StringBuilder("Patch Set ")
+ .append(patchSetId.get())
+ .append(": Cherry Picked from branch ")
+ .append(sourceBranch)
+ .append(".");
+ changeMessage.setMessage(sb.toString());
+
+ ChangeControl ctl = refControl.getProjectControl().controlFor(change);
+ ChangeUpdate update = updateFactory.create(ctl, change.getCreatedOn());
+ changeMessagesUtil.addChangeMessage(db.get(), update, changeMessage);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
index 43c379b..febb782 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -50,8 +51,10 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import com.google.gerrit.server.config.GerritServerConfig;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -83,6 +86,7 @@
private final ChangeInserter.Factory changeInserterFactory;
private final ChangeJson json;
private final ChangeUtil changeUtil;
+ private final boolean allowDrafts;
@Inject
CreateChange(Provider<ReviewDb> db,
@@ -93,7 +97,8 @@
CommitValidators.Factory commitValidatorsFactory,
ChangeInserter.Factory changeInserterFactory,
ChangeJson json,
- ChangeUtil changeUtil) {
+ ChangeUtil changeUtil,
+ @GerritServerConfig Config config) {
this.db = db;
this.gitManager = gitManager;
this.serverTimeZone = myIdent.getTimeZone();
@@ -103,14 +108,15 @@
this.changeInserterFactory = changeInserterFactory;
this.json = json;
this.changeUtil = changeUtil;
+ this.allowDrafts = config.getBoolean("change", "allowDrafts", true);
}
@Override
public Response<ChangeInfo> apply(TopLevelResource parent,
ChangeInfo input) throws AuthException, OrmException,
BadRequestException, UnprocessableEntityException, IOException,
- InvalidChangeOperationException, ResourceNotFoundException {
-
+ InvalidChangeOperationException, ResourceNotFoundException,
+ MethodNotAllowedException {
if (Strings.isNullOrEmpty(input.project)) {
throw new BadRequestException("project must be non-empty");
}
@@ -128,6 +134,10 @@
&& input.status != ChangeStatus.DRAFT) {
throw new BadRequestException("unsupported change status");
}
+
+ if (!allowDrafts && input.status == ChangeStatus.DRAFT) {
+ throw new MethodNotAllowedException("draft workflow is disabled");
+ }
}
String refName = input.branch;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
index d1742cc..b276aae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftChange.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.change;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
@@ -57,7 +58,8 @@
@Override
public Response<?> apply(ChangeResource rsrc, Input input)
throws ResourceConflictException, AuthException,
- ResourceNotFoundException, OrmException, IOException {
+ ResourceNotFoundException, MethodNotAllowedException,
+ OrmException, IOException {
if (rsrc.getChange().getStatus() != Status.DRAFT) {
throw new ResourceConflictException("Change is not a draft");
}
@@ -67,7 +69,7 @@
}
if (!allowDrafts) {
- throw new ResourceConflictException("Draft workflow is disabled");
+ throw new MethodNotAllowedException("draft workflow is disabled");
}
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
index 373ec27..f52435e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftPatchSet.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.change;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
@@ -69,7 +70,8 @@
@Override
public Response<?> apply(RevisionResource rsrc, Input input)
throws AuthException, ResourceNotFoundException,
- ResourceConflictException, OrmException, IOException {
+ ResourceConflictException, MethodNotAllowedException,
+ OrmException, IOException {
PatchSet patchSet = rsrc.getPatchSet();
PatchSet.Id patchSetId = patchSet.getId();
Change change = rsrc.getChange();
@@ -79,7 +81,7 @@
}
if (!allowDrafts) {
- throw new ResourceConflictException("Draft workflow is disabled");
+ throw new MethodNotAllowedException("draft workflow is disabled");
}
if (!rsrc.getControl().canDeleteDraft(dbProvider.get())) {
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 a6a6d1c..5f1ed5b 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
@@ -241,7 +241,6 @@
rw,
null /*inserter*/,
canMerge,
- null /*batchRefUpdate*/,
accepted,
key.load.dest).dryRun(tip, rev);
} finally {
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 7ba0548..baddd40 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
@@ -43,6 +43,7 @@
import com.google.gerrit.server.notedb.ReviewerState;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ChangeModifiedException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.ssh.NoSshInfo;
@@ -52,7 +53,6 @@
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
@@ -111,7 +111,6 @@
private boolean runHooks;
private boolean sendMail;
private Account.Id uploader;
- private BatchRefUpdate batchRefUpdate;
@Inject
public PatchSetInserter(ChangeHooks hooks,
@@ -216,11 +215,6 @@
return this;
}
- public PatchSetInserter setBatchRefUpdate(BatchRefUpdate batchRefUpdate) {
- this.batchRefUpdate = batchRefUpdate;
- return this;
- }
-
public Change insert() throws InvalidChangeOperationException, OrmException,
IOException, NoSuchChangeException {
init();
@@ -228,23 +222,16 @@
Change c = ctl.getChange();
Change updatedChange;
-
- if (batchRefUpdate != null) {
- // Caller passed in update; add command, but don't execute.
- batchRefUpdate.addCommand(new ReceiveCommand(ObjectId.zeroId(), commit,
- patchSet.getRefName(), ReceiveCommand.Type.CREATE));
- } else {
- RefUpdate ru = git.updateRef(patchSet.getRefName());
- ru.setExpectedOldObjectId(ObjectId.zeroId());
- ru.setNewObjectId(commit);
- ru.disableRefLog();
- if (ru.update(revWalk) != RefUpdate.Result.NEW) {
- throw new IOException(String.format(
- "Failed to create ref %s in %s: %s", patchSet.getRefName(),
- c.getDest().getParentKey().get(), ru.getResult()));
- }
- gitRefUpdated.fire(c.getProject(), ru);
+ RefUpdate ru = git.updateRef(patchSet.getRefName());
+ ru.setExpectedOldObjectId(ObjectId.zeroId());
+ ru.setNewObjectId(commit);
+ ru.disableRefLog();
+ if (ru.update(revWalk) != RefUpdate.Result.NEW) {
+ throw new IOException(String.format(
+ "Failed to create ref %s in %s: %s", patchSet.getRefName(),
+ c.getDest().getParentKey().get(), ru.getResult()));
}
+ gitRefUpdated.fire(c.getProject(), ru);
final PatchSet.Id currentPatchSetId = c.currentPatchSetId();
@@ -394,12 +381,4 @@
return changeMessage != null && changeMessage.getKey().getParentKey()
.equals(patchSet.getId().getParentKey());
}
-
- public class ChangeModifiedException extends InvalidChangeOperationException {
- private static final long serialVersionUID = 1L;
-
- public ChangeModifiedException(String msg) {
- super(msg);
- }
- }
}
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 47e7b65..cd54b78 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
@@ -358,7 +358,7 @@
new PatchLineComment.Key(
new Patch.Key(rsrc.getPatchSet().getId(), path),
ChangeUtil.messageUUID(db.get())),
- c.line,
+ c.line != null ? c.line : 0,
rsrc.getAccountId(),
parent, timestamp);
} else if (parent != null) {
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 47de348..9f77f0e 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
@@ -27,7 +27,6 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.PublishDraftPatchSet.Input;
-import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.PatchSetNotificationSender;
import com.google.gerrit.server.notedb.ChangeUpdate;
@@ -37,8 +36,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import org.eclipse.jgit.lib.Config;
-
import java.io.IOException;
@Singleton
@@ -52,21 +49,18 @@
private final PatchSetNotificationSender sender;
private final ChangeHooks hooks;
private final ChangeIndexer indexer;
- private final boolean allowDrafts;
@Inject
public PublishDraftPatchSet(Provider<ReviewDb> dbProvider,
ChangeUpdate.Factory updateFactory,
PatchSetNotificationSender sender,
ChangeHooks hooks,
- ChangeIndexer indexer,
- @GerritServerConfig Config cfg) {
+ ChangeIndexer indexer) {
this.dbProvider = dbProvider;
this.updateFactory = updateFactory;
this.sender = sender;
this.hooks = hooks;
this.indexer = indexer;
- this.allowDrafts = cfg.getBoolean("change", "allowDrafts", true);
}
@Override
@@ -81,10 +75,6 @@
throw new AuthException("Cannot publish this draft patch set");
}
- if (!allowDrafts) {
- throw new ResourceConflictException("Draft workflow is disabled");
- }
-
PatchSet updatedPatchSet = updateDraftPatchSet(rsrc);
Change updatedChange = updateDraftChange(rsrc);
ChangeUpdate update = updateFactory.create(rsrc.getControl(),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index 2642831..6891fc7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -227,6 +227,7 @@
if (msg != null) {
throw new ResourceConflictException(msg.getMessage());
}
+ //$FALL-THROUGH$
default:
throw new ResourceConflictException("change is " + status(change));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
index 8b80acd..9890f53 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/RebaseChange.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.changedetail;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.reviewdb.client.Branch;
@@ -43,7 +42,6 @@
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -140,7 +138,7 @@
rebase(git, rw, inserter, patchSetId, change,
uploader, baseCommit, mergeUtilFactory.create(
changeControl.getProjectControl().getProjectState(), true),
- committerIdent, true, true, ValidatePolicy.GERRIT, null);
+ committerIdent, true, true, ValidatePolicy.GERRIT);
} catch (MergeConflictException e) {
throw new IOException(e.getMessage());
} finally {
@@ -270,8 +268,6 @@
* @param sendMail if a mail notification should be sent for the new patch set
* @param runHooks if hooks should be run for the new patch set
* @param validate if commit validation should be run for the new patch set
- * @param batchRefUpdate if not null, a batch ref update for creating new
- * refs, which will not be executed.
* @return the new patch set which is based on the given base commit
* @throws NoSuchChangeException thrown if the change to which the patch set
* belongs does not exist or is not visible to the user
@@ -279,13 +275,14 @@
* @throws IOException thrown if rebase is not possible or not needed
* @throws InvalidChangeOperationException thrown if rebase is not allowed
*/
- public PatchSet rebase(Repository git, RevWalk revWalk,
- ObjectInserter inserter, PatchSet.Id patchSetId, Change change,
- IdentifiedUser uploader, RevCommit baseCommit, MergeUtil mergeUtil,
- PersonIdent committerIdent, boolean sendMail, boolean runHooks,
- ValidatePolicy validate, @Nullable BatchRefUpdate batchRefUpdate)
- throws NoSuchChangeException, OrmException, IOException,
- InvalidChangeOperationException, MergeConflictException {
+ public PatchSet rebase(final Repository git, final RevWalk revWalk,
+ final ObjectInserter inserter, final PatchSet.Id patchSetId,
+ final Change change, final IdentifiedUser uploader, final RevCommit baseCommit,
+ final MergeUtil mergeUtil, PersonIdent committerIdent,
+ boolean sendMail, boolean runHooks, ValidatePolicy validate)
+ throws NoSuchChangeException,
+ OrmException, IOException, InvalidChangeOperationException,
+ MergeConflictException {
if (!change.currentPatchSetId().equals(patchSetId)) {
throw new InvalidChangeOperationException("patch set is not current");
}
@@ -308,9 +305,6 @@
.setUploader(uploader.getAccountId())
.setSendMail(sendMail)
.setRunHooks(runHooks);
- if (batchRefUpdate != null) {
- patchSetInserter.setBatchRefUpdate(batchRefUpdate);
- }
final PatchSet.Id newPatchSetId = patchSetInserter.getPatchSetId();
final ChangeMessage cmsg = new ChangeMessage(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SetPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SetPreferences.java
index 0044ebd..d97499c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SetPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SetPreferences.java
@@ -52,6 +52,7 @@
|| i.relativeDateInChangeTable != null
|| i.sizeBarInChangeTable != null
|| i.legacycidInChangeTable != null
+ || i.muteCommonPathPrefixes != null
|| i.reviewCategoryStrategy != null) {
throw new BadRequestException("unsupported option");
}
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/MergeException.java
index 54cd329..d3ebb95 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/MergeException.java
@@ -18,11 +18,15 @@
public class MergeException extends Exception {
private static final long serialVersionUID = 1L;
- public MergeException(final String msg) {
- super(msg, null);
+ public MergeException(String msg) {
+ super(msg);
}
- public MergeException(final String msg, final Throwable why) {
+ public MergeException(Throwable why) {
+ super(why);
+ }
+
+ public MergeException(String msg, Throwable why) {
super(msg, why);
}
}
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 9fa341a..1f0f6648 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
@@ -76,19 +76,17 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
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.eclipse.jgit.transport.ReceiveCommand;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -172,7 +170,6 @@
private Repository repo;
private RevWalk rw;
private RevFlag canMergeFlag;
- private ObjectId oldBranchTip;
private CodeReviewCommit branchTip;
private MergeTip mergeTip;
private ObjectInserter inserter;
@@ -260,7 +257,7 @@
openSchema();
openRepository();
- BatchRefUpdate branchUpdate = openBranch();
+ RefUpdate branchUpdate = openBranch();
boolean reopen = false;
ListMultimap<SubmitType, Change> toSubmit =
@@ -279,9 +276,9 @@
logDebug("Reopening branch");
branchUpdate = openBranch();
}
- SubmitStrategy strategy = createStrategy(submitType, branchUpdate);
+ SubmitStrategy strategy = createStrategy(submitType);
MergeTip mergeTip = preMerge(strategy, toMerge.get(submitType));
- BatchRefUpdate update = updateBranch(strategy, branchUpdate);
+ RefUpdate update = updateBranch(strategy, branchUpdate);
reopen = true;
updateChangeStatus(toSubmit.get(submitType), mergeTip);
@@ -417,11 +414,10 @@
return mergeTip;
}
- private SubmitStrategy createStrategy(SubmitType submitType,
- BatchRefUpdate branchUpdate)
+ private SubmitStrategy createStrategy(SubmitType submitType)
throws MergeException, NoSuchProjectException {
return submitStrategyFactory.create(submitType, db, repo, rw, inserter,
- canMergeFlag, branchUpdate, getAlreadyAccepted(branchTip), destBranch);
+ canMergeFlag, getAlreadyAccepted(branchTip), destBranch);
}
private void openRepository() throws MergeException, NoSuchProjectException {
@@ -434,23 +430,25 @@
String m = "Error opening repository \"" + name.get() + '"';
throw new MergeException(m, err);
}
- inserter = repo.newObjectInserter();
- rw = CodeReviewCommit.newRevWalk(inserter.newReader());
+
+ rw = CodeReviewCommit.newRevWalk(repo);
rw.sort(RevSort.TOPO);
rw.sort(RevSort.COMMIT_TIME_DESC, true);
canMergeFlag = rw.newFlag("CAN_MERGE");
+
+ inserter = repo.newObjectInserter();
}
- private BatchRefUpdate openBranch()
+ private RefUpdate openBranch()
throws MergeException, OrmException, NoSuchChangeException {
try {
- BatchRefUpdate branchUpdate = repo.getRefDatabase().newBatchUpdate();
- Ref oldRef = repo.getRef(destBranch.get());
- oldBranchTip = oldRef != null ? oldRef.getObjectId() : ObjectId.zeroId();
- if (!ObjectId.zeroId().equals(oldBranchTip)) {
- branchTip = (CodeReviewCommit) rw.parseCommit(oldBranchTip);
+ RefUpdate branchUpdate = repo.updateRef(destBranch.get());
+ if (branchUpdate.getOldObjectId() != null) {
+ branchTip =
+ (CodeReviewCommit) rw.parseCommit(branchUpdate.getOldObjectId());
} else if (repo.getFullBranch().equals(destBranch.get())) {
branchTip = null;
+ branchUpdate.setExpectedOldObjectId(ObjectId.zeroId());
} else {
for (ChangeData cd : queryProvider.get().submitted(destBranch)) {
try {
@@ -462,6 +460,7 @@
}
}
}
+ logDebug("Opened branch {}: {}", destBranch.get(), branchTip);
return branchUpdate;
} catch (IOException e) {
throw new MergeException("Cannot open branch", e);
@@ -495,6 +494,7 @@
private ListMultimap<SubmitType, Change> validateChangeList(
List<ChangeData> submitted) throws MergeException {
+ logDebug("Validating {} changes", submitted.size());
ListMultimap<SubmitType, Change> toSubmit = ArrayListMultimap.create();
Map<String, Ref> allRefs;
@@ -514,11 +514,16 @@
Change chg;
try {
ctl = cd.changeControl();
- chg = cd.change();
+ // Reload change in case index was stale.
+ chg = cd.reloadChange();
} catch (OrmException e) {
throw new MergeException("Failed to validate changes", e);
}
Change.Id changeId = cd.getId();
+ if (chg.getStatus() != Change.Status.SUBMITTED) {
+ logDebug("Change {} is not submitted: {}", changeId, chg.getStatus());
+ continue;
+ }
if (chg.currentPatchSetId() == null) {
logError("Missing current patch set on change " + changeId);
commits.put(changeId, CodeReviewCommit.noPatchSet(ctl));
@@ -632,6 +637,7 @@
toMerge.put(submitType, commit);
toSubmit.put(submitType, chg);
}
+ logDebug("Submitting on this run: {}", toSubmit);
return toSubmit;
}
@@ -651,11 +657,11 @@
}
}
- private BatchRefUpdate updateBranch(SubmitStrategy strategy,
- BatchRefUpdate branchUpdate) throws MergeException {
+ private RefUpdate updateBranch(SubmitStrategy strategy,
+ RefUpdate branchUpdate) throws MergeException {
CodeReviewCommit currentTip =
mergeTip != null ? mergeTip.getCurrentTip() : null;
- if (branchTip == currentTip) {
+ if (Objects.equals(branchTip, currentTip)) {
logDebug("Branch already at merge tip {}, no update to perform",
currentTip.name());
return null;
@@ -664,7 +670,7 @@
return null;
}
- if (RefNames.REFS_CONFIG.equals(destBranch.get())) {
+ if (RefNames.REFS_CONFIG.equals(branchUpdate.getName())) {
logDebug("Loading new configuration from {}", RefNames.REFS_CONFIG);
try {
ProjectConfig cfg =
@@ -676,34 +682,27 @@
+ destProject.getProject().getName(), e);
}
}
- try {
- inserter.flush();
- } catch (IOException e) {
- throw new MergeException("Cannot flush merge results", e);
- }
branchUpdate.setRefLogIdent(refLogIdent);
- branchUpdate.setAllowNonFastForwards(false);
- // TODO(dborowitz): This message is also used by all new patch set ref
- // updates; find a better wording that works for that case too.
+ branchUpdate.setForceUpdate(false);
+ branchUpdate.setNewObjectId(currentTip);
branchUpdate.setRefLogMessage("merged", true);
- ReceiveCommand cmd =
- new ReceiveCommand(oldBranchTip, currentTip, destBranch.get());
- branchUpdate.addCommand(cmd);
-
try {
- branchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
- logDebug("Executed batch update: {}", branchUpdate);
- switch (cmd.getResult()) {
- case OK:
- if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
+ RefUpdate.Result result = branchUpdate.update(rw);
+ logDebug("Update of {}: {}..{} returned status {}",
+ branchUpdate.getName(), branchUpdate.getOldObjectId(),
+ branchUpdate.getNewObjectId(), result);
+ switch (result) {
+ case NEW:
+ case FAST_FORWARD:
+ if (branchUpdate.getResult() == RefUpdate.Result.FAST_FORWARD) {
tagCache.updateFastForward(destBranch.getParentKey(),
- destBranch.get(),
- oldBranchTip,
+ branchUpdate.getName(),
+ branchUpdate.getOldObjectId(),
currentTip);
}
- if (RefNames.REFS_CONFIG.equals(destBranch.get())) {
+ if (RefNames.REFS_CONFIG.equals(branchUpdate.getName())) {
Project p = destProject.getProject();
projectCache.evict(p);
destProject = projectCache.get(p.getNameKey());
@@ -722,26 +721,23 @@
} else {
msg = "will not retry";
}
- throw new IOException(cmd.getResult().name() + ", " + msg);
+ // TODO(dborowitz): Implement RefUpdate.toString().
+ throw new IOException(branchUpdate.getResult().name() + ", " + msg
+ + '\n' + branchUpdate);
default:
- throw new IOException(cmd.getResult().name());
+ throw new IOException(branchUpdate.getResult().name()
+ + '\n' + branchUpdate);
}
} catch (IOException e) {
- throw new MergeException("Cannot update " + destBranch.get(), e);
+ throw new MergeException("Cannot update " + branchUpdate.getName(), e);
}
}
- private void fireRefUpdated(BatchRefUpdate branchUpdate) {
- logDebug("Firing ref updated hooks for {}", destBranch.get());
+ private void fireRefUpdated(RefUpdate branchUpdate) {
+ logDebug("Firing ref updated hooks for {}", branchUpdate.getName());
gitRefUpdated.fire(destBranch.getParentKey(), branchUpdate);
- Account account = getAccount(mergeTip.getCurrentTip());
- for (ReceiveCommand cmd : branchUpdate.getCommands()) {
- if (cmd.getRefName().equals(destBranch.get())) {
- hooks.doRefUpdatedHook(
- destBranch, cmd.getOldId(), cmd.getNewId(), account);
- break;
- }
- }
+ hooks.doRefUpdatedHook(destBranch, branchUpdate,
+ getAccount(mergeTip.getCurrentTip()));
}
private Account getAccount(CodeReviewCommit codeReviewCommit) {
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 c798f4d..295ad52 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
@@ -43,6 +43,7 @@
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
@@ -66,6 +67,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
@@ -77,12 +79,6 @@
import java.util.Set;
import java.util.TimeZone;
-/**
- * Utilities for various kinds of merges and cherry-picks.
- * <p>
- * <b>Note:</b> Unless otherwise noted, the methods in this class do not flush
- * the {@link ObjectInserter}s passed in after performing a merge.
- */
public class MergeUtil {
private static final Logger log = LoggerFactory.getLogger(MergeUtil.class);
private static final String R_HEADS_MASTER =
@@ -181,7 +177,7 @@
final ThreeWayMerger m = newThreeWayMerger(repo, inserter);
m.setBase(originalCommit.getParent(0));
- if (m.merge(false, mergeTip, originalCommit)) {
+ if (m.merge(mergeTip, originalCommit)) {
ObjectId tree = m.getResultTreeId();
if (tree.equals(mergeTip.getTree())) {
throw new MergeIdenticalTreeException("identical tree");
@@ -193,7 +189,7 @@
mergeCommit.setAuthor(originalCommit.getAuthorIdent());
mergeCommit.setCommitter(cherryPickCommitterIdent);
mergeCommit.setMessage(commitMsg);
- return rw.parseCommit(inserter.insert(mergeCommit));
+ return rw.parseCommit(commit(inserter, mergeCommit));
} else {
throw new MergeConflictException("merge conflict");
}
@@ -397,7 +393,7 @@
ThreeWayMerger m = newThreeWayMerger(repo, createDryRunInserter(repo));
try {
- return m.merge(false, mergeTip, toMerge);
+ return m.merge(new AnyObjectId[] {mergeTip, toMerge});
} catch (LargeObjectException e) {
log.warn("Cannot merge due to LargeObjectException: " + toMerge.name());
return false;
@@ -446,7 +442,7 @@
try {
ThreeWayMerger m = newThreeWayMerger(repo, createDryRunInserter(repo));
m.setBase(toMerge.getParent(0));
- return m.merge(false, mergeTip, toMerge);
+ return m.merge(mergeTip, toMerge);
} catch (IOException e) {
throw new MergeException("Cannot merge " + toMerge.name(), e);
}
@@ -498,7 +494,7 @@
throws MergeException {
final ThreeWayMerger m = newThreeWayMerger(repo, inserter);
try {
- if (m.merge(false, mergeTip, n)) {
+ if (m.merge(new AnyObjectId[] {mergeTip, n})) {
return writeMergeCommit(myIdent, rw, inserter, canMergeFlag, destBranch,
mergeTip, m.getResultTreeId(), n);
} else {
@@ -586,7 +582,7 @@
mergeCommit.setMessage(msgbuf.toString());
CodeReviewCommit mergeResult =
- (CodeReviewCommit) rw.parseCommit(inserter.insert(mergeCommit));
+ (CodeReviewCommit) rw.parseCommit(commit(inserter, mergeCommit));
mergeResult.setControl(n.getControl());
return mergeResult;
}
@@ -660,10 +656,31 @@
Merger m = strategy.newMerger(repo, true);
checkArgument(m instanceof ThreeWayMerger,
"merge strategy %s does not support three-way merging", strategyName);
- m.setObjectInserter(inserter);
+ m.setObjectInserter(new ObjectInserter.Filter() {
+ @Override
+ protected ObjectInserter delegate() {
+ return inserter;
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void release() {
+ }
+ });
return (ThreeWayMerger) m;
}
+ public ObjectId commit(final ObjectInserter inserter,
+ final CommitBuilder mergeCommit) throws IOException,
+ UnsupportedEncodingException {
+ ObjectId id = inserter.insert(mergeCommit);
+ inserter.flush();
+ return id;
+ }
+
public PatchSetApproval markCleanMerges(final RevWalk rw,
final RevFlag canMergeFlag, final CodeReviewCommit mergeTip,
final Set<RevCommit> alreadyAccepted) throws MergeException {
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 604d995..5959b1d 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
@@ -1268,13 +1268,17 @@
return;
}
- if (magicBranch.draft
- && (!receiveConfig.allowDrafts
- || projectControl.controlForRef("refs/drafts/" + ref)
- .isBlocked(Permission.PUSH))) {
- errors.put(Error.CODE_REVIEW, ref);
- reject(cmd, "cannot upload drafts");
- return;
+ if (magicBranch.draft) {
+ if (!receiveConfig.allowDrafts) {
+ errors.put(Error.CODE_REVIEW, ref);
+ reject(cmd, "draft workflow is disabled");
+ return;
+ } else if (projectControl.controlForRef("refs/drafts/" + ref)
+ .isBlocked(Permission.PUSH)) {
+ errors.put(Error.CODE_REVIEW, ref);
+ reject(cmd, "cannot upload drafts");
+ return;
+ }
}
if (!magicBranch.ctl.canUpload()) {
@@ -1729,6 +1733,7 @@
addMessage("Change " + c.getChangeId() + ": " + msg.getMessage());
break;
}
+ //$FALL-THROUGH$
default:
addMessage("change " + c.getChangeId() + " is "
+ c.getStatus().name().toLowerCase());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteMerger.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteMerger.java
index 0c384b7..8b6da7b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteMerger.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteMerger.java
@@ -43,6 +43,7 @@
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ObjectStream;
import org.eclipse.jgit.notes.Note;
import org.eclipse.jgit.notes.NoteMerger;
import org.eclipse.jgit.util.io.UnionInputStream;
@@ -67,12 +68,13 @@
ObjectLoader lo = reader.open(ours.getData());
byte[] sep = new byte[] {'\n'};
ObjectLoader lt = reader.open(theirs.getData());
- UnionInputStream union = new UnionInputStream(
- lo.openStream(),
- new ByteArrayInputStream(sep),
- lt.openStream());
- ObjectId noteData = inserter.insert(Constants.OBJ_BLOB,
- lo.getSize() + sep.length + lt.getSize(), union);
- return new Note(ours, noteData);
+ try (ObjectStream os = lo.openStream();
+ ByteArrayInputStream b = new ByteArrayInputStream(sep);
+ ObjectStream ts = lt.openStream();
+ UnionInputStream union = new UnionInputStream(os, b, ts)) {
+ ObjectId noteData = inserter.insert(Constants.OBJ_BLOB,
+ lo.getSize() + sep.length + lt.getSize(), union);
+ return new Note(ours, noteData);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
index 2b73ff9..3d65fa7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
@@ -207,11 +207,11 @@
schema.submoduleSubscriptions().bySubmodule(updatedBranch).toList();
if (!subscribers.isEmpty()) {
- String msgbuf = msg;
- if (msgbuf == null) {
- // Initialize the message buffer
- msgbuf = "";
-
+ // Initialize the message buffer
+ StringBuilder sb = new StringBuilder();
+ if (msg != null) {
+ sb.append(msg);
+ } else {
// The first updatedBranch on a cascade event of automatic
// updates of repos is added to updatedSubscribers set so
// if we face a situation having
@@ -227,8 +227,8 @@
&& (c.getStatusCode() == CommitMergeStatus.CLEAN_MERGE
|| c.getStatusCode() == CommitMergeStatus.CLEAN_PICK
|| c.getStatusCode() == CommitMergeStatus.CLEAN_REBASE)) {
- msgbuf += "\n";
- msgbuf += c.getFullMessage();
+ sb.append("\n")
+ .append(c.getFullMessage());
}
}
}
@@ -246,7 +246,7 @@
Map<Branch.NameKey, String> paths = new HashMap<>(1);
paths.put(updatedBranch, s.getPath());
- updateGitlinks(s.getSuperProject(), myRw, modules, paths, msgbuf);
+ updateGitlinks(s.getSuperProject(), myRw, modules, paths, sb.toString());
}
} catch (SubmoduleException e) {
log.warn("Cannot update gitlinks for " + s + " due to " + e.getMessage());
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 b56ac01..c9fe1d5 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
@@ -24,6 +24,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CommitMergeStatus;
import com.google.gerrit.server.git.MergeConflictException;
@@ -36,8 +37,8 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
import java.io.IOException;
import java.util.ArrayList;
@@ -49,13 +50,16 @@
public class CherryPick extends SubmitStrategy {
private final PatchSetInfoFactory patchSetInfoFactory;
+ private final GitReferenceUpdated gitRefUpdated;
private final Map<Change.Id, CodeReviewCommit> newCommits;
CherryPick(SubmitStrategy.Arguments args,
- PatchSetInfoFactory patchSetInfoFactory) {
+ PatchSetInfoFactory patchSetInfoFactory,
+ GitReferenceUpdated gitRefUpdated) {
super(args);
this.patchSetInfoFactory = patchSetInfoFactory;
+ this.gitRefUpdated = gitRefUpdated;
this.newCommits = new HashMap<>();
}
@@ -170,6 +174,8 @@
ps.setUploader(cherryPickUser.getAccountId());
ps.setRevision(new RevId(newCommit.getId().getName()));
+ RefUpdate ru;
+
args.db.changes().beginTransaction(n.change().getId());
try {
insertAncestors(args.db, ps.getId(), newCommit);
@@ -185,14 +191,23 @@
}
args.db.patchSetApprovals().insert(approvals);
- args.batchRefUpdate.addCommand(
- new ReceiveCommand(ObjectId.zeroId(), newCommit, ps.getRefName()));
+ ru = args.repo.updateRef(ps.getRefName());
+ ru.setExpectedOldObjectId(ObjectId.zeroId());
+ ru.setNewObjectId(newCommit);
+ ru.disableRefLog();
+ if (ru.update(args.rw) != RefUpdate.Result.NEW) {
+ throw new IOException(String.format(
+ "Failed to create ref %s in %s: %s", ps.getRefName(), n.change()
+ .getDest().getParentKey().get(), ru.getResult()));
+ }
args.db.commit();
} finally {
args.db.rollback();
}
+ gitRefUpdated.fire(n.change().getProject(), ru);
+
newCommit.copyFrom(n);
newCommit.setStatusCode(CommitMergeStatus.CLEAN_PICK);
newCommit.setControl(args.changeControlFactory.controlFor(n.change(), cherryPickUser));
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 5afe0e2..482dae3 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
@@ -87,11 +87,11 @@
IdentifiedUser uploader =
args.identifiedUserFactory.create(args.mergeUtil
.getSubmitter(n).getAccountId());
- PatchSet newPatchSet = rebaseChange.rebase(args.repo, args.rw,
- args.inserter, n.getPatchsetId(), n.change(), uploader,
- mergeTip.getCurrentTip(), args.mergeUtil,
- args.serverIdent.get(), false, false, ValidatePolicy.NONE,
- args.batchRefUpdate);
+ PatchSet newPatchSet =
+ rebaseChange.rebase(args.repo, args.rw, args.inserter,
+ n.getPatchsetId(), n.change(), uploader,
+ mergeTip.getCurrentTip(), args.mergeUtil,
+ args.serverIdent.get(), false, false, ValidatePolicy.NONE);
List<PatchSetApproval> approvals = Lists.newArrayList();
for (PatchSetApproval a : args.approvalsUtil.byPatchSet(args.db,
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 bc536a7..b25b17e 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
@@ -30,7 +30,6 @@
import com.google.gerrit.server.project.ChangeControl;
import com.google.inject.Provider;
-import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -61,7 +60,6 @@
protected final RevWalk rw;
protected final ObjectInserter inserter;
protected final RevFlag canMergeFlag;
- protected final BatchRefUpdate batchRefUpdate;
protected final Set<RevCommit> alreadyAccepted;
protected final Branch.NameKey destBranch;
protected final ApprovalsUtil approvalsUtil;
@@ -73,9 +71,9 @@
Provider<PersonIdent> serverIdent, ReviewDb db,
ChangeControl.GenericFactory changeControlFactory, Repository repo,
RevWalk rw, ObjectInserter inserter, RevFlag canMergeFlag,
- BatchRefUpdate batchRefUpdate, Set<RevCommit> alreadyAccepted,
- Branch.NameKey destBranch, ApprovalsUtil approvalsUtil,
- MergeUtil mergeUtil, ChangeIndexer indexer) {
+ Set<RevCommit> alreadyAccepted, Branch.NameKey destBranch,
+ ApprovalsUtil approvalsUtil, MergeUtil mergeUtil,
+ ChangeIndexer indexer) {
this.identifiedUserFactory = identifiedUserFactory;
this.serverIdent = serverIdent;
this.db = db;
@@ -84,7 +82,6 @@
this.repo = repo;
this.rw = rw;
this.inserter = inserter;
- this.batchRefUpdate = batchRefUpdate;
this.canMergeFlag = canMergeFlag;
this.alreadyAccepted = alreadyAccepted;
this.destBranch = destBranch;
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 af7f622..ac65f8d 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
@@ -21,6 +21,7 @@
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.changedetail.RebaseChange;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.index.ChangeIndexer;
@@ -33,7 +34,6 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
@@ -55,6 +55,7 @@
private final Provider<PersonIdent> myIdent;
private final ChangeControl.GenericFactory changeControlFactory;
private final PatchSetInfoFactory patchSetInfoFactory;
+ private final GitReferenceUpdated gitRefUpdated;
private final RebaseChange rebaseChange;
private final ProjectCache projectCache;
private final ApprovalsUtil approvalsUtil;
@@ -67,7 +68,7 @@
@GerritPersonIdent Provider<PersonIdent> myIdent,
final ChangeControl.GenericFactory changeControlFactory,
final PatchSetInfoFactory patchSetInfoFactory,
- final RebaseChange rebaseChange,
+ final GitReferenceUpdated gitRefUpdated, final RebaseChange rebaseChange,
final ProjectCache projectCache,
final ApprovalsUtil approvalsUtil,
final MergeUtil.Factory mergeUtilFactory,
@@ -76,6 +77,7 @@
this.myIdent = myIdent;
this.changeControlFactory = changeControlFactory;
this.patchSetInfoFactory = patchSetInfoFactory;
+ this.gitRefUpdated = gitRefUpdated;
this.rebaseChange = rebaseChange;
this.projectCache = projectCache;
this.approvalsUtil = approvalsUtil;
@@ -83,19 +85,20 @@
this.indexer = indexer;
}
- public SubmitStrategy create(SubmitType submitType, ReviewDb db,
- Repository repo, RevWalk rw, ObjectInserter inserter,
- RevFlag canMergeFlag, BatchRefUpdate batchRefUpdated,
- Set<RevCommit> alreadyAccepted, Branch.NameKey destBranch)
+ public SubmitStrategy create(final SubmitType submitType, final ReviewDb db,
+ final Repository repo, final RevWalk rw, final ObjectInserter inserter,
+ final RevFlag canMergeFlag, final Set<RevCommit> alreadyAccepted,
+ final Branch.NameKey destBranch)
throws MergeException, NoSuchProjectException {
ProjectState project = getProject(destBranch);
- SubmitStrategy.Arguments args = new SubmitStrategy.Arguments(
- identifiedUserFactory, myIdent, db, changeControlFactory, repo, rw,
- inserter, canMergeFlag, batchRefUpdated, alreadyAccepted, destBranch,
- approvalsUtil, mergeUtilFactory.create(project), indexer);
+ final SubmitStrategy.Arguments args =
+ new SubmitStrategy.Arguments(identifiedUserFactory, myIdent, db,
+ changeControlFactory, repo, rw, inserter, canMergeFlag,
+ alreadyAccepted, destBranch,approvalsUtil,
+ mergeUtilFactory.create(project), indexer);
switch (submitType) {
case CHERRY_PICK:
- return new CherryPick(args, patchSetInfoFactory);
+ return new CherryPick(args, patchSetInfoFactory, gitRefUpdated);
case FAST_FORWARD_ONLY:
return new FastForwardOnly(args);
case MERGE_ALWAYS:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
index 652f37e..34ecbc0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/CommentsInNotesUtil.java
@@ -232,7 +232,9 @@
int startLine = RawParseUtils.parseBase10(note, ptr.value, ptr);
if (startLine == 0) {
- return null;
+ range.setEndLine(0);
+ ptr.value += 1;
+ return range;
}
if (note[ptr.value] == '\n') {
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 50a8117..0c8c5ec 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
@@ -275,11 +275,24 @@
try {
DirCache dc = DirCache.newInCore();
m.setDirCache(dc);
- m.setObjectInserter(ins);
+ m.setObjectInserter(new ObjectInserter.Filter() {
+ @Override
+ protected ObjectInserter delegate() {
+ return ins;
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void release() {
+ }
+ });
boolean couldMerge;
try {
- couldMerge = m.merge(false, b.getParents());
+ couldMerge = m.merge(b.getParents());
} catch (IOException e) {
// It is not safe to continue further down in this method as throwing
// an exception most likely means that the merge tree was not created
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
index 7c35014..53f39f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -142,9 +142,10 @@
new URLClassLoader(urls.toArray(new URL[urls.size()]),
PluginLoader.parentFor(type));
+ JarScanner jarScanner = createJarScanner(srcJar);
ServerPlugin plugin =
new ServerPlugin(name, description.canonicalUrl, description.user,
- srcJar, snapshot, new JarScanner(srcJar), description.dataDir,
+ srcJar, snapshot, jarScanner, description.dataDir,
pluginLoader);
plugin.setCleanupHandle(new CleanupHandle(tmp, jarFile));
keep = true;
@@ -155,4 +156,13 @@
}
}
}
+
+ private JarScanner createJarScanner(File srcJar)
+ throws InvalidPluginException {
+ try {
+ return new JarScanner(srcJar);
+ } catch (IOException e) {
+ throw new InvalidPluginException("Cannot scan plugin file " + srcJar, e);
+ }
+ }
}
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 d7d0efd..6a65272 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
@@ -43,9 +43,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
@@ -67,12 +69,8 @@
private final JarFile jarFile;
- public JarScanner(File srcFile) throws InvalidPluginException {
- try {
- this.jarFile = new JarFile(srcFile);
- } catch (IOException e) {
- throw new InvalidPluginException("Cannot scan plugin file " + srcFile, e);
- }
+ public JarScanner(File srcFile) throws IOException {
+ this.jarFile = new JarFile(srcFile);
}
@Override
@@ -136,6 +134,36 @@
return result.build();
}
+ public List<String> findImplementationsOf(Class<?> requestedInterface)
+ throws IOException {
+ List<String> result = Lists.newArrayList();
+ String name = requestedInterface.getName().replace('.', '/');
+
+ Enumeration<JarEntry> e = jarFile.entries();
+ while (e.hasMoreElements()) {
+ JarEntry entry = e.nextElement();
+ if (skip(entry)) {
+ continue;
+ }
+
+ ClassData def = new ClassData(Collections.<String>emptySet());
+ try {
+ new ClassReader(read(jarFile, entry)).accept(def, SKIP_ALL);
+ } catch (RuntimeException err) {
+ PluginLoader.log.warn(String.format("Jar %s has invalid class file %s",
+ jarFile.getName(), entry.getName()), err);
+ continue;
+ }
+
+ if (def.isConcrete() && def.interfaces != null
+ && Iterables.contains(Arrays.asList(def.interfaces), name)) {
+ result.add(def.className);
+ }
+ }
+
+ return result;
+ }
+
private static boolean skip(JarEntry entry) {
if (!entry.getName().endsWith(".class")) {
return true; // Avoid non-class resources.
@@ -166,6 +194,7 @@
String className;
String annotationName;
String annotationValue;
+ String[] interfaces;
Iterable<String> exports;
private ClassData(Iterable<String> exports) {
@@ -183,6 +212,7 @@
String superName, String[] interfaces) {
this.className = Type.getObjectType(name).getClassName();
this.access = access;
+ this.interfaces = interfaces;
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
index a1d8ea5..0b037fb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
@@ -253,12 +253,14 @@
public File get() {
if (!ready) {
synchronized (dataDir) {
- if (!dataDir.exists() && !dataDir.mkdirs()) {
- throw new ProvisionException(String.format(
- "Cannot create %s for plugin %s",
- dataDir.getAbsolutePath(), getName()));
+ if (!ready) {
+ if (!dataDir.exists() && !dataDir.mkdirs()) {
+ throw new ProvisionException(String.format(
+ "Cannot create %s for plugin %s",
+ dataDir.getAbsolutePath(), getName()));
+ }
+ ready = true;
}
- ready = true;
}
}
return dataDir;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeModifiedException.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeModifiedException.java
new file mode 100644
index 0000000..f3ca8b6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeModifiedException.java
@@ -0,0 +1,23 @@
+// 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.project;
+
+public class ChangeModifiedException extends InvalidChangeOperationException {
+ private static final long serialVersionUID = 1L;
+
+ public ChangeModifiedException(String msg) {
+ super(msg);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
index 983549d..834dba5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateBranch.java
@@ -166,6 +166,7 @@
}
refPrefix = getRefPrefix(refPrefix);
}
+ //$FALL-THROUGH$
default: {
throw new IOException(result.name());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
index 9a714f8..4aba333 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranch.java
@@ -32,6 +32,7 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
@@ -42,6 +43,8 @@
@Singleton
public class DeleteBranch implements RestModifyView<BranchResource, Input>{
private static final Logger log = LoggerFactory.getLogger(DeleteBranch.class);
+ private static final int MAX_LOCK_FAILURE_CALLS = 10;
+ private static final long SLEEP_ON_LOCK_FAILURE_MS = 15;
static class Input {
}
@@ -81,14 +84,28 @@
Repository r = repoManager.openRepository(rsrc.getNameKey());
try {
RefUpdate.Result result;
- RefUpdate u;
- try {
- u = r.updateRef(rsrc.getRef());
- u.setForceUpdate(true);
- result = u.delete();
- } catch (IOException e) {
- log.error("Cannot delete " + rsrc.getBranchKey(), e);
- throw e;
+ RefUpdate u = r.updateRef(rsrc.getRef());
+ u.setForceUpdate(true);
+ int remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
+ for (;;) {
+ try {
+ result = u.delete();
+ } catch (LockFailedException e) {
+ result = RefUpdate.Result.LOCK_FAILURE;
+ } catch (IOException e) {
+ log.error("Cannot delete " + rsrc.getBranchKey(), e);
+ throw e;
+ }
+ if (result == RefUpdate.Result.LOCK_FAILURE
+ && --remainingLockFailureCalls > 0) {
+ try {
+ Thread.sleep(SLEEP_ON_LOCK_FAILURE_MS);
+ } catch (InterruptedException ie) {
+ // ignore
+ }
+ } else {
+ break;
+ }
}
switch (result) {
@@ -104,7 +121,7 @@
break;
case REJECTED_CURRENT_BRANCH:
- log.warn("Cannot delete " + rsrc.getBranchKey() + ": " + result.name());
+ log.error("Cannot delete " + rsrc.getBranchKey() + ": " + result.name());
throw new ResourceConflictException("cannot delete current branch");
default:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java
new file mode 100644
index 0000000..bdc67ac
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DeleteBranches.java
@@ -0,0 +1,181 @@
+// 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.project;
+
+import static java.lang.String.format;
+
+import com.google.common.collect.Lists;
+import com.google.gerrit.common.ChangeHooks;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.SubmoduleSubscription;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.DeleteBranches.Input;
+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 com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+
+@Singleton
+class DeleteBranches implements RestModifyView<ProjectResource, Input> {
+ private static final Logger log = LoggerFactory.getLogger(DeleteBranches.class);
+
+ static class Input {
+ List<String> branches;
+
+ static Input init(Input in) {
+ if (in == null) {
+ in = new Input();
+ }
+ if (in.branches == null) {
+ in.branches = Lists.newArrayListWithCapacity(1);
+ }
+ return in;
+ }
+ }
+
+ private final Provider<IdentifiedUser> identifiedUser;
+ private final GitRepositoryManager repoManager;
+ private final Provider<ReviewDb> dbProvider;
+ private final Provider<InternalChangeQuery> queryProvider;
+ private final GitReferenceUpdated referenceUpdated;
+ private final ChangeHooks hooks;
+
+ @Inject
+ DeleteBranches(Provider<IdentifiedUser> identifiedUser,
+ GitRepositoryManager repoManager,
+ Provider<ReviewDb> dbProvider,
+ Provider<InternalChangeQuery> queryProvider,
+ GitReferenceUpdated referenceUpdated,
+ ChangeHooks hooks) {
+ this.identifiedUser = identifiedUser;
+ this.repoManager = repoManager;
+ this.dbProvider = dbProvider;
+ this.queryProvider = queryProvider;
+ this.referenceUpdated = referenceUpdated;
+ this.hooks = hooks;
+ }
+
+ @Override
+ public Response<?> apply(ProjectResource project, Input input)
+ throws OrmException, IOException, ResourceConflictException {
+ input = Input.init(input);
+ Repository r = repoManager.openRepository(project.getNameKey());
+ try {
+ BatchRefUpdate batchUpdate = r.getRefDatabase().newBatchUpdate();
+ for (String branch : input.branches) {
+ batchUpdate.addCommand(createDeleteCommand(project, r, branch));
+ }
+ RevWalk rw = new RevWalk(r);
+ try {
+ batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
+ } finally {
+ rw.release();
+ }
+ StringBuilder errorMessages = new StringBuilder();
+ for (ReceiveCommand command : batchUpdate.getCommands()) {
+ if (command.getResult() == Result.OK) {
+ postDeletion(project, command);
+ } else {
+ appendAndLogErrorMessage(errorMessages, command);
+ }
+ }
+ if (errorMessages.length() > 0) {
+ throw new ResourceConflictException(errorMessages.toString());
+ }
+ } finally {
+ r.close();
+ }
+ return Response.none();
+ }
+
+ private ReceiveCommand createDeleteCommand(ProjectResource project,
+ Repository r, String branch) throws OrmException, IOException {
+ Ref ref = r.getRefDatabase().getRef(branch);
+ ReceiveCommand command;
+ if (ref == null) {
+ command = new ReceiveCommand(ObjectId.zeroId(), ObjectId.zeroId(), branch);
+ command.setResult(Result.REJECTED_OTHER_REASON,
+ "it doesn't exist or you do not have permission to delete it");
+ return command;
+ }
+ command =
+ new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName());
+ Branch.NameKey branchKey =
+ new Branch.NameKey(project.getNameKey(), ref.getName());
+ if (!project.getControl().controlForRef(branchKey).canDelete()) {
+ command.setResult(Result.REJECTED_OTHER_REASON,
+ "it doesn't exist or you do not have permission to delete it");
+ }
+ if (!queryProvider.get().setLimit(1).byBranchOpen(branchKey).isEmpty()) {
+ command.setResult(Result.REJECTED_OTHER_REASON, "it has open changes");
+ }
+ return command;
+ }
+
+ private void appendAndLogErrorMessage(StringBuilder errorMessages,
+ ReceiveCommand cmd) {
+ String msg = null;
+ switch (cmd.getResult()) {
+ case REJECTED_CURRENT_BRANCH:
+ msg = format("Cannot delete %s: it is the current branch",
+ cmd.getRefName());
+ break;
+ case REJECTED_OTHER_REASON:
+ msg = format("Cannot delete %s: %s", cmd.getRefName(), cmd.getMessage());
+ break;
+ default:
+ msg = format("Cannot delete %s: %s", cmd.getRefName(), cmd.getResult());
+ break;
+ }
+ log.error(msg);
+ errorMessages.append(msg);
+ errorMessages.append("\n");
+ }
+
+ private void postDeletion(ProjectResource project, ReceiveCommand cmd)
+ throws OrmException {
+ referenceUpdated.fire(project.getNameKey(), cmd.getRefName(),
+ cmd.getOldId(), cmd.getNewId());
+ Branch.NameKey branchKey =
+ new Branch.NameKey(project.getNameKey(), cmd.getRefName());
+ hooks.doRefUpdatedHook(branchKey, cmd.getOldId(), cmd.getNewId(),
+ identifiedUser.get().getAccount());
+ ResultSet<SubmoduleSubscription> submoduleSubscriptions =
+ dbProvider.get().submoduleSubscriptions().bySuperProject(branchKey);
+ dbProvider.get().submoduleSubscriptions().delete(submoduleSubscriptions);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
index ace221d..430d8f5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
@@ -64,6 +64,7 @@
put(BRANCH_KIND).to(PutBranch.class);
get(BRANCH_KIND).to(GetBranch.class);
delete(BRANCH_KIND).to(DeleteBranch.class);
+ post(PROJECT_KIND, "branches:delete").to(DeleteBranches.class);
install(new FactoryModuleBuilder().build(CreateBranch.Factory.class));
get(BRANCH_KIND, "reflog").to(GetReflog.class);
child(BRANCH_KIND, "files").to(FilesCollection.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
index 519f4f2..2f0386f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
@@ -31,6 +31,8 @@
import java.io.IOException;
+import org.eclipse.jgit.lib.Constants;
+
@Singleton
public class ProjectsCollection implements
RestCollection<TopLevelResource, ProjectResource>,
@@ -88,6 +90,9 @@
}
private ProjectResource _parse(String id) throws IOException {
+ if (id.endsWith(Constants.DOT_GIT_EXT)) {
+ id = id.substring(0, id.length() - Constants.DOT_GIT_EXT.length());
+ }
ProjectControl ctl;
try {
ctl = controlFactory.controlFor(
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 33252e8..ca15287 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
@@ -253,6 +253,7 @@
"The value '%s' is not permitted for parameter '%s' of plugin '"
+ pluginName + "'", value, v.getKey()));
}
+ //$FALL-THROUGH$
case STRING:
cfg.setString(v.getKey(), value);
break;
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 82260b4..f48cfd8 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
@@ -130,7 +130,6 @@
Paginated p = (Paginated) source;
while (skipped && r.size() < p.limit() + start) {
skipped = false;
- last = null;
ResultSet<ChangeData> next = p.restart(nextStart);
for (ChangeData data : buffer(source, next)) {
@@ -139,7 +138,6 @@
} else {
skipped = true;
}
- last = data;
nextStart++;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index b3a0a9a..62e1ba9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -445,11 +445,16 @@
public Change change() throws OrmException {
if (change == null) {
- change = db.changes().get(legacyId);
+ reloadChange();
}
return change;
}
+ public Change reloadChange() throws OrmException {
+ change = db.changes().get(legacyId);
+ return change;
+ }
+
public ChangeNotes notes() throws OrmException {
if (notes == null) {
notes = notesFactory.create(change());
@@ -651,7 +656,13 @@
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).addValue(getId()).toString();
+ MoreObjects.ToStringHelper h = MoreObjects.toStringHelper(this);
+ if (change != null) {
+ h.addValue(change);
+ } else {
+ h.addValue(legacyId);
+ }
+ return h.toString();
}
public static class ChangedLines {
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 2c55f8c..87c33a2 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
@@ -119,7 +119,7 @@
SubmitStrategy strategy =
args.submitStrategyFactory.create(submitType,
db.get(), repo, rw, null, canMergeFlag,
- null, getAlreadyAccepted(repo, rw, commit),
+ getAlreadyAccepted(repo, rw, commit),
otherChange.getDest());
CodeReviewCommit otherCommit =
(CodeReviewCommit) rw.parseCommit(other);
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 50b3d88..945baa8 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_106> C = Schema_106.class;
+ public static final Class<Schema_107> C = Schema_107.class;
public static int getBinaryVersion() {
return guessVersion(C);
@@ -46,7 +46,7 @@
this.versionNbr = guessVersion(getClass());
}
- private static int guessVersion(Class<?> c) {
+ public static int guessVersion(Class<?> c) {
String n = c.getName();
n = n.substring(n.lastIndexOf('_') + 1);
while (n.startsWith("0"))
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_107.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_107.java
new file mode 100644
index 0000000..13ab09a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_107.java
@@ -0,0 +1,41 @@
+// 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.schema;
+
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class Schema_107 extends SchemaVersion {
+
+ @Inject
+ Schema_107(Provider<Schema_106> prior) {
+ super(prior);
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
+ Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+ try {
+ stmt.executeUpdate("UPDATE accounts set mute_common_path_prefixes = 'Y'");
+ } finally {
+ stmt.close();
+ }
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
index c356a19..db17f80 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -812,7 +812,6 @@
noteString);
}
-
@Test
public void patchLineCommentMultipleOnePatchsetOneFileBothSides()
throws Exception {
@@ -1154,6 +1153,34 @@
}
@Test
+ public void fileComment() throws Exception {
+ Change c = newChange();
+ ChangeUpdate update = newUpdate(c, otherUser);
+ String uuid = "uuid";
+ String messageForBase = "comment for base";
+ Timestamp now = TimeUtil.nowTs();
+ PatchSet.Id psId = c.currentPatchSetId();
+
+ PatchLineComment commentForBase =
+ newPublishedPatchLineComment(psId, "filename", uuid,
+ null, 0, otherUser, null, now, messageForBase,
+ (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
+ update.setPatchSetId(psId);
+ update.upsertComment(commentForBase);
+ update.commit();
+
+ ChangeNotes notes = newNotes(c);
+ Multimap<PatchSet.Id, PatchLineComment> commentsForBase =
+ notes.getBaseComments();
+ Multimap<PatchSet.Id, PatchLineComment> commentsForPs =
+ notes.getPatchSetComments();
+
+ assertTrue(commentsForPs.isEmpty());
+ assertEquals(commentForBase,
+ Iterables.getOnlyElement(commentsForBase.get(psId)));
+ }
+
+ @Test
public void patchLineCommentNoRange() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, otherUser);
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 94967f0..3306592 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
@@ -411,8 +411,8 @@
a.add(null);
a.add(new CipherNone.Factory());
- setCipherFactories(filter(cfg, "cipher", a.toArray(new NamedFactory[a
- .size()])));
+ setCipherFactories(filter(cfg, "cipher",
+ (NamedFactory<Cipher>[])a.toArray(new NamedFactory[a.size()])));
}
private void initMacs(final Config cfg) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
index e093cfb..439b8c8 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
@@ -268,11 +268,11 @@
}
private String extractWhat(DispatchCommand dcmd) {
- String commandName = dcmd.getCommandName();
+ StringBuilder commandName = new StringBuilder(dcmd.getCommandName());
String[] args = dcmd.getArguments();
for (int i = 1; i < args.length; i++) {
- commandName = commandName + "." + args[i];
+ commandName.append(".").append(args[i]);
}
- return commandName;
+ return commandName.toString();
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 5e33c8f..3ac72a7 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -340,15 +340,16 @@
}
for (LabelType type : allProjectsControl.getLabelTypes().getLabelTypes()) {
- String usage;
- usage = "score for " + type.getName() + "\n";
+ StringBuilder usage = new StringBuilder("score for ")
+ .append(type.getName())
+ .append("\n");
for (LabelValue v : type.getValues()) {
- usage += v.format() + "\n";
+ usage.append(v.format()).append("\n");
}
final String name = "--" + type.getName().toLowerCase();
- optionList.add(new ApproveOption(name, usage, type));
+ optionList.add(new ApproveOption(name, usage.toString(), type));
}
super.parseCommandLine();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index b484a3f..1cb442d 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -307,17 +307,17 @@
private List<String> readSshKey(final List<String> sshKeys)
throws UnsupportedEncodingException, IOException {
if (!sshKeys.isEmpty()) {
- String sshKey;
int idx = sshKeys.indexOf("-");
if (idx >= 0) {
- sshKey = "";
+ StringBuilder sshKey = new StringBuilder();
BufferedReader br =
new BufferedReader(new InputStreamReader(in, "UTF-8"));
String line;
while ((line = br.readLine()) != null) {
- sshKey += line + "\n";
+ sshKey.append(line)
+ .append("\n");
}
- sshKeys.set(idx, sshKey);
+ sshKeys.set(idx, sshKey.toString());
}
}
return sshKeys;
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs
index cc0880e..db87b25 100644
--- a/lib/codemirror/cm.defs
+++ b/lib/codemirror/cm.defs
@@ -53,6 +53,7 @@
'erlang',
'gas',
'gfm',
+ 'go',
'groovy',
'haskell',
'htmlmixed',
diff --git a/plugins/replication b/plugins/replication
index 10fbbce..38c2a6b 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 10fbbcecedc3f85289f8b164a0c7e2968d61a5c6
+Subproject commit 38c2a6b62ecc6fea9b41b1388c56f15ae760f3bb
diff --git a/tools/checkstyle.xml b/tools/checkstyle.xml
new file mode 100644
index 0000000..a9f8cfe
--- /dev/null
+++ b/tools/checkstyle.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+ This configuration file was written by the eclipse-cs plugin configuration editor
+-->
+<!--
+ Checkstyle-Configuration: Google Checks for Gerrit
+ Description:
+Checkstyle configuration based on the Google coding conventions (https://google-styleguide.googlecode.com/svn-history/r130/trunk/javaguide.html),
+edited to remove noisy warnings.
+-->
+<module name="Checker">
+ <property name="severity" value="warning"/>
+ <property name="charset" value="UTF-8"/>
+ <module name="TreeWalker">
+ <module name="OuterTypeFilename"/>
+ <module name="LineLength">
+ <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
+ <property name="max" value="100"/>
+ </module>
+ <module name="OneTopLevelClass"/>
+ <module name="NoLineWrap"/>
+ <module name="EmptyBlock">
+ <property name="option" value="TEXT"/>
+ <property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+ </module>
+ <module name="NeedBraces"/>
+ <module name="LeftCurly">
+ <property name="maxLineLength" value="100"/>
+ </module>
+ <module name="RightCurly"/>
+ <module name="RightCurly">
+ <property name="option" value="alone"/>
+ <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
+ </module>
+ <module name="WhitespaceAround">
+ <property name="severity" value="ignore"/>
+ <property name="allowEmptyConstructors" value="true"/>
+ <property name="allowEmptyMethods" value="true"/>
+ <property name="allowEmptyTypes" value="true"/>
+ <property name="allowEmptyLoops" value="true"/>
+ <message key="ws.notFollowed" value="WhitespaceAround: ''{0}'' is not followed by whitespace."/>
+ <message key="ws.notPreceded" value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="OneStatementPerLine"/>
+ <module name="MultipleVariableDeclarations"/>
+ <module name="ArrayTypeStyle"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="FallThrough"/>
+ <module name="UpperEll"/>
+ <module name="ModifierOrder"/>
+ <module name="EmptyLineSeparator">
+ <property name="severity" value="ignore"/>
+ <property name="allowNoEmptyLineBetweenFields" value="true"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="severity" value="ignore"/>
+ <property name="option" value="nl"/>
+ <property name="tokens" value="DOT"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="severity" value="ignore"/>
+ <property name="option" value="EOL"/>
+ <property name="tokens" value="COMMA"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="NoFinalizer"/>
+ <module name="GenericWhitespace">
+ <property name="severity" value="ignore"/>
+ <message key="ws.followed" value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+ <message key="ws.illegalFollow" value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+ <message key="ws.preceded" value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+ <message key="ws.notPreceded" value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="Indentation">
+ <property name="severity" value="ignore"/>
+ <property name="basicOffset" value="2"/>
+ <property name="caseIndent" value="2"/>
+ <property name="arrayInitIndent" value="2"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="MethodParamPad">
+ <property name="severity" value="ignore"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ <module name="OperatorWrap">
+ <property name="severity" value="ignore"/>
+ <property name="option" value="NL"/>
+ <property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+ </module>
+ <module name="FileTabCharacter">
+ <property name="severity" value="ignore"/>
+ <property name="eachLine" value="true"/>
+ <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
+ </module>
+</module>