Merge "Refactor sending notifications from PublishDraft"
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index 512c801..10689b6 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -70,7 +70,7 @@
change:: link:json.html#change[change attribute]
-patchset:: link:json.html#patchSet[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
uploader:: link:json.html#account[account attribute]
@@ -148,7 +148,7 @@
change:: link:json.html#change[change attribute]
-patchset:: link:json.html#patchSet[patchset attribute]
+patchSet:: link:json.html#patchSet[patchSet attribute]
reviewer:: link:json.html#account[account attribute]
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index 7671767..8fecd64 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -289,7 +289,6 @@
)
----
-
Caching Build Results
~~~~~~~~~~~~~~~~~~~~~
@@ -306,6 +305,28 @@
EOF
----
+Using Buck daemon
+~~~~~~~~~~~~~~~~~
+
+Buck ships with daemon command `buckd`, which uses
+link:https://github.com/martylamb/nailgun[Nailgun] protocol for running
+Java programs from the command line without incurring the JVM startup
+overhead. Using a Buck daemon can save significant amounts of time as it
+avoids the overhead of starting a Java virtual machine, loading the buck class
+files and parsing the build files for each command. It is safe to run several
+buck daemons started from different project directories and they will not
+interfere with each other. Buck's documentation covers daemon in
+http://facebook.github.io/buck/command/buckd.html[buckd]. The trivial case is
+to run `buckd` from the project's root directory and use `buck` as usually:
+
+----
+$>buckd
+$>buck build gerrit
+Using buckd.
+[-] PARSING BUILD FILES...FINISHED 0.6s
+[-] BUILDING...FINISHED 0.2s
+----
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 5439869..0ba51c4 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -215,28 +215,29 @@
Plugins InitStep cannot refer to Gerrit DB Schema or any other Gerrit runtime
objects injected at startup.
-====
- public class MyInitStep implements InitStep {
- private final ConsoleUI ui;
- private final Section.Factory sections;
- private final String pluginName;
+[source,java]
+----
+public class MyInitStep implements InitStep {
+ private final ConsoleUI ui;
+ private final Section.Factory sections;
+ private final String pluginName;
- @Inject
- public GitBlitInitStep(final ConsoleUI ui, Section.Factory sections, @PluginName String pluginName) {
- this.ui = ui;
- this.sections = sections;
- this.pluginName = pluginName;
- }
-
- @Override
- public void run() throws Exception {
- ui.header("\nMy plugin");
-
- Section mySection = getSection("myplugin", null);
- mySection.string("Link name", "linkname", "MyLink");
- }
+ @Inject
+ public GitBlitInitStep(final ConsoleUI ui, Section.Factory sections, @PluginName String pluginName) {
+ this.ui = ui;
+ this.sections = sections;
+ this.pluginName = pluginName;
}
-====
+
+ @Override
+ public void run() throws Exception {
+ ui.header("\nMy plugin");
+
+ Section mySection = getSection("myplugin", null);
+ mySection.string("Link name", "linkname", "MyLink");
+ }
+}
+----
[[classpath]]
Classpath
@@ -263,44 +264,47 @@
Command implementations must extend the base class SshCommand:
-====
- import com.google.gerrit.sshd.SshCommand;
+[source,java]
+----
+import com.google.gerrit.sshd.SshCommand;
- class PrintHello extends SshCommand {
- protected abstract void run() {
- stdout.print("Hello\n");
- }
+class PrintHello extends SshCommand {
+ protected abstract void run() {
+ stdout.print("Hello\n");
}
-====
+}
+----
If no Guice modules are declared in the manifest, SSH commands may
use auto-registration by providing an `@Export` annotation:
-====
- import com.google.gerrit.extensions.annotations.Export;
- import com.google.gerrit.sshd.SshCommand;
+[source,java]
+----
+import com.google.gerrit.extensions.annotations.Export;
+import com.google.gerrit.sshd.SshCommand;
- @Export("print")
- class PrintHello extends SshCommand {
- protected abstract void run() {
- stdout.print("Hello\n");
- }
+@Export("print")
+class PrintHello extends SshCommand {
+ protected abstract void run() {
+ stdout.print("Hello\n");
}
-====
+}
+----
If explicit registration is being used, a Guice module must be
supplied to register the SSH command and declared in the manifest
with the `Gerrit-SshModule` attribute:
-====
- import com.google.gerrit.sshd.PluginCommandModule;
+[source,java]
+----
+import com.google.gerrit.sshd.PluginCommandModule;
- class MyCommands extends PluginCommandModule {
- protected void configureCommands() {
- command("print").to(PrintHello.class);
- }
+class MyCommands extends PluginCommandModule {
+ protected void configureCommands() {
+ command("print").to(PrintHello.class);
}
-====
+}
+----
For a plugin installed as name `helloworld`, the command implemented
by PrintHello class will be available to users as:
@@ -319,56 +323,64 @@
Plugins define the capabilities by overriding the `CapabilityDefinition`
abstract class:
-====
- public class PrintHelloCapability extends CapabilityDefinition {
- @Override
- public String getDescription() {
- return "Print Hello";
- }
+[source,java]
+----
+public class PrintHelloCapability extends CapabilityDefinition {
+ @Override
+ public String getDescription() {
+ return "Print Hello";
}
-====
+}
+----
If no Guice modules are declared in the manifest, UI actions may
use auto-registration by providing an `@Export` annotation:
-====
- @Export("printHello")
- public class PrintHelloCapability extends CapabilityDefinition {
+[source,java]
+----
+@Export("printHello")
+public class PrintHelloCapability extends CapabilityDefinition {
...
-====
+}
+----
Otherwise the capability must be bound in a plugin module:
-====
- public class HelloWorldModule extends AbstractModule {
- @Override
- protected void configure() {
- bind(CapabilityDefinition.class)
- .annotatedWith(Exports.named("printHello"))
- .to(PrintHelloCapability.class);
- }
+[source,java]
+----
+public class HelloWorldModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(CapabilityDefinition.class)
+ .annotatedWith(Exports.named("printHello"))
+ .to(PrintHelloCapability.class);
}
-====
+}
+----
With a plugin-owned capability defined in this way, it is possible to restrict
usage of an SSH command or `UiAction` to members of the group that were granted
this capability in the usual way, using the `RequiresCapability` annotation:
-====
- @RequiresCapability("printHello")
- @CommandMetaData(name="print", description="Print greeting in different languages")
- public final class PrintHelloWorldCommand extends SshCommand {
+[source,java]
+----
+@RequiresCapability("printHello")
+@CommandMetaData(name="print", description="Print greeting in different languages")
+public final class PrintHelloWorldCommand extends SshCommand {
...
-====
+}
+----
Or with `UiAction`:
-====
- @RequiresCapability("printHello")
- public class SayHelloAction extends UiAction<RevisionResource>
- implements RestModifyView<RevisionResource, SayHelloAction.Input> {
+[source,java]
+----
+@RequiresCapability("printHello")
+public class SayHelloAction extends UiAction<RevisionResource>
+ implements RestModifyView<RevisionResource, SayHelloAction.Input> {
...
-====
+}
+----
Capability scope was introduced to differentiate between plugin-owned
capabilities and core capabilities. Per default the scope of the
@@ -384,11 +396,12 @@
If a plugin needs to use a core capability name (e.g. "administrateServer")
this can be specified by setting `scope = CapabilityScope.CORE`:
-====
- @RequiresCapability(value = "administrateServer", scope =
- CapabilityScope.CORE)
+[source,java]
+----
+@RequiresCapability(value = "administrateServer", scope =
+ CapabilityScope.CORE)
...
-====
+----
[[ui_extension]]
UI Extension
@@ -410,138 +423,146 @@
Plugins contribute UI actions by implementing the `UiAction` interface:
-====
- @RequiresCapability("printHello")
- class HelloWorldAction implements UiAction<RevisionResource>,
- RestModifyView<RevisionResource, HelloWorldAction.Input> {
- static class Input {
- boolean french;
- String message;
- }
-
- private Provider<CurrentUser> user;
-
- @Inject
- HelloWorldAction(Provider<CurrentUser> user) {
- this.user = user;
- }
-
- @Override
- public String apply(RevisionResource rev, Input input) {
- final String greeting = input.french
- ? "Bonjour"
- : "Hello";
- return String.format("%s %s from change %s, patch set %d!",
- greeting,
- Strings.isNullOrEmpty(input.message)
- ? Objects.firstNonNull(user.get().getUserName(), "world")
- : input.message,
- rev.getChange().getId().toString(),
- rev.getPatchSet().getPatchSetId());
- }
-
- @Override
- public Description getDescription(
- RevisionResource resource) {
- return new Description()
- .setLabel("Say hello")
- .setTitle("Say hello in different languages");
- }
+[source,java]
+----
+@RequiresCapability("printHello")
+class HelloWorldAction implements UiAction<RevisionResource>,
+ RestModifyView<RevisionResource, HelloWorldAction.Input> {
+ static class Input {
+ boolean french;
+ String message;
}
-====
+
+ private Provider<CurrentUser> user;
+
+ @Inject
+ HelloWorldAction(Provider<CurrentUser> user) {
+ this.user = user;
+ }
+
+ @Override
+ public String apply(RevisionResource rev, Input input) {
+ final String greeting = input.french
+ ? "Bonjour"
+ : "Hello";
+ return String.format("%s %s from change %s, patch set %d!",
+ greeting,
+ Strings.isNullOrEmpty(input.message)
+ ? Objects.firstNonNull(user.get().getUserName(), "world")
+ : input.message,
+ rev.getChange().getId().toString(),
+ rev.getPatchSet().getPatchSetId());
+ }
+
+ @Override
+ public Description getDescription(
+ RevisionResource resource) {
+ return new Description()
+ .setLabel("Say hello")
+ .setTitle("Say hello in different languages");
+ }
+}
+----
`UiAction` must be bound in a plugin module:
-====
- public class Module extends AbstractModule {
- @Override
- protected void configure() {
- install(new RestApiModule() {
- @Override
- protected void configure() {
- post(REVISION_KIND, "say-hello")
- .to(HelloWorldAction.class);
- }
- });
- }
+[source,java]
+----
+public class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ install(new RestApiModule() {
+ @Override
+ protected void configure() {
+ post(REVISION_KIND, "say-hello")
+ .to(HelloWorldAction.class);
+ }
+ });
}
-====
+}
+----
The module above must be declared in pom.xml for Maven driven plugins:
-====
- <manifestEntries>
- <Gerrit-Module>com.googlesource.gerrit.plugins.cookbook.Module</Gerrit-Module>
- </manifestEntries>
-====
+[source,xml]
+----
+<manifestEntries>
+ <Gerrit-Module>com.googlesource.gerrit.plugins.cookbook.Module</Gerrit-Module>
+</manifestEntries>
+----
or in the BUCK configuration file for Buck driven plugins:
-====
- manifest_entries = [
- 'Gerrit-Module: com.googlesource.gerrit.plugins.cookbook.Module',
- ]
-====
+[source,python]
+----
+manifest_entries = [
+ 'Gerrit-Module: com.googlesource.gerrit.plugins.cookbook.Module',
+]
+----
In some use cases more user input must be gathered, for that `UiAction` can be
combined with the JavaScript API. This would display a small popup near the
activation button to gather additional input from the user. The JS file is
typically put in the `static` folder within the plugin's directory:
-====
- Gerrit.install(function(self) {
- function onSayHello(c) {
- var f = c.textfield();
- var t = c.checkbox();
- var b = c.button('Say hello', {onclick: function(){
- c.call(
- {message: f.value, french: t.checked},
- function(r) {
- c.hide();
- window.alert(r);
- c.refresh();
- });
- }});
- c.popup(c.div(
- c.prependLabel('Greeting message', f),
- c.br(),
- c.label(t, 'french'),
- c.br(),
- b));
- f.focus();
- }
- self.onAction('revision', 'say-hello', onSayHello);
- });
-====
+[source,javascript]
+----
+Gerrit.install(function(self) {
+ function onSayHello(c) {
+ var f = c.textfield();
+ var t = c.checkbox();
+ var b = c.button('Say hello', {onclick: function(){
+ c.call(
+ {message: f.value, french: t.checked},
+ function(r) {
+ c.hide();
+ window.alert(r);
+ c.refresh();
+ });
+ }});
+ c.popup(c.div(
+ c.prependLabel('Greeting message', f),
+ c.br(),
+ c.label(t, 'french'),
+ c.br(),
+ b));
+ f.focus();
+ }
+ self.onAction('revision', 'say-hello', onSayHello);
+});
+----
The JS module must be exposed as a `WebUiPlugin` and bound as
an HTTP Module:
-====
- public class HttpModule extends HttpPluginModule {
- @Override
- protected void configureServlets() {
- DynamicSet.bind(binder(), WebUiPlugin.class)
- .toInstance(new JavaScriptPlugin("hello.js"));
- }
+[source,java]
+----
+public class HttpModule extends HttpPluginModule {
+ @Override
+ protected void configureServlets() {
+ DynamicSet.bind(binder(), WebUiPlugin.class)
+ .toInstance(new JavaScriptPlugin("hello.js"));
}
-====
+}
+----
The HTTP module above must be declared in pom.xml for Maven driven plugins:
-====
- <manifestEntries>
- <Gerrit-HttpModule>com.googlesource.gerrit.plugins.cookbook.HttpModule</Gerrit-HttpModule>
- </manifestEntries>
-====
+[source,xml]
+----
+<manifestEntries>
+ <Gerrit-HttpModule>com.googlesource.gerrit.plugins.cookbook.HttpModule</Gerrit-HttpModule>
+</manifestEntries>
+----
or in the BUCK configuration file for Buck driven plugins
-====
- manifest_entries = [
- 'Gerrit-HttpModule: com.googlesource.gerrit.plugins.cookbook.HttpModule',
- ]
-====
+[source,python]
+----
+manifest_entries = [
+ 'Gerrit-HttpModule: com.googlesource.gerrit.plugins.cookbook.HttpModule',
+]
+----
If `UiAction` is annotated with the `@RequiresCapability` annotation, then the
capability check is done during the `UiAction` gathering, so the plugin author
@@ -579,38 +600,40 @@
Servlets may use auto-registration to declare the URL they handle:
-====
- import com.google.gerrit.extensions.annotations.Export;
- import com.google.inject.Singleton;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
+[source,java]
+----
+import com.google.gerrit.extensions.annotations.Export;
+import com.google.inject.Singleton;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
- @Export("/print")
- @Singleton
- class HelloServlet extends HttpServlet {
- protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
- res.setContentType("text/plain");
- res.setCharacterEncoding("UTF-8");
- res.getWriter().write("Hello");
- }
+@Export("/print")
+@Singleton
+class HelloServlet extends HttpServlet {
+ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
+ res.setContentType("text/plain");
+ res.setCharacterEncoding("UTF-8");
+ res.getWriter().write("Hello");
}
-====
+}
+----
The auto registration only works for standard servlet mappings like
`/foo` or `/foo/*`. Regex style bindings must use a Guice ServletModule
to register the HTTP servlets and declare it explicitly in the manifest
with the `Gerrit-HttpModule` attribute:
-====
- import com.google.inject.servlet.ServletModule;
+[source,java]
+----
+import com.google.inject.servlet.ServletModule;
- class MyWebUrls extends ServletModule {
- protected void configureServlets() {
- serve("/print").with(HelloServlet.class);
- }
+class MyWebUrls extends ServletModule {
+ protected void configureServlets() {
+ serve("/print").with(HelloServlet.class);
}
-====
+}
+----
For a plugin installed as name `helloworld`, the servlet implemented
by HelloServlet class will be available to users as:
@@ -629,12 +652,13 @@
Plugins can use this to store any data they want.
-====
- @Inject
- MyType(@PluginData java.io.File myDir) {
- new FileInputStream(new File(myDir, "my.config"));
- }
-====
+[source,java]
+----
+@Inject
+MyType(@PluginData java.io.File myDir) {
+ new FileInputStream(new File(myDir, "my.config"));
+}
+----
[[documentation]]
Documentation
diff --git a/Documentation/index.txt b/Documentation/index.txt
index a30d16f..28e0184 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -65,6 +65,7 @@
.. link:pgm-index.html[Server Side Administrative Tools]
. Developer
.. link:dev-readme.html[Developer Setup]
+.. link:dev-buck.html[Building with Buck]
.. link:dev-eclipse.html[Eclipse Setup]
.. link:dev-contributing.html[Contributing to Gerrit]
.. Documentation formatting guide for contributions
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 394a7b1..9e0c308 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -12,6 +12,7 @@
within an anonymous function passed to `Gerrit.install()`. The plugin
will be passed an object describing its registration with Gerrit:
+[source,javascript]
----
Gerrit.install(function (self) {
// ... plugin JavaScript code here ...
@@ -32,6 +33,7 @@
Issues a DELETE REST API request to the Gerrit server.
.Signature
+[source,javascript]
----
Gerrit.delete(url, callback)
----
@@ -49,6 +51,7 @@
Issues a GET REST API request to the Gerrit server.
.Signature
+[source,javascript]
----
self.get(url, callback)
----
@@ -74,6 +77,7 @@
Issues a POST REST API request to the Gerrit server.
.Signature
+[source,javascript]
----
self.post(url, input, callback)
----
@@ -88,6 +92,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
self.post(
'/my-servlet',
@@ -101,6 +106,7 @@
Issues a PUT REST API request to the Gerrit server.
.Signature
+[source,javascript]
----
self.put(url, input, callback)
----
@@ -115,6 +121,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
self.put(
'/builds',
@@ -129,6 +136,7 @@
on a button associated with a server side `UiAction`.
.Signature
+[source,javascript]
----
Gerrit.onAction(type, view_name, callback);
----
@@ -150,6 +158,7 @@
parameter the URL of the plugin is returned. If passed a string
the argument is appended to the plugin URL.
+[source,javascript]
----
self.url(); // "https://gerrit-review.googlesource.com/plugins/demo/"
self.url('/static/icon.png'); // "https://gerrit-review.googlesource.com/plugins/demo/static/icon.png"
@@ -167,7 +176,7 @@
[[context_action]]
context.action
~~~~~~~~~~~~~~
-A link:rest-api-changes.html#action-info[ActionInfo] object instance
+An link:rest-api-changes.html#action-info[ActionInfo] object instance
supplied by the server describing the UI button the user used to
invoke the action.
@@ -179,6 +188,7 @@
needing to care.
.Signature
+[source,javascript]
----
context.call(input, callback)
----
@@ -191,6 +201,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
context.call(
{message: "..."},
@@ -213,6 +224,7 @@
Issues a DELETE REST API call to the URL associated with the action.
.Signature
+[source,javascript]
----
context.delete(callback)
----
@@ -221,6 +233,7 @@
JSON result of the API call. DELETE methods often return
`204 No Content`, which is passed as null.
+[source,javascript]
----
context.delete(function () {});
----
@@ -231,6 +244,7 @@
Issues a GET REST API call to the URL associated with the action.
.Signature
+[source,javascript]
----
context.get(callback)
----
@@ -240,6 +254,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
context.get(function (result) {
// ... use result here ...
@@ -263,6 +278,7 @@
Issues a POST REST API call to the URL associated with the action.
.Signature
+[source,javascript]
----
context.post(input, callback)
----
@@ -274,6 +290,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
context.post(
{message: "..."},
@@ -295,6 +312,7 @@
the popup.
.Signature
+[source,javascript]
----
context.popup(element)
----
@@ -304,6 +322,8 @@
element. CSS can be used to style the element beyond the defaults.
A common usage is to gather more input:
+
+[source,javascript]
----
self.onAction('revision', 'start-build', function (c) {
var l = c.checkbox();
@@ -329,6 +349,7 @@
Issues a PUT REST API call to the URL associated with the action.
.Signature
+[source,javascript]
----
context.put(input, callback)
----
@@ -340,6 +361,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
context.put(
{message: "..."},
@@ -417,6 +439,7 @@
private REST API URLs see link:#self_delete[self.delete()].
.Signature
+[source,javascript]
----
Gerrit.delete(url, callback)
----
@@ -428,6 +451,7 @@
JSON result of the API call. DELETE methods often return
`204 No Content`, which is passed as null.
+[source,javascript]
----
Gerrit.delete(
'/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
@@ -441,6 +465,7 @@
private REST API URLs see link:#self_get[self.get()].
.Signature
+[source,javascript]
----
Gerrit.get(url, callback)
----
@@ -453,6 +478,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
Gerrit.get('/changes/?q=status:open', function (open) {
for (var i = 0; i < open.length; i++) {
@@ -478,6 +504,7 @@
Updates the web UI to display the view identified by the supplied
URL token. The URL token is the text after `#` in the browser URL.
+[source,javascript]
----
Gerrit.go('/admin/projects/');
----
@@ -492,6 +519,7 @@
Registers a new plugin by invoking the supplied initialization
function. The function is passed the link:#self[plugin instance].
+[source,javascript]
----
Gerrit.install(function (self) {
// ... plugin JavaScript code here ...
@@ -505,6 +533,7 @@
private REST API URLs see link:#self_post[self.post()].
.Signature
+[source,javascript]
----
Gerrit.post(url, input, callback)
----
@@ -519,6 +548,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
Gerrit.post(
'/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
@@ -533,6 +563,7 @@
private REST API URLs see link:#self_put[self.put()].
.Signature
+[source,javascript]
----
Gerrit.put(url, input, callback)
----
@@ -547,6 +578,7 @@
a string, otherwise the result is a JavaScript object or array,
as described in the relevant REST API documentation.
+[source,javascript]
----
Gerrit.put(
'/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
@@ -561,6 +593,7 @@
on a button associated with a server side `UiAction`.
.Signature
+[source,javascript]
----
Gerrit.onAction(type, view_name, callback);
----
@@ -587,6 +620,7 @@
no parameter the URL of the site is returned. If passed a string
the argument is appended to the site URL.
+[source,javascript]
----
Gerrit.url(); // "https://gerrit-review.googlesource.com/"
Gerrit.url('/123'); // "https://gerrit-review.googlesource.com/123"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 6835670..97ce2a1 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -48,7 +48,6 @@
"status": "NEW",
"created": "2012-07-17 07:18:30.854000000",
"updated": "2012-07-17 07:19:27.766000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "001e7057000006dc",
"_number": 1756,
@@ -120,7 +119,6 @@
"status": "NEW",
"created": "2012-07-17 07:18:30.854000000",
"updated": "2012-07-17 07:19:27.766000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "001e7057000006dc",
"_number": 1756,
@@ -220,6 +218,12 @@
authenticated to obtain the available actions.
--
+[[reviewed]]
+--
+* `REVIEWED`: include the `reviewed` field if the caller is
+ authenticated and has commented on the current revision.
+--
+
.Request
----
GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES HTTP/1.0
@@ -351,7 +355,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -401,7 +404,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -640,7 +642,6 @@
"status": "ABANDONED",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -699,7 +700,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -847,7 +847,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -912,7 +911,6 @@
"status": "MERGED",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
"owner": {
@@ -1220,7 +1218,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -2271,7 +2268,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -2322,7 +2318,6 @@
"status": "NEW",
"created": "2013-02-01 09:59:32.126000000",
"updated": "2013-02-21 11:16:36.775000000",
- "reviewed": true,
"mergeable": true,
"_sortkey": "0023412400000f7d",
"_number": 3965,
@@ -2503,6 +2498,7 @@
Whether the calling user has starred this change.
|`reviewed` |not set if `false`|
Whether the change was reviewed by the calling user.
+Only set if link:#reviewed[reviewed] is requested.
|`mergeable` |optional|
Whether the change is mergeable. +
Not set for merged changes.
@@ -2529,7 +2525,7 @@
link:rest-api-accounts.html#account-info[AccountInfo] entities. +
Only set if link:#detailed-labels[detailed labels] are requested.
|`messages`|optional|
-Messages associated with the change as a list of
+Messages associated with the change as a list of
link:#change-message-info[ChangeMessageInfo] entities. +
Only set if link:#messages[messages] are requested.
|`current_revision` |optional|
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index d7e9fd7..f1c13f8 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -168,6 +168,11 @@
+
Changes that match 'MESSAGE' arbitrary string in the commit message body.
+[[comment]]
+comment:'TEXT'::
++
+Changes that match 'TEXT' string in any comment left by a reviewer.
+
[[file]]
file:^'REGEX'::
+
@@ -187,12 +192,6 @@
ones using a bracket expression). For example, to match all XML
files named like 'name1.xml', 'name2.xml', and 'name3.xml' use
`file:"^name[1-3].xml"`.
-+
-Currently this operator is only available on a watched project
-and may not be used in the search bar. The same holds true for web UI
-"My > Watched Changes", i. e. file:regex is used over the is:watched
-expression. It never produces any results, because the error message:
-"operator not permitted here: file:regex" is suppressed.
[[has]]
has:draft::
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
index f56ef07..cb785b9 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -114,6 +114,12 @@
return create(username, null, username, (String[]) null);
}
+ public TestAccount admin()
+ throws UnsupportedEncodingException, OrmException, JSchException {
+ return create("admin", "admin@example.com", "Administrator",
+ "Administrators");
+ }
+
private AccountExternalId.Key getEmailKey(String email) {
return new AccountExternalId.Key(AccountExternalId.SCHEME_MAILTO, email);
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index a9ce827..de70796 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -84,7 +84,8 @@
File tmp = TempFileUtil.createTempDirectory();
Init init = new Init();
int rc = init.main(new String[] {
- "-d", tmp.getPath(), "--batch", "--no-auto-start"});
+ "-d", tmp.getPath(), "--batch", "--no-auto-start",
+ "--skip-plugins"});
if (rc != 0) {
throw new RuntimeException("Couldn't initialize site");
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
index 6db0847..94d8389 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/changes/ListChangesOption.java
@@ -40,7 +40,10 @@
MESSAGES(9),
/** Include allowed actions client could perform. */
- CURRENT_ACTIONS(10);
+ CURRENT_ACTIONS(10),
+
+ /** Set the reviewed boolean for the caller. */
+ REVIEWED(11);
private final int value;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectDeletedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectDeletedListener.java
new file mode 100644
index 0000000..b03f99c
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectDeletedListener.java
@@ -0,0 +1,27 @@
+// 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.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Notified whenever a project is deleted on the master. */
+@ExtensionPoint
+public interface ProjectDeletedListener {
+ public interface Event {
+ String getProjectName();
+ }
+
+ void onProjectDeleted(Event event);
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
index 25208d3..ae29801 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
@@ -181,7 +181,6 @@
case ANON_HTTP:
return "http";
case SSH:
- case ANON_SSH:
return "ssh";
default:
return null;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
index 6273717..5f89bf0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -19,6 +19,7 @@
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.changes.ListChangesOption;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.KeyPressEvent;
@@ -26,6 +27,7 @@
import java.util.Collections;
import java.util.Comparator;
+import java.util.EnumSet;
public class AccountDashboardScreen extends Screen implements ChangeListScreen {
private final Account.Id ownerId;
@@ -61,7 +63,7 @@
outgoing.setTitleText(Util.C.outgoingReviews());
incoming.setTitleText(Util.C.incomingReviews());
- incoming.setHighlightUnreviewed(true);
+ incoming.setHighlightUnreviewed(mine);
closed.setTitleText(Util.C.recentlyClosed());
table.addSection(outgoing);
@@ -83,6 +85,9 @@
display(result);
}
},
+ mine
+ ? EnumSet.of(ListChangesOption.REVIEWED)
+ : EnumSet.noneOf(ListChangesOption.class),
"is:open owner:" + who,
"is:open reviewer:" + who + " -owner:" + who,
"is:closed owner:" + who + " -age:4w limit:10");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
index fea117f..773313a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
@@ -28,13 +28,18 @@
/** Run 2 or more queries in a single remote invocation. */
public static void query(
- AsyncCallback<JsArray<ChangeList>> callback, String... queries) {
+ AsyncCallback<JsArray<ChangeList>> callback,
+ EnumSet<ListChangesOption> options,
+ String... queries) {
assert queries.length >= 2; // At least 2 is required for correct result.
RestApi call = new RestApi(URI);
for (String q : queries) {
call.addParameterRaw("q", KeyUtil.encode(q));
}
- addOptions(call, EnumSet.of(ListChangesOption.LABELS));
+
+ EnumSet<ListChangesOption> o = EnumSet.of(ListChangesOption.LABELS);
+ o.addAll(options);
+ addOptions(call, o);
call.get(callback);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
index 7b387ab..3002e48 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
@@ -19,10 +19,12 @@
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.InlineHyperlink;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.changes.ListChangesOption;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.http.client.URL;
import java.util.ArrayList;
+import java.util.EnumSet;
import java.util.List;
import java.util.ListIterator;
@@ -103,6 +105,7 @@
finishDisplay();
}
},
+ EnumSet.noneOf(ListChangesOption.class),
queries.toArray(new String[queries.size()]));
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 06cc2f0..4ecd020 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -309,9 +309,6 @@
}
if (!rp.isCheckReferencedObjectsAreReachable()) {
- if (isGet) {
- rc.advertiseHistory();
- }
chain.doFilter(request, response);
return;
}
@@ -326,7 +323,6 @@
projectName);
if (isGet) {
- rc.advertiseHistory();
cache.invalidate(cacheKey);
} else {
Set<ObjectId> ids = cache.getIfPresent(cacheKey);
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 284f6c1..c98bd5d 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -25,9 +25,12 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.ChangeField.ChangeProtoField;
+import com.google.gerrit.server.index.ChangeField.PatchSetApprovalProtoField;
import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.FieldDef.FillArgs;
@@ -45,11 +48,13 @@
import com.google.inject.assistedinject.AssistedInject;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.analysis.util.CharArraySet;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.LongField;
+import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
@@ -63,6 +68,7 @@
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.Version;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
@@ -98,6 +104,8 @@
public static final String CHANGES_OPEN = "open";
public static final String CHANGES_CLOSED = "closed";
private static final String ID_FIELD = ChangeField.LEGACY_ID.getName();
+ private static final String CHANGE_FIELD = ChangeField.CHANGE.getName();
+ private static final String APPROVAL_FIELD = ChangeField.APPROVAL.getName();
static interface Factory {
LuceneChangeIndex create(Schema<ChangeData> schema, String base);
@@ -105,7 +113,7 @@
private static IndexWriterConfig getIndexWriterConfig(Config cfg, String name) {
IndexWriterConfig writerConfig = new IndexWriterConfig(LUCENE_VERSION,
- new StandardAnalyzer(LUCENE_VERSION));
+ new StandardAnalyzer(LUCENE_VERSION, CharArraySet.EMPTY_SET));
writerConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
double m = 1 << 20;
writerConfig.setRAMBufferSizeMB(cfg.getLong("index", name, "ramBufferSize",
@@ -256,7 +264,8 @@
}
private static class QuerySource implements ChangeDataSource {
- private static final ImmutableSet<String> FIELDS = ImmutableSet.of(ID_FIELD);
+ private static final ImmutableSet<String> FIELDS =
+ ImmutableSet.of(ID_FIELD, CHANGE_FIELD, APPROVAL_FIELD);
private final List<SubIndex> indexes;
private final Query query;
@@ -303,8 +312,7 @@
Lists.newArrayListWithCapacity(docs.scoreDocs.length);
for (ScoreDoc sd : docs.scoreDocs) {
Document doc = searchers[sd.shardIndex].doc(sd.doc, FIELDS);
- Number v = doc.getField(ID_FIELD).numericValue();
- result.add(new ChangeData(new Change.Id(v.intValue())));
+ result.add(toChangeData(doc));
}
final List<ChangeData> r = Collections.unmodifiableList(result);
@@ -340,6 +348,30 @@
}
}
+ private static ChangeData toChangeData(Document doc) {
+ BytesRef cb = doc.getBinaryValue(CHANGE_FIELD);
+ if (cb == null) {
+ int id = doc.getField(ID_FIELD).numericValue().intValue();
+ return new ChangeData(new Change.Id(id));
+ }
+
+ Change change = ChangeProtoField.CODEC.decode(
+ cb.bytes, cb.offset, cb.length);
+ ChangeData cd = new ChangeData(change);
+
+ BytesRef[] approvalsBytes = doc.getBinaryValues(APPROVAL_FIELD);
+ if (approvalsBytes != null) {
+ List<PatchSetApproval> approvals =
+ Lists.newArrayListWithCapacity(approvalsBytes.length);
+ for (BytesRef ab : approvalsBytes) {
+ approvals.add(PatchSetApprovalProtoField.CODEC.decode(
+ ab.bytes, ab.offset, ab.length));
+ }
+ cd.setCurrentApprovals(approvals);
+ }
+ return cd;
+ }
+
private Document toDocument(ChangeData cd) throws IOException {
try {
Document result = new Document();
@@ -386,6 +418,10 @@
for (Object value : values) {
doc.add(new TextField(name, (String) value, store));
}
+ } else if (f.getType() == FieldType.STORED_ONLY) {
+ for (Object value : values) {
+ doc.add(new StoredField(name, (byte[]) value));
+ }
} else {
throw QueryBuilder.badFieldType(f.getType());
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 24c33cd..891dadc 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -56,7 +56,7 @@
import com.google.gerrit.server.mail.SmtpEmailSender;
import com.google.gerrit.server.patch.IntraLineWorkerPool;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
-import com.google.gerrit.server.plugins.PluginModule;
+import com.google.gerrit.server.plugins.PluginRestApiModule;
import com.google.gerrit.server.schema.SchemaVersionCheck;
import com.google.gerrit.server.ssh.NoSshKeyCache;
import com.google.gerrit.server.ssh.NoSshModule;
@@ -255,7 +255,7 @@
modules.add(new DefaultCacheFactory.Module());
modules.add(new SmtpEmailSender.Module());
modules.add(new SignedTokenEmailTokenVerifier.Module());
- modules.add(new PluginModule());
+ modules.add(new PluginRestApiModule());
AbstractModule changeIndexModule;
switch (IndexModule.getIndexType(cfgInjector)) {
case LUCENE:
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 0f0b208..ae1ab71 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -70,6 +70,9 @@
@Option(name = "--no-auto-start", usage = "Don't automatically start daemon after init")
private boolean noAutoStart;
+ @Option(name = "--skip-plugins", usage = "Don't install plugin")
+ private boolean skipPlugins = false;
+
@Option(name = "--list-plugins", usage = "List available plugins")
private boolean listPlugins;
@@ -90,22 +93,25 @@
ErrorLogFile.errorOnlyConsole();
final SiteInit init = createSiteInit();
- final List<PluginData> plugins = InitPlugins.listPlugins(init.site);
- ConsoleUI ui = ConsoleUI.getInstance(false);
- verifyInstallPluginList(ui, plugins);
- if (listPlugins) {
- if (!plugins.isEmpty()) {
- ui.message("Available plugins:\n");
- for (PluginData plugin : plugins) {
- ui.message(" * %s version %s\n", plugin.name, plugin.version);
+ if (!skipPlugins) {
+ final List<PluginData> plugins = InitPlugins.listPlugins(init.site);
+ ConsoleUI ui = ConsoleUI.getInstance(false);
+ verifyInstallPluginList(ui, plugins);
+ if (listPlugins) {
+ if (!plugins.isEmpty()) {
+ ui.message("Available plugins:\n");
+ for (PluginData plugin : plugins) {
+ ui.message(" * %s version %s\n", plugin.name, plugin.version);
+ }
+ } else {
+ ui.message("No plugins found.\n");
}
- } else {
- ui.message("No plugins found.\n");
+ return 0;
}
- return 0;
}
init.flags.autoStart = !noAutoStart && init.site.isNew;
+ init.flags.skipPlugins = skipPlugins;
final SiteRun run;
try {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
index 9c81cfa..5ee335d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
@@ -111,7 +111,7 @@
uri = uri + "?" + qs;
}
- if (user.isIdentifiedUser()) {
+ if (user != null && user.isIdentifiedUser()) {
IdentifiedUser who = (IdentifiedUser) user;
if (who.getUserName() != null && !who.getUserName().isEmpty()) {
event.setProperty(P_USER, who.getUserName());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
index adbb03a..267b41a 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
@@ -34,10 +34,14 @@
/** Run the daemon (and open the web UI in a browser) after initialization. */
public boolean autoStart;
+ /** Skip plugins */
+ public boolean skipPlugins;
+
public final FileBasedConfig cfg;
public final FileBasedConfig sec;
public final List<String> installPlugins;
+
@Inject
InitFlags(final SitePaths site,
final @InstallPlugins List<String> installPlugins) throws IOException,
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
index e71f3d1..1c2d024 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
@@ -75,6 +75,10 @@
mkdir(site.data_dir);
for (InitStep step : steps) {
+ if (step instanceof InitPlugins
+ && flags.skipPlugins) {
+ continue;
+ }
step.run();
}
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 7244f49..92c479e 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
@@ -27,7 +27,7 @@
/** Preferred scheme type to download a change. */
public static enum DownloadScheme {
- ANON_GIT, ANON_HTTP, ANON_SSH, HTTP, SSH, REPO_DOWNLOAD, DEFAULT_DOWNLOADS;
+ ANON_GIT, ANON_HTTP, HTTP, SSH, REPO_DOWNLOAD, DEFAULT_DOWNLOADS;
}
/** Preferred method to download a change. */
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index f12fac1..1e20546 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -32,6 +32,7 @@
'//lib:ow2-asm-util',
'//lib:parboiled-core',
'//lib:pegdown',
+ '//lib:protobuf',
'//lib:velocity',
'//lib/antlr:java_runtime',
'//lib/commons:codec',
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
index e4209f0..7ab2564 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.gerrit.common.data.AccountInfo;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
@@ -34,6 +35,7 @@
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
@@ -53,7 +55,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
@@ -195,6 +196,7 @@
private Set<String> emailAddresses;
private GroupMembership effectiveGroups;
private Set<Change.Id> starredChanges;
+ private ResultSet<StarredChange> starredQuery;
private Collection<AccountProjectWatch> notificationFilters;
private CurrentUser realUser;
@@ -296,11 +298,18 @@
if (dbProvider == null) {
throw new OutOfScopeException("Not in request scoped user");
}
- final Set<Change.Id> h = new HashSet<Change.Id>();
+ Set<Change.Id> h = Sets.newHashSet();
try {
- for (final StarredChange sc : dbProvider.get().starredChanges()
- .byAccount(getAccountId())) {
- h.add(sc.getChangeId());
+ if (starredQuery != null) {
+ for (StarredChange sc : starredQuery) {
+ h.add(sc.getChangeId());
+ }
+ starredQuery = null;
+ } else {
+ for (StarredChange sc : dbProvider.get().starredChanges()
+ .byAccount(getAccountId())) {
+ h.add(sc.getChangeId());
+ }
}
} catch (OrmException e) {
log.warn("Cannot query starred by user changes", e);
@@ -310,6 +319,29 @@
return starredChanges;
}
+ public void asyncStarredChanges() {
+ if (starredChanges == null && dbProvider != null) {
+ try {
+ starredQuery =
+ dbProvider.get().starredChanges().byAccount(getAccountId());
+ } catch (OrmException e) {
+ log.warn("Cannot query starred by user changes", e);
+ starredQuery = null;
+ starredChanges = Collections.emptySet();
+ }
+ }
+ }
+
+ public void abortStarredChanges() {
+ if (starredQuery != null) {
+ try {
+ starredQuery.close();
+ } finally {
+ starredQuery = null;
+ }
+ }
+ }
+
@Override
public Collection<AccountProjectWatch> getNotificationFilters() {
if (notificationFilters == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 90ff799..8f16bb4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -25,6 +25,7 @@
import static com.google.gerrit.common.changes.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.common.changes.ListChangesOption.LABELS;
import static com.google.gerrit.common.changes.ListChangesOption.MESSAGES;
+import static com.google.gerrit.common.changes.ListChangesOption.REVIEWED;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
@@ -32,6 +33,7 @@
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
@@ -77,6 +79,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.ssh.SshAdvertisedAddresses;
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;
@@ -90,6 +93,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -97,6 +101,22 @@
public class ChangeJson {
private static final Logger log = LoggerFactory.getLogger(ChangeJson.class);
+ private static final ResultSet<ChangeMessage> NO_MESSAGES =
+ new ResultSet<ChangeMessage>() {
+ @Override
+ public Iterator<ChangeMessage> iterator() {
+ return toList().iterator();
+ }
+
+ @Override
+ public List<ChangeMessage> toList() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void close() {
+ }
+ };
@Singleton
static class Urls {
@@ -140,6 +160,7 @@
private EnumSet<ListChangesOption> options;
private AccountInfo.Loader accountLoader;
private ChangeControl lastControl;
+ private Set<Change.Id> reviewed;
@Inject
ChangeJson(
@@ -214,16 +235,22 @@
public List<List<ChangeInfo>> formatList2(List<List<ChangeData>> in)
throws OrmException {
accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
+ Iterable<ChangeData> all = Iterables.concat(in);
+ ChangeData.ensureChangeLoaded(db, all);
+ if (has(ALL_REVISIONS)) {
+ ChangeData.ensureAllPatchSetsLoaded(db, all);
+ } else {
+ ChangeData.ensureCurrentPatchSetLoaded(db, all);
+ }
+ if (has(REVIEWED)) {
+ ensureReviewedLoaded(all);
+ }
+ ChangeData.ensureCurrentApprovalsLoaded(db, all);
+
List<List<ChangeInfo>> res = Lists.newArrayListWithCapacity(in.size());
+ Map<Change.Id, ChangeInfo> out = Maps.newHashMap();
for (List<ChangeData> changes : in) {
- ChangeData.ensureChangeLoaded(db, changes);
- if (has(ALL_REVISIONS)) {
- ChangeData.ensureAllPatchSetsLoaded(db, changes);
- } else {
- ChangeData.ensureCurrentPatchSetLoaded(db, changes);
- }
- ChangeData.ensureCurrentApprovalsLoaded(db, changes);
- res.add(toChangeInfo(changes));
+ res.add(toChangeInfo(out, changes));
}
accountLoader.fill();
return res;
@@ -233,11 +260,16 @@
return options.contains(option);
}
- private List<ChangeInfo> toChangeInfo(List<ChangeData> changes)
- throws OrmException {
+ private List<ChangeInfo> toChangeInfo(Map<Change.Id, ChangeInfo> out,
+ List<ChangeData> changes) throws OrmException {
List<ChangeInfo> info = Lists.newArrayListWithCapacity(changes.size());
for (ChangeData cd : changes) {
- info.add(toChangeInfo(cd));
+ ChangeInfo i = out.get(cd.getId());
+ if (i == null) {
+ i = toChangeInfo(cd);
+ out.put(cd.getId(), i);
+ }
+ info.add(i);
}
return info;
}
@@ -260,7 +292,9 @@
out.starred = userProvider.get().getStarredChanges().contains(in.getId())
? true
: null;
- out.reviewed = in.getStatus().isOpen() && isChangeReviewed(cd) ? true : null;
+ out.reviewed = in.getStatus().isOpen()
+ && has(REVIEWED)
+ && reviewed.contains(cd.getId()) ? true : null;
out.labels = labelsFor(cd, has(LABELS), has(DETAILED_LABELS));
Collection<PatchSet.Id> limited = cd.getLimitedPatchSets();
@@ -696,39 +730,49 @@
return result;
}
- private boolean isChangeReviewed(ChangeData cd) throws OrmException {
- CurrentUser user = userProvider.get();
- if (user.isIdentifiedUser()) {
- PatchSet currentPatchSet = cd.currentPatchSet(db);
- if (currentPatchSet == null) {
- return false;
- }
-
- List<ChangeMessage> messages =
- db.get().changeMessages().byPatchSet(currentPatchSet.getId()).toList();
-
- if (messages.isEmpty()) {
- return false;
- }
-
- // Sort messages to let the most recent ones at the beginning.
- Collections.sort(messages, new Comparator<ChangeMessage>() {
- @Override
- public int compare(ChangeMessage a, ChangeMessage b) {
- return b.getWrittenOn().compareTo(a.getWrittenOn());
+ private void ensureReviewedLoaded(Iterable<ChangeData> all)
+ throws OrmException {
+ reviewed = Sets.newHashSet();
+ if (userProvider.get().isIdentifiedUser()) {
+ Account.Id self = ((IdentifiedUser) userProvider.get()).getAccountId();
+ for (List<ChangeData> batch : Iterables.partition(all, 50)) {
+ List<ResultSet<ChangeMessage>> m =
+ Lists.newArrayListWithCapacity(batch.size());
+ for (ChangeData cd : batch) {
+ PatchSet.Id ps = cd.change(db).currentPatchSetId();
+ if (ps != null && cd.change(db).getStatus().isOpen()) {
+ m.add(db.get().changeMessages().byPatchSet(ps));
+ } else {
+ m.add(NO_MESSAGES);
+ }
}
- });
-
- Account.Id currentUserId = ((IdentifiedUser) user).getAccountId();
- Account.Id changeOwnerId = cd.change(db).getOwner();
- for (ChangeMessage cm : messages) {
- if (currentUserId.equals(cm.getAuthor())) {
- return true;
- } else if (changeOwnerId.equals(cm.getAuthor())) {
- return false;
+ for (int i = 0; i < m.size(); i++) {
+ if (isChangeReviewed(self, batch.get(i), m.get(i).toList())) {
+ reviewed.add(batch.get(i).getId());
+ }
}
}
}
+ }
+
+ private boolean isChangeReviewed(Account.Id self, ChangeData cd,
+ List<ChangeMessage> msgs) throws OrmException {
+ // Sort messages to keep the most recent ones at the beginning.
+ Collections.sort(msgs, new Comparator<ChangeMessage>() {
+ @Override
+ public int compare(ChangeMessage a, ChangeMessage b) {
+ return b.getWrittenOn().compareTo(a.getWrittenOn());
+ }
+ });
+
+ Account.Id changeOwnerId = cd.change(db).getOwner();
+ for (ChangeMessage cm : msgs) {
+ if (self.equals(cm.getAuthor())) {
+ return true;
+ } else if (changeOwnerId.equals(cm.getAuthor())) {
+ return false;
+ }
+ }
return false;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index 5a6d441..f41d97d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -48,6 +48,7 @@
import com.google.gerrit.server.change.ReviewerJson.ReviewerInfo;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.mail.AddReviewerSender;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
@@ -94,6 +95,7 @@
private final ChangeHooks hooks;
private final AccountCache accountCache;
private final ReviewerJson json;
+ private final ChangeIndexer indexer;
@Inject
PostReviewers(AccountsCollection accounts,
@@ -108,7 +110,8 @@
@GerritServerConfig Config cfg,
ChangeHooks hooks,
AccountCache accountCache,
- ReviewerJson json) {
+ ReviewerJson json,
+ ChangeIndexer indexer) {
this.accounts = accounts;
this.reviewerFactory = reviewerFactory;
this.addReviewerSenderFactory = addReviewerSenderFactory;
@@ -122,6 +125,7 @@
this.hooks = hooks;
this.accountCache = accountCache;
this.json = json;
+ this.indexer = indexer;
}
@Override
@@ -255,6 +259,7 @@
db.rollback();
}
+ indexer.index(rsrc.getChange());
accountLoaderFactory.create(true).fill(result.reviewers);
postAdd(rsrc.getChange(), result);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java
new file mode 100644
index 0000000..f9e9949
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthModule.java
@@ -0,0 +1,54 @@
+// 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.git;
+
+package com.google.gerrit.server.config;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gerrit.server.account.DefaultRealm;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.auth.AuthBackend;
+import com.google.gerrit.server.auth.InternalAuthBackend;
+import com.google.gerrit.server.auth.ldap.LdapModule;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+
+public class AuthModule extends AbstractModule {
+ private final AuthType loginType;
+
+ @Inject
+ AuthModule(AuthConfig authConfig) {
+ loginType = authConfig.getAuthType();
+ }
+
+ @Override
+ protected void configure() {
+ switch (loginType) {
+ case HTTP_LDAP:
+ case LDAP:
+ case LDAP_BIND:
+ case CLIENT_SSL_CERT_LDAP:
+ install(new LdapModule());
+ break;
+
+ case CUSTOM_EXTENSION:
+ break;
+
+ default:
+ bind(Realm.class).to(DefaultRealm.class);
+ DynamicSet.bind(binder(), AuthBackend.class).to(InternalAuthBackend.class);
+ break;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 66ee72b..50b8075 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -21,11 +21,12 @@
import com.google.gerrit.common.ChangeListener;
import com.google.gerrit.extensions.config.CapabilityDefinition;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
+import com.google.gerrit.extensions.events.ProjectDeletedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.rules.PrologModule;
import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.AnonymousUser;
@@ -46,7 +47,6 @@
import com.google.gerrit.server.account.AccountVisibilityProvider;
import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.account.ChangeUserName;
-import com.google.gerrit.server.account.DefaultRealm;
import com.google.gerrit.server.account.EmailExpander;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupCacheImpl;
@@ -59,12 +59,9 @@
import com.google.gerrit.server.account.InternalGroupBackend;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.server.account.PerformRenameGroup;
-import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.account.UniversalGroupBackend;
import com.google.gerrit.server.auth.AuthBackend;
-import com.google.gerrit.server.auth.InternalAuthBackend;
import com.google.gerrit.server.auth.UniversalAuthBackend;
-import com.google.gerrit.server.auth.ldap.LdapModule;
import com.google.gerrit.server.avatar.AvatarProvider;
import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.events.EventFactory;
@@ -118,45 +115,27 @@
import com.google.inject.TypeLiteral;
import org.apache.velocity.runtime.RuntimeInstance;
-import org.eclipse.jgit.lib.Config;
import java.util.List;
/** Starts global state with standard dependencies. */
public class GerritGlobalModule extends FactoryModule {
- private final AuthType loginType;
+ private final AuthModule authModule;
@Inject
- GerritGlobalModule(final AuthConfig authConfig,
- @GerritServerConfig final Config config) {
- loginType = authConfig.getAuthType();
+ GerritGlobalModule(AuthModule authModule) {
+ this.authModule = authModule;
}
@Override
protected void configure() {
- switch (loginType) {
- case HTTP_LDAP:
- case LDAP:
- case LDAP_BIND:
- case CLIENT_SSL_CERT_LDAP:
- install(new LdapModule());
- break;
-
- case CUSTOM_EXTENSION:
- break;
-
- default:
- bind(Realm.class).to(DefaultRealm.class);
- DynamicSet.bind(binder(), AuthBackend.class).to(InternalAuthBackend.class);
- break;
- }
-
bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in(
SINGLETON);
bind(IdGenerator.class);
bind(RulesCache.class);
+ install(authModule);
install(AccountByEmailCacheImpl.module());
install(AccountCacheImpl.module());
install(GroupCacheImpl.module());
@@ -253,11 +232,13 @@
DynamicMap.mapOf(binder(), CapabilityDefinition.class);
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
+ DynamicSet.setOf(binder(), ProjectDeletedListener.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ChangeCache.class);
DynamicSet.setOf(binder(), ChangeListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), MergeValidationListener.class);
DynamicItem.itemOf(binder(), AvatarProvider.class);
+ DynamicSet.setOf(binder(), LifecycleListener.class);
bind(AnonymousUser.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
index 49c90bd..04fccc2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/GitReferenceUpdated.java
@@ -22,11 +22,16 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
public class GitReferenceUpdated {
+ private static final Logger log = LoggerFactory
+ .getLogger(GitReferenceUpdated.class);
+
public static final GitReferenceUpdated DISABLED = new GitReferenceUpdated(
Collections.<GitReferenceUpdatedListener> emptyList());
@@ -52,7 +57,11 @@
ObjectId n = newObjectId != null ? newObjectId : ObjectId.zeroId();
Event event = new Event(project, ref, o.name(), n.name());
for (GitReferenceUpdatedListener l : listeners) {
- l.onGitReferenceUpdated(event);
+ try {
+ l.onGitReferenceUpdated(event);
+ } catch (RuntimeException e) {
+ log.warn("Failure in GitReferenceUpdatedListener", e);
+ }
}
}
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 e1d8627..f09015b 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
@@ -15,7 +15,7 @@
package com.google.gerrit.server.git;
import static com.google.gerrit.server.git.MergeUtil.getSubmitter;
-import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -38,6 +38,7 @@
import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
@@ -56,7 +57,6 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmConcurrencyException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -89,6 +89,8 @@
import java.util.Map;
import java.util.Set;
+import javax.annotation.Nullable;
+
/**
* Merges changes in submission order into a single branch.
* <p>
@@ -117,8 +119,8 @@
private static final long LOCK_FAILURE_RETRY_DELAY =
MILLISECONDS.convert(15, SECONDS);
- private static final long DUPLICATE_MESSAGE_INTERVAL =
- MILLISECONDS.convert(1, DAYS);
+ private static final long MAX_SUBMIT_WINDOW =
+ MILLISECONDS.convert(12, HOURS);
private final GitRepositoryManager repoManager;
private final SchemaFactory<ReviewDb> schemaFactory;
@@ -211,7 +213,7 @@
}
}
- public void merge() throws MergeException, NoSuchProjectException {
+ public void merge() throws MergeException {
setDestProject();
try {
openSchema();
@@ -270,6 +272,11 @@
message(commit.change, capable.getMessage()), false);
}
}
+ } catch (NoSuchProjectException noProject) {
+ log.warn(String.format(
+ "Project %s no longer exists, abandoning open changes",
+ destBranch.getParentKey().get()));
+ abandonAllOpenChanges();
} catch (OrmException e) {
throw new MergeException("Cannot query the database", e);
} finally {
@@ -343,13 +350,12 @@
canMergeFlag, getAlreadyAccepted(branchTip), destBranch);
}
- private void openRepository() throws MergeException {
+ private void openRepository() throws MergeException, NoSuchProjectException {
final Project.NameKey name = destBranch.getParentKey();
try {
repo = repoManager.openRepository(name);
- } catch (RepositoryNotFoundException notGit) {
- final String m = "Repository \"" + name.get() + "\" unknown.";
- throw new MergeException(m, notGit);
+ } catch (RepositoryNotFoundException notFound) {
+ throw new NoSuchProjectException(name, notFound);
} catch (IOException err) {
final String m = "Error opening repository \"" + name.get() + '"';
throw new MergeException(m, err);
@@ -667,7 +673,6 @@
} catch (OrmException err) {
log.warn("Error updating change status for " + c.getId(), err);
}
- indexer.index(c);
}
}
@@ -811,6 +816,7 @@
} finally {
db.rollback();
}
+ indexer.index(c);
}
private void setMergedPatchSet(Change.Id changeId, final PatchSet.Id merged)
@@ -932,61 +938,41 @@
sendMergeFail(c, msg, true);
}
- private boolean isDuplicate(ChangeMessage msg) {
+ private enum RetryStatus {
+ UNSUBMIT, RETRY_NO_MESSAGE, RETRY_ADD_MESSAGE;
+ }
+
+ private RetryStatus getRetryStatus(
+ @Nullable PatchSetApproval submitter,
+ ChangeMessage msg) {
+ if (submitter != null
+ && System.currentTimeMillis() - submitter.getGranted().getTime()
+ > MAX_SUBMIT_WINDOW) {
+ return RetryStatus.UNSUBMIT;
+ }
+
try {
ChangeMessage last = Iterables.getLast(db.changeMessages().byChange(
msg.getPatchSetId().getParentKey()), null);
if (last != null) {
- long lastMs = last.getWrittenOn().getTime();
- long msgMs = msg.getWrittenOn().getTime();
if (Objects.equal(last.getAuthor(), msg.getAuthor())
- && Objects.equal(last.getMessage(), msg.getMessage())
- && msgMs - lastMs < DUPLICATE_MESSAGE_INTERVAL) {
- return true;
+ && Objects.equal(last.getMessage(), msg.getMessage())) {
+ long lastMs = last.getWrittenOn().getTime();
+ long msgMs = msg.getWrittenOn().getTime();
+ return msgMs - lastMs > MAX_SUBMIT_WINDOW
+ ? RetryStatus.UNSUBMIT
+ : RetryStatus.RETRY_NO_MESSAGE;
}
}
+ return RetryStatus.RETRY_ADD_MESSAGE;
} catch (OrmException err) {
- log.warn("Cannot check previous merge failure message", err);
+ log.warn("Cannot check previous merge failure, unsubmitting", err);
+ return RetryStatus.UNSUBMIT;
}
- return false;
}
private void sendMergeFail(final Change c, final ChangeMessage msg,
- final boolean makeNew) {
- if (isDuplicate(msg)) {
- return;
- }
-
- try {
- db.changeMessages().insert(Collections.singleton(msg));
- } catch (OrmException err) {
- log.warn("Cannot record merge failure message", err);
- }
-
- if (makeNew) {
- try {
- db.changes().atomicUpdate(c.getId(), new AtomicUpdate<Change>() {
- @Override
- public Change update(Change c) {
- if (c.getStatus().isOpen()) {
- c.setStatus(Change.Status.NEW);
- ChangeUtil.updated(c);
- }
- return c;
- }
- });
- } catch (OrmConcurrencyException err) {
- } catch (OrmException err) {
- log.warn("Cannot update change status", err);
- }
- } else {
- try {
- ChangeUtil.touch(c, db);
- } catch (OrmException err) {
- log.warn("Cannot update change timestamp", err);
- }
- }
-
+ boolean makeNew) {
PatchSetApproval submitter = null;
try {
submitter = getSubmitter(db, c.currentPatchSetId());
@@ -994,6 +980,43 @@
log.error("Cannot get submitter", e);
}
+ if (!makeNew) {
+ RetryStatus retryStatus = getRetryStatus(submitter, msg);
+ if (retryStatus == RetryStatus.RETRY_NO_MESSAGE) {
+ return;
+ } else if (retryStatus == RetryStatus.UNSUBMIT) {
+ makeNew = true;
+ }
+ }
+
+ final boolean setStatusNew = makeNew;
+ try {
+ db.changes().beginTransaction(c.getId());
+ try {
+ Change change = db.changes().atomicUpdate(
+ c.getId(),
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change c) {
+ if (c.getStatus().isOpen()) {
+ if (setStatusNew) {
+ c.setStatus(Change.Status.NEW);
+ }
+ ChangeUtil.updated(c);
+ }
+ return c;
+ }
+ });
+ db.changeMessages().insert(Collections.singleton(msg));
+ db.commit();
+ indexer.index(change);
+ } finally {
+ db.rollback();
+ }
+ } catch (OrmException err) {
+ log.warn("Cannot record merge failure message", err);
+ }
+
final PatchSetApproval from = submitter;
workQueue.getDefaultQueue()
.submit(requestScopePropagator.wrap(new Runnable() {
@@ -1041,4 +1064,53 @@
}
}
}
+
+ private void abandonAllOpenChanges() {
+ try {
+ openSchema();
+ for (Change c : db.changes().byProjectOpenAll(destBranch.getParentKey())) {
+ abandonOneChange(c);
+ }
+ db.close();
+ db = null;
+ } catch (OrmException e) {
+ log.warn(String.format(
+ "Cannot abandon changes for deleted project %s",
+ destBranch.getParentKey().get()), e);
+ }
+ }
+
+ private void abandonOneChange(Change change) throws OrmException {
+ db.changes().beginTransaction(change.getId());
+ try {
+ change = db.changes().atomicUpdate(
+ change.getId(),
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change change) {
+ if (change.getStatus().isOpen()) {
+ change.setStatus(Change.Status.ABANDONED);
+ return change;
+ }
+ return null;
+ }
+ });
+ if (change != null) {
+ ChangeMessage msg = new ChangeMessage(
+ new ChangeMessage.Key(
+ change.getId(),
+ ChangeUtil.messageUUID(db)),
+ null,
+ change.getLastUpdatedOn(),
+ change.currentPatchSetId());
+ msg.setMessage("Project was deleted.");
+ db.changeMessages().insert(Collections.singleton(msg));
+ new ApprovalsUtil(db).syncChangeStatus(change);
+ db.commit();
+ indexer.index(change);
+ }
+ } finally {
+ db.rollback();
+ }
+ }
}
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 86b7e0d..5245957 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
@@ -404,7 +404,8 @@
}
});
advHooks.add(rp.getAdvertiseRefsHook());
- advHooks.add(new ReceiveCommitsAdvertiseRefsHook());
+ advHooks.add(new ReceiveCommitsAdvertiseRefsHook(
+ db, projectControl.getProject().getNameKey()));
rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
}
@@ -439,80 +440,6 @@
return rp;
}
- /** Scan part of history and include it in the advertisement. */
- public void advertiseHistory() {
- Set<ObjectId> toInclude = new HashSet<ObjectId>();
-
- // Advertise some recent open changes, in case a commit is based one.
- try {
- Set<PatchSet.Id> toGet = new HashSet<PatchSet.Id>();
- for (Change change : db.changes()
- .byProjectOpenNext(project.getNameKey(), "z", 32)) {
- PatchSet.Id id = change.currentPatchSetId();
- if (id != null) {
- toGet.add(id);
- }
- }
- for (PatchSet ps : db.patchSets().get(toGet)) {
- if (ps.getRevision() != null && ps.getRevision().get() != null) {
- toInclude.add(ObjectId.fromString(ps.getRevision().get()));
- }
- }
- } catch (OrmException err) {
- log.error("Cannot list open changes of " + project.getNameKey(), err);
- }
-
- // Size of an additional ".have" line.
- final int haveLineLen = 4 + Constants.OBJECT_ID_STRING_LENGTH + 1 + 5 + 1;
-
- // Maximum number of bytes to "waste" in the advertisement with
- // a peek at this repository's current reachable history.
- final int maxExtraSize = 8192;
-
- // Number of recent commits to advertise immediately, hoping to
- // show a client a nearby merge base.
- final int base = 64;
-
- // Number of commits to skip once base has already been shown.
- final int step = 16;
-
- // Total number of commits to extract from the history.
- final int max = maxExtraSize / haveLineLen;
-
- // Scan history until the advertisement is full.
- Set<ObjectId> alreadySending = rp.getAdvertisedObjects();
- RevWalk rw = rp.getRevWalk();
- for (ObjectId haveId : alreadySending) {
- try {
- rw.markStart(rw.parseCommit(haveId));
- } catch (IOException badCommit) {
- continue;
- }
- }
-
- int stepCnt = 0;
- RevCommit c;
- try {
- while ((c = rw.next()) != null && toInclude.size() < max) {
- if (alreadySending.contains(c)) {
- } else if (toInclude.contains(c)) {
- } else if (c.getParentCount() > 1) {
- } else if (toInclude.size() < base) {
- toInclude.add(c);
- } else {
- stepCnt = ++stepCnt % step;
- if (stepCnt == 0) {
- toInclude.add(c);
- }
- }
- }
- } catch (IOException err) {
- log.error("Error trying to advertise history on " + project.getNameKey(), err);
- }
- rw.reset();
- rp.getAdvertisedObjects().addAll(toInclude);
- }
-
/** Determine if the user can upload commits. */
public Capable canUpload() {
Capable result = projectControl.canPushToAtLeastOneRef();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java
index 0eb9b61..ace4e90 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java
@@ -15,17 +15,43 @@
package com.google.gerrit.server.git;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.util.MagicBranch;
+import com.google.gwtorm.server.OrmException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.AdvertiseRefsHook;
import org.eclipse.jgit.transport.BaseReceivePack;
import org.eclipse.jgit.transport.UploadPack;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.util.Map;
+import java.util.Set;
/** Exposes only the non refs/changes/ reference names. */
public class ReceiveCommitsAdvertiseRefsHook implements AdvertiseRefsHook {
+ private static final Logger log = LoggerFactory
+ .getLogger(ReceiveCommitsAdvertiseRefsHook.class);
+
+ private final ReviewDb db;
+ private final Project.NameKey projectName;
+
+ public ReceiveCommitsAdvertiseRefsHook(ReviewDb db,
+ Project.NameKey projectName) {
+ this.db = db;
+ this.projectName = projectName;
+ }
+
@Override
public void advertiseRefs(UploadPack us) {
throw new UnsupportedOperationException(
@@ -45,7 +71,84 @@
r.put(name, e.getValue());
}
}
- rp.setAdvertisedRefs(r, rp.getAdvertisedObjects());
+ rp.setAdvertisedRefs(r, advertiseHistory(r.values(), rp));
+ }
+
+ private Set<ObjectId> advertiseHistory(
+ Iterable<Ref> sending,
+ BaseReceivePack rp) {
+ Set<ObjectId> toInclude = Sets.newHashSet();
+
+ // Advertise some recent open changes, in case a commit is based one.
+ try {
+ Set<PatchSet.Id> toGet = Sets.newHashSetWithExpectedSize(32);
+ for (Change c : db.changes().byProjectOpenNext(projectName, "z", 32)) {
+ PatchSet.Id id = c.currentPatchSetId();
+ if (id != null) {
+ toGet.add(id);
+ }
+ }
+ for (PatchSet ps : db.patchSets().get(toGet)) {
+ if (ps.getRevision() != null && ps.getRevision().get() != null) {
+ toInclude.add(ObjectId.fromString(ps.getRevision().get()));
+ }
+ }
+ } catch (OrmException err) {
+ log.error("Cannot list open changes of " + projectName, err);
+ }
+
+ // Size of an additional ".have" line.
+ final int haveLineLen = 4 + Constants.OBJECT_ID_STRING_LENGTH + 1 + 5 + 1;
+
+ // Maximum number of bytes to "waste" in the advertisement with
+ // a peek at this repository's current reachable history.
+ final int maxExtraSize = 8192;
+
+ // Number of recent commits to advertise immediately, hoping to
+ // show a client a nearby merge base.
+ final int base = 64;
+
+ // Number of commits to skip once base has already been shown.
+ final int step = 16;
+
+ // Total number of commits to extract from the history.
+ final int max = maxExtraSize / haveLineLen;
+
+ // Scan history until the advertisement is full.
+ Set<ObjectId> alreadySending = Sets.newHashSet();
+ RevWalk rw = rp.getRevWalk();
+ for (Ref ref : sending) {
+ try {
+ if (ref.getObjectId() != null) {
+ alreadySending.add(ref.getObjectId());
+ rw.markStart(rw.parseCommit(ref.getObjectId()));
+ }
+ } catch (IOException badCommit) {
+ continue;
+ }
+ }
+
+ int stepCnt = 0;
+ RevCommit c;
+ try {
+ while ((c = rw.next()) != null && toInclude.size() < max) {
+ if (alreadySending.contains(c)) {
+ } else if (toInclude.contains(c)) {
+ } else if (c.getParentCount() > 1) {
+ } else if (toInclude.size() < base) {
+ toInclude.add(c);
+ } else {
+ stepCnt = ++stepCnt % step;
+ if (stepCnt == 0) {
+ toInclude.add(c);
+ }
+ }
+ }
+ } catch (IOException err) {
+ log.error("Error trying to advertise history on " + projectName, err);
+ }
+ rw.reset();
+ return toInclude;
}
private static boolean skip(String name) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index d62df95..20b61e7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -14,8 +14,10 @@
package com.google.gerrit.server.index;
+import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -25,10 +27,16 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
+import com.google.gwtorm.protobuf.CodecFactory;
+import com.google.gwtorm.protobuf.ProtobufCodec;
import com.google.gwtorm.server.OrmException;
+import com.google.protobuf.CodedOutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.List;
import java.util.Set;
/**
@@ -233,6 +241,47 @@
}
};
+ public static class ChangeProtoField extends FieldDef.Single<ChangeData, byte[]> {
+ public static final ProtobufCodec<Change> CODEC =
+ CodecFactory.encoder(Change.class);
+
+ private ChangeProtoField() {
+ super("_change", FieldType.STORED_ONLY, true);
+ }
+
+ @Override
+ public byte[] get(ChangeData input, FieldDef.FillArgs args)
+ throws OrmException {
+ return CODEC.encodeToByteArray(input.change(args.db));
+ }
+ }
+
+ /** Serialized change object, used for pre-populating results. */
+ public static final ChangeProtoField CHANGE = new ChangeProtoField();
+
+ public static class PatchSetApprovalProtoField
+ extends FieldDef.Repeatable<ChangeData, byte[]> {
+ public static final ProtobufCodec<PatchSetApproval> CODEC =
+ CodecFactory.encoder(PatchSetApproval.class);
+
+ private PatchSetApprovalProtoField() {
+ super("_approval", FieldType.STORED_ONLY, true);
+ }
+
+ @Override
+ public Iterable<byte[]> get(ChangeData input, FillArgs args)
+ throws OrmException {
+ return toProtos(CODEC, input.currentApprovals(args.db));
+ }
+ }
+
+ /**
+ * Serialized approvals for the current patch set, used for pre-populating
+ * results.
+ */
+ public static final PatchSetApprovalProtoField APPROVAL =
+ new PatchSetApprovalProtoField();
+
public static String formatLabel(String label, int value) {
return formatLabel(label, value, null);
}
@@ -273,4 +322,22 @@
return r;
}
};
+
+ private static <T> List<byte[]> toProtos(ProtobufCodec<T> codec, Collection<T> objs)
+ throws OrmException {
+ List<byte[]> result = Lists.newArrayListWithCapacity(objs.size());
+ ByteArrayOutputStream out = new ByteArrayOutputStream(256);
+ try {
+ for (T obj : objs) {
+ out.reset();
+ CodedOutputStream cos = CodedOutputStream.newInstance(out);
+ codec.encode(obj, cos);
+ cos.flush();
+ result.add(out.toByteArray());
+ }
+ } catch (IOException e) {
+ throw new OrmException(e);
+ }
+ return result;
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
index 5b3cdad..1ed6c47 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -49,6 +49,28 @@
ChangeField.COMMIT_MESSAGE,
ChangeField.COMMENT);
+ @SuppressWarnings({"unchecked", "deprecation"})
+ static final Schema<ChangeData> V2 = release(
+ ChangeField.LEGACY_ID,
+ ChangeField.ID,
+ ChangeField.STATUS,
+ ChangeField.PROJECT,
+ ChangeField.REF,
+ ChangeField.TOPIC,
+ ChangeField.UPDATED,
+ ChangeField.SORTKEY,
+ ChangeField.FILE,
+ ChangeField.OWNER,
+ ChangeField.REVIEWER,
+ ChangeField.COMMIT,
+ ChangeField.TR,
+ ChangeField.LABEL,
+ ChangeField.REVIEWED,
+ ChangeField.COMMIT_MESSAGE,
+ ChangeField.COMMENT,
+ ChangeField.CHANGE,
+ ChangeField.APPROVAL);
+
private static Schema<ChangeData> release(FieldDef<ChangeData, ?>... fields) {
return new Schema<ChangeData>(true, Arrays.asList(fields));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
index ded98ea..a3247b9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
@@ -43,6 +43,10 @@
public static final FieldType<String> FULL_TEXT =
new FieldType<String>("FULL_TEXT");
+ /** A field that is only stored as raw bytes and cannot be queried. */
+ public static final FieldType<byte[]> STORED_ONLY =
+ new FieldType<byte[]>("STORED_ONLY");
+
private final String name;
private FieldType(String name) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
index dd94577..ccd6c89 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexedChangeQuery.java
@@ -126,7 +126,10 @@
@Override
public int getCost() {
- return pred.getCost();
+ // Index queries are assumed to be cheaper than any other type of query, so
+ // so try to make sure they get picked. Note that pred's cost may be higher
+ // because it doesn't know whether it's being used in an index query or not.
+ return 0;
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java
index 3e50283..7798028 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java
@@ -30,7 +30,6 @@
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
-import com.google.gerrit.server.query.change.ChangeQueryRewriter;
import com.google.gerrit.server.ssh.SshAdvertisedAddresses;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -60,7 +59,6 @@
final List<String> sshAddresses;
final ChangeQueryBuilder.Factory queryBuilder;
- final Provider<ChangeQueryRewriter> queryRewriter;
final Provider<ReviewDb> db;
final RuntimeInstance velocityRuntime;
final EmailSettings settings;
@@ -78,7 +76,7 @@
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
AllProjectsName allProjectsName,
ChangeQueryBuilder.Factory queryBuilder,
- Provider<ChangeQueryRewriter> queryRewriter, Provider<ReviewDb> db,
+ Provider<ReviewDb> db,
RuntimeInstance velocityRuntime,
EmailSettings settings,
@SshAdvertisedAddresses List<String> sshAddresses) {
@@ -98,7 +96,6 @@
this.urlProvider = urlProvider;
this.allProjectsName = allProjectsName;
this.queryBuilder = queryBuilder;
- this.queryRewriter = queryRewriter;
this.db = db;
this.velocityRuntime = velocityRuntime;
this.settings = settings;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
index 69cc947..757562c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ProjectWatch.java
@@ -88,9 +88,9 @@
add(matching, nc, state.getProject().getNameKey());
} catch (QueryParseException e) {
log.warn(String.format(
- "Project %s has invalid notify %s filter \"%s\": %s",
+ "Project %s has invalid notify %s filter \"%s\"",
state.getProject().getName(), nc.getName(),
- nc.getFilter(), e.getMessage()));
+ nc.getFilter()), e);
}
}
}
@@ -209,7 +209,6 @@
} else {
p = Predicate.and(filterPredicate, p);
}
- p = args.queryRewriter.get().rewrite(p);
}
return p == null ? true : p.match(changeData);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java
index 4cdafb3..bd1c3c8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginModule.java
@@ -14,21 +14,17 @@
package com.google.gerrit.server.plugins;
-import static com.google.gerrit.server.plugins.PluginResource.PLUGIN_KIND;
-
-import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.inject.AbstractModule;
-public class PluginModule extends RestApiModule {
+public class PluginModule extends AbstractModule {
@Override
protected void configure() {
bind(ServerInformationImpl.class);
bind(ServerInformation.class).to(ServerInformationImpl.class);
bind(PluginCleanerTask.class);
- bind(PluginsCollection.class);
bind(PluginGuiceEnvironment.class);
bind(PluginLoader.class);
bind(CopyConfigModule.class);
@@ -38,13 +34,5 @@
listener().to(PluginLoader.class);
}
});
-
- DynamicMap.mapOf(binder(), PLUGIN_KIND);
- put(PLUGIN_KIND).to(InstallPlugin.Overwrite.class);
- delete(PLUGIN_KIND).to(DisablePlugin.class);
- get(PLUGIN_KIND, "status").to(GetStatus.class);
- post(PLUGIN_KIND, "disable").to(DisablePlugin.class);
- post(PLUGIN_KIND, "enable").to(EnablePlugin.class);
- post(PLUGIN_KIND, "reload").to(ReloadPlugin.class);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginRestApiModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginRestApiModule.java
new file mode 100644
index 0000000..6b52a59
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginRestApiModule.java
@@ -0,0 +1,35 @@
+// 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.plugins;
+
+import static com.google.gerrit.server.plugins.PluginResource.PLUGIN_KIND;
+
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.RestApiModule;
+
+public class PluginRestApiModule extends RestApiModule {
+ @Override
+ protected void configure() {
+ install(new PluginModule());
+ bind(PluginsCollection.class);
+ DynamicMap.mapOf(binder(), PLUGIN_KIND);
+ put(PLUGIN_KIND).to(InstallPlugin.Overwrite.class);
+ delete(PLUGIN_KIND).to(DisablePlugin.class);
+ get(PLUGIN_KIND, "status").to(GetStatus.class);
+ post(PLUGIN_KIND, "disable").to(DisablePlugin.class);
+ post(PLUGIN_KIND, "enable").to(EnablePlugin.class);
+ post(PLUGIN_KIND, "reload").to(ReloadPlugin.class);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java
index 29317440..86410af 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerformCreateProject.java
@@ -118,7 +118,11 @@
}
};
for (NewProjectCreatedListener l : createdListener) {
- l.onNewProjectCreated(event);
+ try {
+ l.onNewProjectCreated(event);
+ } catch (RuntimeException e) {
+ log.warn("Failure in NewProjectCreatedListener", e);
+ }
}
final RefUpdate u = repo.updateRef(Constants.HEAD);
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 0555fc7..5282b49 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
@@ -162,12 +162,15 @@
}
private ChangeDataSource source() {
+ int minCost = Integer.MAX_VALUE;
+ Predicate<ChangeData> s = null;
for (Predicate<ChangeData> p : getChildren()) {
- if (p instanceof ChangeDataSource) {
- return (ChangeDataSource) p;
+ if (p instanceof ChangeDataSource && p.getCost() < minCost) {
+ s = p;
+ minCost = p.getCost();
}
}
- return null;
+ return (ChangeDataSource) s;
}
@Override
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 ff44c73..4522fe6 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
@@ -71,8 +71,8 @@
return SORT_APPROVALS.sortedCopy(approvals);
}
- public static void ensureChangeLoaded(
- Provider<ReviewDb> db, List<ChangeData> changes) throws OrmException {
+ public static void ensureChangeLoaded(Provider<ReviewDb> db,
+ Iterable<ChangeData> changes) throws OrmException {
Map<Change.Id, ChangeData> missing = Maps.newHashMap();
for (ChangeData cd : changes) {
if (cd.change == null) {
@@ -87,14 +87,14 @@
}
public static void ensureAllPatchSetsLoaded(Provider<ReviewDb> db,
- List<ChangeData> changes) throws OrmException {
+ Iterable<ChangeData> changes) throws OrmException {
for (ChangeData cd : changes) {
cd.patches(db);
}
}
- public static void ensureCurrentPatchSetLoaded(
- Provider<ReviewDb> db, List<ChangeData> changes) throws OrmException {
+ public static void ensureCurrentPatchSetLoaded(Provider<ReviewDb> db,
+ Iterable<ChangeData> changes) throws OrmException {
Map<PatchSet.Id, ChangeData> missing = Maps.newHashMap();
for (ChangeData cd : changes) {
if (cd.currentPatchSet == null && cd.patches == null) {
@@ -112,8 +112,8 @@
}
}
- public static void ensureCurrentApprovalsLoaded(
- Provider<ReviewDb> db, List<ChangeData> changes) throws OrmException {
+ public static void ensureCurrentApprovalsLoaded(Provider<ReviewDb> db,
+ Iterable<ChangeData> changes) throws OrmException {
List<ResultSet<PatchSetApproval>> pending = Lists.newArrayList();
for (ChangeData cd : changes) {
if (cd.currentApprovals == null && cd.limitedApprovals == null) {
@@ -272,6 +272,10 @@
return change;
}
+ void setChange(Change c) {
+ change = c;
+ }
+
public PatchSet currentPatchSet(Provider<ReviewDb> db) throws OrmException {
if (currentPatchSet == null) {
Change c = change(db);
@@ -307,6 +311,10 @@
return currentApprovals;
}
+ public void setCurrentApprovals(List<PatchSetApproval> approvals) {
+ currentApprovals = approvals;
+ }
+
public String commitMessage(GitRepositoryManager repoManager,
Provider<ReviewDb> db) throws IOException, OrmException {
if (commitMessage == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 5084fff..76d873a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -20,12 +20,15 @@
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import org.kohsuke.args4j.Option;
@@ -39,6 +42,7 @@
public class QueryChanges implements RestReadView<TopLevelResource> {
private final ChangeJson json;
private final QueryProcessor imp;
+ private final Provider<CurrentUser> user;
private boolean reverse;
private EnumSet<ListChangesOption> options;
@@ -80,9 +84,11 @@
@Inject
QueryChanges(ChangeJson json,
QueryProcessor qp,
- ChangeControl.Factory cf) {
+ ChangeControl.Factory cf,
+ Provider<CurrentUser> user) {
this.json = json;
this.imp = qp;
+ this.user = user;
options = EnumSet.noneOf(ListChangesOption.class);
json.setChangeControlFactory(cf);
@@ -127,12 +133,27 @@
throw new QueryParseException("limit of 10 queries");
}
+ IdentifiedUser self = null;
+ try {
+ if (user.get().isIdentifiedUser()) {
+ self = (IdentifiedUser) user.get();
+ self.asyncStarredChanges();
+ }
+ return query0();
+ } finally {
+ if (self != null) {
+ self.abortStarredChanges();
+ }
+ }
+ }
+
+ private List<List<ChangeInfo>> query0() throws OrmException,
+ QueryParseException {
int cnt = queries.size();
BitSet more = new BitSet(cnt);
- List<List<ChangeData>> data = Lists.newArrayListWithCapacity(cnt);
+ List<List<ChangeData>> data = imp.queryChanges(queries);
for (int n = 0; n < cnt; n++) {
- String query = queries.get(n);
- List<ChangeData> changes = imp.queryChanges(query);
+ List<ChangeData> changes = data.get(n);
if (imp.getLimit() > 0 && changes.size() > imp.getLimit()) {
if (reverse) {
changes = changes.subList(1, changes.size());
@@ -141,7 +162,6 @@
}
more.set(n, true);
}
- data.add(changes);
}
List<List<ChangeInfo>> res = json.addOptions(options).formatList2(data);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index 18ebdb1..bdce7f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.query.change;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
@@ -32,6 +33,7 @@
import com.google.gerrit.server.query.QueryParseException;
import com.google.gson.Gson;
import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -52,7 +54,6 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
-import java.util.HashSet;
import java.util.List;
public class QueryProcessor {
@@ -211,47 +212,70 @@
* there are more than {@code limit} matches and suggest to its own caller
* that the query could be retried with {@link #setSortkeyBefore(String)}.
*/
- public List<ChangeData> queryChanges(final String queryString)
+ public List<ChangeData> queryChanges(String queryString)
+ throws OrmException, QueryParseException {
+ return queryChanges(ImmutableList.of(queryString)).get(0);
+ }
+
+ /**
+ * Query for changes that match the query string.
+ * <p>
+ * If a limit was specified using {@link #setLimit(int)} this method may
+ * return up to {@code limit + 1} results, allowing the caller to determine if
+ * there are more than {@code limit} matches and suggest to its own caller
+ * that the query could be retried with {@link #setSortkeyBefore(String)}.
+ */
+ public List<List<ChangeData>> queryChanges(List<String> queries)
throws OrmException, QueryParseException {
final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible();
- Predicate<ChangeData> s = compileQuery(queryString, visibleToMe);
- List<ChangeData> results = new ArrayList<ChangeData>();
- HashSet<Change.Id> want = new HashSet<Change.Id>();
- for (ChangeData d : ((ChangeDataSource) s).read()) {
- if (d.hasChange()) {
- // Checking visibleToMe here should be unnecessary, the
- // query should have already performed it. But we don't
- // want to trust the query rewriter that much yet.
- //
- if (visibleToMe.match(d)) {
- results.add(d);
- }
- } else {
- want.add(d.getId());
+ int cnt = queries.size();
+
+ // Parse and rewrite all queries.
+ List<Integer> limits = Lists.newArrayListWithCapacity(cnt);
+ List<ChangeDataSource> sources = Lists.newArrayListWithCapacity(cnt);
+ for (int i = 0; i < cnt; i++) {
+ Predicate<ChangeData> q = parseQuery(queries.get(i), visibleToMe);
+ Predicate<ChangeData> s = queryRewriter.rewrite(q);
+ if (!(s instanceof ChangeDataSource)) {
+ @SuppressWarnings("unchecked")
+ Predicate<ChangeData> o = Predicate.and(queryBuilder.status_open(), q);
+ q = o;
+ s = queryRewriter.rewrite(q);
}
+ if (!(s instanceof ChangeDataSource)) {
+ throw new QueryParseException("invalid query: " + s);
+ }
+
+ // Don't trust QueryRewriter to have left the visible predicate.
+ AndSource a = new AndSource(db, ImmutableList.of(s, visibleToMe));
+ limits.add(limit(q));
+ sources.add(a);
}
- if (!want.isEmpty()) {
- for (Change c : db.get().changes().get(want)) {
- ChangeData d = new ChangeData(c);
- if (visibleToMe.match(d)) {
- results.add(d);
- }
- }
+ // Run each query asynchronously, if supported.
+ List<ResultSet<ChangeData>> matches = Lists.newArrayListWithCapacity(cnt);
+ for (ChangeDataSource s : sources) {
+ matches.add(s.read());
}
+ sources = null;
- Collections.sort(results, sortkeyAfter != null ? cmpAfter : cmpBefore);
- int limit = limit(s);
- if (results.size() > maxLimit) {
- moreResults = true;
+ List<List<ChangeData>> out = Lists.newArrayListWithCapacity(cnt);
+ for (int i = 0; i < cnt; i++) {
+ List<ChangeData> results = matches.get(i).toList();
+ Collections.sort(results, sortkeyAfter != null ? cmpAfter : cmpBefore);
+ if (results.size() > maxLimit) {
+ moreResults = true;
+ }
+ int limit = limits.get(i);
+ if (limit < results.size()) {
+ results = results.subList(0, limit);
+ }
+ if (sortkeyAfter != null) {
+ Collections.reverse(results);
+ }
+ out.add(results);
}
- if (limit < results.size()) {
- results = results.subList(0, limit);
- }
- if (sortkeyAfter != null) {
- Collections.reverse(results);
- }
- return results;
+ return out;
}
public void query(String queryString) throws IOException {
@@ -378,9 +402,8 @@
}
@SuppressWarnings("unchecked")
- private Predicate<ChangeData> compileQuery(String queryString,
+ private Predicate<ChangeData> parseQuery(String queryString,
final Predicate<ChangeData> visibleToMe) throws QueryParseException {
-
Predicate<ChangeData> q = queryBuilder.parse(queryString);
if (!ChangeQueryBuilder.hasSortKey(q)) {
if (sortkeyBefore != null) {
@@ -391,20 +414,9 @@
q = Predicate.and(q, queryBuilder.sortkey_before("z"));
}
}
- q = Predicate.and(q,
+ return Predicate.and(q,
queryBuilder.limit(limit > 0 ? Math.min(limit, maxLimit) + 1 : maxLimit),
visibleToMe);
-
- Predicate<ChangeData> s = queryRewriter.rewrite(q);
- if (!(s instanceof ChangeDataSource)) {
- s = queryRewriter.rewrite(Predicate.and(queryBuilder.status_open(), q));
- }
-
- if (!(s instanceof ChangeDataSource)) {
- throw new QueryParseException("invalid query: " + s);
- }
-
- return s;
}
private void show(Object data) {
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 5120bfd..8c17407 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
@@ -233,7 +233,7 @@
String userName = "-", accountId = "-";
- if (user.isIdentifiedUser()) {
+ if (user != null && user.isIdentifiedUser()) {
IdentifiedUser u = (IdentifiedUser) user;
userName = u.getAccount().getUserName();
accountId = "a/" + u.getAccountId().toString();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java
index 70a8592..9b19e02 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScope.java
@@ -81,7 +81,7 @@
@Override
public CurrentUser getCurrentUser() {
final CurrentUser user = session.getCurrentUser();
- if (user.isIdentifiedUser()) {
+ if (user != null && user.isIdentifiedUser()) {
IdentifiedUser identifiedUser = userFactory.create(((IdentifiedUser) user).getAccountId());
identifiedUser.setAccessPath(user.getAccessPath());
return identifiedUser;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
index b3aad6f..cd50499 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
@@ -97,7 +97,6 @@
rp.setMaxObjectSizeLimit(config.getEffectiveMaxObjectSizeLimit(
projectControl.getProjectState()));
try {
- receive.advertiseHistory();
rp.receive(in, out, err);
} catch (UnpackException badStream) {
// In case this was caused by the user pushing an object whose size
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
index 09ce920..c0e5d6e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -146,7 +146,7 @@
}
final CurrentUser user = sd.getCurrentUser();
- if (user.isIdentifiedUser()) {
+ if (user != null && user.isIdentifiedUser()) {
IdentifiedUser u = (IdentifiedUser) user;
if (!numeric) {
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index dd41f52..3c70cf6 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -44,7 +44,7 @@
import com.google.gerrit.server.mail.SmtpEmailSender;
import com.google.gerrit.server.patch.IntraLineWorkerPool;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
-import com.google.gerrit.server.plugins.PluginModule;
+import com.google.gerrit.server.plugins.PluginRestApiModule;
import com.google.gerrit.server.schema.DataSourceModule;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.DataSourceType;
@@ -257,7 +257,7 @@
modules.add(new DefaultCacheFactory.Module());
modules.add(new SmtpEmailSender.Module());
modules.add(new SignedTokenEmailTokenVerifier.Module());
- modules.add(new PluginModule());
+ modules.add(new PluginRestApiModule());
AbstractModule changeIndexModule;
switch (IndexModule.getIndexType(cfgInjector)) {
case LUCENE:
diff --git a/lib/BUCK b/lib/BUCK
index 1a2681a..d9f1ecf 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -19,6 +19,7 @@
define_license(name = 'ow2')
define_license(name = 'postgresql')
define_license(name = 'prologcafe')
+define_license(name = 'protobuf')
define_license(name = 'slf4j')
define_license(name = 'DO_NOT_DISTRIBUTE')
@@ -28,6 +29,7 @@
bin_sha1 = 'ee3b316a023f1422dd4b6654a3d51d0e5690809c',
src_sha1 = 'a145bde4cc87a4ff4cec283880e2a03be32cc868',
license = 'Apache2.0',
+ deps = [':protobuf'],
repository = GERRIT,
)
@@ -205,6 +207,15 @@
)
maven_jar(
+ name = 'protobuf',
+ # Must match version in gwtorm/pom.xml.
+ id = 'com.google.protobuf:protobuf-java:2.4.1',
+ bin_sha1 = '0c589509ec6fd86d5d2fda37e07c08538235d3b9',
+ src_sha1 = 'e406f69360f2a89cb4aa724ed996a1c5599af383',
+ license = 'protobuf',
+)
+
+maven_jar(
name = 'junit',
id = 'junit:junit:4.11',
sha1 = '4e031bb61df09069aeb2bffb4019e7a5034a4ee0',
diff --git a/lib/LICENSE-protobuf b/lib/LICENSE-protobuf
new file mode 100644
index 0000000..705db57
--- /dev/null
+++ b/lib/LICENSE-protobuf
@@ -0,0 +1,33 @@
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it. This code is not
+standalone and requires a support library to be linked with it. This
+support library is itself covered by the above license.
diff --git a/website/releases/index.html b/website/releases/index.html
index 27d4fb9..0ca0cb4 100644
--- a/website/releases/index.html
+++ b/website/releases/index.html
@@ -31,7 +31,7 @@
<h1>Gerrit Code Review - Releases</h1>
<a href="http://code.google.com/p/gerrit">
-<img id="diffy_logo" src="https://gerrit-review.googlesource.com/static/logo.png">
+ <img id="diffy_logo" src="https://gerrit-review.googlesource.com/static/diffy1.cache.png">
</a>
<div id='download_container'>